Implement G2A health exemptions in QA and reconcile L1 branch inventory

This commit is contained in:
2026-10-24 14:04:18 +02:00
parent 89ab288334
commit 675cf38f67
8 changed files with 363 additions and 8 deletions
@@ -4878,5 +4878,233 @@
"qa-recovered.png": "00a9deb00cbad607fc58fe2252ed96b385f7d2b3a1e9b749621a90faf096ffa6",
"qa-result.png": "350f12a8f2abd57c907eee2f93991e4ff06432afe3624dd08f089026f59edd2c"
}
},
"iva_l1_g2_health_qa_20261024": {
"status": "G2A_executable_demonstrated_QA;inventory_reconciled;L1_open;no_homologation_deployment",
"candidate": null,
"preserved_candidate": "5030116e",
"preserved_record": "89ab2883",
"reconciliation": {
"unit": "Existing matrix ID; functional branch is distinct from executable treatment, group and documentary dependency",
"original_total_IDs": 133,
"original_functional_branches": 132,
"documentary_dependency_IDs": [
"AUD-SIM-L1-P001"
],
"G1_accepted_QA_mapping": {
"AUD-SIM-L1-P017": "financial_stamp",
"AUD-SIM-L1-P018": "insurance_stamp",
"AUD-SIM-L1-P029": "public_waste",
"AUD-SIM-L1-P030": "public_funeral"
},
"after_G1": {
"QA_covered_functional": 4,
"pending_functional": 128,
"pending_documentary": 1,
"pending_IDs": 129
},
"G2A_new_QA_mapping": {
"AUD-SIM-L1-P003": "health_assistive",
"AUD-SIM-L1-P004": "human_materials",
"AUD-SIM-L1-P005": "patient_transport",
"AUD-SIM-L1-P006": "mosquito_nets",
"AUD-SIM-L1-P007": "medicines"
},
"current": {
"QA_covered_functional": 9,
"pending_functional": 123,
"pending_documentary": 1,
"pending_IDs": 124
},
"remaining_functional_IDs": [
"AUD-SIM-L1-P002",
"AUD-SIM-L1-P008",
"AUD-SIM-L1-P009",
"AUD-SIM-L1-P010",
"AUD-SIM-L1-P011",
"AUD-SIM-L1-P012",
"AUD-SIM-L1-P013",
"AUD-SIM-L1-P014",
"AUD-SIM-L1-P015",
"AUD-SIM-L1-P016",
"AUD-SIM-L1-P019",
"AUD-SIM-L1-P020",
"AUD-SIM-L1-P021",
"AUD-SIM-L1-P022",
"AUD-SIM-L1-P023",
"AUD-SIM-L1-P024",
"AUD-SIM-L1-P025",
"AUD-SIM-L1-P026",
"AUD-SIM-L1-P027",
"AUD-SIM-L1-P028",
"AUD-SIM-L1-P031",
"AUD-SIM-L1-P032",
"AUD-SIM-L1-P033",
"AUD-SIM-L1-P034",
"AUD-SIM-L1-P035",
"AUD-SIM-L1-P036",
"AUD-SIM-L1-P037",
"AUD-SIM-L1-P038",
"AUD-SIM-L1-P039",
"AUD-SIM-L1-P040",
"AUD-SIM-L1-P041",
"AUD-SIM-L1-P042",
"AUD-SIM-L1-P043",
"AUD-SIM-L1-P044",
"AUD-SIM-L1-P045",
"AUD-SIM-L1-P046",
"AUD-SIM-L1-P047",
"AUD-SIM-L1-P048",
"AUD-SIM-L1-P049",
"AUD-SIM-L1-P050",
"AUD-SIM-L1-P051",
"AUD-SIM-L1-P052",
"AUD-SIM-L1-P053",
"AUD-SIM-L1-P054",
"AUD-SIM-L1-P055",
"AUD-SIM-L1-P056",
"AUD-SIM-L1-P057",
"AUD-SIM-L1-P058",
"AUD-SIM-L1-P059",
"AUD-SIM-L1-P060",
"AUD-SIM-L1-P061",
"AUD-SIM-L1-P062",
"AUD-SIM-L1-P063",
"AUD-SIM-L1-P064",
"AUD-SIM-L1-P065",
"AUD-SIM-L1-P066",
"AUD-SIM-L1-P067",
"AUD-SIM-L1-P068",
"AUD-SIM-L1-P069",
"AUD-SIM-L1-P070",
"AUD-SIM-L1-P071",
"AUD-SIM-L1-P072",
"AUD-SIM-L1-P073",
"AUD-SIM-L1-P074",
"AUD-SIM-L1-P075",
"AUD-SIM-L1-P076",
"AUD-SIM-L1-P077",
"AUD-SIM-L1-P078",
"AUD-SIM-L1-P079",
"AUD-SIM-L1-P080",
"AUD-SIM-L1-P081",
"AUD-SIM-L1-P082",
"AUD-SIM-L1-P083",
"AUD-SIM-L1-P084",
"AUD-SIM-L1-P085",
"AUD-SIM-L1-P086",
"AUD-SIM-L1-P087",
"AUD-SIM-L1-P088",
"AUD-SIM-L1-P089",
"AUD-SIM-L1-P090",
"AUD-SIM-L1-P091",
"AUD-SIM-L1-P092",
"AUD-SIM-L1-P093",
"AUD-SIM-L1-P094",
"AUD-SIM-L1-P095",
"AUD-SIM-L1-P096",
"AUD-SIM-L1-P097",
"AUD-SIM-L1-P098",
"AUD-SIM-L1-P099",
"AUD-SIM-L1-P100",
"AUD-SIM-L1-P101",
"AUD-SIM-L1-P102",
"AUD-SIM-L1-P103",
"AUD-SIM-L1-P104",
"AUD-SIM-L1-P105",
"AUD-SIM-L1-P106",
"AUD-SIM-L1-P107",
"AUD-SIM-L1-P108",
"AUD-SIM-L1-P109",
"AUD-SIM-L1-P110",
"AUD-SIM-L1-P111",
"AUD-SIM-L1-P112",
"AUD-SIM-L1-P113",
"AUD-SIM-L1-P114",
"AUD-SIM-L1-P115",
"AUD-SIM-L1-P116",
"AUD-SIM-L1-P117",
"AUD-SIM-L1-P118",
"AUD-SIM-L1-P119",
"AUD-SIM-L1-P120",
"AUD-SIM-L1-P121",
"AUD-SIM-L1-P122",
"AUD-SIM-L1-P123",
"AUD-SIM-L1-P124",
"AUD-SIM-L1-P125",
"AUD-SIM-L1-P126",
"AUD-SIM-L1-P127",
"AUD-SIM-L1-P128",
"AUD-SIM-L1-P129",
"AUD-SIM-L1-P130",
"AUD-SIM-L1-P131",
"AUD-SIM-L1-P132",
"AUD-SIM-L1-P133"
],
"IDs_removed_merged_renumbered": [],
"G2": {
"total": 32,
"covered_G2A": 5,
"pending": 27
},
"grouping_reference": "iva_l1_grouped_increment_qa_20261024.grouping.groups; all133IDs retain existing group;G2A is a subincrement ofG2"
},
"source_scope": {
"applied": "CIVA2016 PDF9 p1426(267),9(1)(b-f);19(1),PDF15 p1426(273);2022PDF1 maintains b-f; rectification2017PDF1 affects6/14/annexes;2025art1/19(1),PDF12-13",
"2020_official_reference": "https://www.ufsa.gov.mz/query/Baixar_documento.php?nome=LEI+DO+SISTAFAE+-+LEI+14%2F2020+DE+23+DE+DEZEMBRO",
"2020_scope": "BR246 supplement PDF20 p2414(20),Lei16/2020art1 changes9(13),not9(1)(b-f); fullPDF read through web tool; direct Python download returnedHTTP403,no local original claimed",
"located_not_applied": [
"https://www.parlamento.mz/wp-content/uploads/2025/05/iva.pdf:8pages,no extractable text;not used as enacted law orG2A rule"
],
"P008_dependency": "Establishment integrated in NationalHealthService and exact eligible AnnexIII position,rectification2017/2022;no added autonomous approval/registration requirement;does not block9(1)(b-f)"
},
"reuse": "Existing motor,controls,original currency,opportunity,location,establishment,period,price representation,evidence,snapshotAPI andreport exporter;no new schema/form or fiscal rate",
"proofs": {
"new_frontend_tests": 2,
"new_API_tests": 1,
"API_assertions": 13,
"database": "simulator_fiscal_delta_20261021_test:55441",
"rollback": true,
"actual_UI": "Patienttransport authorization removed -> indeterminate,restored ->5rows100MZN/field07=500/IVA0;revision3,opportunity309/Manica/2026-02/MZN",
"UI_API_roundtrip_identical": true,
"other_opportunity_G1_snapshot_unchanged": true,
"new_report": "Actual HTML exporter from recovered snapshot,selected309 only;fiveoperations/value/context/source/version shown;noG1operation leaked",
"types": "passed",
"build": "passed",
"accepted_tests_repeated": false,
"accepted_fraction_Word_PDF_regenerated": false,
"accepted_artifact_hashes_unchanged": true,
"actual_UI_reload": "Revision3,opportunity309/Manica,2026-02,MZN and500MZN field07 preserved after reload;screenshotqa-result.png"
},
"limits": [
"Technicalscope2026/MZN;internaloperations only",
"No automatic deduction or finalVATdetermination/submissionAT",
"G2notfullyclosed;27branches remain",
"L1open;123functionalbranches+D06remain",
"G2Aspecific reportproofHTML;acceptedWord/PDFproofs reused withoutclaimingnewG2Aformatproof",
"Deploy coherent usable groups independently ofcompleteL1;no deployment inthispass"
],
"active_changes": false,
"schema_or_permission_changes": false,
"published_rule_activation": false,
"artifacts": {
"calculated.json": "0d8da2cdd8c12580e5d5ab19bb60d24b807ad57307ec52d5c31355494138ec97",
"ui-calculated.json": "59f87132fa0e0c096d8fbe46ee1766ccceed2d48c9c511dd8693c5f6b9e2d1de",
"recovered.json": "f03ba2de3fff4459cf38fe8c49d1de803021d65148f96fd616d25fe7c62fe390",
"frontend-tests.txt": "5638c293e800ece5b70ea826ece4fede69f92bbe347d6813dd12ae90f30f8e79",
"api-tests.txt": "2a03b8b8eba58bc64fa39886ff9c6a6a52bb520d5190538c7a40408e00fe468c",
"api-tests.xml": "31c14340cd9820395202854c383a8d2d0cfdc698cf808bf4183ced4bdabfccc5",
"health-model.json": "d67b045262c75b639d9a69dfcf48cf469155807aa4f2152beb4b68c41db11a4c",
"health-report.html": "7567d7b9ebf73ec862e8243c328ba93cee6e159ccf361afd3e8be67bfd1e5a10",
"types.txt": "5346ac8a6566c1c38cb0bbda6a029579df58cd78af78e2c4845d9c82b5d1d76c",
"build.txt": "432a1e08734e9fbd290fafa029a51290b050323505e5b4ce8daf1523a3dadb85",
"checks.json": "f366fb2d17bd44e91ee7642dd28e935d71f574652e62868f612a83de257eacea",
"qa-recovered.png": "ebc9c14c7a489e17ca59a272c1f2f0217dcaf22ac469480e32a41e636df0e1db",
"qa-result.png": "ca1013fc3d1579779e6db5f20061d34e8a6d8eeeaf47cf5c0f7967a89df6f95d"
},
"frozen_G1_fixture": {
"path": "tests/frontend/fixtures/iva-services.json",
"sha256": "8b215b97f4044d259a6f6d0075738591eccb4a1abd1b06cd4f819d5d83c430d2"
}
}
}
@@ -1,5 +1,61 @@
# Rastreabilidade, utilização e formação
## 24/10/2026 — reconciliação dos IDs L1 e incremento G2A de saúde
**Incremento executável em QA; L1 aberto; homologação inalterada.** Preservados `5030116e`/registo `89ab2883`, G1 e a apresentação das fracções Word/PDF, aceites pela Tempo com base no relatório. Não se repetiram esses ensaios nem se regeneraram os respectivos ficheiros. As entregas podem reunir conjuntos coerentes/utilizáveis; o fecho de todos os ramos não é condição técnica ou organizacional para entregar um conjunto. Esta passagem prepara QA, sem realizar integração/disponibilização.
### Reconciliação: ramo, tratamento e pendência documental
Os **133 IDs** do inventário original incluem **132 ramos funcionais (`P002`–`P133`) e uma pendência documental (`P001`/D06)**. Todos os IDs desta secção têm prefixo `AUD-SIM-L1-`. Um ramo representa uma cobertura legal/operacional e pode conter vários produtos/variantes. Um tratamento é uma opção executável no motor. Um grupo agrega ramos por regras/dados comuns; agrupar não encerra os ramos nem cria uma aprovação por ID. Não houve eliminação, fusão ou renumeração de IDs.
| Momento | Ramos cobertos em QA | Ramos funcionais pendentes | Pendências documentais | Total de IDs |
| --- | ---: | ---: | ---: | ---: |
| Inventário inicial do saldo, excluindo os dez tratamentos anteriores já aceites | 0 | 132 | 1 | 133 |
| Após G1, candidata5030116e | 4 | 128 | 1 | 133 |
| Após este incremento G2A | 9 | 123 | 1 | 133 |
Portanto, **133−4=129IDs pendentes após G1:128funcionais+P001**. O resumo anterior «128restantes» referia apenas os funcionais e precisava desta distinção explícita. P001 não foi encerrado para produzir a diferença. Os nove ramos cobertos correspondem, nestes dois incrementos, a nove tratamentos; essa correspondência não é presumida para o restante inventário.
| ID | Grupo/incremento | Tratamento executável | Estado/referência |
| --- | --- | --- | --- |
| P017 | G1 | `financial_stamp` | Aceite em QA5030116e; preservado |
| P018 | G1 | `insurance_stamp` | Aceite em QA5030116e; preservado |
| P029 | G1 | `public_waste` | Aceite em QA5030116e; preservado |
| P030 | G1 | `public_funeral` | Aceite em QA5030116e; preservado |
| P003 | G2/G2A | `health_assistive` | Executável/demonstrado em QA neste incremento |
| P004 | G2/G2A | `human_materials` | Executável/demonstrado em QA neste incremento |
| P005 | G2/G2A | `patient_transport` | Executável/demonstrado em QA neste incremento |
| P006 | G2/G2A | `mosquito_nets` | Executável/demonstrado em QA neste incremento |
| P007 | G2/G2A | `medicines` | Executável/demonstrado em QA neste incremento |
| P001 | D06 | Nenhum: correspondência05/06 | Pendente: instrução da edição recebida ou correspondência administrativa expressa aplicável |
Os restantes **123IDs funcionais** são exactamente **P002, P008–P016, P019–P028 e P031–P133** (1+9+10+103). Permanecem na mesma matriz com grupo e dependência próprios; o recibo desta passagem enumera o conjunto exacto e verifica a partição disjunta dos133IDs. G2 mantém32ramos:5cobertos por G2A e27pendentes. Os onze grupos originais mantêm-se; G2A é um subincremento de G2, não um novo grupo nem o fecho integral de G2.
### G2A — fonte, condições e reutilização
**Fonte executada:** CIVA republicado pela Lei13/2016, PDF9, p.1426(267), art.9(1)(b–f); direito à dedução confrontado com19(1), PDF15, p.1426(273). A rectificação de08/06/2017, BR89 PDF1, altera6/14 e anexos, não estas alíneas. Lei22/2022 art.1, PDF1, p.2252(25), mantém9(1)(b–f) por remissão. Lei10/2025 art.1, BR248 PDF12–13, altera outros artigos e19(2), conservando19(1); as isenções destas cinco operações não são convertidas em taxa5% nem em dedução automática de compras. O artigo19(1)(b)(v) não enumera9(1)(b–f).
Conferência adicional de âmbito: [Lei16/2020, art.1, BR246 suplemento, PDF20, p.2414(20), exemplar oficial UFSA](https://www.ufsa.gov.mz/query/Baixar_documento.php?nome=LEI+DO+SISTAFAE+-+LEI+14%2F2020+DE+23+DE+DEZEMBRO) altera9(13), não9(1)(b–f). O nome do download menciona SISTAFE, mas o exemplar completo contém as Leis13–16/2020; identificada a página da Lei16, sem inferir conteúdo do nome. A fonte parlamentar `https://www.parlamento.mz/wp-content/uploads/2025/05/iva.pdf` foi localizada:8páginas sem texto extraível; não usada como lei aprovada nem como regra G2A. Continua por cotejar para a operação temporal correspondente, sem bloquear G2A.
| Tratamento | Informação específica necessária nos controlos existentes |
| --- | --- |
| Compensação da deficiência/próteses/braille | Bem na enumeração e destino conforme categoria: deficiência, membro/órgão, fractura, visão ou audição; não qualquer equipamento médico |
| Órgãos/sangue/leite | Origem humana e natureza da transmissão; não origem animal ou serviço autónomo |
| Transporte de doentes/feridos | Doente/ferido, ambulância ou veículo apropriado e organismo devidamente autorizado; não basta destino hospitalar |
| Redes mosquiteiras | Identificação como rede mosquiteira; não extensão automática a outros bens preventivos |
| Medicamentos/artigos elegíveis | Produto farmacêutico exclusivamente terapêutico/profiláctico ou artigo enumerado da alínea com finalidade higiénica/medicinal/cirúrgica; não qualquer produto vendido por farmácia |
Condição comum: operação interna localizada em Moçambique, distinguindo importação/estrangeiro. Âmbito técnico2026/MZN. Reutilizados oportunidade, moeda original, localização, estabelecimento, período, valor/representação e fundamento já guardados. Acrescentam-se apenas a escolha do tratamento e a confirmação das condições específicas em falta, nos controlos existentes. Não há novos formulários, campos persistidos, schema ou permissões. Uma confirmação/documento pressuposto em QA não é certificação da operação real. A falta de natureza, destino, autorização ou fundamento gera indeterminação; não presume zero nem isenção pelo sector.
P008 não integra G2A: matérias-primas/componentes de medicamentos exigem integração do estabelecimento no Serviço Nacional de Saúde e correspondência com a posição aplicável do AnexoIII, incluindo rectificação2017/alteração2022. Não se acrescenta uma aprovação ou inscrição autónoma à condição legal. Essa verificação afecta P008, não as cinco alíneas independentes. Os outros27ramos de G2 conservam as dependências já registadas. O próximo subincremento pode usar perfis sociais/art.10 ou produtos/anexos após o cotejo específico, sem criar um bloqueio comum a G2.
### Prova dirigida e limites
Dois testes frontend novos cobrem os cinco tratamentos, cada condição ausente, fundamento ausente, preço líquido/IVA incluído isento e relatório/contexto recuperados. Um teste da API privada,13asserções, com guarda nominal `simulator_fiscal_delta_20261021_test:55441` e rollback. No componente real em QA18628, retirada a autorização do transporte: indeterminação; reposta: cinco operações de100MZN, campo07=500MZN, IVA0; snapshot da interface → gravação/API → recuperação idêntica → relatório HTML do exportador existente. O contexto de tratamento é309/Manica/2026-02/MZN; o livro G1 da oportunidade310 mantém-se exactamente igual ao snapshot aceite, sem novo cálculo/teste de G1.
O motor, gravação, recuperação e exportador foram reutilizados. As provas Word/PDF das fracções permanecem aceites e intactas; não são apresentadas como uma nova prova específica dos cinco textos G2A. Apenas o relatório/contexto novo foi conferido neste incremento. Não se declara apuramento integral, dedução automática, submissão à AT, fecho integral de G2/L1 ou entrega na homologação. Estatísticas e IA permanecem adiadas.
## 24/10/2026 — L1: incrementos por regras/dados comuns e primeiro grupo executável
**Continuação exclusivamente em QA, candidata `5030116e`, sobre `689e7cbe`/registo `8719e916`. L1 aberto; homologação inalterada.** Os 133 ramos foram distribuídos, sem duplicação, por onze incrementos funcionais e uma dependência documental. Não são 133 desenvolvimentos nem 133 aprovações. A correspondência individual está na mesma matriz, coluna «Grupo de execução», linhas `AUD-SIM-L1-P001`–`P133`; não foi criada outra matriz/backlog.
File diff suppressed because one or more lines are too long
+7
View File
@@ -9,6 +9,8 @@ const legacyV2TreatmentIds = ['general','private_health','private_education','pr
const source2022 = 'Lei22/2022, BR250, 2.º suplemento, PDF1–4 (2252(25)–(28)); arts.1/2/6; efeitos01/01/2023';
const source2016 = 'CIVA republicado Lei13/2016, BR156, 10.º suplemento, PDF9–15 (1426(267)–(273))';
const source2025 = 'Lei10/2025, BR248, suplemento1, PDF14–15 (2180(14)–(15)); arts.1/2/3/5; efeitos01/01/2026';
const healthGoodsSource = 'CIVA republicado Lei13/2016, PDF9, p.1426(267), art.9(1)(b–f); PDF15, p.1426(273), art.19(1); Lei22/2022 art.1, PDF1, p.2252(25), mantém9(1)(b–f). Âmbito técnico2026/MZN';
const internalHealthOperation = 'Operação interna localizada em Moçambique; não é importação nem operação localizada no estrangeiro';
export const modeloA06Correspondence = 'Exemplar recebido:06 é base de operações isentas, com remissão18(1)(b); nessa lei18(1)(b) trata IVA de importações, enquanto19(1)(b) enumera operações com direito. O mesmo exemplar já tem05 para vendas isentas com direito. O formulário antigo da AT usa03 para a remissão e06 para existências. Não há correspondência unívoca nem instrução aplicável que separe05/06 no acervo conferido: não atribuir06 automaticamente a operações no estrangeiro ou importar instruções antigas por número.';
export type OperationTreatment = {id:string; label:string; field:string|null; rate:number|null; article:string; source:string; conditions:string[]};
export const operationTreatments:OperationTreatment[] = [
@@ -26,6 +28,11 @@ export const operationTreatments:OperationTreatment[] = [
{id:'insurance_stamp',label:'Seguro/resseguro ou mediação conexa sujeitos a Selo',field:'07',rate:0,article:'CIVA art.9(6), redacção Lei22/2022; art.19(1)',source:`${source2022}; ${source2016}`,conditions:['Seguro/resseguro ou serviço conexo efectuado por corrector ou outro mediador de seguros','Sujeição a Imposto do Selo identificada para esta operação']},
{id:'public_waste',label:'Remoção de lixo pública ou por entidade contratada',field:'07',rate:0,article:'CIVA art.9(12)(b), redacção Lei22/2022; art.19(1)',source:`${source2022}; ${source2016}`,conditions:['Prestação de serviço de remoção de lixo, não qualquer serviço de limpeza','Efectuada por entidade pública ou por entidade por ela contratada, com relação identificada']},
{id:'public_funeral',label:'Serviços funerários/cremação públicos e bens acessórios',field:'07',rate:0,article:'CIVA art.9(12)(c), redacção Lei22/2022; art.19(1)',source:`${source2022}; ${source2016}`,conditions:['Serviço funerário/cremação ou transmissão de bem acessório a esse serviço','Efectuado por entidade pública; não aplicar automaticamente a funerária privada']},
{id:'health_assistive',label:'Bens de compensação da deficiência, próteses e braille',field:'07',rate:0,article:'CIVA arts.9(1)(b),19(1)',source:healthGoodsSource,conditions:[internalHealthOperation,'Bem identificado na enumeração: cadeira de rodas/veículo semelhante, aparelho/máquina/impressora braille, prótese ou material de compensação','Destino documentado conforme a categoria: deficiência, substituição de membro/órgão, fractura, pessoa invisual ou correcção da audição; não qualquer equipamento médico']},
{id:'human_materials',label:'Órgãos, sangue e leite humanos',field:'07',rate:0,article:'CIVA arts.9(1)(c),19(1)',source:healthGoodsSource,conditions:[internalHealthOperation,'Transmissão de órgãos, sangue ou leite de origem humana, identificada; não produto de origem animal nem serviço autónomo']},
{id:'patient_transport',label:'Transporte de doentes/feridos por organismo autorizado',field:'07',rate:0,article:'CIVA arts.9(1)(d),19(1)',source:healthGoodsSource,conditions:[internalHealthOperation,'Transporte de doente ou ferido em ambulância ou outro veículo apropriado','Organismo que efectua o transporte devidamente autorizado, com autorização identificada; não basta o destino hospitalar']},
{id:'mosquito_nets',label:'Transmissão de redes mosquiteiras',field:'07',rate:0,article:'CIVA arts.9(1)(e),19(1)',source:healthGoodsSource,conditions:[internalHealthOperation,'Bem identificado como rede mosquiteira; não extensão automática a outros produtos de prevenção']},
{id:'medicines',label:'Medicamentos e artigos terapêuticos/profilácticos elegíveis',field:'07',rate:0,article:'CIVA arts.9(1)(f),19(1)',source:healthGoodsSource,conditions:[internalHealthOperation,'Medicamento/especialidade farmacêutica ou produto farmacêutico exclusivamente terapêutico/profiláctico; ou pasta, gaze, algodão hidrófilo, tira/penso adesivo ou análogo','Finalidade elegível identificada: terapêutica/profiláctica nos produtos farmacêuticos; higiénica, medicinal ou cirúrgica nos artigos enumerados, mesmo impregnados/revestidos; não qualquer produto vendido por farmácia']},
{id:'foreign_operation',label:'Operação no estrangeiro — analisar territorialidade',field:null,rate:null,article:'CIVA art.6: localização por natureza da operação; art.19(1)(b)(ii) não determina sozinho a caixa06 do formulário',source:source2016,conditions:[]},
{id:'other',label:'Outra operação / enquadramento ainda desconhecido',field:null,rate:null,article:'Fundamento específico ainda necessário; não presumir taxa ou isenção',source:'Sem fonte operacional determinada para esta linha',conditions:[]},
];
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Tests\TestCase;
final class IvaHealthGoodsDraftTest extends TestCase
{
use DatabaseTransactions;
public function test_health_goods_round_trip_preserves_other_opportunity(): void
{
$target = DB::selectOne('select current_database() as name,inet_server_port() as port');
$this->assertSame('simulator_fiscal_delta_20261021_test', $target->name);
$this->assertSame(55441, (int) $target->port);
config(['investor-path.enabled' => true, 'suitecrm.enabled' => false]);
$this->actingAs(User::factory()->create(['email_verified_at' => now()]));
$draft = json_decode(file_get_contents(getenv('IVA_G2A_INPUT') ?: base_path('outputs/iva-l1-g2-health-20261024/calculated.json')), true, flags: JSON_THROW_ON_ERROR);
$id = $this->postJson('/api/v1/simulation-drafts', ['personal_snapshot' => [], 'economic_snapshot' => $draft], ['Idempotency-Key' => (string) Str::uuid()])->assertCreated()->json('data.id');
$recovered = $this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot');
$this->assertSame($draft['modelo_a_snapshot'], $recovered['modelo_a_snapshot']);
$book = json_decode($recovered['modelo_a_snapshot'], true)['salesOperations']['opportunity:309'];
$this->assertCount(5, $book['result']['rows']);
$this->assertSame('500.00', $book['result']['periods'][0]['fields']['07']);
$this->assertSame(json_decode($draft['modelo_a_snapshot'], true)['salesOperations']['opportunity:310'], json_decode($recovered['modelo_a_snapshot'], true)['salesOperations']['opportunity:310']);
foreach ($book['result']['rows'] as $row) $this->assertSame('0.00', $row['vat']);
if (getenv('IVA_G2A_RECOVERED')) file_put_contents(getenv('IVA_G2A_RECOVERED'), json_encode($recovered, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
}
}
File diff suppressed because one or more lines are too long
+32
View File
@@ -0,0 +1,32 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import fs from 'node:fs';
import {operationTreatments,newSalesOperation,evaluateSalesOperation,recordSales,saveSalesBook,salesBook,salesCurrent} from '../../resources/js/lib/iva-operations.ts';
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
const ids=['health_assistive','human_materials','patient_transport','mosquito_nets','medicines'];
export function healthFixture(){
const draft=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-services.json',import.meta.url),'utf8'));
draft.context_opportunity='opportunity:309';draft.project_title='SYNTHETIC QA — G2A saúde';
let book={currency:'MZN',rows:ids.map((id,i)=>({...newSalesOperation(`g2a-${i}`,'2026-02','MZN'),description:`SYNTHETIC QA ${id}`,establishment:'SYNTHETIC QA estabelecimento Manica',amount:'100',basis:i%2?'included':'net',treatment:id,confirmed:operationTreatments.find(t=>t.id===id).conditions.map((_,n)=>n),evidence:'SYNTHETIC QA — natureza, destino e autorização quando aplicável pressupostos documentados; sem certificação'})),periods:[{period:'2026-02',complete:true}]};
book=recordSales(draft,book);draft.modelo_a_snapshot=saveSalesBook(draft,book);return {draft,book};
}
test('G2A five health branches require their own nature, location, purpose and authorization, not the health sector',()=>{
const {book}=healthFixture();
for(const row of book.rows){const r=evaluateSalesOperation(row,'MZN');assert.equal(r.field,'07');assert.equal(r.net,'100.00');assert.equal(r.vat,'0.00');assert.equal(r.gross,'100.00');assert.match(r.source,/1426\(267\)/);
const count=operationTreatments.find(t=>t.id===row.treatment).conditions.length;
for(let missing=0;missing<count;missing++)assert.equal(evaluateSalesOperation({...row,confirmed:row.confirmed.filter(n=>n!==missing)},'MZN').vat,null);
assert.equal(evaluateSalesOperation({...row,evidence:''},'MZN').vat,null);
}
assert.equal(book.result.periods[0].fields['07'],'500.00');
});
test('G2A reuses saved dossier data, retains G1 unchanged, and reports recovered context without recalculating accepted treatments',()=>{
const original=JSON.parse(fs.readFileSync(new URL('./fixtures/iva-services.json',import.meta.url),'utf8'));
const before=JSON.parse(original.modelo_a_snapshot).salesOperations['opportunity:310'];
const {draft}=healthFixture();const recovered=JSON.parse(JSON.stringify(draft));assert.equal(salesCurrent(recovered,salesBook(recovered)),true);
assert.deepEqual(JSON.parse(recovered.modelo_a_snapshot).salesOperations['opportunity:310'],before);
const model=JSON.stringify(createProjectDossierReport(recovered,{selectedSections:new Set(['finance'])}));
for(const token of [...ids,'500.00','opportunity:309','Manica','MZN','2026-02'])assert.ok(model.includes(token),token);
assert.equal(model.includes('financial_stamp'),false);
assert.equal(draft.province_id,original.province_id);assert.deepEqual(draft.selection_labels,original.selection_labels);
});
if(process.env.IVA_G2A_OUT)fs.writeFileSync(process.env.IVA_G2A_OUT,JSON.stringify(healthFixture().draft,null,2));
+1 -1
View File
@@ -8,7 +8,7 @@ import {createProjectDossierReport} from '../../resources/js/lib/project-dossier
export function operationsFixture(){
const draft=closureFixture().draft;draft.project_title='SYNTHETIC QA — L1 operações IVA';draft.province_id='cf5e81f1-ffbc-535d-b1b6-b44b445c4421';draft.selection_labels={province_id:'Manica'};
const rows=operationTreatments.filter(t=>!['financial_stamp','insurance_stamp','public_waste','public_funeral'].includes(t.id)).map((t,i)=>({...newSalesOperation(`qa-${i}`,'2026-01','MZN'),description:`SYNTHETIC QA ${t.label}`,establishment:'SYNTHETIC QA estabelecimento Manica',amount:t.id==='general'?'1161':'100',basis:t.id==='general'?'included':'net',treatment:t.id,confirmed:t.conditions.map((_,i)=>i),evidence:'SYNTHETIC QA — pressupostos de ensaio, não documento real'}));
const rows=operationTreatments.filter(t=>['general','private_health','private_education','private_training','personal_lessons','export_goods','housing_rent','public_health','public_education','public_training','foreign_operation','other'].includes(t.id)).map((t,i)=>({...newSalesOperation(`qa-${i}`,'2026-01','MZN'),description:`SYNTHETIC QA ${t.label}`,establishment:'SYNTHETIC QA estabelecimento Manica',amount:t.id==='general'?'1161':'100',basis:t.id==='general'?'included':'net',treatment:t.id,confirmed:t.conditions.map((_,i)=>i),evidence:'SYNTHETIC QA — pressupostos de ensaio, não documento real'}));
const book={currency:'MZN',rows,periods:[{period:'2026-01',complete:true}]};return {draft,book};
}
test('each enumerated treatment calculates only with its conditions, article and source',()=>{