# Primeiro cálculo de serviço — entrega local delimitada **Aceitação posterior em 26/10/2026:** o utilizador aceitou expressamente o cálculo de ensaio demonstrado e autorizou o checkpoint local, conforme [conferência e aceitação separadas](SERVICE_CALCULATION_SEPARATE_REVIEW.md). A aplicabilidade fiscal real continua pendente e a activação de perfil fiscal real não foi autorizada. O texto de entrega abaixo preserva o estado anterior a essa aceitação e não a revoga. 26/10/2026. Preservadas identidade, A, B1 (`8fcd52c`), preparação documental, pré-visualização e alterações posteriores existentes. Não constitui aceitação pelo utilizador nem autorização de emissão. ## Comportamento entregue Uma linha, quantidade positiva, preço não negativo, moeda explícita e modo imposto acrescido (`added`). Aritmética Brick\Math já existente, sem dependências novas nem alterações de versões. Base e IVA exactos sem float ou truncagem intermédia. A política **`SGF-SERVICE-NET-VAT-TRUNCATE2-v1`** trunca apenas o IVA documental proposto a duas casas, conserva a diferença e calcula total proposto apenas quando a base é exactamente representável a duas casas. Caso contrário o total fica pendente, sem ajustar ou truncar a base. Truncagem: decisão operacional expressa do utilizador, **não obrigação legal comprovada**. Fontes reutilizadas: ficha InvesteMoz `IVA-GERAL-BASE-LIQUIDA` v1.2, cadeia da Lei 22/2022 republicada/art. 17(1), CIVA art. 15 e acontecimentos arts. 7/8/17(2). A matriz e limites constam de [SERVICE_INVOICE_CALCULATION_SPEC.md](SERVICE_INVOICE_CALCULATION_SPEC.md). Não se repetiu investigação nem se copiou código do portal. Separação confirmada: a classificação contabilística orienta contas; o enquadramento fiscal da operação determina tratamento e taxa de IVA. Nenhuma taxa é inferida da conta/classificação contabilística ou aplicada universalmente a serviços. Os 16% são exclusivamente parâmetro identificado dos ensaios deste incremento. Associações contabilísticas e lançamentos não foram implementados. Perfis e resultados são imutáveis. Cada resultado conserva entradas, moeda, base exacta, IVA exacto, IVA proposto, diferença, total/pendência, política, perfil/versão/fontes e versões de rascunho, preparação e origem. Mudanças tornam o resultado anterior desactualizado na nova consulta; o histórico mantém os valores originais. A leitura não recalcula nem grava. A pré-visualização já aberta conserva a versão consultada e avisa de alterações pelo mecanismo existente, exigindo actualização explícita. Execução por `POST /api/service-calculations/{id}` com contexto validado, sessão, CSRF e versões de rascunho/preparação/perfil. Sem campos arbitrários de taxa ou resultados enviados pelo navegador. Bloqueio fora de `testing`/`browser-testing`, seguido da guarda do destino efectivo `sgf_tempo_test`. O ensaio requer consulta e edição de rascunho já concedidas à identidade sintética; consulta não permite executar. Configurar aparência não dá esse acesso. Nenhuma concessão nova foi aplicada às contas reais e não existe via de activação de perfil real neste incremento. Bloqueio transaccional da empresa serializa comandos com as alterações existentes. Revalida contexto/permissões, rejeita versões antigas e origem/preparação por rever, e garante uma única inserção por entrada/versionamento, com repetição devolvendo o mesmo resultado. Auditoria da criação na mesma transacção. Chaves compostas e trigger de inserção recusam associações cruzadas; triggers append-only impedem actualizações/transferências em massa e apagamentos de perfis/resultados. Não se apresenta isto como prova de RLS. ## Migração e preservação Delta previamente apresentado: `2026_10_26_030000_create_service_calculations.php` cria `service_calculation_profiles`, `service_calculation_results`, índices, relações e triggers. Não altera B1, cadastros, permissões ou dados existentes; não tem rollback destrutivo automático. Aplicada primeiro em `sgf_tempo_test` nas provas; depois em **`sgf_tempo_dev`**, por `scripts/sgf.ps1 migrate`, com proprietário separado. Antes de aplicar em desenvolvimento, o estado das migrações confirmou que apenas esta estava pendente. Verificação posterior: contagens e hashes do conteúdo das 14 tabelas existentes comparadas ficaram idênticos. Tabelas novas em desenvolvimento: **zero perfis, zero resultados**. [Prova de preservação](proofs/calculation-dev-preservation.json). Nenhum reset/fresh em desenvolvimento foi aplicado por este incremento. ## Provas Regressões dirigidas: **53 testes, 743 asserções**, todos passados, abrangendo `ServiceCalculationTest`, `DocumentPreviewTest`, `InternalConfirmationTest`, `DocumentPreparationTest` e `DatabaseSafetyTest`. Execução exclusivamente em `sgf_tempo_test`. Compilação `npm run build` passou (Vite 8.3.5). Verificação de whitespace passou; nenhuma versão de ferramenta foi alterada. Cobertura nova: três casos pedidos, zero explícito, fronteiras imediatamente abaixo/na/acima da truncagem, base com fracções adicionais, perfil/taxa/moeda ausentes ou incompatíveis, modo incluído recusado, repetição, versões antigas de rascunho/perfil, histórico desactualizado, leitor sem edição, revogação no pedido seguinte, unidade de perfil alheia, empresa irmã, tentativa de transferência em massa, bloqueio no ambiente normal e fotografia B1 preservada. Guardas existentes continuam a recusar desenvolvimento e sobreposição por URL e a verificar privilégios do papel de testes. Concorrência separada, em dois processos PHP realmente sobrepostos: primeiro mantém bloqueio enquanto segundo entra; um resultado criado e um replay com o mesmo ID, sem duplicação. [Prova concorrente](proofs/calculation-concurrency.json). Navegador Edge, **login real sintético**, com CSRF/sessão activos em `http://127.0.0.1:8001`, base `sgf_tempo_test`, sessões/cache isolados. Não se usou `actingAs` como demonstração de login. Taxa 16% apenas no perfil identificado `ENSAIO-MZN-16 v1`, moeda MZN explicitamente indicada: | Base | IVA exacto | IVA truncado | Diferença | Total proposto | Prova | | --- | --- | --- | --- | --- | --- | | 100 | 16 | 16,00 | 0 | 116,00 | [Imagem](proofs/calculation-100.png) | | 1000,03 | 160,0048 | 160,00 | 0,0048 | 1160,03 | [Imagem](proofs/calculation-1000.png) | | 0,03 | 0,0048 | 0,00 | 0,0048 | 0,03 | [Imagem](proofs/calculation-small.png) | | 0,0001 | 0,000016 | 0,00 | 0,000016 | Pendente | [Imagem](proofs/calculation-base-pending.png) | Repetição do segundo caso no navegador conservou o mesmo resultado. Em todos: aviso permanente **RASCUNHO — SEM VALIDADE FISCAL**, identificação de ensaio, diferença preservada e pendências visíveis. [Registo da observação](proofs/calculation-browser-results.json). Separador decimal dos valores na interface é actualmente ponto; os valores são strings exactas, sem conversão de apresentação para float. ### Percurso exacto e repetição local da prova 1. Entrar como identidade sintética existente da fixture, com a credencial local criada aleatoriamente; nunca publicar a credencial. Seleccionar **Empresa Horizonte — Demonstração → Unidade Central — Demonstração**. 2. **Rascunhos de facturação → Ensaio base 100 → Pré-visualizar rascunho → Calcular ensaio · ENSAIO-MZN-16 v1**. Consultar os quatro valores e o total proposto. 3. **Voltar ao registo**, seleccionar os casos **Ensaio base 1000.03**, **Ensaio base 0.03** e **Ensaio base 0.0001**, repetir pré-visualização/cálculo. O segundo caso também foi calculado pela prova concorrente e a execução no navegador reutilizou o mesmo resultado. 4. Sair da sessão. A instância de prova é encerrada após a entrega; a aplicação normal não muda de ligação. Para preparar outra prova, parar primeiro a instância com `scripts/browser-proof.ps1 stop`; executar apenas os testes dirigidos abaixo (recriam a base de testes, nunca desenvolvimento); arrancar novamente `scripts/browser-proof.ps1 start` e preparar os quatro rascunhos por `sgf.ps1 php scripts/calculation-fixture.php --env=testing`. O preparador de fixtures recusa uma base não vazia; não apaga dados para conseguir prosseguir. Depois, `sgf.ps1 php scripts/calculation-concurrency.php --env=testing` demonstra a concorrência antes do percurso no navegador. Não repetir o preparador de cálculo sobre a mesma fixture. ```powershell powershell -NoProfile -ExecutionPolicy Bypass -File scripts/sgf.ps1 test --stop-on-failure --filter="ServiceCalculationTest|DocumentPreviewTest|InternalConfirmationTest|DocumentPreparationTest|DatabaseSafetyTest" powershell -NoProfile -ExecutionPolicy Bypass -File scripts/sgf.ps1 npm run build ``` Os scripts PowerShell existentes podem precisar de `-ExecutionPolicy Bypass` apenas nesse processo; não se alterou política global. A execução recusa portas ocupadas, sem terminar processos de outro projecto. ## Ficheiros deste incremento - Núcleo novo: `app/Modules/Services/ServiceCalculation.php`; migração nova acima; `tests/Feature/ServiceCalculationTest.php`. - Integração: `DocumentPreview.php`, `Http/DocumentPreviewController.php`, `routes/web.php`, `resources/js/DocumentPreview.jsx`. - Provas: `scripts/calculation-fixture.php`, `calculation-worker.php`, `calculation-concurrency.php`, `calculation-preservation.php` e ficheiros `docs/proofs/calculation-*`. - Documentação: esta entrega, especificação de cálculo, README e AGENTS. Restantes alterações preexistentes foram preservadas; nenhum commit/checkpoint foi criado sem pedido. ## Condições para activação real Continua por demonstrar a aplicabilidade concreta: regime, serviço/localização, tratamento, base tributável e acontecimento/exigibilidade. Activação requer incremento próprio com perfil real fundamentado/versionado e direitos fiscais especificamente aprovados, sem herdar poderes visuais. A decisão operacional de truncagem não comprova conformidade fiscal do montante documental. Representação de base/total com fracções adicionais fica pendente. Incluído e demais casos continuam excluídos. Não há número/data de emissão fiscal, emissão, PDF, recebimentos ou lançamentos; nenhum documento foi publicado.