Contribuir & Desenvolvimento¶
Obrigado pelo interesse em vendus. Este projeto visa qualidade de produção — as regras
e convenções estão resumidas abaixo; lê-as antes de qualquer mudança substancial.
Setup¶
git clone https://github.com/bilouro/vendus-python.git
cd vendus-python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
Quality gate (tem de passar antes de cada PR)¶
ruff check . # lint
ruff format --check . # formatação
mypy src/ # type checking estrito
pytest # testes unitários + cobertura (≥85% obrigatória)
Se mexeste na documentação, constrói-a em modo estrito:
Testes — duas camadas¶
O SDK é validado em duas camadas, e a segunda é o que o mantém honesto contra a API real.
1. Testes unitários (cada commit)¶
tests/unit/, com mock viarespx— nunca tocam na rede.- Asseram o body exato enviado, não só o valor de retorno. É assim que se apanham bugs de nome/forma de campos.
- Gate de cobertura: ≥85% (
pytestfalha abaixo disso).
2. Testes de integração ao vivo (a pedido)¶
tests/integration/, marcados@pytest.mark.integratione excluídos da run normal dopytest. Atingem a API real da Vendus.- Fazem auto-skip a menos que
VENDUS_API_KEYeVENDUS_REGISTER_IDestejam definidas, por isso nunca falham em CI nem numa máquina sem credenciais. - Correm em modo teste (
mode=tests) onde possível — documentos não-fiscais que a Vendus nunca comunica à AT.
export VENDUS_API_KEY=... # API key de uma conta de teste/demo
export VENDUS_REGISTER_ID=... # ver abaixo
pytest -m integration --no-cov # --no-cov: uma run parcial dispararia o gate de cobertura
Como obter os ids necessários:
register_id—client.documents.list_registers()(ou o backoffice da Vendus). Cada caixa tem umide ummode.- ids dos métodos de pagamento (para uma Fatura-Recibo) —
client.documents.list_payment_methods().
Usa uma conta de teste/demo dedicada
A Vendus não tem host de sandbox separado; "testar" é um modo de teste ao nível do documento. Contas novas têm a caixa em modo teste por defeito, por isso os testes ao vivo emitem documentos não-fiscais. Ver Configuração → Testes. Documentos de teste não são consultáveis nem canceláveis, por isso um teste ao vivo não se limpa a si próprio — são inertes. Tudo o que criares em modo real é um registo fiscal permanente; reverte uma fatura com uma nota de crédito (ver abaixo).
A disciplina de live-validation¶
A documentação de referência da Vendus nem sempre está completa — valida a forma do
body contra a API real antes de afirmar que uma operação funciona. Vários bugs reais só
foram apanhados assim (o create_invoice do SDK nunca funcionou até a validação ao vivo
corrigir os campos das linhas). Os factos verificados ao vivo que um contribuidor tem de
respeitar:
- Linhas enviam
tax_id(um códigoTaxCategory: NOR/INT/RED/ISE/OUT), nãotax_rate;discount_percentage, nãodiscount;idpara uma linha de produto, nãoproduct_id. Nomes errados → a API devolveP001. - Fatura-Recibo (FR) exige
payments—[Payment(method_id=..., amount=...)], com ids de método específicos da conta (delist_payment_methods()). - Notas de crédito (NC) creditam um original real: o SDK faz GET dele e referencia
cada linha via
reference_document(número + linha) + o id da linha original. Uma NC não pode ser criada em modo teste (o original de teste não é consultável). - FT / FR / NC não podem ser canceladas — o
cancel()recusa-as; reverte uma fatura com uma nota de crédito. - O
modeherda o modo da caixa (teste em contas novas). DefineVendusClient(default_mode=DocumentMode.NORMAL)para documentos reais, ou passamode=por chamada. Esquecê-lo produz silenciosamente um documento de teste. - Códigos de tipo desconhecidos mapeiam para
DocumentType.UNKNOWN(o código real fica emraw_response) — nunca rebentar num tipo que o enum não modela.
Arquitetura (orientação)¶
VendusClient # a única classe que os utilizadores instanciam; lazy-load dos serviços
└── DocumentsService # create_*, get, list, cancel, list_payment_methods
└── _request / _request_async (auth + base URL injetados pelo HttpTransport)
└── HttpTransport # httpx sync+async, retry, timeout, User-Agent
_ficheiro.py= interno;ficheiro.py= público (exportado em__init__.py).- Dinheiro é
Decimalem todo o lado; converte comfloat()só na fronteira do wire. - Cada método tem uma variante sync e uma
_async.
Adicionar um novo tipo de documento¶
Usa services/documents.py::create_invoice como referência.
- Adiciona o código ao enum
DocumentTypeemmodels/document.py. - Adiciona um builder
_build_X_body(...)emservices/documents.py. - Adiciona
create_Xecreate_X_asyncaoDocumentsService. Passaself._effective_mode(mode)para o argumentomode, para odefault_modedo cliente se aplicar. - Valida ao vivo o body contra a API real antes de afirmar que funciona — a
documentação de referência pode estar incompleta (uma FR precisa de
payments; uma NC precisa dereference_documentpor linha). - Adiciona testes unitários que asseram o body exato (
tests/unit/), com fixtures de resposta emtests/fixtures/. - Adiciona um exemplo executável em
examples/e uma páginadocs/documents/X.md(+ a versão portuguesaX.pt.md). - Atualiza o
CHANGELOG.md.
Pull Requests¶
- Um feature ou fix por PR — não mistures alterações não relacionadas.
- Inclui testes para código novo — cobertura mínima de 85%, a assertar o body exato.
- Atualiza o
CHANGELOG.mdna secção[Unreleased]. - Todos os checks de CI têm de passar — lint, types, testes (Python 3.9–3.13).
- Valida ao vivo tudo o que toca na API antes de afirmar que funciona (ver acima).
- Descreve o que mudou e porquê no corpo do PR.
Workflow típico¶
# 1. Cria um branch
git checkout -b feature/debit-note
# 2. Desenvolve e testa
pytest tests/unit/test_documents.py
# 3. Verifica tudo
ruff check . && ruff format --check . && mypy src/ && pytest
# 4. Commit e push
git add .
git commit -m "Add debit note (ND) support"
git push -u origin feature/debit-note
# 5. Abre um PR no GitHub
Convenções de código¶
- Python ≥3.9 — usa
from __future__ import annotations; nuncamatch/case, e usaOptional[...](nãoX | None) em runtime nos modelos Pydantic. Decimalpara dinheiro — nuncafloat; converte comfloat()só na fronteira do wire.- Type annotations em todas as funções públicas (
mypy --strictpassa). - Docstrings Google style nas funções públicas.
_ficheiro.py= módulo interno,ficheiro.py= API pública (exportada em__init__.py).- Nunca logar PII —
fiscal_id, email, telefone e morada são redigidos automaticamente. - Um método por tipo de documento, cada um com uma variante sync e
_async.