61 lines
9.3 KiB
Markdown
61 lines
9.3 KiB
Markdown
# Endpoint de facturas SGC — implementação local de10/11/2026
|
|
|
|
Continuação autorizada: [workspace persistente SGC entregue](SGC_INVOICE_WORKSPACE_DELIVERY.md), com intenção, decisão, recibo e recuperação auditados. Reutiliza este endpoint/motor sem novas alterações ao seu writer; ambas as capacidades permanecem desligadas no destino.
|
|
|
|
Implementação concluída para transporte sintético até **factura pendente**, reutilizando o controlador/workflow/writer fixados de Compras. Não utiliza `IntegrationService` legado, tabelas espelho `sgc_faturas` ou saldo orçamental fictício. Não valida a própria factura, consome orçamento, reconhece obrigações, paga ou contabiliza. O percurso e a instalação8085 permanecem intactos.
|
|
|
|
## Rotas e contrato
|
|
|
|
`bootstrap/app.php` regista especificamente `routes/sgc.php`, preservando `routes/api.php` legado sem montagem. Só existem:
|
|
|
|
- `GET /api/v1/integration/sgc/invoices/orders/{order}`: ordem exacta autorizada, linhas, versão actual e progresso da recepção. Não escolhe uma ordem/linha nem devolve a lista de outras facturas.
|
|
- `POST /api/v1/integration/sgc/invoices`: fases `preview` e `confirm` no mesmo endereço. A confirmação cria apenas a factura pendente; a revisão distinta continua no percurso existente.
|
|
|
|
Ambas exigem `Authorization: Bearer …`, `X-SGC-Client`, `X-SGC-Context` e `Accept: application/json`. O POST exige `Content-Type: application/json` e `X-Idempotency-Key` UUID. HTTP só é admitido em loopback; fora dele é exigido TLS. O adaptador nativo conserva o bloqueio `budget-testing` e a validação física do cluster/base/role/manifests QA. Não há capacidade operacional/produtiva presumida.
|
|
|
|
Na prévia enviar `phase`, `order_id`, `revision`, `reason` e `data` com número/data/totais/linhas. Valores monetários são strings decimais, não floats. Quantidades, unidade, preço, imposto, total, linhas aceites e origem são conferidos pelo mesmo motor original. Campos desconhecidos, IDs de autoridade no JSON/query, datas inválidas, linhas repetidas/estrangeiras e corpos acima128KiB são recusados. Até50 linhas. Fundamento de10 a2000 caracteres.
|
|
|
|
A resposta200 apresenta `preview` cifrada e `impact`, sem gravar facturas. O integrador deve apresentar/conferir o impacto antes da confirmação individual, sem confirmação automática. Para confirmar enviar somente:
|
|
|
|
```json
|
|
{"phase":"confirm","order_id":123,"preview":"<prévia devolvida>"}
|
|
```
|
|
|
|
Usar a mesma chave UUID da prévia. Uma confirmação válida devolve201 com `invoice_id`, `order_id`, `status: pending`, `replayed: false`. Alteração da versão/origem/contexto exige nova conferência, não uma nova tentativa automática de escrever.
|
|
|
|
`tests/payloads/invoice_test.json` é um **modelo**: os placeholders de ordem/linha/revisão têm de ser substituídos depois de escolha explícita e GET autenticado.100MZN/1UN/imposto0 são apenas o exemplo QA; não são preenchidos automaticamente a partir de uma entidade real. No PowerShell, gerar a chave com `[guid]::NewGuid().ToString()`; não pressupor `uuidgen`.
|
|
|
|
## Autenticação e isolamento
|
|
|
|
`config/sgc_invoice.php` conserva `enabled=false` e `principals=[]`. Nenhuma credencial, associação, concessão ou activação foi instalada. Uma futura configuração exige aprovação própria; o nome do cliente não concede direitos.
|
|
|
|
Cada principal configurado separadamente contém `enabled`, SHA256 de um bearer aleatório de pelo menos32 caracteres, `user_id`, `auth_version`, `tenant_id`, `company_id`, `unit_id` e `context_version` de64 hexadecimais. O token nunca é aceite por query/cookie, guardado em log ou distribuído no código. `X-SGC-Context` deve corresponder à versão configurada; rodar essa versão invalida as prévias antigas. A versão de autenticação deve coincidir com a pessoa actual.
|
|
|
|
Empresa/unidade/pessoa provêm exclusivamente dessa configuração no servidor. Filiação, empresa/unidade, módulo disponível e direitos existentes são revalidados. Reutilizam-se `company.view`, `unit.view`, `purchasing_invoices.view/prepare` e as funções/autoridades nativas. Nenhum nome de direito novo e nenhuma concessão real. O contexto usa sessão efémera em memória, é limpo ao terminar e não é persistido em cookies/sessões/cache. As respostas de sucesso são `no-store, private`.
|
|
|
|
## Idempotência, transacção e recuperação
|
|
|
|
O adaptador serializa cada UUID com advisory lock transaccional PostgreSQL no host, abrangendo a consulta de recuperação e o commit nativo. A chave é normalizada para minúsculas. O recibo autoritativo é o evento append-only **já existente** `sgf.invoice.command`, gravado na mesma transacção da factura e das linhas no livro nativo. Não há recibo externo ou espelho de dívida no host.
|
|
|
|
Antes de uma recuperação, revalidam-se identidade, contexto, direitos, binding actual e visibilidade exacta da ordem. A mesma chave com fundamento/dados/outra ordem gera409; a mesma intenção confirmada devolve200 e o mesmo `invoice_id`, sem nova factura/auditoria. A recuperação pode ser pedida com a confirmação original ou repetindo a prévia original. A versão de leitura antiga não é uma autorização para outra escrita.
|
|
|
|
Uma prévia não aplicada expira em600s. Um recibo **já confirmado** pode ser recuperado depois desse prazo, com a autoridade actual e sem repetir comandos. Rodar contexto/binding ou revogar acesso impede a recuperação pela prévia antiga. Uma falha depois de criar a factura e antes do recibo reverte tudo; a mesma operação pode ser retomada. Se o commit tiver resultado incerto, repetir **a mesma chave e conteúdo**, sem mudar de número ou inventar nova chave.
|
|
|
|
Bloqueio da chave acima5s devolve503 `RETRY_SAME_KEY`, com `Retry-After: 1`. Recusas de versão/conteúdo:409; validação:422; credencial:401; autoridade:403; configuração desligada:404. Deadlock/serialização também devolvem503 recuperável, sem tratar como sucesso. A duração máxima de todos os possíveis bloqueios nativos/carga sustentada não foi validada.
|
|
|
|
## Provas sintéticas e preservação
|
|
|
|
78 verificações:42 do percurso/validações/atomicidade/concorrência,20 de revogação/isolamento/recuperação de bloqueio,10 da consulta autenticada/compatibilidade de repetição e seis da autoridade nativa na recuperação. Foram executadas pelo kernel HTTP Laravel com PostgreSQL real e processos PHP independentes, **sem criar servidor/porta/base/role/demonstração**. Não são testes TCP no portal, revisão visual ou implantação.
|
|
|
|
Livro de regressão existente `receipt-composition-da77bb730751`, separado de8085. O cenário original tinha a recepção integralmente facturada; a primeira prévia foi correctamente recusada. Foi então acrescentada uma **fixture SQL de origem**, marcada `fixture_only`: cópias de requisição/linha/proposta/adjudicação/ordem/recepção aceite e evento de admissão de teste. Essa preparação não prova uma nova execução desses workflows nem um movimento físico. Facturas/linhas/recibos de comando, por sua vez, passaram pelo motor original: quatro novas facturas pendentes, nenhum consumo/obrigação/pagamento/lançamento novo. Registos originais e provas aceites preservados.
|
|
|
|
Concorrência da mesma chave:201/200, um `invoice_id`. Falha injectada no evento imediatamente após o writer:500, sem factura parcial; nova confirmação da mesma intenção:201. Bloqueio da chave mantido por outra ligação:503 após o limite; depois da libertação:200 com a factura original. Conteúdo diferente, versão desactualizada, prévia adulterada, outra ordem/tenant/empresa/unidade, permissões revogadas, token/contexto rodados e módulo indisponível foram recusados.
|
|
|
|
Comparação de todas as linhas e sequências do host/livro de8085 antes/depois: idênticas. Todas as linhas/sequências/permissões/orçamento do host de regressão também idênticas. Nenhuma ligação de escrita a55432 ou à base da demonstração8039; seis associações protegidas e cache bloqueada não foram alteradas/limpas. Dados de autenticação, cut cifrado, logs e resultados detalhados permanecem privados fora do repositório. Sem checkpoint, publicação ou activação.
|
|
|
|
Depois das primeiras72 provas, a conferência do operador no caminho de recuperação foi reforçada com o mesmo `CurrentActor::lock` e a recusa de operador limitado a manutenção. As seis verificações adicionais cobrem essa alteração sem repetir as escritas: função financeira revogada dentro de transacção QA/rollback e manutenção injectada no harness recusam a recuperação; autoridade válida recupera o mesmo recibo. A inclusão da consulta autenticada foi coberta por dez verificações próprias; provas anteriores não foram sobrescritas nem suites alheias repetidas.
|
|
|
|
`node scripts/sgc-invoice-check.mjs` apenas consulta o resultado válido, sem repetir ensaios. `--new` executa outra prova no mesmo livro de regressão existente e acrescenta novas fixtures/documentos QA; usar apenas perante alteração/falha relevante e com o cluster próprio disponível. Não modifica8085 nem instala o endpoint lá.
|
|
|
|
[Prova consolidada](proofs/sgc-invoice-endpoint-20261110.json). Mantêm-se pendentes activação aprovada/configuração de clientes, utilização real, transporte TLS operacional, política de rotação/rate limiting em implantação, carga sustentada e capacidade de implantação. O serviço legado e os endpoints de pagamentos/analytics continuam fora deste recorte e sem montagem.
|