Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aaf1a56f9 | ||
|
|
2116f66b2b | ||
|
|
56475acb2f | ||
|
|
0effdc9fcb | ||
|
|
e9fe102259 |
@@ -631,3 +631,113 @@ Mesma matriz AUD-SIM-INT02. Custos/provisões/periodização demonstrados entreg
|
||||
| Outras periodizações,âmbitos eM20/M22 | Tratar apenas os casos específicos18(6–10),períodos curtos/conversão anual,representação fiscal e campos ainda sem correspondência,com os parâmetros da edição/caso;divergência201/Q8 versus200/Q10 só afecta o transporte indicado. | Apenas o caso/campo dependente; cálculos independentes preservados. |
|
||||
|
||||
L6 continua parcial. L7 mantém benefícios/casos especiais;L8 mantém retenções/pagamentos/créditos/saldo final. As dez dúvidasL5 não foram reabertas.
|
||||
|
||||
## 26/10/2026 — L6: amortizações em QA, sem instalação
|
||||
|
||||
O portal mantém `043dd35d` instalado: colecta demonstrada1417,60MZN, sem saldo finalL8. O novo conjunto optativo `IRPC-normal-adjustments/7` reutiliza os activos e o resultado financeiro já declarados. Versões1–6 e históricos permanecem intactos; activar o novo mapa substitui o efeito fiscal anterior das amortizações, sem o somar novamente. Ausência declarada de quotas anteriores é distinta de informação em falta e exige referência.
|
||||
|
||||
Cobertura delimitada ao exercício anual2026/MZN e bens novos ao custo histórico: tabelaII/3/9 computadores25%,3/9.1 servidores20%,3/23 segurança20%,3/22 máquinas não especificadas10%,5/7 mobiliário10%,1/2.2 edifícios comerciais/administrativos2%,4/9.2 veículos ligeiros/mistos25%,5/8 software25%; tabelaI/84.2/B decoração hoteleira20% e84.3/A máquinas de lavandaria12,5%. A tabelaI prevalece quando aplicável; não se presume que esta lista cobre todo o catálogo. Fonte: Decreto72/2013, artigos3–10/17 e tabelas, exemplar original anteriormente aceite e hash preservado; CIRPC27e para limite800000MZN de viaturas e exclusão de terrenos.
|
||||
|
||||
Método constante com taxa expressamente escolhida no recorte metade–máximo; degressivo apenas para bens elegíveis novos, factores máximos1,5/2/2,5 segundo vida útil. Opção8(2) demonstrada apenas em ano civil/início1Janeiro, com quotas anteriores completas documentadas. Duodécimos incluem mês de entrada e excluem mês de saída, respeitando vida máxima. Software exige direitos onerosos/exclusivos e duração limitada; art17 exige bem individual até20000 e opção documentada. Quotas anteriores recuperáveis têm prova contabilística/fiscal e são limitadas à parte elegível: quotas nunca contabilizadas ou permanentemente excluídas não se tornam dedutíveis. Nenhum saldo oficial é consumido.
|
||||
|
||||
| Novo caso | Efeito demonstrado em MZN: lucro → matéria colectável → colecta |
|
||||
|---|---|
|
||||
| Computador em uso desdeJulho: quota fiscal50, ajustamento70, substituindo anterior20 | 6780 →4480 →1433,60 |
|
||||
| Mesmo activo, saída15Outubro:3meses/quota25 | Colecta1441,60; entrada/saída identificadas no relatório |
|
||||
| Degressivo elegível: quota200, ajustamento40 | 6630 →4330 →1385,60 |
|
||||
| Quota anterior documentada:20 recuperados,20 restantes; ajustamento−20 | 6730 →4430 →1417,60 |
|
||||
| Máquina de lavandariaI/84.3/A: quota62,50, ajustamento57,50 | 6767,50 →4467,50 →1429,60 |
|
||||
| Quota contabilística anterior em falta | Lucro/base/colecta indeterminados; ajustamentos independentes conhecidos continuam discriminados |
|
||||
|
||||
Cálculo → gravação → recuperação pela API privada → relatório/dossier demonstrado em cinco casos. **18novos testes frontend;1testeAPI/84asserções**, rollback na baseQA127.0.0.1:55441; routes/actions gerados, TypeScript/build e interface real passaram, incluindo edição, reutilização, ausência declarada, falta de informação, recuperação e exportação. Provas aceites reutilizadas. **7ficheiros de runtime no delta,43protegidos e31originais conferidos**; sem migração/API/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
| Saldo concreto das amortizações | Condição de retoma |
|
||||
|---|---|
|
||||
| Outros códigos/taxas, por exemploII/3/3 ar condicionado12,5% | Acrescentar correspondência verificada, elegibilidade e prova apenas desse delta; sem aprovação genérica adicional |
|
||||
| Bens usados, abertura de escrita, grandes reparações/benfeitorias7(2)/(4), reavaliações15 e transição anterior2014 | Documentar vida esperada, bases/quotas anteriores e implementar a regra específica aplicável |
|
||||
| Uso intensivo11, concessões12, peças14, intangíveis específicos16 e eventos de transferência/relocação | Corresponder condições legais e histórico do evento; desenvolvimento/goodwill não herdam taxa do software |
|
||||
| Mudança de método10, excepções, taxas fora do recorte, vida máxima anual terminada a meio do período e opção8(2) fora do caso demonstrado | Histórico concreto e regra/excepção correspondente; autorização apenas quando a norma a exigir |
|
||||
| Restantes custos/provisões/periodizações da entrega043dd35d; períodos curtos, conversão anual, representação fiscal e correspondênciasM20/M22 | Mantêm os critérios concretos já registados na matriz; este incremento não os encerra |
|
||||
|
||||
A divergência201/quadro8 versus200/quadro10 bloqueia apenas o transporte afectado. Não foram presumidas correspondências oficiais para estes novos campos. **L6 continua parcial;L7 eL8 mantêm os seus âmbitos.** As dez dúvidasL5 não foram reabertas. Recibo: `docs/investmoz/simulations/irpc-l6-depreciation-qa-20261026.json`.
|
||||
|
||||
## 26/10/2026 — amortizações e9fe1022 entregues pelo Git
|
||||
|
||||
Instalado `e9fe102259c183a9d9bf7e39f246a8c3860839c6` sobre `043dd35d`, preservado como versão anterior. Backup fresco `2026-10-26T07:31:29Z` e restauro isolado sem rede/porta55441: **174tabelas** por contagem/checksum e **369ficheiros/configuração** conferidos. **7ficheiros do delta,43protegidos e31originais** verificados; schema,roles/grants e permissões iguais. As172tabelas de negócio mantêm os valores do backup. Cache:reinício da fila,contadores/timers e renovação da validade do mesmo valor de cache da página inicial. Sessões:expiração individual comprovada pelo prazo configurado120minutos,novas consultas da entrega e acessos anónimos correlacionados por agente/data no nginx;sem perda de dados de negócio. Dependências de5versões antigas arquivadas/verificadas para a margem do pipeline,preservando versões actual/anterior,fontes,dados e backups.
|
||||
|
||||
Revisão/manifest público,5marcadores compilados,4assets e simuladorHTTP200 conferidos. Pipeline normal concluído; provas QA18frontend/API1-84/UI/tipos/build reutilizadas,sem nova ronda manual. **Exemplo disponível: lucro6780 → matéria colectável4480 → colecta1433,60MZN. Saldo final a pagar/recuperar indeterminado emL8.** Históricos1–6 preservados; versão7 apenas por activação do novo mapa,substituindo o efeito anterior sem duplicação. Recibo:`docs/investmoz/simulations/irpc-l6-depreciation-delivery-20261026.json`.
|
||||
|
||||
O estado anterior «QA sem instalação» de e9fe1022 é histórico. Mantêm-se explícitos os outros códigos de tabela;usados/abertura de escrita/reparações/reavaliação/transição;uso intensivo/concessões/peças/intangíveis específicos/eventos;mudanças/excepções de método e opções fora do recorte. Retoma por correspondência da regra específica,base/vida/quotas e prova do delta,sem autorização genérica adicional. Períodos curtos e planos noutra moeda são o próximo incrementoQA,reutilizando o mecanismo cambial. Restantes custos/períodos/representação/camposM20-M22 conservam critérios próprios;divergência201/Q8 versus200/Q10 restringe apenas o transporte afectado. **L6 parcial;L7/L8 próprios;dez dúvidasL5 não reabertas.**
|
||||
|
||||
## 26/10/2026 — L6: períodos curtos e planos noutra moeda em QA
|
||||
|
||||
`e9fe1022` está instalado, com o âmbito de amortizações já demonstrado e os restantes casos de tabela/método explícitos acima. Este novo conjunto optativo `IRPC-normal-adjustments/8` está **emQA,sem instalação**. Reutiliza os meses e montantes do plano,os mapas fiscais emMZN e o mecanismo cambial entregue. Os históricos1–7 e o plano original não são reescritos.
|
||||
|
||||
Períodos de meses completos com termo em2026: início7(4)a,cessação7(4)b,início/fim de sujeição7(4)c e transição7(4)d,mediante identificação do evento/datas/suporte; plano com6meses ou selecção explícita de6dos12meses,sem anualizar receita,resultado,base ou taxa. Primeiro mês civil do plano é declarado,não inferido do mês orçamental. Anual diferente do ano civil conserva a correspondência do período autorizado/comunicado existente. Duodécimos ordinários do art9(3) do Decreto72/2013 aplicam o número de meses de utilização;art17 integral em período curto e restantes excepções não se tornam suportados por aproximação. Elemento conhecido como não amortizável mantém acréscimo integral,sem exigir método irrelevante.
|
||||
|
||||
Planos em outra moeda: cada mês seleccionado reutiliza ou confirma selecção cambial pelo procedimento existente,coluna/data/boletim/sentido e vínculo à origem. Moeda e valores originais permanecem no relatório. Não se adopta taxa actual,única ou de outro mês por defeito. **A conversão mensal para simulação exige pressuposto documentado;não confirma automaticamente uma regra fiscal de conversão anual.** Os mapas/activos/quotas/perdas existentes continuam emMZN e são reconciliados expressamente;nenhuma segunda conversão automática. Saldos patrimoniais de créditos em moeda estrangeira não são apresentados comoMZN pela conversão dos fluxos do plano;correspondência de saldo/data/taxa continua separada.
|
||||
|
||||
| Caso novo | Efeito demonstrado emMZN |
|
||||
|---|---|
|
||||
| Início1Julho–31Dezembro,meses7–12: resultado3120;quota fiscal50 e ajustamento10 | Lucro3130 → matéria colectável3130 → colecta1001,60,à taxa32%;ausência de perdas/benefícios declarada |
|
||||
| Mesmo recorte emUSD,pressuposto sintético64MZN/USD identificado em cada mês;mapasMZN reconciliados | Resultado199680;ajustamento640;lucro/base200320 → colecta64102,40. Taxa64 é pressupostoQA,não boletimBM real ou parâmetro fiscal confirmado |
|
||||
| Anual emUSD;plano de6meses iniciado emJulho;cessação e sujeição no mesmo ano | Novos testes próprios;respectivamente colecta128204,80 e1001,60nos casos correspondidos,sem anualização do semestre |
|
||||
| Falta de selecção de um mês,moeda incompatível ou taxa editada sem confirmação | Valores originais e conversões independentes visíveis;resultado/lucro/base/colecta dependentes indeterminados |
|
||||
| Perdas reportadas propostas num período curto | Lucro3130conhecido;dedução/base/colecta pendentes da sequência de períodos. Art41(5) exclui da contagem o período de transição7(4)d inferior a6meses;não autoriza agregar todos os períodos curtos |
|
||||
|
||||
Cálculo → gravação → recuperaçãoAPI → relatório/dossier demonstrado em4casos. **13testes novos frontend,API1teste/58asserções**, rollback na baseQA127.0.0.1:55441;routes/actions pelo procedimento existente,TypeScript/build e interface real passaram. Interface demonstrou alteração de datas e taxa,confirmação cambial,recuperação e exportação. Mudança na origem cambial invalida a actualidade do novo resultado,conservando o histórico. Provas aceites reutilizadas,sem repetição manual. Delta:**9ficheiros de runtime,43protegidos e31originais**;sem API/schema/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
| Pendência concreta | Condição de retoma e bloqueio |
|
||||
|---|---|
|
||||
| Datas parciais,períodos superiores a12meses/liquidação e curtos fora do recorte2026 | Corresponder datas/evento,parcelas de dias e regra aplicável;apenas esse período depende da implementação |
|
||||
| Dedução de perdas em sequência com períodos curtos/transição | Declarar sequência efectiva de períodos posteriores e aplicar41(5) ao caso7(4)d inferior a6meses;sem prazo presumido só por ano civil. Lucro independente continua calculável |
|
||||
| Limites anuais de publicidade e provisão de créditos em período curto | Corresponder expressamente a base/limite do período e regra de proporcionalização,se aplicável;sem anualização ou rateio presumidos. Outros custos e períodos anuais não bloqueados |
|
||||
| Quota integral art17 em período curto,opções/métodos/tabelas e casos de amortização ainda não suportados | Implementar combinação específica e provar só o novo efeito;condições restantes do conjuntoe9fe1022 continuam válidas |
|
||||
| Conversão fiscal anual confirmada,saldos em moeda estrangeira e fracções de cêntimo da conversão | Identificar regra fiscal/data/base,taxa e âmbito;para saldos,conversão própria em vez de somar fluxos;para fracções,regra de representação. Até lá,pressupostos de simulação explícitos e total indeterminado quando não representável neste recorte |
|
||||
| Restantes custos/periodizações e correspondênciasM20/M22 | Mantêm os critérios já registados. Divergência201/Q8 versus200/Q10 limita só o transporte afectado |
|
||||
|
||||
Fontes primárias reutilizadas: CIRPC Lei34/2007 PDF4/p768(58),art7(4)/(7),ePDF12/p768(66),art41(5);Decreto72/2013 PDF3/p1074(7),art9(3). Exemplar e originais/hash preservados;sem importação/publicação documental. **L6 continua parcial. Colecta não é saldo final a pagar;L7 eL8 mantêm âmbitos próprios.** As dez dúvidasL5 não foram reabertas. Recibo:`docs/investmoz/simulations/irpc-l6-periods-fx-qa-20261026.json`.
|
||||
|
||||
## 26/10/2026 — períodos curtos e planos noutra moeda56475acb entregues pelo Git
|
||||
|
||||
Instalado `56475acb2f39e58cb51664913f7fb499f58eb7ab` sobre `e9fe1022`,preservado como versão anterior. Backup fresco `2026-10-26T08:17:39Z` e restauro isolado sem rede/porta55441: **174tabelas** por contagem/checksum e **369ficheiros/configuração** conferidos. **9ficheiros do delta,43protegidos e31originais** verificados;schema,roles/grants e permissões iguais. As172tabelas de negócio mantêm os valores do backup. Cache/sessões reconciliadas com os eventos operacionais e acessos anónimos registados;expiração,quando aplicável,comprovada individualmente pelo prazo configurado. Dependências de5versões antigas arquivadas/verificadas,preservando versões actual/anterior,fontes,dados e backups.
|
||||
|
||||
Revisão/manifest público,6marcadores compilados,4assets e simuladorHTTP200 conferidos. Pipeline normal concluído;provasQA13frontend/API1-58/UI/tipos/build reutilizadas,sem nova ronda manual. **Semestre demonstrado: lucro3130 → matéria colectável3130 → colecta1001,60MZN.** PlanoUSD:pressuposto sintético64MZN/USD por mês identificado,originais conservados e mapasMZN reconciliados;lucro/base200320 → colecta64102,40MZN. **Não confirma regra fiscal de conversão anual nem boletimBM real. Saldo final a pagar/recuperar continua indeterminado emL8.** Históricos1–7 preservados;versão8 optativa,sem anualizar resultados ou usar taxa actual por defeito. Recibo:`docs/investmoz/simulations/irpc-l6-periods-fx-delivery-20261026.json`.
|
||||
|
||||
O estado anterior «QA sem instalação» de56475acb é histórico. Permanecem explícitos os casos de datas parciais/liquidação,sequência de perdas41(5),limites anuais em período curto,art17 curto/restantes amortizações,conversão fiscal anual/saldos em outra moeda/fracções e demais custos/periodizações. Condições de retoma continuam na mesma matriz. Próximo incrementoQA:representação de valores e camposM20/M22 por corresponder;divergência201/Q8 versus200/Q10 limitada ao transporte do resultado afectado,sem reabrir cálculos demonstrados. **L6 parcial;L7/L8 próprios;dez dúvidasL5 não reabertas.**
|
||||
|
||||
## 26/10/2026 — L6: representação dos valores e campos M20/M22 em QA
|
||||
|
||||
`56475acb` está instalado. Este novo conjunto optativo `IRPC-normal-adjustments/9` está **em QA, sem instalação** e acrescenta preparação dos valores/campos, sem alterar fórmulas de lucro tributável, matéria colectável ou colecta. Históricos1–8, plano original, pressupostos cambiais e demais livros preservados. A falta de uma correspondência documental afecta o campo e a sua reconciliação, mantendo os totais fiscais existentes.
|
||||
|
||||
**Representação:** valor racional exacto conservado, apresentação aproximada identificada por«≈» e fracção original; zero conhecido, informação em falta e campo sem aplicação distintos. M20A.1/quadro4/M12 pede meticais sem centavos: valores inteiros são representáveis, fracções ficam por corresponder, sem arredondamento/truncagem. Preparação M22 já aceite com valores exactos ao cêntimo preservada; fracções de cêntimo continuam explícitas e pendentes da regra fiscal aplicável. Resultado líquido negativo M20 é apresentado entre parêntesis; M22 separa campo de lucro do de prejuízo, sem colocar perda no lucro ou criar matéria colectável negativa.
|
||||
|
||||
**M20:** reutiliza as quatro correspondências directas de custos das vendas,pessoal,depreciação e juros. Discrimina as receitas e despesas operacionais por conta com suporte e reconciliação exacta ao plano do período/moeda. Demonstra prestação de serviços7.2,fornecimentos/serviços6.3,outros gastos6.8 e imposto contabilizado8.5, além das contas directas6.1.1/6.2/6.5/6.9. Rótulos das demais contas são estrutura disponível; não constituem novas regras fiscais nem provam a qualificação de uma parcela. Uma origem incompleta não apaga as contas directas independentes.
|
||||
|
||||
O imposto contabilizado tem valor/ausência e suporte próprios, e localização já incluída ou exterior ao plano. Colecta não é despesa contabilística presumida. Valor já incluído não é subtraído novamente; contradição com o mapa de imposto próprio ou coexistência das duas localizações fica identificada. O resultado líquido é apurado para preparação; o transporte automático entre edições continua impedido só no resultado afectado.
|
||||
|
||||
**M22 PDF:** correspondências de variações patrimoniais201/202,resultado203,amortizações não aceites206,provisões/imparidade209,impostos de outrem213,multas/juros compensatórios215,ajudas219,representação220,publicidade221,despesas não documentadas223,aluguer224,combustíveis225,viaturas226 e reposição de provisões tributadas239. Reutiliza os ajustamentos demonstrados; não reaplica as percentagens impressas. A representação já calculada usa80%,compatível com220. Campo213 é apenas de outrem: imposto próprio36(1)a continua **sem campo presumido**. Efeitos positivos de amortização vão a206; recuperações negativas de quotas anteriores ficam separadas até correspondência própria, sem compensação no mesmo campo.
|
||||
|
||||
Somas238/258 e lucro reconstruído apenas quando todas as parcelas têm correspondência e reconciliam; campos259/260/269/270/276/277/278/300 reutilizam o cálculo existente, mesmo havendo uma parcela sem campo. Colecta total302 só é preparada com ausência de parcela de taxa reduzida declarada com suporte;caso conhecido mantém âmbitoL7 e não apaga300. Campos309–316/saldo final permanecem emL8.
|
||||
|
||||
| Caso novo | Resultado e limite de aplicação |
|
||||
|---|---|
|
||||
| Correspondência no semestre demonstrado, ausência de imposto contabilizado e taxa reduzida declaradas | Resultado líquido3120;203=3120;206=10;238=3130;258=0;lucro/base3130;colecta normal300 e total302=1001,60MZN. Reconciliação aritmética exacta, sem transporte automático entre edições |
|
||||
| Fornecimentos300,50 e outros gastos299,50, origem600 | Parcela300,50 conserva601/2;M20 em meticais inteiros pendente só nas parcelas fraccionárias;resultado e colecta1001,60inalterados |
|
||||
| Imposto contabilizado em falta | Resultado líquido/campo200 e somas dependentes indeterminados;lucro/base3130 e colecta1001,60mantidos |
|
||||
| Recuperação de quota anterior:ajustamento−26 | Efeito fiscal e colecta1001,60preservados;sem código de dedução inventado,somas238/258 pendentes;260/278/300 conhecidos |
|
||||
| Imposto próprio320 contabilizado exterior ao plano | Resultado líquido2800 conhecido;campo de acréscimo próprio por corresponder,sem usar213 de outrem;lucro/base3130 e colecta1001,60preservados |
|
||||
|
||||
Percurso cálculo → gravação → recuperação privada → relatório/dossier demonstrado em **5casos**. **15testes novos frontend;API1teste/97asserções**, transacção revertida na baseQA127.0.0.1:55441;routes/actions gerados pelo procedimento existente,TypeScript/build e interface real passaram. Interface demonstrou edição de parcelas,imposto em falta sem apagar valor anterior,recuperação/exportação e pendências apenas dependentes. Provas aceites reutilizadas;sem repetir suites anteriores. **4ficheiros de runtime no delta,50protegidos e31originais** conferidos;sem API/schema/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
Fontes: PDF M22 SHA2564b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88,3páginas já aceites reutilizadas;M20A.1 SHA2561ceca6f4c6d77853c1b2b445876599b42093175fa89eecf40590180956d83a9e,M12/D13–D35. Inventariados os20ficheiros acessíveis deModelos/Notas,incluindo temporários;originais/hash preservados. Só rótulos genéricos necessários extraídos do XLSX;sem valores privados,fórmulas/cache do exemplar,importação ou publicação documental.
|
||||
|
||||
| Saldo concreto da preparação M20/M22 | Condição de retoma e alcance |
|
||||
|---|---|
|
||||
| Transporte do resultado M20A.1/D35→M22 | Exemplar compatível ou instrução que resolva201/quadro8 versus200/quadro10. Só esse transporte fica impedido;valor líquido próprio e demais campos continuam visíveis |
|
||||
| Imposto próprio36(1)a,quota anterior recuperada e restantes efeitos de periodização/obras/reversões sem campo | Corresponder rótulo,edição/período e regra específica do campo. Efeito/valor conservados sem código provisório;apenas somas/campos dependentes aguardam |
|
||||
| Fracções M20 e subcentavos M22 | Identificar regra de representação do caso;preservar fracção e apresentar aproximação identificada até então. InteirosM20 e cêntimos exactosM22 não bloqueados |
|
||||
| Imposto contabilizado simultaneamente incluído e exterior ao plano | Discriminar ambas as componentes/referências e conciliar8.5/acréscimo próprio sem repetir gasto;sem inferir o valor da colecta |
|
||||
| Contas/quadros e anexos restantes fora das seis linhas financeiras demonstradas | Reutilizar dados existentes,classificar componentes com suporte e acrescentar correspondência específica;sem tomar campos preenchidos do exemplar como dados do investidor |
|
||||
|
||||
L7 mantém benefícios/casos especiais/taxas reduzidas eL8 mantém retenções,pagamentos,créditos e saldo final. Restantes regras deL6 e as dez dúvidasL5 conservam o saldo e as condições já documentadas. **L6 continua parcial;colecta não é saldo final a pagar.** Recibo:`docs/investmoz/simulations/irpc-l6-values-forms-qa-20261026.json`.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,682 @@
|
||||
{
|
||||
"status": "installed_L6_short_periods_and_foreign_currency_plans",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "56475acb2f39e58cb51664913f7fb499f58eb7ab",
|
||||
"release": "/var/www/investmoz-homologacao/releases/56475acb2f39e58cb51664913f7fb499f58eb7ab-694b3932",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/e9fe102259c183a9d9bf7e39f246a8c3860839c6-0e29d858",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"previous_baseline": "e9fe102259c183a9d9bf7e39f246a8c3860839c6",
|
||||
"backup": {
|
||||
"stage": "/var/backups/investmoz-irpc-l6-periods-fx-delivery-20261026/backup",
|
||||
"created_at": "2026-10-26T08:17:39Z",
|
||||
"target": {
|
||||
"database": "teste_investidor",
|
||||
"port": 5432
|
||||
},
|
||||
"tables": 174,
|
||||
"files_verified": 369,
|
||||
"dump_sha256": "9ecd9f379351437f88009aa6da072e09ec8616fd782b929919ade05be1493bdc",
|
||||
"files_sha256": "6da03ab176387b105da1345764f3b292794d8867d0ed4add47197e0a1e5854fa",
|
||||
"restore_verified": false
|
||||
},
|
||||
"restore": {
|
||||
"restore_verified": true,
|
||||
"tables_verified": 174,
|
||||
"container_network": "none",
|
||||
"database": "irpc_l5_restore_20261025",
|
||||
"port": 55441,
|
||||
"source_dump_sha256": "9ecd9f379351437f88009aa6da072e09ec8616fd782b929919ade05be1493bdc",
|
||||
"active_database_writes": 0
|
||||
},
|
||||
"preservation": {
|
||||
"previous_release_preserved": "/var/www/investmoz-homologacao/releases/e9fe102259c183a9d9bf7e39f246a8c3860839c6-0e29d858",
|
||||
"installed": {
|
||||
"status": "installed",
|
||||
"commit": "56475acb2f39e58cb51664913f7fb499f58eb7ab",
|
||||
"release": "/var/www/investmoz-homologacao/releases/56475acb2f39e58cb51664913f7fb499f58eb7ab-694b3932",
|
||||
"previous_release": "/var/www/investmoz-homologacao/releases/e9fe102259c183a9d9bf7e39f246a8c3860839c6-0e29d858",
|
||||
"database_migrations": 0,
|
||||
"content_imports": 0
|
||||
},
|
||||
"runtime_files_verified": 9,
|
||||
"preserved_L3_ISPC_FX_runtime_files_verified": 43,
|
||||
"documentary_files_verified": 31,
|
||||
"persistent_files_and_env_verified": 369,
|
||||
"tables_compared": 174,
|
||||
"changed_tables": {
|
||||
"cache": {
|
||||
"before": {
|
||||
"rows": 106,
|
||||
"checksum": "1b8bf615a2fdea986761abc88bfb4419"
|
||||
},
|
||||
"after": {
|
||||
"rows": 106,
|
||||
"checksum": "a845f75cfcf49b86ebb8a3816f886a63"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"before": {
|
||||
"rows": 9,
|
||||
"checksum": "635e8bece48c8f7780635c0c79cd6010"
|
||||
},
|
||||
"after": {
|
||||
"rows": 12,
|
||||
"checksum": "703dbf39af0534461019da7c73354454"
|
||||
}
|
||||
}
|
||||
},
|
||||
"preserved_counts": {
|
||||
"published_fiches": 29,
|
||||
"relations": 28,
|
||||
"geometries": 408
|
||||
},
|
||||
"migration_count": 0,
|
||||
"content_imports": 0,
|
||||
"comparison_role": "postgres_before_and_after;sameexportedsnapshotbaseline;noapplicationRLSvisibilityinference",
|
||||
"writes_by_verification": 0
|
||||
},
|
||||
"schema_permissions": {
|
||||
"schema_sha256": "5045b3e51d2185377aa3d9ac7fa07a44fad3ec620cdfa83ffe31c8884ee1601d",
|
||||
"roles_grants_sha256": "1530c33cb016e67bfdc64891ddfce7f185b13b56b2cd87c012871a685e118109",
|
||||
"filesystem_permissions": {
|
||||
".env": {
|
||||
"mode": 416,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"storage/app": {
|
||||
"mode": 1533,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
},
|
||||
"bootstrap/cache": {
|
||||
"mode": 1528,
|
||||
"uid": 1001,
|
||||
"gid": 33
|
||||
}
|
||||
},
|
||||
"comparison_identity": "postgres;catalogue read only;no passwords collected",
|
||||
"unchanged": true
|
||||
},
|
||||
"operational_delta": {
|
||||
"status": "operational_delta_reconciled",
|
||||
"comparison_identity": "postgres before/after;isolated restored baseline;no RLS visibility inference",
|
||||
"cache": "queue:restart;requestthrottle;identical home-sector cached value with refreshed expiry(HomeController remember10min);no deletedcache",
|
||||
"sessions": "anonymous sessions from delivery user-agent or exactUA/time correlated with nginx public request;deleted sessions individually expired beyond configured lifetime,normal database session garbage collection(config/session lottery2/100)",
|
||||
"delta": {
|
||||
"cache": {
|
||||
"before_rows": 106,
|
||||
"after_rows": 106,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "dbb99b7e17f5b4b9bb7a8bdf9350d9b2ae368aaa4bc44593a2522d87617d8461",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-{hash}",
|
||||
"is_queue_restart_key": false,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": true,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
},
|
||||
{
|
||||
"key_sha256": "c1c7bc86ef3255c103a0be6aeb651b824eb07259e993cc5daf3665c1a6afff2d",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-{hash}:timer",
|
||||
"is_queue_restart_key": false,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": true,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
},
|
||||
{
|
||||
"key_sha256": "7f7dd83f47f5bf404d55e6e9f1367948721579970b195680645e356031a070ff",
|
||||
"kind": "updated",
|
||||
"fields": [
|
||||
"value",
|
||||
"expiration"
|
||||
],
|
||||
"key_pattern": "investemoz-cache-illuminate:queue:restart",
|
||||
"is_queue_restart_key": true,
|
||||
"expiration_unchanged": false,
|
||||
"throttle_timer_pair": false,
|
||||
"same_value_home_cache_expiry_refresh": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"sessions": {
|
||||
"before_rows": 9,
|
||||
"after_rows": 12,
|
||||
"changes": [
|
||||
{
|
||||
"key_sha256": "6e12944bc8f670195d0c6d2638337e2bf922261a158d130ba09f640a5722f0df",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "11bbe5f81c536cf605ed71f402fbaa6e9ef551d97c0a76e098078521d9f37f71",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
},
|
||||
{
|
||||
"key_sha256": "d0a4dbafaf19e2eee3af4ea41ce9aabf52cb42d5a61a598c54b37d8863b533d7",
|
||||
"kind": "added",
|
||||
"fields": [
|
||||
"id",
|
||||
"ip_address",
|
||||
"user_agent",
|
||||
"payload",
|
||||
"last_activity"
|
||||
],
|
||||
"anonymous_user": true,
|
||||
"delivery_user_agent": true,
|
||||
"expired_before_comparison": false,
|
||||
"session_lifetime_minutes": 120,
|
||||
"anonymous_public_request_logged": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"business_tables_to_confirm_separately": 172,
|
||||
"active_data_writes_by_comparison": 0,
|
||||
"restore_network": "none"
|
||||
},
|
||||
"dependency_archiving": {
|
||||
"status": "dependencies_archived_and_verified",
|
||||
"versions": 5,
|
||||
"free_before": 5329760256,
|
||||
"free_after": 6645653504,
|
||||
"receipt": "/var/backups/investmoz-build-dependencies-irpc-l6-periods-fx-20261026/receipt.json",
|
||||
"protected": [
|
||||
"/var/www/investmoz-homologacao/releases/043dd35d8ee247123b3a32f1fc10fd2ede81e9f4-917b3c47",
|
||||
"/var/www/investmoz-homologacao/releases/e9fe102259c183a9d9bf7e39f246a8c3860839c6-0e29d858"
|
||||
],
|
||||
"active_or_previous_release_modified": false,
|
||||
"source_data_backups_deleted": 0
|
||||
},
|
||||
"public_verification": {
|
||||
"status": "installed_delta_verified",
|
||||
"served_revision": {
|
||||
"commit": "56475acb2f39e58cb51664913f7fb499f58eb7ab",
|
||||
"branch": "main",
|
||||
"built_at": "2026-10-26T08:23:47.654431+00:00",
|
||||
"manifest_sha256": "f6121cade74d12c6de25630b23a1bd73f244a089c2649bf2c3aa76c68491431c"
|
||||
},
|
||||
"manifest_hash_matches": true,
|
||||
"compiled_delta_markers": {
|
||||
"IRPC-normal-adjustments/8": [
|
||||
"assets/project-dossier-BGxws1O_.js"
|
||||
],
|
||||
"Preparar período curto ou plano noutra moeda": [
|
||||
"assets/services-08vnaMl1.js"
|
||||
],
|
||||
"Primeiro mês civil do plano": [
|
||||
"assets/services-08vnaMl1.js"
|
||||
],
|
||||
"Pressuposto documentado da conversão mensal para simulação": [
|
||||
"assets/services-08vnaMl1.js"
|
||||
],
|
||||
"Saldo final — âmbito L8": [
|
||||
"assets/project-dossier-BGxws1O_.js"
|
||||
],
|
||||
"Reutilizar selecção cambial": [
|
||||
"assets/services-08vnaMl1.js"
|
||||
]
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"file": "assets/services-08vnaMl1.js",
|
||||
"status": 200,
|
||||
"sha256": "6cc2b1bfb11744376ae71d1f0429ee776ef6f004edca72a4f22c5a9ef4937688"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-BGxws1O_.js",
|
||||
"status": 200,
|
||||
"sha256": "f2dc461eca8219602099ac2cf17f52f5adedc4553f4b0e48bbeba273041d9bca"
|
||||
},
|
||||
{
|
||||
"file": "assets/project-dossier-document-BYlep2jg.js",
|
||||
"status": 200,
|
||||
"sha256": "0ece3300111d8e5c0e094c2c1688604349de11cde057583bd83d8ae3742c8a99"
|
||||
},
|
||||
{
|
||||
"file": "assets/simulation-form-BqVD91Ek.js",
|
||||
"status": 200,
|
||||
"sha256": "85cbd533c5bc92af5a448db71da006d4346d2d0adb09dde825db0aa19ad5048c"
|
||||
}
|
||||
],
|
||||
"simulator_status": 200,
|
||||
"simulator_entry": "assets/app-DoIk7pr3.js",
|
||||
"existing_fiscal_availability": {
|
||||
"data": {
|
||||
"enabled": true,
|
||||
"coverage_start": "2026-01-01",
|
||||
"coverage_end": "2026-12-31",
|
||||
"message": "IVA geral sobre base líquida disponível; enquadramento declarado."
|
||||
}
|
||||
},
|
||||
"synthetic_private_writes": 0,
|
||||
"method": "Read-only installedrevision/manifest/newcompiledcatalogueandreaders/simulatorentry;acceptedQAproofsreused"
|
||||
},
|
||||
"runtime_delta": [
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-normal.tsx",
|
||||
"sha256_LF": "557714bb3af75080e443ae67b11a66d9feb49d9a4b269065769b128a6d712837"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-period-fx.tsx",
|
||||
"sha256_LF": "4b5f718b5ef415ecf5f96dabb0e3806254558c20905de6478e10dacf67569c49"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-schedules.tsx",
|
||||
"sha256_LF": "3768c316d5887d337eb3647671863c9464f961d1df4703403e7d57e5641273ed"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-assessment.ts",
|
||||
"sha256_LF": "a9b3ced852e37e3e087ce5dab1add4ec0737145ce19f61857a54d5d3d24a6f8c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-base.ts",
|
||||
"sha256_LF": "22019d678f6eb16fead2207dc3d3fa9fd63413c6254633c1ffd4f1c0320344ac"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-costs.ts",
|
||||
"sha256_LF": "471457692a2a2f70a1bd1c7abb38d3ce42f07b62a63e5a4ab985ad1bbd068b46"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-depreciation.ts",
|
||||
"sha256_LF": "1dad3053157aeac44f6d3bd58885cfc47a73c19d2808284c16931679dfe4291b"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-period-fx.ts",
|
||||
"sha256_LF": "a2e7b066623eae17f0d8185d065d0adf5d3a485f780f8b09118a664dc4ba0531"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal.ts",
|
||||
"sha256_LF": "e4de2fe0421e489bf555885b9cbb459c00ee77803ea9b6e5af45179bd6bb4f21"
|
||||
}
|
||||
],
|
||||
"protected_runtime": [
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-depreciation.tsx",
|
||||
"sha256": "1f9918bfb435bb5b7c0ddd2fafd7bace2a02514c6ece04b93f15975e494a5f53"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-profit.ts",
|
||||
"sha256": "909e129a0245e6bf098317485b886a2d19e02c64bbfd9c6f718e98ec6b935b8d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-schedules.ts",
|
||||
"sha256": "edfb6fd338411fa8f87a6bdf9a9d4fd5c6dc2fad9d2eb3d32f2640e2951cbbe9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-costs.tsx",
|
||||
"sha256": "c4156916835daf0f33addf89dfedd40c31b8dc3297112cd98beac33422af274d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-forms.ts",
|
||||
"sha256": "4e986addb9f2d74e7a80345f8f1e4d8e0b2f562c867fa6eee84880d9f72a3649"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-taxable-base.tsx",
|
||||
"sha256": "e4fe9f66d35d24aa68fd87de6a5a6a0e901706b270c18962fd0c7ab00f9abd93"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-assessment.tsx",
|
||||
"sha256": "7174cc0160005a0c332ea40f02ba8a6d57985081b5ab45177ccd6e721bceea54"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/acquisition-beverage-restitution.tsx",
|
||||
"sha256": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-calculation-gaps.tsx",
|
||||
"sha256": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-l3-preparation.tsx",
|
||||
"sha256": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-acquisitions.tsx",
|
||||
"sha256": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-operations.tsx",
|
||||
"sha256": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisition-bc.ts",
|
||||
"sha256": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-allocation.ts",
|
||||
"sha256": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-restitution.ts",
|
||||
"sha256": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-calculation-gaps.ts",
|
||||
"sha256": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-operations.ts",
|
||||
"sha256": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/l3-form-preparation.ts",
|
||||
"sha256": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-residuals.ts",
|
||||
"sha256": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-residuals.tsx",
|
||||
"sha256": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-eligibility.ts",
|
||||
"sha256": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-quarter.tsx",
|
||||
"sha256": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-withholding.tsx",
|
||||
"sha256": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-quarter.ts",
|
||||
"sha256": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-withholding.ts",
|
||||
"sha256": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-eligibility.tsx",
|
||||
"sha256": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisitions.ts",
|
||||
"sha256": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-modelo-a.ts",
|
||||
"sha256": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-fx-selection.ts",
|
||||
"sha256": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-fx-selection.tsx",
|
||||
"sha256": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/business-financial-plan.ts",
|
||||
"sha256": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-currency-events.ts",
|
||||
"sha256": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-fx.ts",
|
||||
"sha256": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-withholdings.ts",
|
||||
"sha256": "8e5f3c035ce5e4d6c3fc82f933439ab580fa5519492f9c4d32cfb71b77b54803"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
|
||||
"sha256": "751af4fab346659bc5ccb08b8a2169769af7521ea0035050af32b41ea7f5aa76"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-interest.ts",
|
||||
"sha256": "f8e6390b3b2c58c296f7e3fd14576e06ae248bc0cabce1a666b7094611f2a7dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta.ts",
|
||||
"sha256": "76dcce8954f68b081d500be61de9c5b146a452a3eb578a0b78776ba9e1f8051c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta-cases.ts",
|
||||
"sha256": "4266746076ab3f70312fd042351e24dd2f136c4f3e8a277b57dd97849a9f1862"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-mrfta.tsx",
|
||||
"sha256": "913d1bb4ac0fc59763cf5d27bc637f6a713b2050c4463748db554af4be451538"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/financial-fiscal-bridge.ts",
|
||||
"sha256": "5a30c501653b16b5fedcb12631ce81fcce4fe473aa542e94374986b3c50f2d33"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-form.tsx",
|
||||
"sha256": "85b024a7f45ceb52efd6ff1f21d0eab1fd8fcf1ab59e4b5302c08685235c472a"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier.ts",
|
||||
"sha256": "a0909bbfcdf7e8542b010faa104fcb918e86ae38ddf096082d4d5be98d31b2b9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier-report.ts",
|
||||
"sha256": "5207a2ffeb208cd5ace6713038cee85208020ee618194dca7114e94fc3200b6c"
|
||||
}
|
||||
],
|
||||
"originals": [
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Comunicado-9-2026-ISPC.pdf",
|
||||
"sha256": "106f8a9ce9255c03549c70bf07fe999d32e6e55302d009b509862aa74ccb4973"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-30-ISPC.pdf",
|
||||
"sha256": "6802edd06b6ff638a0b851a3ec7ecf69af8f07840faa47446becad67d35d445c"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-B-Geral.pdf",
|
||||
"sha256": "bd977c7c59d4d98befd92e2c566b1bf168cb1343bb519f51b41a3807f431fc57"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-MRFTA-19-39.pdf",
|
||||
"sha256": "8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BM-Mercado-Monetario-MAIBOR-20261025.html",
|
||||
"sha256": "00e8a7e0d3a1db4ca3beb56d5361b2084a0f1684f3d40fc1c203702a2190e063"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-16-I-Serie-2008-04-16.pdf",
|
||||
"sha256": "fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-248-I-Suplemento-1-2025.pdf",
|
||||
"sha256": "ccf2a8deeca820f2dd076b4b2141b1efe3f6a806568c3a587ca59a8e92f4c251"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-89-I-Serie-2017-06-08.pdf",
|
||||
"sha256": "11f0cab15eb6e51010952203ffa24fdc533a24fd642e19525fe4f46b17a1f5e5"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-95-I-Suplemento-2025-05-21.pdf",
|
||||
"sha256": "e0e682e2c027c3cd9c1d46a73a3f48bc5f6bdbf05667fd97314d7a3f6a456358"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR252-I-Serie-2022-Lei20-PESOE.pdf",
|
||||
"sha256": "1159fd3ce1ba5581671796d083534da48bd3c5578298819e896f1c2a8026318d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-22-2021-BR70.pdf",
|
||||
"sha256": "a8fac38dcfe08f9c7a8e46254cff10e1ec82fc4f7a8d88ad6e4fe7d91ffc21a5"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-27-2026-BR123.pdf",
|
||||
"sha256": "26249c3fa58f149c81de36e99d1aac2819a4771379d9e8833159660dabf539bb"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-29-1998-BR22-5-suplemento.pdf",
|
||||
"sha256": "6242cbe675faadf085f43f95ec8c7e8868aafe67c3b2ab2bb010992288f954f8"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-3-2011-BR-11-I-mineiros.pdf",
|
||||
"sha256": "5bca1133666ba9c69870b89823420175b7b5b8d0eccb667ecdf5c29716f3b6be"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-37-2023-BR124.pdf",
|
||||
"sha256": "7e680b191481faa0e2e9057fa5053587eb2a29d1d8c819e0eb9f02814255a04c"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-9-2008-BR16.pdf",
|
||||
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-13-2016-CIVA-BR-156-10-suplemento.pdf",
|
||||
"sha256": "b90dc7c14ecea2e378c4a62e8e5a47d54a5644c457baeb8b8053b92fd2a5710b"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-14-1992-BR42.pdf",
|
||||
"sha256": "089fff7423e99a70a230a73ca379e4ea3a91d124703f91016e57ff82794b9e56"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-BR251-exemplar-MCNet5.pdf",
|
||||
"sha256": "14eb34f0af91a8f5cb1770c7b52d6ff839c6818302d1ed96d79afbfb9fe999d4"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-IPP-BR250-Parlamento.pdf",
|
||||
"sha256": "978cd22403ffd336a631704ecd7a5bbd814a427c815b9acdaca5515591dd9e2a"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-19-2013-BR76.pdf",
|
||||
"sha256": "bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-19-2017-IRPS-BR-OCAM.pdf",
|
||||
"sha256": "c3a0186cb9ad090d7a9533006295dc9bb6a62ad0d6150173cbd49dba4ee4b52d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-2-2011-BR-1-4-suplemento-MCNet.pdf",
|
||||
"sha256": "a7543a68cc3287a46a83c39a04821b40f649dd06c4e9b5dc51ff0b47fba08899"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-20-2009-BR36-AT-exemplar19.pdf",
|
||||
"sha256": "fc1c8144db42960cbf73e717e095896f511ece0f907f58f78819b27d6917a7ee"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-22-2022-IVA-republicada-BR-250-2-suplemento.pdf",
|
||||
"sha256": "ea1b0dc6f5cf6e595f6d9a19e7042f398999e0087caef1785f6e8fac571b5f53"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-34-2007-BR52.pdf",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-1994-BR37-3-suplemento.pdf",
|
||||
"sha256": "210df9db8980d0a5690cd187a6eeec7e37a900d743e39062c16f4e0d12215c7e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-2012-BR3.pdf",
|
||||
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-7-1991-Partidos-IFES.pdf",
|
||||
"sha256": "6b783a0bb3330688ef10660a58060d9724aa2da6a873d234fe902f0eaaeda42d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-CDT-1992.pdf",
|
||||
"sha256": "d504e1024b28aee5c6acf4592acb96ec9c915a8414f67a61ab6984d6acf9df9d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-Protocolo-2009.pdf",
|
||||
"sha256": "f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b"
|
||||
}
|
||||
],
|
||||
"pipeline_checks": [
|
||||
"OK (1 test, 3106 assertions)",
|
||||
"built in 31.08s",
|
||||
"OK (59 tests, 317 assertions)",
|
||||
"# tests 973",
|
||||
"# pass 973",
|
||||
"# fail 0"
|
||||
],
|
||||
"accepted_QA_proofs": {
|
||||
"frontend_tests": 13,
|
||||
"API_tests": 1,
|
||||
"API_assertions": 58,
|
||||
"database": "127.0.0.1:55441/simulator_fiscal_delta_20261021_test;DatabaseTransactions rollback",
|
||||
"routes_actions": "generated by guarded existing procedure",
|
||||
"typescript": "passed",
|
||||
"build": "passed",
|
||||
"UI": "actual component;dates/FX edit+confirmation;originals/assumptions;save/recovery/export;missingdata",
|
||||
"accepted_proofs": "reused"
|
||||
},
|
||||
"accepted_proofs_reused": true,
|
||||
"manually_repeated_accepted_suites": false,
|
||||
"sequence": {
|
||||
"short_period": "3130→3130→colecta1001.60MZN",
|
||||
"USD_simulation64": "200320→200320→colecta64102.40MZN;synthetic QA assumption,not real bulletin or fiscal annual confirmation",
|
||||
"final_payable": null,
|
||||
"final_recoverable": null,
|
||||
"final_balance_scope": "L8"
|
||||
},
|
||||
"remaining_L6": "Representation/fiscal precision and remaining M20/M22 fields nextQA;partial dates/loss period sequence/short annualcaps/remaining depreciation and costs/annualFX and foreignbalances;conditions preserved in same matrix.",
|
||||
"L6_complete": false,
|
||||
"L7_L8_own_scopes": true,
|
||||
"proof_files": {
|
||||
"backup.txt": "b1b580ee73bdf6f2a2d3daaecf1b983e2084fea0a5e69e7fa3486d938cf7a87f",
|
||||
"compact-dependencies.txt": "18375c30afc5fac24e8111779d455808fa363e02c5e38c8a92af6268dcbae7b4",
|
||||
"installed-protected.txt": "39cd2444e61f5aca2ce41f2eba245defa246aacb908e066d43019e44c2e07e4e",
|
||||
"installed-public-delta.json": "f35d40c06ff3a428c37e88b63dfa09ab745376c4c15747176382e9b364f80d5b",
|
||||
"installed-public.txt": "7cd1a559a1c7d327a2d71846abdbe33cda63fdb99c0fdcf1584a75a5ff7c138a",
|
||||
"integration.txt": "2526b168e5c6ef2515a9351dcdd56f1bbe9a119c073522376715cc815f7e673b",
|
||||
"manifest-served.json": "f6121cade74d12c6de25630b23a1bd73f244a089c2649bf2c3aa76c68491431c",
|
||||
"operational-delta.txt": "cadac629ef03871c14e3dd7186ce95379c526927bfefcae4466d6cb88897c1a4",
|
||||
"preflight.txt": "dbdc76e4c45a45c127ce733def4260601074cee2bcf4cd6182dae06b3e7652a8",
|
||||
"preparation.json": "dd755c3cad122e3730f57f8a94ee82dda74bfe49925cea95a518199437706e1a",
|
||||
"progress.txt": "8ea7368149372f43ee10075198a3cc840394f866158a242353bdd7a3fd8e27a2",
|
||||
"restore.txt": "5a7358ce267845262582c50261f85ab5d035bc16dc9573dd0554083f4457b324",
|
||||
"schema-permissions-after.txt": "76b5a4d8af11e4898a205062766cfb7ab6ec0783200e77e60a1927aa1ca18597",
|
||||
"schema-permissions-before.txt": "0ab7c964792acdd8917bce256c472e55e034b103500a58b49f020b5084823ed7",
|
||||
"status-final.txt": "3b882f9da83f52e66876e3a2456d50e0454d44668e168e9967c290411af9b0e1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,869 @@
|
||||
{
|
||||
"status": "short_period_and_foreign_currency_plan_QA_prepared_not_installed",
|
||||
"installed_baseline": "e9fe102259c183a9d9bf7e39f246a8c3860839c6",
|
||||
"branch": "codex/irpc-l6-periods-fx-qa-20261026",
|
||||
"version": "IRPC-normal-adjustments/8",
|
||||
"cases_recovered": {
|
||||
"short": {
|
||||
"accountingProfit": {
|
||||
"numerator": "3120",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableProfit": {
|
||||
"numerator": "3130",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableBase": {
|
||||
"numerator": "3130",
|
||||
"denominator": "1"
|
||||
},
|
||||
"colecta": {
|
||||
"numerator": "5008",
|
||||
"denominator": "5"
|
||||
},
|
||||
"period": {
|
||||
"version": 1,
|
||||
"kind": "start",
|
||||
"from": "2026-07-01",
|
||||
"to": "2026-12-31",
|
||||
"planStart": "2026-01-01",
|
||||
"reference": "QA início documentado1Julho;correspondência completa dos meses7–12 ao período do sujeito",
|
||||
"conversionReference": "",
|
||||
"mznMapsReference": "QA activo/quotas/juros e demais mapas reconciliados explicitamente emMZN no período",
|
||||
"months": []
|
||||
},
|
||||
"pending": []
|
||||
},
|
||||
"fx": {
|
||||
"accountingProfit": {
|
||||
"numerator": "199680",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableProfit": {
|
||||
"numerator": "200320",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableBase": {
|
||||
"numerator": "200320",
|
||||
"denominator": "1"
|
||||
},
|
||||
"colecta": {
|
||||
"numerator": "320512",
|
||||
"denominator": "5"
|
||||
},
|
||||
"period": {
|
||||
"version": 1,
|
||||
"kind": "start",
|
||||
"from": "2026-07-01",
|
||||
"to": "2026-12-31",
|
||||
"planStart": "2026-01-01",
|
||||
"reference": "QA início documentado1Julho;correspondência completa dos meses7–12 ao período do sujeito",
|
||||
"conversionReference": "QA pressuposto de simulação64MZN/USD aplicado aos meses seleccionados;sem regra fiscal anual confirmada",
|
||||
"mznMapsReference": "QA activo/quotas/juros e demais mapas reconciliados explicitamente emMZN no período",
|
||||
"months": [
|
||||
{
|
||||
"period": 7,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 8,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 9,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 10,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 11,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 12,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.920Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"pending": []
|
||||
},
|
||||
"missing": {
|
||||
"accountingProfit": null,
|
||||
"taxableProfit": null,
|
||||
"taxableBase": null,
|
||||
"colecta": null,
|
||||
"period": {
|
||||
"version": 1,
|
||||
"kind": "start",
|
||||
"from": "2026-07-01",
|
||||
"to": "2026-12-31",
|
||||
"planStart": "2026-01-01",
|
||||
"reference": "QA início documentado1Julho;correspondência completa dos meses7–12 ao período do sujeito",
|
||||
"conversionReference": "QA pressuposto de simulação64MZN/USD aplicado aos meses seleccionados;sem regra fiscal anual confirmada",
|
||||
"mznMapsReference": "QA activo/quotas/juros e demais mapas reconciliados explicitamente emMZN no período",
|
||||
"months": [
|
||||
{
|
||||
"period": 7,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.924Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 8,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.924Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 9,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.924Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 10,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.924Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"period": 11,
|
||||
"currency": "USD",
|
||||
"fxSelection": {
|
||||
"nature": "commercial",
|
||||
"submissionDate": "2026-10-26",
|
||||
"bulletinDate": "2026-10-26",
|
||||
"bulletinRef": "QA pressuposto sintético64; não boletim real",
|
||||
"source": "https://www.bancomoc.mz/",
|
||||
"sourceCurrency": "USD",
|
||||
"direction": "mzn_per_unit",
|
||||
"bulletinPerspective": "",
|
||||
"relation": "",
|
||||
"buy": "",
|
||||
"sell": "",
|
||||
"mean": "64",
|
||||
"mode": "general",
|
||||
"exceptionRef": "",
|
||||
"exceptionScope": "",
|
||||
"exceptionDate": "",
|
||||
"exceptionRate": "",
|
||||
"applied": {
|
||||
"column": "mean",
|
||||
"rate": "64",
|
||||
"date": "2026-10-26",
|
||||
"source": "QA pressuposto sintético64; não boletim real;https://www.bancomoc.mz/;perspectiva:média de referênciaBM;natureza:commercial;relação:não aplicável à média;Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.",
|
||||
"selectedAt": "2026-10-26T07:51:24.924Z",
|
||||
"selectionKey": "{\"nature\":\"commercial\",\"submissionDate\":\"2026-10-26\",\"bulletinDate\":\"2026-10-26\",\"bulletinRef\":\"QA pressuposto sintético64; não boletim real\",\"source\":\"https://www.bancomoc.mz/\",\"sourceCurrency\":\"USD\",\"direction\":\"mzn_per_unit\",\"bulletinPerspective\":\"\",\"relation\":\"\",\"buy\":\"\",\"sell\":\"\",\"mean\":\"64\",\"mode\":\"general\",\"exceptionRef\":\"\",\"exceptionScope\":\"\",\"exceptionDate\":\"\",\"exceptionRate\":\"\"}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"pending": [
|
||||
"revenue/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.",
|
||||
"cost_of_sales/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.",
|
||||
"operating_expenses/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.",
|
||||
"personnel/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.",
|
||||
"depreciation/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.",
|
||||
"interest_expense/mês12: Error: Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito."
|
||||
]
|
||||
},
|
||||
"loss": {
|
||||
"accountingProfit": {
|
||||
"numerator": "3120",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableProfit": {
|
||||
"numerator": "3130",
|
||||
"denominator": "1"
|
||||
},
|
||||
"taxableBase": null,
|
||||
"colecta": null,
|
||||
"period": {
|
||||
"version": 1,
|
||||
"kind": "start",
|
||||
"from": "2026-07-01",
|
||||
"to": "2026-12-31",
|
||||
"planStart": "2026-01-01",
|
||||
"reference": "QA início documentado1Julho;correspondência completa dos meses7–12 ao período do sujeito",
|
||||
"conversionReference": "",
|
||||
"mznMapsReference": "QA activo/quotas/juros e demais mapas reconciliados explicitamente emMZN no período",
|
||||
"months": []
|
||||
},
|
||||
"pending": []
|
||||
}
|
||||
},
|
||||
"verification": {
|
||||
"frontend_tests": 13,
|
||||
"API_tests": 1,
|
||||
"API_assertions": 58,
|
||||
"database": "127.0.0.1:55441/simulator_fiscal_delta_20261021_test;DatabaseTransactions rollback",
|
||||
"routes_actions": "generated by guarded existing procedure",
|
||||
"typescript": "passed",
|
||||
"build": "passed",
|
||||
"UI": "actual component;dates/FX edit+confirmation;originals/assumptions;save/recovery/export;missingdata",
|
||||
"accepted_proofs": "reused"
|
||||
},
|
||||
"runtime_delta": [
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-normal.tsx",
|
||||
"sha256_LF": "557714bb3af75080e443ae67b11a66d9feb49d9a4b269065769b128a6d712837"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-period-fx.tsx",
|
||||
"sha256_LF": "4b5f718b5ef415ecf5f96dabb0e3806254558c20905de6478e10dacf67569c49"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-schedules.tsx",
|
||||
"sha256_LF": "3768c316d5887d337eb3647671863c9464f961d1df4703403e7d57e5641273ed"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-assessment.ts",
|
||||
"sha256_LF": "a9b3ced852e37e3e087ce5dab1add4ec0737145ce19f61857a54d5d3d24a6f8c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-base.ts",
|
||||
"sha256_LF": "22019d678f6eb16fead2207dc3d3fa9fd63413c6254633c1ffd4f1c0320344ac"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-costs.ts",
|
||||
"sha256_LF": "471457692a2a2f70a1bd1c7abb38d3ce42f07b62a63e5a4ab985ad1bbd068b46"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-depreciation.ts",
|
||||
"sha256_LF": "1dad3053157aeac44f6d3bd58885cfc47a73c19d2808284c16931679dfe4291b"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-period-fx.ts",
|
||||
"sha256_LF": "a2e7b066623eae17f0d8185d065d0adf5d3a485f780f8b09118a664dc4ba0531"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal.ts",
|
||||
"sha256_LF": "e4de2fe0421e489bf555885b9cbb459c00ee77803ea9b6e5af45179bd6bb4f21"
|
||||
}
|
||||
],
|
||||
"protected_runtime": [
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-depreciation.tsx",
|
||||
"sha256_LF": "1f9918bfb435bb5b7c0ddd2fafd7bace2a02514c6ece04b93f15975e494a5f53"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-profit.ts",
|
||||
"sha256_LF": "909e129a0245e6bf098317485b886a2d19e02c64bbfd9c6f718e98ec6b935b8d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-schedules.ts",
|
||||
"sha256_LF": "edfb6fd338411fa8f87a6bdf9a9d4fd5c6dc2fad9d2eb3d32f2640e2951cbbe9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-costs.tsx",
|
||||
"sha256_LF": "c4156916835daf0f33addf89dfedd40c31b8dc3297112cd98beac33422af274d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-normal-forms.ts",
|
||||
"sha256_LF": "4e986addb9f2d74e7a80345f8f1e4d8e0b2f562c867fa6eee84880d9f72a3649"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-taxable-base.tsx",
|
||||
"sha256_LF": "e4fe9f66d35d24aa68fd87de6a5a6a0e901706b270c18962fd0c7ab00f9abd93"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-assessment.tsx",
|
||||
"sha256_LF": "7174cc0160005a0c332ea40f02ba8a6d57985081b5ab45177ccd6e721bceea54"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/acquisition-beverage-restitution.tsx",
|
||||
"sha256_LF": "6f44897b8b12569314551df0022c26da57a165634087942a724293e66837558d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-calculation-gaps.tsx",
|
||||
"sha256_LF": "1dab6ceb73756d4e023096b3b2ba767aaa843ff6cc69046ea630ab449a00b0dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/fiscal-l3-preparation.tsx",
|
||||
"sha256_LF": "e84a7b1ccadaa1b5bd78d778a3287f96ccaf530855eef404786cd7aa3d321b73"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-acquisitions.tsx",
|
||||
"sha256_LF": "c206d4cfe575d6ba007aa2b425b5f898efa97e4162b8710e358b6ecfd9458542"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-operations.tsx",
|
||||
"sha256_LF": "a7e55972fa790063dcf11945055de2af46a77db9e509900af22ede7ec47bb4cf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisition-bc.ts",
|
||||
"sha256_LF": "b0ef55e0917bdb79f14b53ba9e95d73c08153befeb7d4ce73f81e6fb13992225"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-allocation.ts",
|
||||
"sha256_LF": "32968d198c7fe0c53ca89641c22d5749be6636be2c79127b867e4e3afd0dd40d"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-beverage-restitution.ts",
|
||||
"sha256_LF": "5c650cbd9469d5f99001f56fc4ff99ac973eb368036d4e96c6d171f1b220bd9e"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-calculation-gaps.ts",
|
||||
"sha256_LF": "2e7d84a0c196bd5b56fd67cc1a389d43d5041894184e675680a4de3cd10b31a2"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-operations.ts",
|
||||
"sha256_LF": "ec37b949f1233c5f5c14a87450c421ed29fabf1ed5b73fb32564564a2bc31f54"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/l3-form-preparation.ts",
|
||||
"sha256_LF": "fc47f5fa5ec1420fbd847d0d081f00d99ebbb524164eef785b20877e0562f704"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-residuals.ts",
|
||||
"sha256_LF": "40d5751c06c7c155ab4b7a17ee2d1653da2fbbf075807fbb8561cdbe19820955"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-residuals.tsx",
|
||||
"sha256_LF": "a9219d6f41ccbd0ce45659d4f6aa0fa1c169a685840f5cb1e18daeca377d5e18"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-eligibility.ts",
|
||||
"sha256_LF": "44675c5efacdc1ae19505667611ba3b522d3043cebacc8754597c3a68ca89d25"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-quarter.tsx",
|
||||
"sha256_LF": "2c2555309b32d4261f14e7164f39314c9188129c2990357fc54090a2468dd2b4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-withholding.tsx",
|
||||
"sha256_LF": "e0445d015b72859e48726959aa52686f79b1cf5c2ade29751efa83257068b878"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-quarter.ts",
|
||||
"sha256_LF": "f66bd52e9cdfe1ddaa6b00a931465862b3d890b9be216b7a056740f027f24129"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/ispc-withholding.ts",
|
||||
"sha256_LF": "6c31b240299edd3262efa28f7f3d5aee21123f94e30ef4ab90e26c35777cb6e4"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-ispc-eligibility.tsx",
|
||||
"sha256_LF": "f48dd4a8f87a994eda0447f81a463589905e5873b2e1044debac9d10825a880c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-acquisitions.ts",
|
||||
"sha256_LF": "cce283b1d0a07fb383fcb527225f4dd5bec829010a662cbaae1d60e595221977"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-modelo-a.ts",
|
||||
"sha256_LF": "c641619a05613c1620385baad0e9ffbf810dd970aafb30e12b3a7f9a3bf11846"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-fx-selection.ts",
|
||||
"sha256_LF": "9aa07ad443bc777b80510a3b5bd6aff9ddb9814d2450f3c690e531e74d71d998"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-fx-selection.tsx",
|
||||
"sha256_LF": "39bb6d87d404745b0b66349bc11626545a3672ae27321ce9b4658416592059bf"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/business-financial-plan.ts",
|
||||
"sha256_LF": "c20274722d1ffcd0360f041a6cacb5d868fe0621cb27a1aca6e06826612c3ebb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/iva-currency-events.ts",
|
||||
"sha256_LF": "cc9f2045d302d7987310da4006cc250fc5c99a27397918d166d649bfc32fab08"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-fx.ts",
|
||||
"sha256_LF": "5c4a7a477429f8d775acf6db3e117008468f5554e5731dec81f60fbb75e948fb"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-withholdings.ts",
|
||||
"sha256_LF": "8e5f3c035ce5e4d6c3fc82f933439ab580fa5519492f9c4d32cfb71b77b54803"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-withholdings.tsx",
|
||||
"sha256_LF": "751af4fab346659bc5ccb08b8a2169769af7521ea0035050af32b41ea7f5aa76"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-interest.ts",
|
||||
"sha256_LF": "f8e6390b3b2c58c296f7e3fd14576e06ae248bc0cabce1a666b7094611f2a7dd"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta.ts",
|
||||
"sha256_LF": "76dcce8954f68b081d500be61de9c5b146a452a3eb578a0b78776ba9e1f8051c"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/irpc-mrfta-cases.ts",
|
||||
"sha256_LF": "4266746076ab3f70312fd042351e24dd2f136c4f3e8a277b57dd97849a9f1862"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-irpc-mrfta.tsx",
|
||||
"sha256_LF": "913d1bb4ac0fc59763cf5d27bc637f6a713b2050c4463748db554af4be451538"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/financial-fiscal-bridge.ts",
|
||||
"sha256_LF": "5a30c501653b16b5fedcb12631ce81fcce4fe473aa542e94374986b3c50f2d33"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/components/simulation-form.tsx",
|
||||
"sha256_LF": "85b024a7f45ceb52efd6ff1f21d0eab1fd8fcf1ab59e4b5302c08685235c472a"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier.ts",
|
||||
"sha256_LF": "a0909bbfcdf7e8542b010faa104fcb918e86ae38ddf096082d4d5be98d31b2b9"
|
||||
},
|
||||
{
|
||||
"path": "resources/js/lib/project-dossier-report.ts",
|
||||
"sha256_LF": "5207a2ffeb208cd5ace6713038cee85208020ee618194dca7114e94fc3200b6c"
|
||||
}
|
||||
],
|
||||
"originals_preserved": [
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Comunicado-9-2026-ISPC.pdf",
|
||||
"sha256": "106f8a9ce9255c03549c70bf07fe999d32e6e55302d009b509862aa74ccb4973"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-30-ISPC.pdf",
|
||||
"sha256": "6802edd06b6ff638a0b851a3ec7ecf69af8f07840faa47446becad67d35d445c"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-Modelo-B-Geral.pdf",
|
||||
"sha256": "bd977c7c59d4d98befd92e2c566b1bf168cb1343bb519f51b41a3807f431fc57"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/AT-MRFTA-19-39.pdf",
|
||||
"sha256": "8ea62c53066b482f87078a7a7b1ecf15ca98a12a122e8d7abca63acfc993186b"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BM-Mercado-Monetario-MAIBOR-20261025.html",
|
||||
"sha256": "00e8a7e0d3a1db4ca3beb56d5361b2084a0f1684f3d40fc1c203702a2190e063"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-16-I-Serie-2008-04-16.pdf",
|
||||
"sha256": "fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-248-I-Suplemento-1-2025.pdf",
|
||||
"sha256": "ccf2a8deeca820f2dd076b4b2141b1efe3f6a806568c3a587ca59a8e92f4c251"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-89-I-Serie-2017-06-08.pdf",
|
||||
"sha256": "11f0cab15eb6e51010952203ffa24fdc533a24fd642e19525fe4f46b17a1f5e5"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR-95-I-Suplemento-2025-05-21.pdf",
|
||||
"sha256": "e0e682e2c027c3cd9c1d46a73a3f48bc5f6bdbf05667fd97314d7a3f6a456358"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/BR252-I-Serie-2022-Lei20-PESOE.pdf",
|
||||
"sha256": "1159fd3ce1ba5581671796d083534da48bd3c5578298819e896f1c2a8026318d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-22-2021-BR70.pdf",
|
||||
"sha256": "a8fac38dcfe08f9c7a8e46254cff10e1ec82fc4f7a8d88ad6e4fe7d91ffc21a5"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-27-2026-BR123.pdf",
|
||||
"sha256": "26249c3fa58f149c81de36e99d1aac2819a4771379d9e8833159660dabf539bb"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-29-1998-BR22-5-suplemento.pdf",
|
||||
"sha256": "6242cbe675faadf085f43f95ec8c7e8868aafe67c3b2ab2bb010992288f954f8"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-3-2011-BR-11-I-mineiros.pdf",
|
||||
"sha256": "5bca1133666ba9c69870b89823420175b7b5b8d0eccb667ecdf5c29716f3b6be"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-37-2023-BR124.pdf",
|
||||
"sha256": "7e680b191481faa0e2e9057fa5053587eb2a29d1d8c819e0eb9f02814255a04c"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Decreto-9-2008-BR16.pdf",
|
||||
"sha256": "96fccdecbfdc11c9eafb90fbc6c7bc259f88bbd4c7507d1ac3af1fa03f960697"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-13-2016-CIVA-BR-156-10-suplemento.pdf",
|
||||
"sha256": "b90dc7c14ecea2e378c4a62e8e5a47d54a5644c457baeb8b8053b92fd2a5710b"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-14-1992-BR42.pdf",
|
||||
"sha256": "089fff7423e99a70a230a73ca379e4ea3a91d124703f91016e57ff82794b9e56"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-BR251-exemplar-MCNet5.pdf",
|
||||
"sha256": "14eb34f0af91a8f5cb1770c7b52d6ff839c6818302d1ed96d79afbfb9fe999d4"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-17-2022-IPP-BR250-Parlamento.pdf",
|
||||
"sha256": "978cd22403ffd336a631704ecd7a5bbd814a427c815b9acdaca5515591dd9e2a"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-19-2013-BR76.pdf",
|
||||
"sha256": "bba96d645cc935ffc3d1a79b9f0f73ac1c693d6459c2ad41f56cd2841accae76"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-19-2017-IRPS-BR-OCAM.pdf",
|
||||
"sha256": "c3a0186cb9ad090d7a9533006295dc9bb6a62ad0d6150173cbd49dba4ee4b52d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-2-2011-BR-1-4-suplemento-MCNet.pdf",
|
||||
"sha256": "a7543a68cc3287a46a83c39a04821b40f649dd06c4e9b5dc51ff0b47fba08899"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-20-2009-BR36-AT-exemplar19.pdf",
|
||||
"sha256": "fc1c8144db42960cbf73e717e095896f511ece0f907f58f78819b27d6917a7ee"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-22-2022-IVA-republicada-BR-250-2-suplemento.pdf",
|
||||
"sha256": "ea1b0dc6f5cf6e595f6d9a19e7042f398999e0087caef1785f6e8fac571b5f53"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-34-2007-BR52.pdf",
|
||||
"sha256": "cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-1994-BR37-3-suplemento.pdf",
|
||||
"sha256": "210df9db8980d0a5690cd187a6eeec7e37a900d743e39062c16f4e0d12215c7e"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-4-2012-BR3.pdf",
|
||||
"sha256": "b8845cef41dcbd9ba50c43dbfa743a56030dd3d348242a107e1ae30279eec121"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/Lei-7-1991-Partidos-IFES.pdf",
|
||||
"sha256": "6b783a0bb3330688ef10660a58060d9724aa2da6a873d234fe902f0eaaeda42d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-CDT-1992.pdf",
|
||||
"sha256": "d504e1024b28aee5c6acf4592acb96ec9c915a8414f67a61ab6984d6acf9df9d"
|
||||
},
|
||||
{
|
||||
"path": "docs/investmoz/simulations/source-archive/PT-MZ-Protocolo-2009.pdf",
|
||||
"sha256": "f353773dcf41f09f4daa6cf5ba5a5f7994f3e02615867c2dcffc61c3d901bf9b"
|
||||
}
|
||||
],
|
||||
"source_evidence": {
|
||||
"CIRPC": "Lei34/2007 PDF4 art7(4)/(7),PDF12 art41(5);original preserved",
|
||||
"decree72_reused_sha256": "922ef67cb0c1111405c3c0a092d8c302a74fba8a34d627b6e9e13223612e0659",
|
||||
"visual_pages": [
|
||||
"cirpc-page4.png",
|
||||
"cirpc-page12.png",
|
||||
"decreto72-page3.png"
|
||||
]
|
||||
},
|
||||
"installed7_fixture_sha256": "d58beac3cf7db665a632f516a736f754a5874f82585291dfbc5fa6bcda654d0d",
|
||||
"history_versions1_to7": "unchanged;optional8;source plan not rewritten",
|
||||
"FX_engine": "Existing selectFx/convertIrpcAmount/convertCents/direction/evidence/link unchanged;monthly assumptions explicit;no fiscal annual rule asserted",
|
||||
"FX_rate64": "synthetic QA assumption,not real BM bulletin",
|
||||
"schema_API_config_permissions_dependencies_changed": false,
|
||||
"active_data_changed": false,
|
||||
"L6_complete": false,
|
||||
"L7_L8_own_scopes": true,
|
||||
"remaining_reference": "Same matrix section26/10 períodos curtos e planos noutra moeda;concrete conditions per case",
|
||||
"proof_sha256": {
|
||||
"api.txt": "e2c50de5a45a76cdd596d4093974b88c449d23ba8011edf5f8a6325d2cf01d07",
|
||||
"build-exit.txt": "13bf7b3039c63bf5a50491fa3cfd8eb4e699d1ba1436315aef9cbe5711530354",
|
||||
"build.txt": "9b167ca7bcd724a65f6a4030a46ce9e81accaad5a2e80160b623ca3807400bc7",
|
||||
"cirpc-page12.png": "12bb97ef4964b69816c30894bca201cedf2dcbadb29b3be9e867f2325e575abd",
|
||||
"cirpc-page4.png": "3f6fdfd75d4fef4d07f461969675118f5f000c33ab593fcb6e0ee0fc93f56737",
|
||||
"cirpc.txt": "c3ba3b845fcb12c25bc28b08485f3e5d70034895ece4ff60cf640fd0be796f9a",
|
||||
"decreto-72-2013.pdf": "922ef67cb0c1111405c3c0a092d8c302a74fba8a34d627b6e9e13223612e0659",
|
||||
"decreto72-page3.png": "4a150dd00dc7c7d4602c231bb42ebea5d7505917f1e21781c2d967cf5da8e327",
|
||||
"dossier-fx.json": "5d1e429a48e8fd6c53a9116b3176641146e2aa6b68c888558ac6c4ff0950138c",
|
||||
"dossier-loss.json": "862ee41f5b38546255335ab569039e52388d64f7bf30f38c3033f6f583e04d7c",
|
||||
"dossier-missing.json": "514c2386e8e1cd687545c880fd5e66a0099c851433c0c8f337439a69e56db5b1",
|
||||
"dossier-short.json": "cdab2ed8019f46f132f600c6b301d99ad44e8027926117abf3e57eb6c6000071",
|
||||
"dossier.mjs": "cd967cfdba196bf0b7c691fe418f38dd39232030c6e601c22f8e25bd27e93d46",
|
||||
"download.html": "95f59ff469af21bc2a41225358769bf99f25cf056479ff9a9c3d1dad4cb5f521",
|
||||
"frontend.txt": "d9c7e51b63e020bf35d2a4a3aa22f29ca71d003e4bcf22b111e90173e442745b",
|
||||
"fx.html": "5bae85164ca7cec78d76fc4c72c3c33cdaca70c63c42929c8e037ab7e91ba93f",
|
||||
"fx.json": "e92ed4b718eef390aef573728a459679fdd3130bdf923db4cfd8f7a560958fbf",
|
||||
"index.html": "09941968500d2910121bdcb0029e326fc6d44bb025b01766654289a28caa62fa",
|
||||
"loss.html": "b672fd43361efcc81c4da4f83b7eba12dd9bd7799de0b463f248928065a4de97",
|
||||
"loss.json": "07e56fb88034d377ca9bc2b098b9a4a266058a46aeb7b127a99b3c539bceec1b",
|
||||
"missing.html": "ed41bf1b5ffab532a59b8e07d8a3700bd62576bc15c9256750c84a371a2cbc8a",
|
||||
"missing.json": "5022f8eddd7f09b9a10a837f0e407e7db3619e27302e19f367923a956ebe47a6",
|
||||
"qa.tsx": "c7c06f793e4a5aa3df70f36ec6681a6399b6016f27168d18917be2f22dffdbaf",
|
||||
"recovered-fx.html": "5bae85164ca7cec78d76fc4c72c3c33cdaca70c63c42929c8e037ab7e91ba93f",
|
||||
"recovered-fx.json": "2c7d1e8858af254930bff8a420f3369475ebd335ca4251b4ddc691558ed1c9aa",
|
||||
"recovered-loss.html": "b672fd43361efcc81c4da4f83b7eba12dd9bd7799de0b463f248928065a4de97",
|
||||
"recovered-loss.json": "2618a36d92005ebf791c938687662789086d351606f95f62f4eb1133f4b029e2",
|
||||
"recovered-missing.html": "ed41bf1b5ffab532a59b8e07d8a3700bd62576bc15c9256750c84a371a2cbc8a",
|
||||
"recovered-missing.json": "094b1ee4822bdcee6ac8ce5dc9ea5cfc178bf555b15f31a6cd2a1c6becd759ae",
|
||||
"recovered-short.html": "955f182b29c160774bacc8997477c2da44cd1fcc401d1ed1955fa62f9f94d743",
|
||||
"recovered-short.json": "e4399308561d5f412c9bb803c83fd6e045d89d24986a4d6a8279c2977044bb96",
|
||||
"short.html": "955f182b29c160774bacc8997477c2da44cd1fcc401d1ed1955fa62f9f94d743",
|
||||
"short.json": "a6b0e4ea322353f84c0ba6ff09d6cf89ee270dbefb850f225c4f8fe4485f68fb",
|
||||
"types.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"ui-fx.png": "deaede13c0c4eb6cc74ce0a55fc9b7bc2948f52fe7863ffc53ab83ca42456587",
|
||||
"ui-proof.json": "56b6005ea84dfd370ac55167a5f47d19bffcee8408982c2046cb1ad27ecc9f20",
|
||||
"ui-short.png": "b716d583fc3ca286c2e19cbeefca018ec08e8b75e0e95499ff7d27ab06a843d6",
|
||||
"ui.cjs": "1f5506f7865cdbfcfa8aea853be241bab849807e6d641f3c5596534727cb5938",
|
||||
"ui.txt": "85e77d9f361edf72d68990272040cc9b6edab3cc0873f921b1d5e561dc0553af",
|
||||
"vite.config.ts": "0351ec239d5a6ba429ee17b9aa68757e31f59902f8f88a522afdd56bd3b44879"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6148,3 +6148,113 @@ Mesma matriz AUD-SIM-INT02. Custos/provisões/periodização demonstrados entreg
|
||||
| Outras periodizações,âmbitos eM20/M22 | Tratar apenas os casos específicos18(6–10),períodos curtos/conversão anual,representação fiscal e campos ainda sem correspondência,com os parâmetros da edição/caso;divergência201/Q8 versus200/Q10 só afecta o transporte indicado. | Apenas o caso/campo dependente; cálculos independentes preservados. |
|
||||
|
||||
L6 continua parcial. L7 mantém benefícios/casos especiais;L8 mantém retenções/pagamentos/créditos/saldo final. As dez dúvidasL5 não foram reabertas.
|
||||
|
||||
## 26/10/2026 — L6: amortizações em QA, sem instalação
|
||||
|
||||
O portal mantém `043dd35d` instalado: colecta demonstrada1417,60MZN, sem saldo finalL8. O novo conjunto optativo `IRPC-normal-adjustments/7` reutiliza os activos e o resultado financeiro já declarados. Versões1–6 e históricos permanecem intactos; activar o novo mapa substitui o efeito fiscal anterior das amortizações, sem o somar novamente. Ausência declarada de quotas anteriores é distinta de informação em falta e exige referência.
|
||||
|
||||
Cobertura delimitada ao exercício anual2026/MZN e bens novos ao custo histórico: tabelaII/3/9 computadores25%,3/9.1 servidores20%,3/23 segurança20%,3/22 máquinas não especificadas10%,5/7 mobiliário10%,1/2.2 edifícios comerciais/administrativos2%,4/9.2 veículos ligeiros/mistos25%,5/8 software25%; tabelaI/84.2/B decoração hoteleira20% e84.3/A máquinas de lavandaria12,5%. A tabelaI prevalece quando aplicável; não se presume que esta lista cobre todo o catálogo. Fonte: Decreto72/2013, artigos3–10/17 e tabelas, exemplar original anteriormente aceite e hash preservado; CIRPC27e para limite800000MZN de viaturas e exclusão de terrenos.
|
||||
|
||||
Método constante com taxa expressamente escolhida no recorte metade–máximo; degressivo apenas para bens elegíveis novos, factores máximos1,5/2/2,5 segundo vida útil. Opção8(2) demonstrada apenas em ano civil/início1Janeiro, com quotas anteriores completas documentadas. Duodécimos incluem mês de entrada e excluem mês de saída, respeitando vida máxima. Software exige direitos onerosos/exclusivos e duração limitada; art17 exige bem individual até20000 e opção documentada. Quotas anteriores recuperáveis têm prova contabilística/fiscal e são limitadas à parte elegível: quotas nunca contabilizadas ou permanentemente excluídas não se tornam dedutíveis. Nenhum saldo oficial é consumido.
|
||||
|
||||
| Novo caso | Efeito demonstrado em MZN: lucro → matéria colectável → colecta |
|
||||
|---|---|
|
||||
| Computador em uso desdeJulho: quota fiscal50, ajustamento70, substituindo anterior20 | 6780 →4480 →1433,60 |
|
||||
| Mesmo activo, saída15Outubro:3meses/quota25 | Colecta1441,60; entrada/saída identificadas no relatório |
|
||||
| Degressivo elegível: quota200, ajustamento40 | 6630 →4330 →1385,60 |
|
||||
| Quota anterior documentada:20 recuperados,20 restantes; ajustamento−20 | 6730 →4430 →1417,60 |
|
||||
| Máquina de lavandariaI/84.3/A: quota62,50, ajustamento57,50 | 6767,50 →4467,50 →1429,60 |
|
||||
| Quota contabilística anterior em falta | Lucro/base/colecta indeterminados; ajustamentos independentes conhecidos continuam discriminados |
|
||||
|
||||
Cálculo → gravação → recuperação pela API privada → relatório/dossier demonstrado em cinco casos. **18novos testes frontend;1testeAPI/84asserções**, rollback na baseQA127.0.0.1:55441; routes/actions gerados, TypeScript/build e interface real passaram, incluindo edição, reutilização, ausência declarada, falta de informação, recuperação e exportação. Provas aceites reutilizadas. **7ficheiros de runtime no delta,43protegidos e31originais conferidos**; sem migração/API/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
| Saldo concreto das amortizações | Condição de retoma |
|
||||
|---|---|
|
||||
| Outros códigos/taxas, por exemploII/3/3 ar condicionado12,5% | Acrescentar correspondência verificada, elegibilidade e prova apenas desse delta; sem aprovação genérica adicional |
|
||||
| Bens usados, abertura de escrita, grandes reparações/benfeitorias7(2)/(4), reavaliações15 e transição anterior2014 | Documentar vida esperada, bases/quotas anteriores e implementar a regra específica aplicável |
|
||||
| Uso intensivo11, concessões12, peças14, intangíveis específicos16 e eventos de transferência/relocação | Corresponder condições legais e histórico do evento; desenvolvimento/goodwill não herdam taxa do software |
|
||||
| Mudança de método10, excepções, taxas fora do recorte, vida máxima anual terminada a meio do período e opção8(2) fora do caso demonstrado | Histórico concreto e regra/excepção correspondente; autorização apenas quando a norma a exigir |
|
||||
| Restantes custos/provisões/periodizações da entrega043dd35d; períodos curtos, conversão anual, representação fiscal e correspondênciasM20/M22 | Mantêm os critérios concretos já registados na matriz; este incremento não os encerra |
|
||||
|
||||
A divergência201/quadro8 versus200/quadro10 bloqueia apenas o transporte afectado. Não foram presumidas correspondências oficiais para estes novos campos. **L6 continua parcial;L7 eL8 mantêm os seus âmbitos.** As dez dúvidasL5 não foram reabertas. Recibo: `docs/investmoz/simulations/irpc-l6-depreciation-qa-20261026.json`.
|
||||
|
||||
## 26/10/2026 — amortizações e9fe1022 entregues pelo Git
|
||||
|
||||
Instalado `e9fe102259c183a9d9bf7e39f246a8c3860839c6` sobre `043dd35d`, preservado como versão anterior. Backup fresco `2026-10-26T07:31:29Z` e restauro isolado sem rede/porta55441: **174tabelas** por contagem/checksum e **369ficheiros/configuração** conferidos. **7ficheiros do delta,43protegidos e31originais** verificados; schema,roles/grants e permissões iguais. As172tabelas de negócio mantêm os valores do backup. Cache:reinício da fila,contadores/timers e renovação da validade do mesmo valor de cache da página inicial. Sessões:expiração individual comprovada pelo prazo configurado120minutos,novas consultas da entrega e acessos anónimos correlacionados por agente/data no nginx;sem perda de dados de negócio. Dependências de5versões antigas arquivadas/verificadas para a margem do pipeline,preservando versões actual/anterior,fontes,dados e backups.
|
||||
|
||||
Revisão/manifest público,5marcadores compilados,4assets e simuladorHTTP200 conferidos. Pipeline normal concluído; provas QA18frontend/API1-84/UI/tipos/build reutilizadas,sem nova ronda manual. **Exemplo disponível: lucro6780 → matéria colectável4480 → colecta1433,60MZN. Saldo final a pagar/recuperar indeterminado emL8.** Históricos1–6 preservados; versão7 apenas por activação do novo mapa,substituindo o efeito anterior sem duplicação. Recibo:`docs/investmoz/simulations/irpc-l6-depreciation-delivery-20261026.json`.
|
||||
|
||||
O estado anterior «QA sem instalação» de e9fe1022 é histórico. Mantêm-se explícitos os outros códigos de tabela;usados/abertura de escrita/reparações/reavaliação/transição;uso intensivo/concessões/peças/intangíveis específicos/eventos;mudanças/excepções de método e opções fora do recorte. Retoma por correspondência da regra específica,base/vida/quotas e prova do delta,sem autorização genérica adicional. Períodos curtos e planos noutra moeda são o próximo incrementoQA,reutilizando o mecanismo cambial. Restantes custos/períodos/representação/camposM20-M22 conservam critérios próprios;divergência201/Q8 versus200/Q10 restringe apenas o transporte afectado. **L6 parcial;L7/L8 próprios;dez dúvidasL5 não reabertas.**
|
||||
|
||||
## 26/10/2026 — L6: períodos curtos e planos noutra moeda em QA
|
||||
|
||||
`e9fe1022` está instalado, com o âmbito de amortizações já demonstrado e os restantes casos de tabela/método explícitos acima. Este novo conjunto optativo `IRPC-normal-adjustments/8` está **emQA,sem instalação**. Reutiliza os meses e montantes do plano,os mapas fiscais emMZN e o mecanismo cambial entregue. Os históricos1–7 e o plano original não são reescritos.
|
||||
|
||||
Períodos de meses completos com termo em2026: início7(4)a,cessação7(4)b,início/fim de sujeição7(4)c e transição7(4)d,mediante identificação do evento/datas/suporte; plano com6meses ou selecção explícita de6dos12meses,sem anualizar receita,resultado,base ou taxa. Primeiro mês civil do plano é declarado,não inferido do mês orçamental. Anual diferente do ano civil conserva a correspondência do período autorizado/comunicado existente. Duodécimos ordinários do art9(3) do Decreto72/2013 aplicam o número de meses de utilização;art17 integral em período curto e restantes excepções não se tornam suportados por aproximação. Elemento conhecido como não amortizável mantém acréscimo integral,sem exigir método irrelevante.
|
||||
|
||||
Planos em outra moeda: cada mês seleccionado reutiliza ou confirma selecção cambial pelo procedimento existente,coluna/data/boletim/sentido e vínculo à origem. Moeda e valores originais permanecem no relatório. Não se adopta taxa actual,única ou de outro mês por defeito. **A conversão mensal para simulação exige pressuposto documentado;não confirma automaticamente uma regra fiscal de conversão anual.** Os mapas/activos/quotas/perdas existentes continuam emMZN e são reconciliados expressamente;nenhuma segunda conversão automática. Saldos patrimoniais de créditos em moeda estrangeira não são apresentados comoMZN pela conversão dos fluxos do plano;correspondência de saldo/data/taxa continua separada.
|
||||
|
||||
| Caso novo | Efeito demonstrado emMZN |
|
||||
|---|---|
|
||||
| Início1Julho–31Dezembro,meses7–12: resultado3120;quota fiscal50 e ajustamento10 | Lucro3130 → matéria colectável3130 → colecta1001,60,à taxa32%;ausência de perdas/benefícios declarada |
|
||||
| Mesmo recorte emUSD,pressuposto sintético64MZN/USD identificado em cada mês;mapasMZN reconciliados | Resultado199680;ajustamento640;lucro/base200320 → colecta64102,40. Taxa64 é pressupostoQA,não boletimBM real ou parâmetro fiscal confirmado |
|
||||
| Anual emUSD;plano de6meses iniciado emJulho;cessação e sujeição no mesmo ano | Novos testes próprios;respectivamente colecta128204,80 e1001,60nos casos correspondidos,sem anualização do semestre |
|
||||
| Falta de selecção de um mês,moeda incompatível ou taxa editada sem confirmação | Valores originais e conversões independentes visíveis;resultado/lucro/base/colecta dependentes indeterminados |
|
||||
| Perdas reportadas propostas num período curto | Lucro3130conhecido;dedução/base/colecta pendentes da sequência de períodos. Art41(5) exclui da contagem o período de transição7(4)d inferior a6meses;não autoriza agregar todos os períodos curtos |
|
||||
|
||||
Cálculo → gravação → recuperaçãoAPI → relatório/dossier demonstrado em4casos. **13testes novos frontend,API1teste/58asserções**, rollback na baseQA127.0.0.1:55441;routes/actions pelo procedimento existente,TypeScript/build e interface real passaram. Interface demonstrou alteração de datas e taxa,confirmação cambial,recuperação e exportação. Mudança na origem cambial invalida a actualidade do novo resultado,conservando o histórico. Provas aceites reutilizadas,sem repetição manual. Delta:**9ficheiros de runtime,43protegidos e31originais**;sem API/schema/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
| Pendência concreta | Condição de retoma e bloqueio |
|
||||
|---|---|
|
||||
| Datas parciais,períodos superiores a12meses/liquidação e curtos fora do recorte2026 | Corresponder datas/evento,parcelas de dias e regra aplicável;apenas esse período depende da implementação |
|
||||
| Dedução de perdas em sequência com períodos curtos/transição | Declarar sequência efectiva de períodos posteriores e aplicar41(5) ao caso7(4)d inferior a6meses;sem prazo presumido só por ano civil. Lucro independente continua calculável |
|
||||
| Limites anuais de publicidade e provisão de créditos em período curto | Corresponder expressamente a base/limite do período e regra de proporcionalização,se aplicável;sem anualização ou rateio presumidos. Outros custos e períodos anuais não bloqueados |
|
||||
| Quota integral art17 em período curto,opções/métodos/tabelas e casos de amortização ainda não suportados | Implementar combinação específica e provar só o novo efeito;condições restantes do conjuntoe9fe1022 continuam válidas |
|
||||
| Conversão fiscal anual confirmada,saldos em moeda estrangeira e fracções de cêntimo da conversão | Identificar regra fiscal/data/base,taxa e âmbito;para saldos,conversão própria em vez de somar fluxos;para fracções,regra de representação. Até lá,pressupostos de simulação explícitos e total indeterminado quando não representável neste recorte |
|
||||
| Restantes custos/periodizações e correspondênciasM20/M22 | Mantêm os critérios já registados. Divergência201/Q8 versus200/Q10 limita só o transporte afectado |
|
||||
|
||||
Fontes primárias reutilizadas: CIRPC Lei34/2007 PDF4/p768(58),art7(4)/(7),ePDF12/p768(66),art41(5);Decreto72/2013 PDF3/p1074(7),art9(3). Exemplar e originais/hash preservados;sem importação/publicação documental. **L6 continua parcial. Colecta não é saldo final a pagar;L7 eL8 mantêm âmbitos próprios.** As dez dúvidasL5 não foram reabertas. Recibo:`docs/investmoz/simulations/irpc-l6-periods-fx-qa-20261026.json`.
|
||||
|
||||
## 26/10/2026 — períodos curtos e planos noutra moeda56475acb entregues pelo Git
|
||||
|
||||
Instalado `56475acb2f39e58cb51664913f7fb499f58eb7ab` sobre `e9fe1022`,preservado como versão anterior. Backup fresco `2026-10-26T08:17:39Z` e restauro isolado sem rede/porta55441: **174tabelas** por contagem/checksum e **369ficheiros/configuração** conferidos. **9ficheiros do delta,43protegidos e31originais** verificados;schema,roles/grants e permissões iguais. As172tabelas de negócio mantêm os valores do backup. Cache/sessões reconciliadas com os eventos operacionais e acessos anónimos registados;expiração,quando aplicável,comprovada individualmente pelo prazo configurado. Dependências de5versões antigas arquivadas/verificadas,preservando versões actual/anterior,fontes,dados e backups.
|
||||
|
||||
Revisão/manifest público,6marcadores compilados,4assets e simuladorHTTP200 conferidos. Pipeline normal concluído;provasQA13frontend/API1-58/UI/tipos/build reutilizadas,sem nova ronda manual. **Semestre demonstrado: lucro3130 → matéria colectável3130 → colecta1001,60MZN.** PlanoUSD:pressuposto sintético64MZN/USD por mês identificado,originais conservados e mapasMZN reconciliados;lucro/base200320 → colecta64102,40MZN. **Não confirma regra fiscal de conversão anual nem boletimBM real. Saldo final a pagar/recuperar continua indeterminado emL8.** Históricos1–7 preservados;versão8 optativa,sem anualizar resultados ou usar taxa actual por defeito. Recibo:`docs/investmoz/simulations/irpc-l6-periods-fx-delivery-20261026.json`.
|
||||
|
||||
O estado anterior «QA sem instalação» de56475acb é histórico. Permanecem explícitos os casos de datas parciais/liquidação,sequência de perdas41(5),limites anuais em período curto,art17 curto/restantes amortizações,conversão fiscal anual/saldos em outra moeda/fracções e demais custos/periodizações. Condições de retoma continuam na mesma matriz. Próximo incrementoQA:representação de valores e camposM20/M22 por corresponder;divergência201/Q8 versus200/Q10 limitada ao transporte do resultado afectado,sem reabrir cálculos demonstrados. **L6 parcial;L7/L8 próprios;dez dúvidasL5 não reabertas.**
|
||||
|
||||
## 26/10/2026 — L6: representação dos valores e campos M20/M22 em QA
|
||||
|
||||
`56475acb` está instalado. Este novo conjunto optativo `IRPC-normal-adjustments/9` está **em QA, sem instalação** e acrescenta preparação dos valores/campos, sem alterar fórmulas de lucro tributável, matéria colectável ou colecta. Históricos1–8, plano original, pressupostos cambiais e demais livros preservados. A falta de uma correspondência documental afecta o campo e a sua reconciliação, mantendo os totais fiscais existentes.
|
||||
|
||||
**Representação:** valor racional exacto conservado, apresentação aproximada identificada por«≈» e fracção original; zero conhecido, informação em falta e campo sem aplicação distintos. M20A.1/quadro4/M12 pede meticais sem centavos: valores inteiros são representáveis, fracções ficam por corresponder, sem arredondamento/truncagem. Preparação M22 já aceite com valores exactos ao cêntimo preservada; fracções de cêntimo continuam explícitas e pendentes da regra fiscal aplicável. Resultado líquido negativo M20 é apresentado entre parêntesis; M22 separa campo de lucro do de prejuízo, sem colocar perda no lucro ou criar matéria colectável negativa.
|
||||
|
||||
**M20:** reutiliza as quatro correspondências directas de custos das vendas,pessoal,depreciação e juros. Discrimina as receitas e despesas operacionais por conta com suporte e reconciliação exacta ao plano do período/moeda. Demonstra prestação de serviços7.2,fornecimentos/serviços6.3,outros gastos6.8 e imposto contabilizado8.5, além das contas directas6.1.1/6.2/6.5/6.9. Rótulos das demais contas são estrutura disponível; não constituem novas regras fiscais nem provam a qualificação de uma parcela. Uma origem incompleta não apaga as contas directas independentes.
|
||||
|
||||
O imposto contabilizado tem valor/ausência e suporte próprios, e localização já incluída ou exterior ao plano. Colecta não é despesa contabilística presumida. Valor já incluído não é subtraído novamente; contradição com o mapa de imposto próprio ou coexistência das duas localizações fica identificada. O resultado líquido é apurado para preparação; o transporte automático entre edições continua impedido só no resultado afectado.
|
||||
|
||||
**M22 PDF:** correspondências de variações patrimoniais201/202,resultado203,amortizações não aceites206,provisões/imparidade209,impostos de outrem213,multas/juros compensatórios215,ajudas219,representação220,publicidade221,despesas não documentadas223,aluguer224,combustíveis225,viaturas226 e reposição de provisões tributadas239. Reutiliza os ajustamentos demonstrados; não reaplica as percentagens impressas. A representação já calculada usa80%,compatível com220. Campo213 é apenas de outrem: imposto próprio36(1)a continua **sem campo presumido**. Efeitos positivos de amortização vão a206; recuperações negativas de quotas anteriores ficam separadas até correspondência própria, sem compensação no mesmo campo.
|
||||
|
||||
Somas238/258 e lucro reconstruído apenas quando todas as parcelas têm correspondência e reconciliam; campos259/260/269/270/276/277/278/300 reutilizam o cálculo existente, mesmo havendo uma parcela sem campo. Colecta total302 só é preparada com ausência de parcela de taxa reduzida declarada com suporte;caso conhecido mantém âmbitoL7 e não apaga300. Campos309–316/saldo final permanecem emL8.
|
||||
|
||||
| Caso novo | Resultado e limite de aplicação |
|
||||
|---|---|
|
||||
| Correspondência no semestre demonstrado, ausência de imposto contabilizado e taxa reduzida declaradas | Resultado líquido3120;203=3120;206=10;238=3130;258=0;lucro/base3130;colecta normal300 e total302=1001,60MZN. Reconciliação aritmética exacta, sem transporte automático entre edições |
|
||||
| Fornecimentos300,50 e outros gastos299,50, origem600 | Parcela300,50 conserva601/2;M20 em meticais inteiros pendente só nas parcelas fraccionárias;resultado e colecta1001,60inalterados |
|
||||
| Imposto contabilizado em falta | Resultado líquido/campo200 e somas dependentes indeterminados;lucro/base3130 e colecta1001,60mantidos |
|
||||
| Recuperação de quota anterior:ajustamento−26 | Efeito fiscal e colecta1001,60preservados;sem código de dedução inventado,somas238/258 pendentes;260/278/300 conhecidos |
|
||||
| Imposto próprio320 contabilizado exterior ao plano | Resultado líquido2800 conhecido;campo de acréscimo próprio por corresponder,sem usar213 de outrem;lucro/base3130 e colecta1001,60preservados |
|
||||
|
||||
Percurso cálculo → gravação → recuperação privada → relatório/dossier demonstrado em **5casos**. **15testes novos frontend;API1teste/97asserções**, transacção revertida na baseQA127.0.0.1:55441;routes/actions gerados pelo procedimento existente,TypeScript/build e interface real passaram. Interface demonstrou edição de parcelas,imposto em falta sem apagar valor anterior,recuperação/exportação e pendências apenas dependentes. Provas aceites reutilizadas;sem repetir suites anteriores. **4ficheiros de runtime no delta,50protegidos e31originais** conferidos;sem API/schema/configuração/permissões/dependências ou escrita no activo.
|
||||
|
||||
Fontes: PDF M22 SHA2564b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88,3páginas já aceites reutilizadas;M20A.1 SHA2561ceca6f4c6d77853c1b2b445876599b42093175fa89eecf40590180956d83a9e,M12/D13–D35. Inventariados os20ficheiros acessíveis deModelos/Notas,incluindo temporários;originais/hash preservados. Só rótulos genéricos necessários extraídos do XLSX;sem valores privados,fórmulas/cache do exemplar,importação ou publicação documental.
|
||||
|
||||
| Saldo concreto da preparação M20/M22 | Condição de retoma e alcance |
|
||||
|---|---|
|
||||
| Transporte do resultado M20A.1/D35→M22 | Exemplar compatível ou instrução que resolva201/quadro8 versus200/quadro10. Só esse transporte fica impedido;valor líquido próprio e demais campos continuam visíveis |
|
||||
| Imposto próprio36(1)a,quota anterior recuperada e restantes efeitos de periodização/obras/reversões sem campo | Corresponder rótulo,edição/período e regra específica do campo. Efeito/valor conservados sem código provisório;apenas somas/campos dependentes aguardam |
|
||||
| Fracções M20 e subcentavos M22 | Identificar regra de representação do caso;preservar fracção e apresentar aproximação identificada até então. InteirosM20 e cêntimos exactosM22 não bloqueados |
|
||||
| Imposto contabilizado simultaneamente incluído e exterior ao plano | Discriminar ambas as componentes/referências e conciliar8.5/acréscimo próprio sem repetir gasto;sem inferir o valor da colecta |
|
||||
| Contas/quadros e anexos restantes fora das seis linhas financeiras demonstradas | Reutilizar dados existentes,classificar componentes com suporte e acrescentar correspondência específica;sem tomar campos preenchidos do exemplar como dados do investidor |
|
||||
|
||||
L7 mantém benefícios/casos especiais/taxas reduzidas eL8 mantém retenções,pagamentos,créditos e saldo final. Restantes regras deL6 e as dez dúvidasL5 conservam o saldo e as condições já documentadas. **L6 continua parcial;colecta não é saldo final a pagar.** Recibo:`docs/investmoz/simulations/irpc-l6-values-forms-qa-20261026.json`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
|
||||
import {useState}from'react';
|
||||
import{depreciationCatalog,newNormalDepreciation,newDepreciationAsset,reuseNormalAssets,type DepreciationAsset}from'@/lib/irpc-normal-depreciation';
|
||||
import{normalFinancialSource,type NormalBook}from'@/lib/irpc-normal';
|
||||
import{fiscalMoney}from'@/lib/irpc-normal-schedules';import{addFractions,subtractFractions,fractionPresentation}from'@/lib/fiscal-rational';
|
||||
export function SimulationIrpcDepreciation({draft,book,save}:{draft:Record<string,unknown>;book:NormalBook;save:(b:NormalBook)=>void}){
|
||||
const[error,setError]=useState(''),input=book.depreciation,source=normalFinancialSource(draft);
|
||||
if(!input)return<div><button type="button" onClick={()=>save({...book,depreciation:newNormalDepreciation()})}>Iniciar amortizações — tabelas, métodos e quotas anteriores</button>{!!book.schedules?.assets.length&&<button type="button" onClick={()=>save({...book,depreciation:{...reuseNormalAssets(book.schedules!.assets),completenessReference:book.schedules!.assetsReference}})}>Reutilizar activos e valores do mapa inicial</button>}</div>;
|
||||
const patch=(id:string,p:Partial<DepreciationAsset>)=>save({...book,depreciation:{...input,assets:input.assets.map(a=>a.id===id?{...a,...p}:a)}});
|
||||
const field=(a:DepreciationAsset,n:number,k:keyof DepreciationAsset,label:string)=><label key={k}className="block">{label}<input aria-label={'Activo '+n+' '+k}value={a[k]}onChange={e=>patch(a.id,{[k]:e.target.value})}/></label>;
|
||||
const choice=(a:DepreciationAsset,n:number,k:'specificRate'|'consistentMethod'|'newTangible'|'switchToStraight'|'priorFullQuotas'|'vehicleException'|'limitedRights'|'individual',label:string)=><label key={k}className="block">{label}<select aria-label={'Activo '+n+' '+k}value={a[k]}onChange={e=>patch(a.id,{[k]:e.target.value})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — documentado</option></select></label>;
|
||||
return<fieldset aria-label="Amortizações ampliadas L6"className="space-y-3 rounded border p-3"><legend>Amortizações — tabelas, métodos, duodécimos e quotas anteriores</legend>
|
||||
<p>Decreto72/2013 eCIRPC26/27. Reutiliza a depreciação financeira e os activos do mapa inicial; os originais/históricos ficam conservados. O novo mapa substitui exclusivamente o cálculo de amortizações, sem somar novamente o mapa inicial. Custo histórico e condições ordinárias documentados; reavaliações, turnos intensivos, concessões e outros casos especiais permanecem separados.</p>
|
||||
<p>Depreciação contabilística a repartir: {source?.accountingLines.find(l=>l.code==='depreciation')?.total??'Em falta'} {source?.currency}. O catálogo apresenta apenas os códigos correspondidos; outra categoria permanece identificada, sem taxa livre.</p>{error&&<p role="alert">{error}</p>}
|
||||
<label className="block">Suporte da reconciliação integral dos activos e quotas anteriores<input value={input.completenessReference}onChange={e=>save({...book,depreciation:{...input,completenessReference:e.target.value}})}/></label>
|
||||
<button type="button"onClick={()=>save({...book,depreciation:{...input,assets:[...input.assets,newDepreciationAsset(crypto.randomUUID())]}})}>Acrescentar activo ao mapa fiscal</button>
|
||||
{input.assets.map((a,index)=>{const n=index+1,entry=Object.hasOwn(depreciationCatalog,a.category)?depreciationCatalog[a.category as keyof typeof depreciationCatalog]:null;return<fieldset key={a.id}className="rounded border p-3"><legend>Activo fiscal {n} — {a.description||'Por identificar'}</legend>
|
||||
{field(a,n,'assetKey','Identificação única do activo')}{field(a,n,'description','Descrição')}{field(a,n,'document','Documento, pressuposto ou mapa de origem')}{field(a,n,'line','Linha distinta do documento/mapa')}
|
||||
<label>Categoria/tabela<select aria-label={'Activo '+n+' category'}value={a.category}onChange={e=>{const category=e.target.value as DepreciationAsset['category'],c=Object.hasOwn(depreciationCatalog,category)?depreciationCatalog[category as keyof typeof depreciationCatalog]:null;patch(a.id,{category,rate:c?.rate??''});}}><option value="">Por identificar</option>{Object.entries(depreciationCatalog).map(([k,c])=><option key={k}value={k}>Tabela{c.table}/{c.code} — {c.label} — {c.rate}%</option>)}<option value="nondepreciable">Elemento comprovadamente não depreciável — exclusão</option><option value="other">Outra categoria — correspondência pendente</option></select></label>
|
||||
{entry&&<p>Taxa máxima: {entry.rate}%; taxas constantes cobertas: {Number(entry.rate)/2}% a{entry.rate}%; vida útil mínima: {100/Number(entry.rate)}anos, máxima: {200/Number(entry.rate)}anos. Coeficiente degressivo máximo: {100/Number(entry.rate)<5?'1,5':100/Number(entry.rate)<=6?'2':'2,5'}, apenas nos activos elegíveis. Fonte:tabela{entry.table}/{entry.code},PDF{entry.page}.</p>}{field(a,n,'cost','Custo histórico documentado MZN')}{field(a,n,'accounting','Depreciação contabilística do exercício MZN')}
|
||||
<button type="button"onClick={()=>{try{const total=fiscalMoney(source?.accountingLines.find(l=>l.code==='depreciation')?.total??''),used=addFractions(input.assets.filter(q=>q.id!==a.id).map(q=>fiscalMoney(q.accounting))),left=subtractFractions(total,used);if(BigInt(left.numerator)<0n)throw Error('Valores repartidos excedem a origem.');patch(a.id,{accounting:fractionPresentation(left).decimal});setError('');}catch(e){setError(String(e));}}}>Reutilizar depreciação financeira ainda não repartida — activo {n}</button>
|
||||
{a.category!=='nondepreciable'&&<>
|
||||
<label>Tipo de aquisição/base<select aria-label={'Activo '+n+' valuation'} value={a.valuation} onChange={e=>patch(a.id,{valuation:e.target.value as DepreciationAsset['valuation']})}><option value="">Por identificar</option><option value="historical_new">Bem adquirido novo/construído ou produzido — custo histórico</option><option value="used">Bem adquirido usado — regra própria7(2)</option><option value="revalued">Bem reavaliado — regra própria15</option><option value="other">Outra base — por corresponder</option></select></label>{entry&&choice(a,n,'specificRate','Existe taxa específica da tabelaI para o activo/actividade')}{field(a,n,'residual','Valor residual MZN')}{entry?.kind==='building'&&field(a,n,'land','Terreno/parte não depreciável incluídos no custo MZN')}
|
||||
<label>Método<select aria-label={'Activo '+n+' method'}value={a.method}onChange={e=>patch(a.id,{method:e.target.value as DepreciationAsset['method']})}><option value="">Por identificar</option><option value="constant">Quotas constantes — artigos5/7</option><option value="declining">Quotas degressivas — artigos6/8</option><option value="low_value">Valor reduzido, opção integral — artigo17</option></select></label>
|
||||
{a.method==='constant'&&field(a,n,'rate','Taxa constante escolhida %')}
|
||||
{a.method==='declining'&&<>{choice(a,n,'newTangible','Activo tangível novo adquirido a terceiros ou construído/produzido')}{field(a,n,'factor','Coeficiente degressivo escolhido (máximo1,5/2/2,5)')}{choice(a,n,'switchToStraight','Aplicar saldo/vida útil restante8(2) quando mais favorável')}{a.switchToStraight==='yes'&&choice(a,n,'priorFullQuotas','Condição de quotas anteriores do artigo8(2) integralmente documentada')}</>}
|
||||
{a.method==='low_value'?<>{choice(a,n,'individual','Activo individual, fora de conjunto depreciado como um todo')}<p>Art17:até20000MZN, sem amortização fiscal anterior e entrada emuso neste período; a dedução não excede o contabilizado e o saldo fiscal.</p></>:<label>Período da quota<select aria-label={'Activo '+n+' timing'}value={a.timing}onChange={e=>patch(a.id,{timing:e.target.value as DepreciationAsset['timing']})}><option value="">Por identificar</option><option value="annual">Quota anual</option><option value="months">Duodécimos em vigor desde o início — artigo9</option></select></label>}
|
||||
{field(a,n,'start','Entrada em funcionamento/utilização AAAA-MM-DD')}
|
||||
<label>Transmissão, inutilização ou termo da vida útil<select aria-label={'Activo '+n+' exitState'}value={a.exitState}onChange={e=>patch(a.id,{exitState:e.target.value as DepreciationAsset['exitState']})}><option value="">Informação em falta</option><option value="absent">Ausência declarada de evento</option><option value="known">Evento/data conhecidos</option></select></label>{(a.exitState==='known'||a.exitDate)&&field(a,n,'exitDate','Data do evento AAAA-MM-DD')}
|
||||
{choice(a,n,'consistentMethod','Método uniforme desde o início, sem mudança ou excepção por corresponder')}{entry?.kind==='vehicle'&&choice(a,n,'vehicleException','Afectação a serviço público de transportes/locação na actividade normal')}{entry?.kind==='software'&&choice(a,n,'limitedRights','Aquisição onerosa e utilização exclusiva por tempo limitado')}
|
||||
{field(a,n,'previousAccounting','Quotas contabilísticas anteriores desde a utilização MZN')}{field(a,n,'previousFiscal','Quotas fiscais anteriormente deduzidas MZN')}{field(a,n,'priorAvailable','Parcela anterior temporariamente tributada, recuperável MZN')}{field(a,n,'previousReference','Suporte das quotas anteriores e da recuperabilidade fiscal')}
|
||||
{['previousAccounting','previousFiscal','priorAvailable'].every(k=>!a[k as 'previousFiscal']||/^0(?:\.0+)?$/.test(a[k as 'previousFiscal']))&&<button type="button"onClick={()=>{if(!a.reference.trim()){setError('Identifique primeiro o suporte da ausência de quotas anteriores.');return;}patch(a.id,{previousAccounting:'0',previousFiscal:'0',priorAvailable:'0',previousReference:'Ausência de quotas anteriores expressamente declarada; '+a.reference});setError('');}}>Declarar ausência de quotas anteriores — activo {n}</button>}
|
||||
<p>Quota anterior recuperável exige contabilização e tributação temporária demonstradas. Não inclui encargos permanentemente excluídos ou quota nunca contabilizada. O cálculo apresenta o saldo posterior aritmético; não consome saldos oficiais.</p></>}
|
||||
{field(a,n,'reference','Suporte do activo, categoria, condições e período')}<label>Condições ordinárias e documentos conferidos<select aria-label={'Activo '+n+' conditions'}value={a.conditions}onChange={e=>patch(a.id,{conditions:e.target.value as ''|'yes'})}><option value="">Informação em falta</option><option value="yes">Sim, no recorte documentado</option></select></label>
|
||||
<button type="button"onClick={()=>save({...book,depreciation:{...input,assets:input.assets.filter(q=>q.id!==a.id)}})}>Retirar activo {n} das entradas actuais</button></fieldset>;})}
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import {normalFinancialSource,type NormalBook} from '@/lib/irpc-normal';
|
||||
import {m20Accounts,newNormalFormPreparation,newFormAllocation,reuseDirectFormAllocations,normalFormAccountsFor,type NormalFormPreparationInput,type FormAllocation} from '@/lib/irpc-normal-form-preparation';
|
||||
export function SimulationIrpcFormPreparation({draft,book,save}:{draft:Record<string,unknown>;book:NormalBook;save:(b:NormalBook)=>void}){
|
||||
const i=book.forms,source=normalFinancialSource(draft);if(!i)return<button type="button" onClick={()=>save({...book,forms:newNormalFormPreparation()})}>Preparar representação dos valores e campos M20/M22</button>;
|
||||
const patch=(p:Partial<NormalFormPreparationInput>)=>save({...book,forms:{...i,...p}}),row=(id:string,p:Partial<FormAllocation>)=>patch({allocations:i.allocations.map(a=>a.id===id?{...a,...p}:a)});
|
||||
return<fieldset aria-label="Preparação de valores M20 M22" className="space-y-3 rounded border p-3"><legend>Representação dos valores e correspondência M20/M22</legend><p>Reutiliza os valores exactos do cálculo e do período/moeda já documentados. A preparação dos campos não altera lucro tributável,matéria colectável ou colecta. M20A.1 exige meticais sem centavos;fracções conservam o valor exacto e ficam pendentes de regra de representação. O transporte201/quadro8 versus200/quadro10 continua identificado,só no resultado afectado. Não gera declaração oficial nem saldo finalL8.</p>
|
||||
<label className="block">Exemplares de preparação<select value={i.edition} onChange={e=>patch({edition:e.target.value as NormalFormPreparationInput['edition']})}><option value="">Por identificar</option><option value="accepted_pdf_and_m20a1">PDF M22 quadros10–12 e M20A.1 inventariados — edições distintas</option></select></label>
|
||||
<label className="block">Suporte da edição e do período<input value={i.reference} onChange={e=>patch({reference:e.target.value})}/></label><label className="block">Completude das contas e discriminação das linhas do plano<input value={i.completenessReference} onChange={e=>patch({completenessReference:e.target.value})}/></label>
|
||||
<button type="button" onClick={()=>patch({allocations:[...i.allocations,...reuseDirectFormAllocations(source).filter(a=>!i.allocations.some(r=>r.sourceCode===a.sourceCode))]})}>Reutilizar contas directas ainda não discriminadas</button><button type="button" onClick={()=>patch({allocations:[...i.allocations,newFormAllocation(crypto.randomUUID())]})}>Acrescentar parcela contabilística M20</button>
|
||||
{i.allocations.map((a,n)=><fieldset key={a.id} className="rounded border p-2"><legend>Parcela M20 {n+1}</legend><label className="block">Linha de origem<select aria-label={'Origem M20 '+(n+1)} value={a.sourceCode} onChange={e=>row(a.id,{sourceCode:e.target.value})}><option value="">Seleccione</option>{source?.accountingLines.map(l=><option key={l.code} value={l.code}>{l.origin}: {l.total} MZN</option>)}</select></label><label className="block">Conta M20A.1<select aria-label={'Conta M20 '+(n+1)} value={a.account} onChange={e=>row(a.id,{account:e.target.value as FormAllocation['account']})}><option value="">Por corresponder</option>{Object.entries(m20Accounts).filter(([k])=>normalFormAccountsFor(a.sourceCode).includes(k)||k===a.account).map(([k,v])=><option key={k}value={k}>{k} — {v}</option>)}</select></label><label className="block">Montante MZN<input aria-label={'Montante M20 '+(n+1)}value={a.amount}onChange={e=>row(a.id,{amount:e.target.value})}/></label><label className="block">Suporte da classificação<input aria-label={'Suporte M20 '+(n+1)}value={a.reference}onChange={e=>row(a.id,{reference:e.target.value})}/></label></fieldset>)}
|
||||
<fieldset className="rounded border p-2"><legend>Imposto contabilizado e resultado líquido</legend><p>Colecta e saldo a pagar não são usados como gasto contabilístico presumido. Indique imposto reconhecido e se já está incluído numa linha do plano,para evitar subtracção duplicada.</p><label className="block">Estado do imposto contabilizado<select aria-label="Estado do imposto contabilizado" value={i.incomeTax.state} onChange={e=>patch({incomeTax:{...i.incomeTax,state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="known">Valor conhecido</option><option value="absent">Ausência declarada com suporte</option></select></label>{(i.incomeTax.state==='known'||i.incomeTax.amount!==''||i.incomeTax.placement!=='')&&<><label className="block">Imposto contabilizado em MZN<input value={i.incomeTax.amount} onChange={e=>patch({incomeTax:{...i.incomeTax,amount:e.target.value}})}/></label><label className="block">Localização do imposto contabilizado<select value={i.incomeTax.placement} onChange={e=>patch({incomeTax:{...i.incomeTax,placement:e.target.value as ''|'included'|'outside'}})}><option value="">Por identificar</option><option value="included">Já incluído no resultado do plano — discriminado na conta8.5</option><option value="outside">Exterior ao resultado antes de imposto do plano</option></select></label></>}<label className="block">Suporte do imposto contabilizado<input value={i.incomeTax.reference}onChange={e=>patch({incomeTax:{...i.incomeTax,reference:e.target.value}})}/></label></fieldset>
|
||||
<label className="block">Parcela de taxa reduzida para a colecta total302<select aria-label="Parcela de taxa reduzida para a colecta total302" value={i.reducedRate.state} onChange={e=>patch({reducedRate:{...i.reducedRate,state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada — sem parcela reduzida neste sujeito/período</option><option value="known">Situação conhecida — âmbitoL7 por tratar</option></select></label><label className="block">Suporte da situação da taxa reduzida<input value={i.reducedRate.reference}onChange={e=>patch({reducedRate:{...i.reducedRate,reference:e.target.value}})}/></label>
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import {SimulationIrpcFormPreparation} from '@/components/simulation-irpc-form-preparation';
|
||||
import {SimulationIrpcPeriodFx} from '@/components/simulation-irpc-period-fx';
|
||||
import {SimulationIrpcDepreciation} from '@/components/simulation-irpc-depreciation';
|
||||
import {SimulationIrpcCosts} from '@/components/simulation-irpc-costs';
|
||||
import {SimulationIrpcSchedules} from '@/components/simulation-irpc-schedules';
|
||||
import {SimulationIrpcAssessment} from '@/components/simulation-irpc-assessment';
|
||||
@@ -15,9 +18,9 @@ export function SimulationIrpcNormal({draft,onChange}:{draft:Record<string,unkno
|
||||
const profit=(patch:Partial<NormalProfitInput>)=>save({...b,profit:{...(b.profit??newNormalProfitInput()),...patch}});
|
||||
const field=(r:NormalAdjustment,k:'document'|'line'|'amount'|'reference',label:string)=><label className="block">{label}<input value={r[k]} onChange={e=>patch(r.id,{[k]:e.target.value})}/></label>;
|
||||
return<section aria-label="IRPC normal — ajustamentos" className="space-y-3 rounded border p-4"><h4>L6 — IRPC normal: ajustamentos ao resultado financeiro</h4><p>Acréscimos de multas sem origem contratual e juros compensatórios incluídos nos custos do plano, artigo 36(1)c. A conferência anual pode determinar o lucro tributável simulado no recorte ordinário documentado. A matéria colectável exige conferência específica de deduções e perdas; a colecta tem cálculo próprio e o saldo final a pagar ou recuperar pertence aL8. Não reutiliza automaticamente juros simulados de L5.</p>{error&&<p role="alert">{error}</p>}
|
||||
{source?<p>Origem: plano financeiro desta oportunidade; {source.periods} meses; {source.currency}; resultado antes de imposto: {source.accountingProfitBeforeTax}. Cobranças, pagamentos e reembolso de capital ficam fora do resultado.</p>:<p>Prepare o plano financeiro para reutilizar o resultado e os custos reconciliados.</p>}
|
||||
{source?<p>Origem: plano financeiro desta oportunidade; {source.periods} meses; {source.currency}; resultado antes de imposto: {source.accountingProfitBeforeTax}. Cobranças, pagamentos e reembolso de capital ficam fora do resultado.</p>:<p>{b.periodsFx?'Complete a correspondência do período e moeda abaixo para reutilizar o plano existente emMZN.':'Prepare o plano financeiro para reutilizar o resultado e os custos reconciliados.'}</p>}
|
||||
<label className="block">Exercício fiscal<input value={b.year} onChange={e=>save({...b,year:e.target.value})}/></label>
|
||||
<label className="block">{b.schedules?'Correspondência dos12 meses do plano ao período anual documentado':'Correspondência dos 12 meses do plano a Janeiro–Dezembro de 2026'}<input value={b.mappingReference} onChange={e=>save({...b,mappingReference:e.target.value})}/></label>
|
||||
<label className="block">{b.periodsFx?'Correspondência do plano ao período documentado':b.schedules?'Correspondência dos12 meses do plano ao período anual documentado':'Correspondência dos 12 meses do plano a Janeiro–Dezembro de 2026'}<input value={b.mappingReference} onChange={e=>save({...b,mappingReference:e.target.value})}/></label>
|
||||
<label className="block">Âmbito<select value={b.scope} onChange={e=>save({...b,scope:e.target.value as NormalBook['scope']})}><option value="">Por identificar</option><option value="resident_general">Sujeito residente — regime geral, fora de transparência e regimes especiais</option><option value="ep_general">Não residente com estabelecimento estável — regime geral, só resultado imputável ao EP (mapas complementares)</option><option value="other">Outro âmbito — por corresponder</option></select></label>
|
||||
<label className="block">Suporte do âmbito e da imputação integral do resultado ao sujeito/EP<input value={b.scopeReference} onChange={e=>save({...b,scopeReference:e.target.value})}/></label>
|
||||
<button type="button" onClick={()=>save({...b,rows:[...b.rows,newNormalAdjustment(crypto.randomUUID())]})}>Acrescentar ajustamento ao resultado</button>
|
||||
@@ -30,10 +33,13 @@ export function SimulationIrpcNormal({draft,onChange}:{draft:Record<string,unkno
|
||||
<p>Depreciação positiva e juros financeiros não integralmente acrescidos exigem correspondência própria;uma declaração de ausência não apaga os valores do plano.</p>
|
||||
{profitTopics.map(([k,label])=>{const t=b.profit!.topics[k];const change=(p:Partial<ProfitEvidence>)=>profit({topics:{...b.profit!.topics,[k]:{...t,...p}}});return<fieldset key={k} className="rounded border p-2"><legend>{label}</legend><label className="block">Estado — {label}<select aria-label={'Estado — '+label} value={t.state} onChange={e=>change({state:e.target.value as ProfitEvidence['state']})}><option value="">Informação em falta</option><option value="known">Valor conhecido</option><option value="absent">Ausência declarada com suporte</option></select></label>{(t.state==='known'||t.amount!=='')&&<label className="block">Montante conhecido — {label}<input value={t.amount} onChange={e=>change({amount:e.target.value})}/></label>}<label className="block">Suporte — {label}<input value={t.reference} onChange={e=>change({reference:e.target.value})}/></label></fieldset>;})}
|
||||
</fieldset>}
|
||||
<SimulationIrpcPeriodFx draft={draft} book={b} save={save}/>
|
||||
<SimulationIrpcSchedules book={b} save={save}/>
|
||||
<SimulationIrpcDepreciation draft={draft} book={b} save={save}/>
|
||||
<SimulationIrpcCosts draft={draft} book={b} save={save}/>
|
||||
<SimulationIrpcTaxableBase draft={draft} book={b} save={save}/>
|
||||
<SimulationIrpcAssessment book={b} save={save}/>
|
||||
<SimulationIrpcFormPreparation draft={draft} book={b} save={save}/>
|
||||
<button type="button" onClick={()=>{try{save(recordNormal(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar ajustamentos L6</button>
|
||||
<button type="button" onClick={()=>{try{const html=normalHtml(draft),url=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),a=document.createElement('a');a.href=url;a.download='IRPC-normal-ajustamentos.html';a.click();URL.revokeObjectURL(url);setError('');}catch(e){setError(String(e));}}}>Exportar mapa parcial L6</button>
|
||||
{normalReportRows(draft).map((r,n)=><p key={n}>{r[1]} — {r[2]} — {r[3]}</p>)}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import {SimulationFxSelection} from '@/components/simulation-fx-selection';
|
||||
import {rawNormalFinancialSource,type NormalBook} from '@/lib/irpc-normal';
|
||||
import {newNormalPeriodFx,mapNormalPeriodFx,type NormalPeriodFxInput} from '@/lib/irpc-normal-period-fx';
|
||||
import {irpcFxSources,irpcFxRow,updateIrpcFx} from '@/lib/irpc-fx';
|
||||
export function SimulationIrpcPeriodFx({draft,book,save}:{draft:Record<string,unknown>;book:NormalBook;save:(b:NormalBook)=>void}){
|
||||
const raw=rawNormalFinancialSource(draft),i=book.periodsFx;
|
||||
if(!i)return<button type="button" onClick={()=>save({...book,periodsFx:newNormalPeriodFx()})}>Preparar período curto ou plano noutra moeda</button>;
|
||||
const patch=(p:Partial<NormalPeriodFxInput>)=>save({...book,periodsFx:{...i,...p}}),model=mapNormalPeriodFx(draft,i,raw),sources=irpcFxSources(draft);
|
||||
const text=(k:'from'|'to'|'planStart'|'reference'|'conversionReference'|'mznMapsReference',label:string)=><label className="block">{label}<input value={i[k]} onChange={e=>patch({[k]:e.target.value})}/></label>;
|
||||
return<fieldset aria-label="Período e moeda L6" className="space-y-3 rounded border p-3"><legend>Período fiscal e conversão do plano — simulação L6</legend><p>Reutiliza os meses e valores originais do plano. Seleccione apenas os meses do período documentado;sem anualizar o resultado. Os mapas,activos e saldos fiscais permanecem emMZN e exigem reconciliação explícita. A conversão cambial para simulação usa o mecanismo existente; não confirma automaticamente a regra fiscal de conversão anual.</p>
|
||||
<label className="block">Tipo de período<select value={i.kind} onChange={e=>patch({kind:e.target.value as NormalPeriodFxInput['kind']})}><option value="">Por identificar</option><option value="annual">Anual documentado — 12meses</option><option value="start">Início de actividade — 7(4)a</option><option value="cessation">Cessação de actividade — 7(4)b</option><option value="same_year">Início e fim de sujeição no mesmo exercício — 7(4)c</option><option value="transition">Transição para período autorizado/comunicado — 7(4)d</option></select></label>
|
||||
{text('from','Início do período fiscal (AAAA-MM-DD)')}{text('to','Fim do período fiscal (AAAA-MM-DD)')}{text('planStart','Primeiro mês civil do plano (AAAA-MM-01)')}{text('reference','Suporte do evento,período e correspondência dos meses')}
|
||||
{book.schedules&&<button type="button" onClick={()=>patch({from:i.from||book.schedules!.period.from,to:i.to||book.schedules!.period.to,reference:i.reference||book.schedules!.period.reference})}>Reutilizar datas e suporte já declarados nos campos vazios</button>}
|
||||
{text('mznMapsReference','Reconciliação dos mapas,parcelas,activos e saldos existentes em MZN para este período')}
|
||||
{raw?.currency!=='MZN'&&<>{text('conversionReference','Pressuposto documentado da conversão mensal para simulação')}
|
||||
{[...new Set(model.rows.map(r=>r.period))].map(period=>{const date=model.rows.find(r=>r.period===period)?.date,entry=i.months.find(m=>m.period===period)??{period,currency:raw?.currency??''};const change=(p:Partial<typeof entry>)=>patch({months:[...i.months.filter(m=>m.period!==period),{...entry,...p}]});return<fieldset key={period} aria-label={'Câmbio do mês '+period} className="rounded border p-2"><legend>Mês{period} — {date} — {raw?.currency}</legend><label>Reutilizar selecção cambial existente<select value={entry.fxLink?.id??''} onChange={e=>{const s=sources.find(s=>s.id===e.target.value);if(s)change({currency:raw?.currency??'',fxSelection:s.selection,fxLink:{id:s.id,fingerprint:s.fingerprint}});}}><option value="">Seleccione uma origem documentada</option>{sources.map(s=><option value={s.id} key={s.id}>{s.label}</option>)}</select></label><SimulationFxSelection row={irpcFxRow('normal-'+period,entry)} onChange={p=>{if(p.fxSelection)change({...updateIrpcFx(entry,p.fxSelection),currency:raw?.currency??''});}}/></fieldset>;})}
|
||||
</>}
|
||||
{i.months.filter(m=>!model.rows.some(r=>r.period===m.period)).map(m=><p key={m.period}>Selecção conservada do mês{m.period},fora do período actual. <button type="button" onClick={()=>patch({months:i.months.filter(x=>x.period!==m.period)})}>Retirar selecção do mês{m.period} deste mapa</button></p>)}
|
||||
<p>{model.months}meses;resultado convertido:{model.source?.accountingProfitBeforeTax??'Indeterminado'} MZN. {model.pending.join(' | ')}</p><p>Meses parciais,limites anuais por corresponder e contagem de perdas41(5) afectam apenas os respectivos casos. Colecta não equivale ao saldo final a pagar.</p>
|
||||
</fieldset>;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export function SimulationIrpcSchedules({book,save}:{book:NormalBook;save:(b:Nor
|
||||
const asset=(id:string,p:Partial<NormalAsset>)=>patch({assets:s.assets.map(a=>a.id===id?{...a,...p}:a)});
|
||||
return<fieldset aria-label="Mapas complementares L6" className="space-y-3 rounded border p-3"><legend>Mapas complementares — regras ordinárias documentadas</legend>
|
||||
<p>Reutiliza o resultado e as linhas do plano. As somas brutas de cada tema devem corresponder à conferência do lucro tributável. Os efeitos com sinal ficam discriminados; uma dúvida não apaga as parcelas independentes. L7 conserva benefícios e casos especiais; L8 conserva o fecho anual.</p>
|
||||
<fieldset><legend>Período anual e correspondência dos meses</legend><label>Tipo de período<select aria-label="Tipo de período L6" value={s.period.kind} onChange={e=>patch({period:{...s.period,kind:e.target.value as NormalSchedules['period']['kind']}})}><option value="">Por identificar</option><option value="calendar">Ano civil — artigo7(1)</option><option value="authorised_annual">Anual diferente — autorização artigo7(2)</option><option value="ep_annual">Anual diferente de EP — comunicação artigo7(3)</option></select></label>{(['from','to','reference']as const).map(k=><label key={k} className="block">{({from:'Início do período AAAA-MM-DD',to:'Termo do período AAAA-MM-DD',reference:'Suporte do período e da correspondência mensal'})[k]}<input aria-label={'Período L6 '+k} value={s.period[k]} onChange={e=>patch({period:{...s.period,[k]:e.target.value}})}/></label>)}<p>Período anual diferente: documente autorização do artigo7(2) ou comunicação do EP do artigo7(3), início de aplicação e manutenção por cinco exercícios. O plano representa o período completo com termo em2026, emMZN. Períodos curtos e conversão fiscal anual de outra moeda ficam por corresponder.</p></fieldset>
|
||||
{book.periodsFx&&book.periodsFx.kind!=='annual'?<p>Período actual no mapa «Período fiscal e conversão do plano». O período anual anterior permanece conservado no histórico.</p>:<fieldset><legend>Período anual e correspondência dos meses</legend><label>Tipo de período<select aria-label="Tipo de período L6" value={s.period.kind} onChange={e=>patch({period:{...s.period,kind:e.target.value as NormalSchedules['period']['kind']}})}><option value="">Por identificar</option><option value="calendar">Ano civil — artigo7(1)</option><option value="authorised_annual">Anual diferente — autorização artigo7(2)</option><option value="ep_annual">Anual diferente de EP — comunicação artigo7(3)</option></select></label>{(['from','to','reference']as const).map(k=><label key={k} className="block">{({from:'Início do período AAAA-MM-DD',to:'Termo do período AAAA-MM-DD',reference:'Suporte do período e da correspondência mensal'})[k]}<input aria-label={'Período L6 '+k} value={s.period[k]} onChange={e=>patch({period:{...s.period,[k]:e.target.value}})}/></label>)}<p>Período anual diferente: documente autorização do artigo7(2) ou comunicação do EP do artigo7(3), início de aplicação e manutenção por cinco exercícios. O plano representa o período completo com termo em2026, emMZN. Períodos curtos e conversão fiscal anual de outra moeda ficam por corresponder.</p></fieldset>}
|
||||
<button type="button" onClick={()=>patch({rows:[...s.rows,newScheduleRow(crypto.randomUUID())]})}>Acrescentar regra complementar</button>
|
||||
{s.rows.map((r,n)=><fieldset key={r.id} className="rounded border p-2"><legend>Regra complementar {n+1}</legend>
|
||||
<label>Tratamento<select aria-label={'Tratamento complementar '+(n+1)} value={r.rule} onChange={e=>row(r.id,{rule:e.target.value as ScheduleRow['rule']})}><option value="">Por identificar</option>{Object.entries(scheduleRules).map(([k,v])=><option key={k} value={k}>{v[0]} — {v[2]}</option>)}</select></label>
|
||||
@@ -19,11 +19,11 @@ export function SimulationIrpcSchedules({book,save}:{book:NormalBook;save:(b:Nor
|
||||
<label className="block">Juros ordinários remanescentes MZN<input value={s.interest.amount} onChange={e=>patch({interest:{...s.interest,amount:e.target.value}})}/></label>
|
||||
<label className="block">Suporte dos juros e condições<input value={s.interest.reference} onChange={e=>patch({interest:{...s.interest,reference:e.target.value}})}/></label>
|
||||
<label>Condições dos juros<select aria-label="Condições dos juros ordinários" value={s.interest.conditions} onChange={e=>patch({interest:{...s.interest,conditions:e.target.value as ''|'yes'}})}><option value="">Informação em falta</option><option value="yes">Demonstradas no recorte ordinário</option></select></label></fieldset>
|
||||
<fieldset><legend>Amortizações fiscais — quotas constantes anuais</legend><p>Decreto72/2013, artigos3–7. Activo próprio sujeito a deperecimento, custo histórico, uso no início de2026, método uniforme, vida útil fiscal não excedida. Documente que não há regra sectorial específica prevalecente ou limitação especial. A categoria exige qualificação documentada; os itens genéricos9/9.1 só se aplicam quando não há taxa específica da tabelaI. Duodécimos, reavaliação, bens usados e recuperação de quotas antes tributadas ficam separados.</p>
|
||||
{!book.depreciation?<fieldset><legend>Amortizações fiscais — quotas constantes anuais</legend><p>Decreto72/2013, artigos3–7. Activo próprio sujeito a deperecimento, custo histórico, uso no início de2026, método uniforme, vida útil fiscal não excedida. Documente que não há regra sectorial específica prevalecente ou limitação especial. A categoria exige qualificação documentada; os itens genéricos9/9.1 só se aplicam quando não há taxa específica da tabelaI. Duodécimos, reavaliação, bens usados e recuperação de quotas antes tributadas ficam separados.</p>
|
||||
<label className="block">Reconciliação de todos os activos com a depreciação do plano<input value={s.assetsReference} onChange={e=>patch({assetsReference:e.target.value})}/></label>
|
||||
<button type="button" onClick={()=>patch({assets:[...s.assets,newNormalAsset(crypto.randomUUID())]})}>Acrescentar activo fiscal</button>
|
||||
{s.assets.map((a,n)=><fieldset key={a.id} className="rounded border p-2"><legend>Activo {n+1}</legend><label>Categoria fiscal<select aria-label={'Categoria activo '+(n+1)} value={a.category} onChange={e=>asset(a.id,{category:e.target.value as NormalAsset['category'],rate:e.target.value==='computer'?'25':e.target.value==='server'?'20':'',tableReference:e.target.value==='computer'?'Decreto72/2013 tabelaII grupo3 item9':e.target.value==='server'?'Decreto72/2013 tabelaII grupo3 item9.1':''})}><option value="">Por identificar</option><option value="computer">Computador, impressora, UPS — II/3/9 — 25%</option><option value="server">Servidor — II/3/9.1 — 20%</option><option value="other">Outra categoria — correspondência pendente</option></select></label>{([['description','Descrição'],['cost','Custo fiscal documentado MZN'],['residual','Valor residual MZN'],['rate','Taxa anual documentada %'],['accounting','Depreciação contabilística de2026 MZN'],['previousFiscal','Amortização fiscal acumulada anterior MZN'],['inUseFrom','Data de entrada em uso AAAA-MM-DD'],['tableReference','Diploma, tabela, código e taxa aplicáveis'],['reference','Documentos e condições do activo']]as const).map(([k,label])=><label key={k} className="block">{label}<input aria-label={label+' activo '+(n+1)} value={a[k]} onChange={e=>asset(a.id,{[k]:e.target.value})}/></label>)}
|
||||
<label>Condições do activo<select aria-label={'Condições activo '+(n+1)} value={a.conditions} onChange={e=>asset(a.id,{conditions:e.target.value as ''|'yes'})}><option value="">Informação em falta</option><option value="yes">Demonstradas no recorte anual constante</option></select></label><button type="button" onClick={()=>patch({assets:s.assets.filter(q=>q.id!==a.id)})}>Retirar activo {n+1} das entradas actuais</button></fieldset>)}</fieldset>
|
||||
<label>Condições do activo<select aria-label={'Condições activo '+(n+1)} value={a.conditions} onChange={e=>asset(a.id,{conditions:e.target.value as ''|'yes'})}><option value="">Informação em falta</option><option value="yes">Demonstradas no recorte anual constante</option></select></label><button type="button" onClick={()=>patch({assets:s.assets.filter(q=>q.id!==a.id)})}>Retirar activo {n+1} das entradas actuais</button></fieldset>)}</fieldset>:<p>Activos do mapa inicial conservados; a depreciação é agora reconciliada exclusivamente pelo mapa de tabelas, métodos, duodécimos e quotas anteriores abaixo.</p>}
|
||||
<label className="block">Suporte da perda fiscal do próprio exercício, se apurada<input value={s.currentLossReference} onChange={e=>patch({currentLossReference:e.target.value})}/></label><p>Perda actual conhecida: matéria colectável e colecta zero apenas depois de conferidas as restantes condições. Não é dedução a si própria nem saldo oficial de reporte. Informação insuficiente mantém os totais dependentes indeterminados.</p>
|
||||
</fieldset>;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export type NormalAssessmentInput={version:1;treatment:''|'general_61_1'|'other'
|
||||
export const newNormalAssessmentInput=():NormalAssessmentInput=>({version:1,treatment:'',reference:''});
|
||||
export function validateNormalAssessment(i:NormalAssessmentInput){if(!i||i.version!==1||!['','general_61_1','other'].includes(i.treatment)||typeof i.reference!=='string')throw Error('Enquadramento da colecta não recuperável;original conservado.');}
|
||||
export type NormalAssessmentModel={input:NormalAssessmentInput;period:{year:string;from:string;to:string};classification:string;rate:FiscalFraction|null;taxableBase:FiscalFraction|null;colecta:FiscalFraction|null;centExact:boolean|null;pending:string[];rule:string;source:string;finalBalance:{scope:'L8';status:'not_calculated';payable:null;recoverable:null;reason:string}};
|
||||
export function calculateNormalAssessment(input:NormalAssessmentInput,base:FiscalFraction|null,context:{year:string;scope:string;currency:string;extended?:boolean;period?:{from:string;to:string}}):NormalAssessmentModel{
|
||||
export function calculateNormalAssessment(input:NormalAssessmentInput,base:FiscalFraction|null,context:{year:string;scope:string;currency:string;extended?:boolean;short?:boolean;period?:{from:string;to:string}}):NormalAssessmentModel{
|
||||
validateNormalAssessment(input);const pending:string[]=[],period={year:context.year,from:/^20\d{2}$/.test(context.year)?context.year+'-01-01':'',to:/^20\d{2}$/.test(context.year)?context.year+'-12-31':''};
|
||||
if(context.year!=='2026')pending.push('Taxa geral correspondida neste incremento apenas ao exercício2026;sem taxa transportada para outro período.');
|
||||
if(!(context.scope==='resident_general'||context.extended&&context.scope==='ep_general')||context.currency!=='MZN')pending.push('Colecta delimitada ao sujeito residente no regime geral emMZN;outros âmbitos por corresponder.');
|
||||
@@ -13,8 +13,8 @@ export function calculateNormalAssessment(input:NormalAssessmentInput,base:Fisca
|
||||
if(!input.reference.trim())pending.push('Identifique suporte ou pressuposto da aplicação da taxa geral ao caso;uma actividade semelhante não prova o regime.');
|
||||
if(base===null)pending.push('Matéria colectável actual indeterminada;lucro tributável não é substituto da base.');
|
||||
else if(!/^-?\d{1,80}$/.test(base.numerator)||!/^\d{1,80}$/.test(base.denominator)||BigInt(base.denominator)===0n||BigInt(base.numerator)<0n)pending.push('Matéria colectável deve ser um valor conhecido não negativo e reconciliado.');
|
||||
if(context.period){period.from=context.period.from;period.to=context.period.to;const start=new Date(period.from+'T00:00:00Z'),end=new Date(period.to+'T00:00:00Z');if(!Number.isFinite(+start)||!Number.isFinite(+end)||start.toISOString().slice(0,10)!==period.from||end.toISOString().slice(0,10)!==period.to||!period.from.endsWith('-01')||!period.to.startsWith('2026-'))pending.push('Período fiscal anual não reconciliado.');else{start.setUTCFullYear(start.getUTCFullYear()+1);start.setUTCDate(start.getUTCDate()-1);if(start.toISOString().slice(0,10)!==period.to)pending.push('Colecta anual: período de12 meses por reconciliar.');}}
|
||||
if(context.period){period.from=context.period.from;period.to=context.period.to;const start=new Date(period.from+'T00:00:00Z'),end=new Date(period.to+'T00:00:00Z');if(!Number.isFinite(+start)||!Number.isFinite(+end)||start.toISOString().slice(0,10)!==period.from||end.toISOString().slice(0,10)!==period.to||!period.from.endsWith('-01')||!period.to.startsWith('2026-'))pending.push('Período fiscal anual não reconciliado.');else if(context.short){if(period.from>period.to||!period.from.startsWith('2026-')||period.from===context.year+'-01-01'&&period.to===context.year+'-12-31')pending.push('Período curto por reconciliar.');}else{start.setUTCFullYear(start.getUTCFullYear()+1);start.setUTCDate(start.getUTCDate()-1);if(start.toISOString().slice(0,10)!==period.to)pending.push('Colecta anual: período de12 meses por reconciliar.');}}
|
||||
const rate=input.treatment==='general_61_1'&&context.year==='2026'?fraction(32n):null;
|
||||
const colecta=pending.length===0&&base!==null&&rate?fraction(BigInt(base.numerator)*32n,BigInt(base.denominator)*100n):null;
|
||||
return{input:structuredClone(input),period,classification:input.treatment==='general_61_1'?'Regime normal — taxa geral do artigo61(1)':input.treatment==='other'?'Caso especial fora deste recorte':'Enquadramento por identificar',rate,taxableBase:base,colecta,centExact:colecta?(BigInt(colecta.numerator)*100n)%BigInt(colecta.denominator)===0n:null,pending,rule:'CIRPC61(1)-general-2026/1',source:context.extended?'CIRPC61(1), taxa geral32% mantida pelos diplomas inventariados até Lei12/2025; período anual documentado com termo em2026. CIRPC15(1)a/c,7(1–3): sujeito residente ou resultado imputável aEP no regime geral; ano civil, autorização ou comunicação expressamente identificadas. Sem arredondamento fiscal ou saldoL8 presumidos.':'CIRPC Lei34/2007 art61(1),PDF17/p768(71):taxa geral32%. Lei4/2012 altera61(2);Lei20/2022 PDF1/p2731 mantém61(1),altera61(2) com termo31/12/2025;Lei12/2025 PDF18/20 não altera61(1),adita61-A para mais-valias,fora deste recorte. Exercício anual2026/MZN/residente geral,matéria colectável actual×32/100 em aritmética racional exacta. Sem arredondamento fiscal ou transcriçãoM20/M22 presumidos.',finalBalance:{scope:'L8',status:'not_calculated',payable:null,recoverable:null,reason:'Saldo final a pagar ou recuperar por determinar emL8:retenções,pagamentos e créditos do fecho anual não apurados nem abatidos à colecta neste incremento. Colecta não equivale ao saldo final.'}};
|
||||
return{input:structuredClone(input),period,classification:input.treatment==='general_61_1'?'Regime normal — taxa geral do artigo61(1)':input.treatment==='other'?'Caso especial fora deste recorte':'Enquadramento por identificar',rate,taxableBase:base,colecta,centExact:colecta?(BigInt(colecta.numerator)*100n)%BigInt(colecta.denominator)===0n:null,pending,rule:'CIRPC61(1)-general-2026/1',source:context.short?'CIRPC7(4)/61(1):período curto documentado em2026,matéria colectável efectiva ×32%;sem anualizar lucro/base ou proporcionalizar a taxa;saldo finalL8 indeterminado.':context.extended?'CIRPC61(1), taxa geral32% mantida pelos diplomas inventariados até Lei12/2025; período anual documentado com termo em2026. CIRPC15(1)a/c,7(1–3): sujeito residente ou resultado imputável aEP no regime geral; ano civil, autorização ou comunicação expressamente identificadas. Sem arredondamento fiscal ou saldoL8 presumidos.':'CIRPC Lei34/2007 art61(1),PDF17/p768(71):taxa geral32%. Lei4/2012 altera61(2);Lei20/2022 PDF1/p2731 mantém61(1),altera61(2) com termo31/12/2025;Lei12/2025 PDF18/20 não altera61(1),adita61-A para mais-valias,fora deste recorte. Exercício anual2026/MZN/residente geral,matéria colectável actual×32/100 em aritmética racional exacta. Sem arredondamento fiscal ou transcriçãoM20/M22 presumidos.',finalBalance:{scope:'L8',status:'not_calculated',payable:null,recoverable:null,reason:'Saldo final a pagar ou recuperar por determinar emL8:retenções,pagamentos e créditos do fecho anual não apurados nem abatidos à colecta neste incremento. Colecta não equivale ao saldo final.'}};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export type NormalBaseModel={input:NormalBaseInput;taxableProfit:FiscalFraction|
|
||||
const zero=()=>fraction(0n);
|
||||
function amount(s:string){if(!/^\d{1,18}(?:\.\d{1,2})?$/.test(s))throw Error();return decimalFraction(s);}
|
||||
const units=(f:FiscalFraction)=>BigInt(f.numerator)*100n/BigInt(f.denominator);
|
||||
export function calculateNormalBase(input:NormalBaseInput,profit:FiscalFraction|null,year:string,currentLossReference?:string):NormalBaseModel{
|
||||
export function calculateNormalBase(input:NormalBaseInput,profit:FiscalFraction|null,year:string,currentLossReference?:string,short=false):NormalBaseModel{
|
||||
validateNormalBase(input);const i=structuredClone(input),pending:string[]=[],losses:NormalBaseModel['losses']=[];
|
||||
if(year!=='2026')pending.push('Matéria colectável delimitada ao exercício2026.');
|
||||
if(!/^\d{9}$/.test(i.subjectNuit)||!i.identityReference.trim())pending.push('Identifique NUIT e suporte do sujeito passivo do lucro e das perdas.');
|
||||
@@ -45,6 +45,7 @@ export function calculateNormalBase(input:NormalBaseInput,profit:FiscalFraction|
|
||||
if(issues.length){r.status='indeterminate';r.reason=issues.join(' ');pending.push(l.year+': '+r.reason);}
|
||||
losses.push(r);
|
||||
}
|
||||
if(short&&losses.some(l=>l.requestedAmount&&BigInt(l.requestedAmount.numerator)>0n))pending.push('Período curto:sequência dos períodos posteriores por reconciliar;41(5) exclui da contagem o período de transição7(4)d inferior a6meses,sem agregar todos os períodos curtos ou presumir prazo por ano civil.');
|
||||
const requestedLosses=addFractions(losses.filter(l=>l.status==='eligible').map(l=>l.requestedAmount!));
|
||||
if(profit&&BigInt(requestedLosses.numerator)*BigInt(profit.denominator)>(BigInt(profit.numerator)>0n?BigInt(profit.numerator)*BigInt(requestedLosses.denominator):0n))pending.push('Utilizações propostas excedem o lucro tributável disponível;reparta explicitamente sem criar base negativa ou escolher uma ordem de perdas.');
|
||||
let benefitDeduction:FiscalFraction|null=null;const benefit=i.benefits;
|
||||
|
||||
@@ -36,7 +36,7 @@ const min=(...v:FiscalFraction[])=>v.reduce((a,b)=>cmp(a,b)<0n?a:b),max0=(v:Fisc
|
||||
const costMoney=(v:FiscalFraction|null)=>{if(!v)return'Indeterminado';const p=fractionPresentation(v);return(p.approximate?'≈':'')+p.decimal+' MZN'+(p.approximate?' (exacto '+v.numerator+'/'+v.denominator+')':'');};
|
||||
const identity=(r:{document:string;line:string})=>r.document.trim()+'\u0000'+r.line.trim();
|
||||
export function reusableCreditBalance(source:Source|null){const l=source?.cashSchedule?.at(-1);if(!l||l.salesReceivablesBalance===null)return null;try{return addFractions([fiscalMoney(l.salesReceivablesBalance),fiscalMoney(l.openingReceivablesBalance)]);}catch{return null;}}
|
||||
export function calculateNormalCosts(input:NormalCostsInput,source:Source|null,existing:CostClaim[],period:{from:string;to:string}){
|
||||
export function calculateNormalCosts(input:NormalCostsInput,source:Source|null,existing:CostClaim[],period:{from:string;to:string},short=false){
|
||||
validateNormalCosts(input);const rows=input.rows,claims=rows.flatMap(r=>[{...r},...(r.rule==='work'?[{id:r.id,document:r.document,line:r.line+'/custos',sourceCode:r.facts.costSource??'',amount:r.facts.bookCost??''}]:[])]),pending:string[]=[];
|
||||
const sourceAmount=(code:string,effect:string)=>{const l=source?.accountingLines.find(l=>l.code===code);if(!l||l.accountingEffect!==effect)throw Error('Seleccione uma linha contabilística compatível.');return fiscalMoney(l.total);};
|
||||
const pool=(rule:CostRule)=>addFractions(rows.filter(r=>r.rule===rule).map(r=>fiscalMoney(r.amount)));
|
||||
@@ -50,6 +50,7 @@ export function calculateNormalCosts(input:NormalCostsInput,source:Source|null,e
|
||||
if(!rule||!r.document.trim()||!r.line.trim()||!r.reference.trim()||r.conditions!=='yes')issues.push('Regra, documento/linha, condições e suporte em falta.');
|
||||
if([...existing,...claims.filter(c=>c!==r&&c.id!==r.id)].some(c=>identity(c)===identity(r)))issues.push('Documento/linha repetidos entre mapas.');
|
||||
if(claims.filter(c=>identity(c)===identity(r)).length>1)issues.push('Documento/linha repetidos no mapa.');
|
||||
if(short&&['credit','advertising'].includes(r.rule))issues.push('Limite anual neste período curto por corresponder;sem proporcionalização ou anualização presumida.');
|
||||
if(source?.currency!=='MZN'||!period.to.startsWith('2026-'))issues.push('Recorte anual emMZN com termo em2026 por reconciliar.');
|
||||
try{amount=fiscalMoney(r.amount);if(cmp(amount,z())<=0n&&r.rule!=='work')throw Error('Identifique parcela positiva conhecida.');
|
||||
const isIncome=['provision_reversal','taxed_reversal','prior_income','work'].includes(r.rule);const omitted=r.rule==='provision_reversal'&&f.inResult==='no';if(!omitted)checkOrigin(r.sourceCode,amount,isIncome?'income':'expense');else if(r.sourceCode)throw Error('Reversão não incluída no resultado não deve repetir uma linha de proveitos.');
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import {addFractions,decimalFraction,fraction,subtractFractions,fractionPresentation,type FiscalFraction} from './fiscal-rational.ts';
|
||||
import {fiscalMoney,type NormalAsset} from './irpc-normal-schedules.ts';
|
||||
export const normalDepreciationVersion='IRPC-normal-adjustments/7';
|
||||
export const depreciationCatalog={
|
||||
computer:{label:'Computadores, impressoras, UPS e material informático',table:'II',code:'3/9',rate:'25',kind:'equipment',page:17},
|
||||
server:{label:'Servidores',table:'II',code:'3/9.1',rate:'20',kind:'equipment',page:17},
|
||||
security:{label:'Equipamento de vigilância e segurança',table:'II',code:'3/23',rate:'20',kind:'equipment',page:18},
|
||||
machine:{label:'Máquinas não especificadas',table:'II',code:'3/22',rate:'10',kind:'equipment',page:18},
|
||||
furniture:{label:'Mobiliário',table:'II',code:'5/7',rate:'10',kind:'furniture',page:18},
|
||||
commercial_building:{label:'Edifícios comerciais e administrativos',table:'II',code:'1/2.2',rate:'2',kind:'building',page:16},
|
||||
passenger_vehicle:{label:'Veículos automóveis ligeiros e mistos',table:'II',code:'4/9.2',rate:'25',kind:'vehicle',page:18},
|
||||
software:{label:'Programas de computadores',table:'II',code:'5/8',rate:'25',kind:'software',page:18},
|
||||
hotel_decoration:{label:'Decorações interiores — hotéis e actividades similares, classe84.2',table:'I',code:'84.2/B',rate:'20',kind:'furniture',page:16},
|
||||
laundry_machine:{label:'Maquinaria específica de lavandarias/tinturarias',table:'I',code:'84.3/A',rate:'12.5',kind:'equipment',page:16},
|
||||
}as const;
|
||||
export type DepreciationCategory=keyof typeof depreciationCatalog;
|
||||
export type DepreciationAsset={id:string;assetKey:string;description:string;document:string;line:string;category:''|DepreciationCategory|'other'|'nondepreciable';valuation:''|'historical_new'|'used'|'revalued'|'other';specificRate:''|'yes'|'no';method:''|'constant'|'declining'|'low_value';rate:string;factor:string;timing:''|'annual'|'months';start:string;exitState:''|'absent'|'known';exitDate:string;cost:string;land:string;residual:string;accounting:string;previousAccounting:string;previousFiscal:string;priorAvailable:string;previousReference:string;newTangible:''|'yes'|'no';consistentMethod:''|'yes'|'no';switchToStraight:''|'yes'|'no';priorFullQuotas:''|'yes'|'no';vehicleException:''|'yes'|'no';limitedRights:''|'yes'|'no';individual:''|'yes'|'no';conditions:''|'yes';reference:string};
|
||||
export type NormalDepreciationInput={version:1;completenessReference:string;assets:DepreciationAsset[]};
|
||||
export const newNormalDepreciation=():NormalDepreciationInput=>({version:1,completenessReference:'',assets:[]});
|
||||
export const newDepreciationAsset=(id:string):DepreciationAsset=>({id,assetKey:'',description:'',document:'',line:'',category:'',valuation:'',specificRate:'',method:'',rate:'',factor:'',timing:'',start:'',exitState:'',exitDate:'',cost:'',land:'',residual:'',accounting:'',previousAccounting:'',previousFiscal:'',priorAvailable:'',previousReference:'',newTangible:'',consistentMethod:'',switchToStraight:'',priorFullQuotas:'',vehicleException:'',limitedRights:'',individual:'',conditions:'',reference:''});
|
||||
export function reuseNormalAssets(assets:NormalAsset[]):NormalDepreciationInput{return{version:1,completenessReference:'',assets:assets.map(a=>({...newDepreciationAsset(a.id),assetKey:a.id,description:a.description,category:a.category,document:a.reference,line:a.id,method:'constant',timing:'annual',specificRate:'no',consistentMethod:'yes',conditions:a.conditions,rate:a.rate,cost:a.cost,residual:a.residual,accounting:a.accounting,previousFiscal:a.previousFiscal,start:a.inUseFrom,reference:a.reference}))};}
|
||||
export function validateNormalDepreciation(i:NormalDepreciationInput){
|
||||
if(!i||i.version!==1||typeof i.completenessReference!=='string'||!Array.isArray(i.assets)||new Set(i.assets.map(a=>a.id)).size!==i.assets.length)throw Error('Mapa de amortizações não recuperável.');
|
||||
for(const a of i.assets){if(!a||Object.keys(newDepreciationAsset('')).some(k=>typeof a[k as keyof DepreciationAsset]!=='string')||!['','other','nondepreciable',...Object.keys(depreciationCatalog)].includes(a.category)||!['','historical_new','used','revalued','other'].includes(a.valuation)||!['','constant','declining','low_value'].includes(a.method)||!['','annual','months'].includes(a.timing)||!['','absent','known'].includes(a.exitState)||!['','yes'].includes(a.conditions)||['specificRate','newTangible','consistentMethod','switchToStraight','priorFullQuotas','vehicleException','limitedRights','individual'].some(k=>!['','yes','no'].includes(a[k as 'specificRate'])))throw Error('Activo ou parâmetros de amortização não recuperáveis.');}
|
||||
}
|
||||
const zero=()=>fraction(0n),cmp=(a:FiscalFraction,b:FiscalFraction)=>BigInt(a.numerator)*BigInt(b.denominator)-BigInt(b.numerator)*BigInt(a.denominator);
|
||||
const min=(...a:FiscalFraction[])=>a.reduce((v,w)=>cmp(v,w)<0n?v:w),positive=(a:FiscalFraction)=>cmp(a,zero())<0n?zero():a;
|
||||
const multiply=(a:FiscalFraction,b:FiscalFraction)=>fraction(BigInt(a.numerator)*BigInt(b.numerator),BigInt(a.denominator)*BigInt(b.denominator));
|
||||
const ratio=(a:FiscalFraction,b:FiscalFraction)=>fraction(BigInt(a.numerator)*BigInt(b.denominator),BigInt(a.denominator)*BigInt(b.numerator));
|
||||
const date=(v:string)=>/^20\d{2}-\d{2}-\d{2}$/.test(v)&&Number.isFinite(Date.parse(v))&&new Date(v).toISOString().slice(0,10)===v;
|
||||
const month=(v:string)=>Number(v.slice(0,4))*12+Number(v.slice(5,7))-1;
|
||||
const show=(v:FiscalFraction|null)=>{if(!v)return'Indeterminado';const p=fractionPresentation(v);return(p.approximate?'≈':'')+p.decimal+' MZN'+(p.approximate?' (exacto '+v.numerator+'/'+v.denominator+')':'');};
|
||||
export function calculateNormalDepreciation(input:NormalDepreciationInput,source:{currency:string;accountingLines:Array<{code:string;total:string}>}|null,period:{from:string;to:string},claims:Array<{sourceCode:string;document:string;line:string;amount:string}>,short=false){
|
||||
validateNormalDepreciation(input);const pending:string[]=[];
|
||||
if(!input.completenessReference.trim())pending.push('Reconciliação integral dos activos e quotas anteriores com a depreciação da origem em falta.');
|
||||
if(source?.currency!=='MZN'||!date(period.from)||!date(period.to)||!period.to.startsWith('2026-')||(!short&&month(period.to)-month(period.from)!==11||short&&(month(period.to)<month(period.from)||month(period.to)-month(period.from)>=11)))pending.push('Período anual emMZN com termo em2026 por reconciliar; períodos curtos permanecem separados.');
|
||||
const assets=input.assets.map(a=>{const issues:string[]=[],notes:string[]=[];let accounting:FiscalFraction|null=null,basis:FiscalFraction|null=null,taxBasis:FiscalFraction|null=null,quota:FiscalFraction|null=null,deduction:FiscalFraction|null=null,adjustment:FiscalFraction|null=null,priorUsed:FiscalFraction|null=null,priorRemaining:FiscalFraction|null=null,remainingFiscal:FiscalFraction|null=null,eligibleCurrent:FiscalFraction|null=null,months:number|null=null;const entry=Object.hasOwn(depreciationCatalog,a.category)?depreciationCatalog[a.category as DepreciationCategory]:null;
|
||||
if(short&&a.category!=='nondepreciable'&&(a.timing!=='months'||a.method==='low_value'))issues.push('Período curto exige duodécimos;opção integral art17 neste período por corresponder.');
|
||||
const money=(s:string,label:string)=>{if(!s.trim())throw Error(label+' em falta; não presumir zero.');return fiscalMoney(s);};
|
||||
const yesno=(v:string,label:string)=>{if(!['yes','no'].includes(v))throw Error(label+' por documentar.');return v==='yes';};
|
||||
if(!a.assetKey.trim()||!a.description.trim()||!a.document.trim()||!a.line.trim()||!a.reference.trim()||a.conditions!=='yes')issues.push('Identificação do activo, documento/linha, condições ordinárias e suporte em falta.');
|
||||
if(input.assets.some(b=>b!==a&&(b.assetKey.trim()===a.assetKey.trim()||b.document.trim()===a.document.trim()&&b.line.trim()===a.line.trim()))||claims.some(c=>c.document.trim()===a.document.trim()&&c.line.trim()===a.line.trim()))issues.push('Activo ou documento/linha repetido entre mapas; reconcilie sem dedução duplicada.');
|
||||
try{accounting=money(a.accounting,'Depreciação contabilística corrente');const cost=money(a.cost,'Custo histórico');
|
||||
if(a.category==='nondepreciable'){basis=cost;taxBasis=zero();quota=zero();deduction=zero();adjustment=accounting;notes.push('CIRPC26/27:elemento expressamente qualificado como não sujeito a deperecimento; depreciação contabilística integralmente acrescida.');}
|
||||
else{
|
||||
if(a.valuation!=='historical_new')throw Error('Tipo de aquisição/base por identificar ou fora do recorte de bens novos a custo histórico; usados/abertura de escrita7(2) e reavaliados15 exigem regra própria.');
|
||||
if(!entry)throw Error('Categoria/tabela ainda não correspondida; sem taxa livre ou código provisório.');
|
||||
if(entry){const specific=yesno(a.specificRate,'Existência de taxa específica da tabelaI');if(entry.table==='II'&&specific)throw Error('Taxa específica da tabelaI prevalece; seleccione a categoria específica correspondente.');if(entry.table==='I'&&!specific)throw Error('Categoria específica da tabelaI sem enquadramento documentado.');}
|
||||
if(!date(a.start)||a.start<'2014-01-01')throw Error('Entrada em funcionamento/utilização desde2014 por identificar; activos anteriores exigem correspondência histórica própria.');
|
||||
if(!yesno(a.consistentMethod,'Método uniforme desde o início'))throw Error('Mudança de método/razão excepcional requer correspondência própria do artigo10.');
|
||||
if(a.exitState==='known'&&(!date(a.exitDate)||a.exitDate<a.start)||!a.exitState||a.exitState==='absent'&&a.exitDate.trim())throw Error('Estado/data de transmissão,inutilização ou termo de vida útil incoerente ou em falta.');
|
||||
if(!a.previousReference.trim())throw Error('Suporte das quotas contabilísticas/fiscais anteriores e da parcela temporariamente tributada em falta.');
|
||||
const priorBook=money(a.previousAccounting,'Quotas contabilísticas anteriores'),priorFiscal=money(a.previousFiscal,'Quotas fiscais anteriores'),available=money(a.priorAvailable,'Parcela anterior temporariamente tributada recuperável');
|
||||
if(cmp(priorFiscal,priorBook)>0n||cmp(available,subtractFractions(priorBook,priorFiscal))>0n)throw Error('Quotas anteriores incoerentes; parcela recuperável não pode exceder a diferença contabilizada e não deduzida, excluindo encargos permanentemente não aceites.');
|
||||
const residual=money(a.residual,'Residual'),land=entry?.kind==='building'?money(a.land,'Terreno/parte não depreciável'):zero();if(entry?.kind!=='building'&&a.land.trim()&&cmp(fiscalMoney(a.land),zero())!==0n)throw Error('Terreno não corresponde a esta categoria de activo.');basis=subtractFractions(subtractFractions(cost,residual),land);if(cmp(basis,zero())<=0n)throw Error('Base depreciável positiva por reconciliar.');if(entry?.kind!=='building'&&cmp(land,zero())!==0n)throw Error('Terreno só deve ser separado no imóvel correspondente.');taxBasis=basis;
|
||||
if(entry?.kind==='vehicle'&&!yesno(a.vehicleException,'Afectação a serviço público de transportes/locação na actividade normal')){taxBasis=min(basis,positive(subtractFractions(fraction(800000n),residual)));notes.push('CIRPC27e:limite de aquisição800000MZN,sem afectar a depreciação contabilística de origem.');}
|
||||
if(entry?.kind==='software'&&!yesno(a.limitedRights,'Aquisição onerosa e direitos de utilização exclusiva por tempo limitado'))throw Error('Direitos de software/intangível sem condições do artigo16 documentadas.');
|
||||
if(cmp(priorFiscal,taxBasis)>0n)throw Error('Quotas fiscais anteriores excedem a base fiscal; reconcilie o saldo anterior.');const remaining=subtractFractions(taxBasis,priorFiscal),priorEligibleBook=min(taxBasis,multiply(priorBook,ratio(taxBasis,basis)));if(cmp(available,positive(subtractFractions(priorEligibleBook,priorFiscal)))>0n)throw Error('Parcela anterior recuperável inclui quota permanentemente não aceite ou excede a contabilização fiscalmente elegível.');eligibleCurrent=min(multiply(accounting,ratio(taxBasis,basis)),positive(subtractFractions(taxBasis,priorEligibleBook)));
|
||||
let maxRate:FiscalFraction|null=entry?decimalFraction(entry.rate):null,maxLifeMonths:FiscalFraction|null=maxRate?ratio(fraction(2400n),maxRate):null;
|
||||
if(a.method==='low_value'){if(cmp(cost,fraction(20000n))>0n||!yesno(a.individual,'Activo individual fora de conjunto depreciado como um todo')||cmp(priorFiscal,zero())!==0n||a.start<period.from||a.start>period.to)throw Error('Art17:valor unitário até20000MZN,activo individual e início no período,sem amortização fiscal anterior; condições em falta/incompatíveis.');quota=remaining;months=month(period.to)-month(a.start)+1;notes.push('Art17:opção de depreciação integral em um período,limitada ao que está contabilizado e ao saldo fiscal.');}
|
||||
else{
|
||||
if(!entry||!maxRate||!maxLifeMonths)throw Error('Taxa aplicável não identificada.');
|
||||
if(a.method==='constant'){if(!/^\d+(?:\.\d{1,6})?$/.test(a.rate))throw Error('Taxa constante escolhida por identificar.');const selected=decimalFraction(a.rate);if(cmp(selected,multiply(maxRate,fraction(1n,2n)))<0n||cmp(selected,maxRate)>0n)throw Error('Taxa constante fora do recorte entre metade da máxima e a máxima;quotas inferiores exigem correspondência própria dos limites de vida útil, artigos5/7.');quota=multiply(taxBasis,multiply(selected,fraction(1n,100n)));notes.push('Quota constante pela taxa escolhida:'+a.rate+'%;tabela'+entry.table+'/'+entry.code+'.');}
|
||||
else if(a.method==='declining'){if(!yesno(a.newTangible,'Activo tangível novo adquirido a terceiros/construído')||['building','furniture','software'].includes(entry.kind)||entry.kind==='vehicle'&&a.vehicleException!=='yes')throw Error('Art6:quotas degressivas só nos activos tangíveis novos elegíveis;edifícios,mobiliário/equipamento social,intangíveis e veículos sem excepção ficam separados.');const life=ratio(fraction(100n),maxRate),maxFactor=cmp(life,fraction(5n))<0n?fraction(3n,2n):cmp(life,fraction(6n))<=0n?fraction(2n):fraction(5n,2n);if(!/^\d+(?:\.\d{1,6})?$/.test(a.factor))throw Error('Coeficiente degressivo escolhido em falta.');const factor=decimalFraction(a.factor);if(cmp(factor,zero())<=0n||cmp(factor,maxFactor)>0n)throw Error('Coeficiente degressivo excede o máximo do artigo8:1,5/2/2,5 conforme vida útil mínima.');quota=multiply(remaining,multiply(maxRate,multiply(factor,fraction(1n,100n))));notes.push('Art8:saldo fiscal por amortizar ×taxa máxima'+entry.rate+'% ×coeficiente'+a.factor+';máximo'+fractionPresentation(maxFactor).decimal+'.');
|
||||
if(yesno(a.switchToStraight,'Opção pela regra de saldo/vida útil restante8(2)')){if(!yesno(a.priorFullQuotas,'Condição das quotas anteriores8(2)')||!a.start.endsWith('-01-01')||period.from!=='2026-01-01'||a.timing!=='annual')throw Error('Art8(2):condição das quotas anteriores por documentar;este recorte da opção exige início emJaneiro e período anual civil.');const remainingYears=subtractFractions(life,fraction(BigInt(2026-Number(a.start.slice(0,4)))));if(cmp(remainingYears,zero())<=0n)throw Error('Art8(2):vida útil mínima esgotada;sem dividir por anos inexistentes.');const linear=ratio(remaining,remainingYears);if(cmp(quota,linear)<0n){quota=linear;notes.push('Art8(2):usado saldo/número de anos de vida útil mínima restantes,com condições anteriores documentadas.');}}
|
||||
}else throw Error('Método de amortização por identificar.');
|
||||
if(!a.timing)throw Error('Opção anual/duodécimos por identificar.');
|
||||
if(a.timing==='annual'){if(a.exitState==='known'&&a.exitDate<=period.to)throw Error('Evento de saída em período anual:seleccione duodécimos e reconcilie a regra aplicável;sem quota anual presumida.');const lifeEnd=month(a.start)+Number(BigInt(maxLifeMonths.numerator)/BigInt(maxLifeMonths.denominator));if(lifeEnd>month(period.from)&&lifeEnd<=month(period.to)&&cmp(remaining,zero())>0n)throw Error('Vida útil máxima termina dentro do período;opção anual e quotas anteriores exigem correspondência própria. Duodécimos em vigor são calculados até ao limite temporal.');months=a.start>period.to?0:12;}
|
||||
else{const start=Math.max(month(period.from),month(a.start)),end=Math.min(month(period.to)+1,a.exitState==='known'?month(a.exitDate):month(period.to)+1,month(a.start)+Number(BigInt(maxLifeMonths.numerator)/BigInt(maxLifeMonths.denominator)));months=Math.max(0,end-start);quota=multiply(quota,fraction(BigInt(months),12n));notes.push('Art9:entrada incluída desde o mês da utilização;saída exclui o mês do evento;'+months+'duodécimos.');}
|
||||
const elapsed=Math.max(0,month(period.from)-month(a.start));if(cmp(fraction(BigInt(elapsed)),maxLifeMonths)>=0n){quota=zero();notes.push('Art5:vida útil máxima esgotada,sem excepção aplicada;nenhuma recuperação de quota fora desse limite.');}else if(a.start>period.to){quota=zero();notes.push('Art3:activo ainda não entrou emuso no período;sem amortização fiscal.');}
|
||||
}
|
||||
deduction=min(quota!,remaining,addFractions([eligibleCurrent,available]));priorUsed=positive(subtractFractions(deduction,eligibleCurrent));priorRemaining=subtractFractions(available,priorUsed);remainingFiscal=subtractFractions(remaining,deduction);adjustment=subtractFractions(accounting,deduction);notes.push('Art3(3):dedução limitada ao contabilizado no período ou antes desde o uso;quotas anteriores recuperáveis usadas:'+show(priorUsed)+';saldo anterior aritmético:'+show(priorRemaining)+'. Sem consumo oficial nem recuperação de quota não contabilizada/permanentemente excluída.');
|
||||
}
|
||||
}catch(e){issues.push(e instanceof Error?e.message:String(e));}
|
||||
if(issues.length){deduction=null;adjustment=null;priorUsed=null;priorRemaining=null;remainingFiscal=null;pending.push((a.description||a.id)+': '+issues.join(' '));}
|
||||
return{...structuredClone(a),catalog:entry,accountingValue:accounting,basis,taxBasis,quota,deduction,adjustment,eligibleCurrent,priorUsed,priorRemaining,remainingFiscal,months,status:issues.length?'indeterminate' as const:'calculated' as const,reason:issues.join(' ')||notes.join(' ')};
|
||||
});
|
||||
let accountingTotal:FiscalFraction|null=null;try{accountingTotal=addFractions(assets.map(a=>{if(!a.accountingValue)throw Error();return a.accountingValue;}));const origin=fiscalMoney(source?.accountingLines.find(l=>l.code==='depreciation')?.total??'');if(cmp(accountingTotal,origin)!==0n)pending.push('Soma das quotas contabilísticas dos activos não corresponde à depreciação financeira declarada.');}catch{pending.push('Depreciação contabilística da origem por reconciliar.');}
|
||||
if(claims.some(c=>c.sourceCode==='depreciation'))pending.push('Depreciação partilhada com outros ajustamentos de origem;reconcilie as parcelas para não duplicar o mesmo encargo.');
|
||||
const knownAdjustment=addFractions(assets.flatMap(a=>a.adjustment?[a.adjustment]:[])),adjustment=pending.length===0?knownAdjustment:null;
|
||||
return{input:structuredClone(input),assets,accountingTotal,knownAdjustment,adjustment,pending,source:'Decreto72/2013,art3–10/16/17,tabelasI/II;CIRPC26/27. Recorte anualMZN com termo2026,custo histórico,sem reavaliação,turnos intensivos,concessões/fusões ou excepções presumidas. Fotografia7;mapa interno,não formulário oficial de amortizações.'};
|
||||
}
|
||||
export type NormalDepreciationModel=ReturnType<typeof calculateNormalDepreciation>;
|
||||
export function normalDepreciationReport(m:NormalDepreciationModel):Array<[string,string,string,string]>{return[['IRPC normal — L6','Amortizações — tabelas, métodos, duodécimos e quotas anteriores',`Quotas contabilísticas:${show(m.accountingTotal)};ajustamento das parcelas conhecidas:${show(m.knownAdjustment)};ajustamento reconciliado:${show(m.adjustment)};${m.pending.join(' | ')}`,m.source],...m.assets.map(a=>['IRPC normal — L6',a.description+' — '+a.document+'/'+a.line,`Estado:${a.status==='calculated'?'Calculado no recorte':'Informação insuficiente'};contabilístico:${show(a.accountingValue)};parcela contabilística elegível:${show(a.eligibleCurrent)};base fiscal:${show(a.taxBasis)};quota calculada:${show(a.quota)};dedução fiscal:${show(a.deduction)};ajustamento com sinal:${show(a.adjustment)};duodécimos:${a.months??'Não aplicável'};quota anterior utilizada:${show(a.priorUsed)};saldo anterior aritmético:${show(a.priorRemaining)};${a.reason}`,`${a.catalog?'Decreto72/2013,tabela'+a.catalog.table+',código'+a.catalog.code+',PDF'+a.catalog.page:'Categoria sem código de tabela aplicado'};${a.reference};quotas anteriores:${a.previousReference};método:${a.method==='constant'?'Constante':a.method==='declining'?'Degressivo':a.method==='low_value'?'Valor reduzido':'Por identificar'};sem alteração dos documentos/saldos oficiais ou históricos.`]as[string,string,string,string])];}
|
||||
@@ -0,0 +1,84 @@
|
||||
import {addFractions,decimalFraction,fraction,fractionPresentation,subtractFractions,type FiscalFraction} from './fiscal-rational.ts';
|
||||
import type {normalFinancialSource} from './irpc-normal.ts';
|
||||
import type {SchedulesModel} from './irpc-normal-schedules.ts';
|
||||
import type {NormalCostsModel} from './irpc-normal-costs.ts';
|
||||
import type {NormalDepreciationModel} from './irpc-normal-depreciation.ts';
|
||||
import type {NormalBaseModel} from './irpc-normal-base.ts';
|
||||
export const normalFormPreparationVersion='IRPC-normal-adjustments/9';
|
||||
export const m20Accounts={
|
||||
'7.1':'Venda de bens','7.2':'Prestação de serviços','7.3':'Investimentos realizados pela própria empresa','7.4':'Reversões do período','7.5':'Rendimentos suplementares','7.6':'Outros rendimentos e ganhos operacionais','7.8':'Rendimentos e ganhos financeiros','7.9':'Ganho por aumento do justo valor',
|
||||
'6.1.1':'Custos dos inventários vendidos ou consumidos','6.2':'Gastos com pessoal','6.3':'Fornecimentos e serviços de terceiros','6.4':'Perda por imparidade / ajustamento','6.5':'Amortizações do período','6.6':'Provisões do período','6.7':'Perda por redução do justo valor','6.8':'Outros gastos e perdas operacionais','6.9':'Gastos e perdas financeiras','8.5':'Imposto sobre o rendimento',
|
||||
}as const;
|
||||
export type FormAllocation={id:string;sourceCode:string;account:''|keyof typeof m20Accounts;amount:string;reference:string};
|
||||
export type NormalFormPreparationInput={version:1;edition:''|'accepted_pdf_and_m20a1';reference:string;completenessReference:string;allocations:FormAllocation[];incomeTax:{state:''|'known'|'absent';amount:string;placement:''|'included'|'outside';reference:string};reducedRate:{state:''|'known'|'absent';reference:string}};
|
||||
export const newNormalFormPreparation=():NormalFormPreparationInput=>({version:1,edition:'',reference:'',completenessReference:'',allocations:[],incomeTax:{state:'',amount:'',placement:'',reference:''},reducedRate:{state:'',reference:''}});
|
||||
export const newFormAllocation=(id:string):FormAllocation=>({id,sourceCode:'',account:'',amount:'',reference:''});
|
||||
const allowed:Record<string,string[]>={revenue:['7.1','7.2','7.3','7.4','7.5','7.6','7.8','7.9'],cost_of_sales:['6.1.1'],personnel:['6.2'],depreciation:['6.5'],interest_expense:['6.9'],operating_expenses:['6.3','6.4','6.6','6.7','6.8','8.5']};
|
||||
export const normalFormAccountsFor=(source:string)=>allowed[source]??[];
|
||||
export function validateNormalFormPreparation(i:NormalFormPreparationInput){
|
||||
if(!i||i.version!==1||!['','accepted_pdf_and_m20a1'].includes(i.edition)||typeof i.reference!=='string'||typeof i.completenessReference!=='string'||!Array.isArray(i.allocations)||new Set(i.allocations.map(a=>a.id)).size!==i.allocations.length||i.allocations.some(a=>!a||Object.keys(newFormAllocation('')).some(k=>typeof a[k as keyof FormAllocation]!=='string')||a.account!==''&&!Object.hasOwn(m20Accounts,a.account))||!i.incomeTax||!['','known','absent'].includes(i.incomeTax.state)||!['','included','outside'].includes(i.incomeTax.placement)||typeof i.incomeTax.amount!=='string'||typeof i.incomeTax.reference!=='string'||!i.reducedRate||!['','known','absent'].includes(i.reducedRate.state)||typeof i.reducedRate.reference!=='string')throw Error('Preparação M20/M22 não recuperável;originais conservados.');
|
||||
}
|
||||
const zero=()=>fraction(0n),neg=(v:FiscalFraction)=>fraction(-BigInt(v.numerator),BigInt(v.denominator)),positive=(v:FiscalFraction)=>BigInt(v.numerator)>=0n;
|
||||
const eq=(a:FiscalFraction,b:FiscalFraction)=>BigInt(a.numerator)*BigInt(b.denominator)===BigInt(b.numerator)*BigInt(a.denominator);
|
||||
function money(s:string){if(!/^\d{1,18}(?:\.\d{1,6})?$/.test(s))throw Error('Montante não negativo conhecido em falta;zero não é presumido.');return decimalFraction(s);}
|
||||
export function normalFormRepresentation(v:FiscalFraction|null,unit:'whole'|'cent'='cent',parentheses=false){
|
||||
if(!v)return{exact:null,display:'Indeterminado',represented:null,ready:false,reason:'Valor em falta;sem zero presumido.'};
|
||||
const n=BigInt(v.numerator),d=BigInt(v.denominator);if(d<=0n)throw Error('Representação racional inválida.');const p=fractionPresentation(v),whole=n%d===0n,cent=n*100n%d===0n;
|
||||
const abs=n<0n?-n:n,scaled=abs*100n/d;const exact=n+'/'+d+' MZN',value=whole?(parentheses&&n<0n?'('+String(-n/d)+')':String(n/d)):unit==='cent'&¢?(n<0n?'-':'')+scaled/100n+'.'+String(scaled%100n).padStart(2,'0'):null;
|
||||
return{exact,display:whole?(value+' MZN'):(p.approximate?'≈':'')+p.decimal+' MZN (exacto '+n+'/'+d+')',represented:value,ready:value!==null,reason:whole?'Valor exacto em meticais inteiros,sem arredondamento.':unit==='whole'?'Exemplar M20 exige meticais sem centavos;regra de representação da fracção por corresponder.':cent?'Valor exacto ao cêntimo na preparação M22 já demonstrada;sem arredondamento fiscal ou declaração oficial presumidos.':'Fracção de cêntimo conservada;regra de representação fiscal por corresponder.'};
|
||||
}
|
||||
type Financial=ReturnType<typeof normalFinancialSource>;
|
||||
export function reuseDirectFormAllocations(source:Financial){return(source?.accountingLines??[]).filter(l=>['cost_of_sales','personnel','depreciation','interest_expense'].includes(l.code)).map(l=>({id:'source-'+l.code,sourceCode:l.code,account:allowed[l.code][0]as keyof typeof m20Accounts,amount:l.total,reference:'Reutilizado da linha financeira '+l.origin+';conferir completude da conta no suporte da preparação.'}));}
|
||||
type Model={accountingProfit:FiscalFraction|null;taxableProfit:FiscalFraction|null;taxableBase:FiscalFraction|null;colecta?:FiscalFraction|null;rows:Array<{rule:string;document:string;line:string;adjustment:FiscalFraction|null}>;schedules?:SchedulesModel;depreciation?:NormalDepreciationModel;costs?:NormalCostsModel;base?:NormalBaseModel};
|
||||
export function prepareNormalForms(input:NormalFormPreparationInput,m:Model,source:Financial){
|
||||
validateNormalFormPreparation(input);const pending:string[]=[],edition=input.edition==='accepted_pdf_and_m20a1'&&!!input.reference.trim();
|
||||
if(!edition)pending.push('Identifique os exemplares M22 PDF e M20A.1 inventariados;sem equivalência automática com as outras edições.');
|
||||
if(!input.completenessReference.trim())pending.push('Suporte da completude contabilística e discriminação da origem em falta.');
|
||||
const allocations=input.allocations.map(a=>{let value:FiscalFraction|null=null;const issues:string[]=[];try{value=money(a.amount);}catch(e){issues.push(String(e));}if(!normalFormAccountsFor(a.sourceCode).includes(a.account)||!source?.accountingLines.some(l=>l.code===a.sourceCode)||!a.reference.trim())issues.push('Origem/conta e suporte compatíveis por identificar.');return{...structuredClone(a),value:issues.length?null:value,reason:issues.join(' ')};});
|
||||
const lines=(source?.accountingLines??[]).map(l=>{const rows=allocations.filter(a=>a.sourceCode===l.code),sum=addFractions(rows.flatMap(a=>a.value?[a.value]:[])),ready=rows.length>0&&rows.every(a=>a.value!==null)&&eq(sum,money(l.total));const reason=ready?'Parcelas reconciliadas exactamente com a linha de origem.':'Discriminação em falta,incompleta ou superior à origem;sem duplicar montantes.';if(!ready)pending.push(l.origin+': '+reason);return{code:l.code,total:l.total,allocated:sum,ready,reason};});
|
||||
for(const a of allocations)if(a.reason)pending.push(a.id+': '+a.reason);
|
||||
if(!source)pending.push('Resultado financeiro do período/moeda em falta;preparação não cria um resultado substituto.');
|
||||
const tax=input.incomeTax;let bookTax:FiscalFraction|null=null;
|
||||
if(!tax.reference.trim()||!tax.state)pending.push('Imposto sobre rendimento contabilizado:valor/ausência e suporte por identificar;colecta não substitui gasto contabilístico.');
|
||||
else if(tax.state==='absent'){if(tax.amount.trim()||tax.placement)pending.push('Ausência de imposto contabilizado contradiz montante/localização preenchidos.');else bookTax=zero();}
|
||||
else{try{bookTax=money(tax.amount);if(!tax.placement)throw Error();}catch{bookTax=null;pending.push('Imposto contabilizado conhecido exige montante e indicação se já incluído ou exterior ao resultado do plano.');}}
|
||||
const allocatedTax=addFractions(allocations.filter(a=>a.account==='8.5'&&a.value).map(a=>a.value!));
|
||||
if(bookTax&&(!eq(allocatedTax,tax.placement==='included'?bookTax:zero()))){pending.push('Conta8.5 não reconciliada com imposto já incluído/exterior ao plano;sem subtracção duplicada.');bookTax=null;}
|
||||
const includedTaxEvidence=(m.schedules?.effects??[]).filter(e=>m.schedules!.input.rows.find(r=>r.id===e.id)?.rule==='tax_own');
|
||||
if(bookTax&&tax.placement!=='included'&&includedTaxEvidence.some(e=>e.effect&&BigInt(e.effect.numerator)>0n)){pending.push(tax.state==='absent'?'Ausência de imposto contabilizado contradiz imposto próprio já identificado nos custos do plano.':'Imposto incluído e exterior coexistem:discriminação das duas componentes ainda por corresponder;sem misturar ou subtrair duas vezes.');bookTax=null;}
|
||||
const classificationReady=edition&&!!input.completenessReference.trim()&&source!==null&&lines.length>0&&lines.every(l=>l.ready)&&allocations.every(a=>a.value!==null),outsideTax=bookTax&&tax.placement==='outside'?bookTax:zero();
|
||||
const m20=Object.entries(m20Accounts).map(([account,label])=>{const related=allocations.filter(a=>a.account===account),categorySources=Object.keys(allowed).filter(k=>allowed[k].includes(account));const complete=edition&&!!input.completenessReference.trim()&&source!==null&&categorySources.every(k=>lines.some(l=>l.code===k&&l.ready));let value=complete?addFractions(related.flatMap(a=>a.value?[a.value]:[])):null;if(account==='8.5')value=complete&&bookTax?bookTax:null;return{account,label,value,representation:normalFormRepresentation(value,'whole'),reference:related.map(a=>a.reference).join(' | '),reason:complete?'Classificação documentada e reconciliada no M20A.1/quadro4.':'Campo dependente da discriminação contabilística;sem zero por falta de parcelas.'};});
|
||||
const netResult=classificationReady&&bookTax&&m.accountingProfit?subtractFractions(m.accountingProfit,outsideTax):null;
|
||||
const entries:Array<{code:string|null;origin:string;value:FiscalFraction|null;reason:string}>=[];
|
||||
const add=(code:string|null,origin:string,value:FiscalFraction|null,reason='')=>entries.push({code,origin,value,reason});
|
||||
for(const r of m.rows)add(r.rule==='noncontractual_fine'||r.rule==='compensatory_interest'?'215':null,r.document+'/'+r.line,r.adjustment,'CIRPC36(1)c;PDF M22/quadro10 campo215.');
|
||||
for(const e of m.schedules?.effects??[]){const r=m.schedules!.input.rows.find(r=>r.id===e.id)!;if(['capital_in','capital_out'].includes(r.rule)&&e.effect&&eq(e.effect,zero()))continue;const code=({patrimonial_gain:'201',patrimonial_cost:'202',tax_other:'213',undocumented:'223'}as Record<string,string>)[r.rule]??null;add(code,r.document+'/'+r.line,e.effect,code?'Correspondência do rótulo do PDF M22.':r.rule==='tax_own'?'Imposto próprio36(1)a:campo específico por corresponder;campo213 do PDF refere apenas responsabilidade de outrem.':'Periodização/variação sem campo correspondido neste recorte;efeito preservado no cálculo.');}
|
||||
if(m.depreciation)for(const asset of m.depreciation.assets){const a=asset.adjustment;add(a&&positive(a)?'206':null,'Mapa de amortizações — '+asset.description,a,a&&positive(a)?'Reintegrações/amortizações não aceites:PDF campo206.':'Recuperação de quota anterior/ajustamento negativo:campo de dedução específico ainda por corresponder.');}
|
||||
else if(m.schedules?.depreciation){const a=m.schedules.depreciation;add(positive(a)?'206':null,'Amortizações do mapa inicial',a);}
|
||||
for(const r of m.costs?.rows??[]){const code=({allowance:'219',representation:'220',advertising:'221',vehicle:'226',fuel:'225',rental:'224',credit:'209',stock:'209',litigation:'209',taxed_reversal:'239'}as Record<string,string>)[r.rule]??null;add(code,r.document+'/'+r.line,r.adjustment,code?'Correspondência do tratamento demonstrado ao rótulo do PDF M22.':'Tratamento demonstrado sem campo específico correspondido;não usar campo livre por presunção.');}
|
||||
if(bookTax&&tax.placement==='outside')add(null,'Imposto contabilístico exterior ao plano',bookTax,'Imposto próprio36(1)a:campo específico por corresponder;213 do exemplar é de outrem. Resultado líquido conhecido,sem código presumido ou alteração do motor fiscal.');
|
||||
const ownTaxEffects=(m.schedules?.effects??[]).filter(e=>m.schedules!.input.rows.find(r=>r.id===e.id)?.rule==='tax_own');
|
||||
const includedTaxCorresponds=!bookTax||tax.placement!=='included'||ownTaxEffects.every(e=>e.effect!==null)&&eq(addFractions(ownTaxEffects.flatMap(e=>e.effect?[e.effect]:[])),bookTax);
|
||||
if(!includedTaxCorresponds)pending.push('Imposto já incluído agora discriminado não corresponde ao ajustamento tax_own existente;conferir esse caso sem alterar cálculos ou históricos.');
|
||||
const unmapped=entries.filter(e=>!e.code&&(!e.value||!eq(e.value,zero())));for(const e of unmapped)pending.push(e.origin+': '+e.reason);
|
||||
const field=(code:string,label:string,value:FiscalFraction|null,reason:string,notApplicable=false)=>({code,label,value,application:notApplicable?'not_applicable':value?'known':'missing',representation:notApplicable?{...normalFormRepresentation(null),display:'Sem aplicação',reason:'Lucro/perda conhecido:não preencher o campo de sinal oposto.'}:normalFormRepresentation(value),reason,transcriptionReady:edition&&code!=='200'&&normalFormRepresentation(value).ready});
|
||||
const mappedValue=(code:string,negative=false)=>{if(code==='206'&&m.depreciation?.adjustment===null)return null;const es=entries.filter(e=>e.code===code);if(es.some(e=>e.value===null)||!es.length&&m.taxableProfit===null)return null;const value=addFractions(es.flatMap(e=>e.value?[negative?neg(e.value):e.value]:[]));return positive(value)?value:null;};
|
||||
const fields=[field('200','Resultado do exercício',netResult,'Valor contabilístico reconciliado próprio;transporte automático M20→M22 impedido só pela divergência201/Q8 versus200/Q10.')];
|
||||
const labels:Record<string,string>={'201':'Variações patrimoniais positivas','202':'Variações patrimoniais negativas','206':'Amortizações não aceites','209':'Provisões/imparidade além dos limites','213':'Impostos e encargos da responsabilidade de outrem','215':'Multas e juros compensatórios','219':'Parcela não aceite de ajudas de custo','220':'Parcela não aceite de representação','221':'Publicidade além do limite','223':'Despesas não documentadas','224':'Parcela não aceite de aluguer','225':'Combustíveis sem prova/consumo normal','226':'Encargos não aceites com viaturas','239':'Reposição/redução de provisões tributadas'};
|
||||
for(const[code,label]of Object.entries(labels))fields.push(field(code,label,mappedValue(code,['202','239'].includes(code)),'PDF M22/quadro10;valor do ajustamento demonstrado,sem reaplicar percentagens do rótulo.'));
|
||||
const v=(code:string)=>fields.find(f=>f.code===code)!.value,patrimonial=netResult&&v('201')&&v('202')?subtractFractions(addFractions([netResult,v('201')!]),v('202')!):null;
|
||||
fields.push(field('203','Resultado após variações patrimoniais',patrimonial,'200+201−202,sem transferência entre edições.'));
|
||||
const up=entries.filter(e=>!['201','202'].includes(e.code??'')&&e.value&&positive(e.value)&&!eq(e.value,zero())),down=entries.filter(e=>!['201','202'].includes(e.code??'')&&e.value&&!positive(e.value));
|
||||
const sumsReady=includedTaxCorresponds&&m.taxableProfit!==null&&entries.every(e=>e.value!==null)&&unmapped.length===0&&patrimonial!==null;
|
||||
const additions=sumsReady?addFractions([patrimonial!,...up.map(e=>e.value!)]):null,deductions=sumsReady?addFractions(down.map(e=>neg(e.value!))):null,reconstructed=additions&&deductions?subtractFractions(additions,deductions):null;
|
||||
const matches=reconstructed!==null&&m.taxableProfit!==null&&eq(reconstructed,m.taxableProfit);if(reconstructed&&!matches)pending.push('Percurso M22 não reconcilia com o lucro tributável existente;rever localização do imposto e campos,sem alterar os cálculos demonstrados.');
|
||||
fields.push(field('238','Soma após acréscimos',additions,'Campo203+campos204–237;indeterminado se há efeito sem correspondência.'),field('258','Soma das deduções',deductions,'Campos239–257;sem presumir campo para dedução não correspondida.'));
|
||||
const p=m.taxableProfit,loss=p&&!positive(p)?neg(p):null,profit=p&&positive(p)?p:null;
|
||||
for(const[code,label,value]of [['259','Prejuízo fiscal',loss],['260','Lucro tributável',profit],['269','Prejuízo fiscal — regime geral',loss],['270','Lucro tributável — regime geral',profit],['276','Prejuízos fiscais deduzidos',m.base?.deductedLosses??null],['277','Benefícios fiscais a deduzir',m.base?.benefitDeduction??null],['278','Matéria colectável — regime geral',m.taxableBase],['300','Imposto à taxa normal',m.colecta??null]]as const)fields.push(field(code,label,value,'Reutiliza o cálculo demonstrado;campos independentes da divergência de transporte.',p!==null&&value===null&&['259','260','269','270'].includes(code)));
|
||||
const reducedAbsent=input.reducedRate.state==='absent'&&!!input.reducedRate.reference.trim();fields.push(field('301','Imposto à taxa reduzida',reducedAbsent?zero():null,'Ausência declarada com suporte;caso conhecido pertence aL7.'),field('302','Colecta total',reducedAbsent?m.colecta??null:null,'300+301 apenas com ausência de taxa reduzida demonstrada;sem saldoL8.'));
|
||||
if(!reducedAbsent)pending.push('Parcela de taxa reduzida em falta ou conhecida:colecta total302 depende desse âmbitoL7;colecta normal300 preservada.');
|
||||
fields.push(field('309–316','Liquidação e saldo final',null,'L8:retenções,pagamentos,créditos e saldo final não apurados.'));
|
||||
return{input:structuredClone(input),allocations,lines,m20,netResult,netRepresentation:normalFormRepresentation(netResult,'whole',true),entries,unmapped,fields,reconciliation:{reconstructedProfit:reconstructed,existingProfit:m.taxableProfit,matches},pending,source:'PDF M22 SHA2564b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88,Q10–12;M20A.1 SHA2561ceca6f4c6d77853c1b2b445876599b42093175fa89eecf40590180956d83a9e,Q4/M12/D13–D35. Rótulos e estrutura reutilizados;sem valores de terceiros,fórmulas do exemplar ou submissão.',transport:{ready:false,reason:'M20A.1 D35 remete paraM22 campo201/quadro8;PDF M22 usa200/quadro10. Impedimento restrito ao transporte deste resultado;campos independentes continuam preparados.'}};
|
||||
}
|
||||
export type NormalFormPreparationModel=ReturnType<typeof prepareNormalForms>;
|
||||
export function normalFormPreparationReport(m:NormalFormPreparationModel):Array<[string,string,string,string]>{return[['IRPC normal — L6','Representação e preparação M20/M22',m.pending.join(' | ')||'Preparação parcial dos campos correspondidos;sem declaração oficial ou submissão.',m.source],...m.m20.map(f=>['IRPC normal — L6','M20 A.1 conta'+f.account+' — '+f.label,`${f.representation.display};exacto:${f.representation.exact??'Indeterminado'};representável:${f.representation.ready?'Sim':'Pendente'};${f.representation.reason}`,`${f.reference};${f.reason}`]as[string,string,string,string]),['IRPC normal — L6','Resultado líquido M20',m.netRepresentation.display+';'+m.netRepresentation.reason,'Imposto contabilístico identificado;sem usar colecta como despesa presumida.'],...m.fields.map(f=>['IRPC normal — L6','M22 PDF campo'+f.code+' — '+f.label,`${f.representation.display};exacto:${f.representation.exact??'Indeterminado'};preparação de transcrição:${f.transcriptionReady?'Exacta neste recorte':'Pendente/sem aplicação'};${f.representation.reason}`,f.reason]as[string,string,string,string]),...m.unmapped.map(e=>['IRPC normal — L6','Ajustamento sem campo correspondido — '+e.origin,normalFormRepresentation(e.value).display,e.reason]as[string,string,string,string]),['IRPC normal — L6','Transporte entre edições',m.transport.reason,`Lucro reconstruído:${normalFormRepresentation(m.reconciliation.reconstructedProfit).display};lucro existente:${normalFormRepresentation(m.reconciliation.existingProfit).display};reconciliação:${m.reconciliation.matches?'Exacta':'Pendente'};sem alteração de cálculos.`]];}
|
||||
@@ -0,0 +1,39 @@
|
||||
import {addFractions,fraction,type FiscalFraction} from './fiscal-rational.ts';
|
||||
import {convertIrpcAmount,irpcFxDependency,type IrpcFxAmount,type IrpcFxEvidence,irpcFxEvidenceLabel} from './irpc-fx.ts';
|
||||
import type {rawNormalFinancialSource} from './irpc-normal.ts';
|
||||
export const normalPeriodFxVersion='IRPC-normal-adjustments/8';
|
||||
export type NormalPeriodFxInput={version:1;kind:''|'annual'|'start'|'cessation'|'same_year'|'transition';from:string;to:string;planStart:string;reference:string;conversionReference:string;mznMapsReference:string;months:Array<IrpcFxAmount&{period:number}>};
|
||||
export const newNormalPeriodFx=():NormalPeriodFxInput=>({version:1,kind:'',from:'',to:'',planStart:'',reference:'',conversionReference:'',mznMapsReference:'',months:[]});
|
||||
export function validateNormalPeriodFx(i:NormalPeriodFxInput){
|
||||
if(!i||i.version!==1||!['','annual','start','cessation','same_year','transition'].includes(i.kind)||['from','to','planStart','reference','conversionReference','mznMapsReference'].some(k=>typeof i[k as 'from']!=='string')||!Array.isArray(i.months)||new Set(i.months.map(m=>m.period)).size!==i.months.length||i.months.some(m=>!Number.isSafeInteger(m.period)||m.period<1||typeof m.currency!=='string'||m.fxSelection!==undefined&&(!m.fxSelection||typeof m.fxSelection!=='object')||m.fxLink!==undefined&&(!m.fxLink||typeof m.fxLink.id!=='string'||typeof m.fxLink.fingerprint!=='string')))throw Error('Correspondência do período/moeda não recuperável;originais preservados.');
|
||||
}
|
||||
const iso=(s:string)=>/^20\d{2}-\d{2}-\d{2}$/.test(s)&&Number.isFinite(Date.parse(s))&&new Date(s).toISOString().slice(0,10)===s;
|
||||
const month=(s:string)=>Number(s.slice(0,4))*12+Number(s.slice(5,7))-1;
|
||||
const monthEnd=(s:string)=>{const d=new Date(s+'T00:00:00Z');d.setUTCMonth(d.getUTCMonth()+1,0);return d.toISOString().slice(0,10);};
|
||||
const amount=(v:FiscalFraction)=>{const n=BigInt(v.numerator)*100n,d=BigInt(v.denominator);if(n%d!==0n)throw Error('Conversão com fracção de cêntimo: representação fiscal por corresponder,sem arredondamento novo.');const c=n/d,a=c<0n?-c:c;return(c<0n?'-':'')+a/100n+'.'+String(a%100n).padStart(2,'0');};
|
||||
type Source=NonNullable<ReturnType<typeof rawNormalFinancialSource>>;
|
||||
export function mapNormalPeriodFx(d:Record<string,unknown>,input:NormalPeriodFxInput,raw:Source|null){
|
||||
validateNormalPeriodFx(input);const pending:string[]=[],short=input.kind!==''&&input.kind!=='annual';let count=0;
|
||||
if(!input.kind||!input.reference.trim())pending.push('Tipo,evento e suporte da correspondência do período por identificar.');
|
||||
if(!iso(input.from)||!iso(input.to)||input.from>input.to||!input.from.endsWith('-01')||monthEnd(input.to.slice(0,7)+'-01')!==input.to||!input.to.startsWith('2026-'))pending.push('Recorte:meses completos,termo em2026;datas parciais ou inválidas permanecem por corresponder.');
|
||||
else{count=month(input.to)-month(input.from)+1;if(count<1||count>12||short&&count>=12||!short&&count!==12)pending.push('Número de meses incompatível com o tipo de período.');}
|
||||
if(short&&(!input.from.startsWith('2026-')||input.kind==='start'&&input.to!=='2026-12-31'||['cessation','transition'].includes(input.kind)&&input.from!=='2026-01-01'))pending.push('Datas incompatíveis com início,cessação ou transição no ano civil2026.');
|
||||
if(!iso(input.planStart)||!input.planStart.endsWith('-01'))pending.push('Identifique o primeiro mês civil do plano;meses orçamentais não têm data fiscal presumida.');
|
||||
if(!input.mznMapsReference.trim())pending.push('Confirme que parcelas,activos,quotas,saldos e mapas fiscais existentes estão reconciliados emMZN com este período;sem converter novamente esses dados.');
|
||||
if(!raw)pending.push('Plano financeiro reconciliado em falta.');
|
||||
const selected=raw?.accountingLines[0]?.monthlyAmounts.filter(m=>iso(input.planStart)&&month(input.planStart)+m.period-1>=month(input.from)&&month(input.planStart)+m.period-1<=month(input.to))??[];
|
||||
if(selected.length!==count||!count)pending.push('Meses do plano não cobrem integralmente o período indicado;sem anualização ou preenchimento de meses em falta.');
|
||||
if(raw?.currency!=='MZN'&&!input.conversionReference.trim())pending.push('Identifique pressuposto documentado da conversão mensal para a simulação;selecção cambial não confirma,por si,a regra fiscal de conversão anual.');
|
||||
for(const m of input.months)if(!selected.some(x=>x.period===m.period))pending.push('Selecção cambial sem mês seleccionado no período: '+m.period);
|
||||
const rows:Array<{code:string;period:number;date:string;originalAmount:string;originalCurrency:string;value:FiscalFraction|null;evidence:IrpcFxEvidence|null;reason:string}>=[];
|
||||
const lines=raw?.accountingLines.map(l=>{const monthlyAmounts=l.monthlyAmounts.filter(m=>selected.some(x=>x.period===m.period)).map(m=>{let value:FiscalFraction|null=null,evidence:IrpcFxEvidence|null=null,reason='';const day=iso(input.planStart)?new Date(Date.UTC(Number(input.planStart.slice(0,4)),Number(input.planStart.slice(5,7))-1+m.period-1,1)).toISOString().slice(0,10):'';
|
||||
try{const sign=m.amount.startsWith('-')?-1n:1n,original=sign<0n?m.amount.slice(1):m.amount;const selection=input.months.find(x=>x.period===m.period);if(raw.currency!=='MZN'&&(!selection||selection.currency!==raw.currency))throw Error('Selecção cambial deste mês/moeda em falta;sem usar taxa de outro mês por defeito.');const converted=convertIrpcAmount(d,original,selection??{currency:'MZN'});value=fraction(BigInt(converted.value.numerator)*sign,BigInt(converted.value.denominator));evidence=converted.evidence;amount(value);}catch(e){value=null;reason=String(e);pending.push(l.code+'/mês'+m.period+': '+reason);}
|
||||
rows.push({code:l.code,period:m.period,date:day,originalAmount:m.amount,originalCurrency:raw.currency,value,evidence,reason});return{period:m.period,amount:value?amount(value):'0.00'};});
|
||||
const total=addFractions(rows.filter(r=>r.code===l.code&&r.value).map(r=>r.value!));return{...l,monthlyAmounts,total:amount(total)};
|
||||
})??[];
|
||||
const ready=pending.length===0;const profit=addFractions(lines.map(l=>{const f=rows.filter(r=>r.code===l.code&&r.value).map(r=>r.value!);const sum=addFractions(f);return l.accountingEffect==='income'?sum:fraction(-BigInt(sum.numerator),BigInt(sum.denominator));}));
|
||||
return{input:structuredClone(input),short,months:count,originalCurrency:raw?.currency??'',rows,pending,source:ready&&raw?{...raw,currency:'MZN',cashSchedule:raw.currency==='MZN'?raw.cashSchedule.filter(r=>selected.some(m=>m.period===r.period)):[],periods:count,accountingLines:lines,accountingProfitBeforeTax:amount(profit)}:null,rule:'CIRPC7(4)a–d;Decreto72/2013 art9(3)a–c para os casos correspondidos. Meses completos do plano,sem anualização. Conversão para simulação reutiliza mecanismo cambial entregue e pressupostos explícitos;sem confirmação fiscal anual presumida.'};
|
||||
}
|
||||
export type NormalPeriodFxModel=ReturnType<typeof mapNormalPeriodFx>;
|
||||
export function normalPeriodFxDependency(d:Record<string,unknown>,i:NormalPeriodFxInput){return i.months.map(m=>irpcFxDependency(d,m)).filter(Boolean).join(' | ');}
|
||||
export function normalPeriodFxReport(m:NormalPeriodFxModel):Array<[string,string,string,string]>{return[['IRPC normal — L6','Período e moeda do plano',`${m.input.from}–${m.input.to};${m.months}meses;origem:${m.originalCurrency};resultado emMZN:${m.source?.accountingProfitBeforeTax??'Indeterminado'};${m.originalCurrency!=='MZN'?'Conversão para simulação com pressupostos documentados;regra fiscal anual não confirmada automaticamente.':''}${m.pending.join(' | ')}`,`${m.input.reference};${m.input.conversionReference};${m.input.mznMapsReference};${m.rule}`],...m.rows.map(r=>['IRPC normal — L6',`${({revenue:'Receitas',cost_of_sales:'Custo das vendas',operating_expenses:'Despesas operacionais',personnel:'Pessoal',depreciation:'Depreciação contabilística',interest_expense:'Juros reconhecidos'}as Record<string,string>)[r.code]??r.code}/mês${r.period} (${r.date})`,`Original:${r.originalAmount} ${r.originalCurrency};MZN:${r.value?amount(r.value):'Indeterminado'};${r.reason}`,r.evidence?irpcFxEvidenceLabel(r.evidence):'OrigemMZN;sem conversão']as[string,string,string,string])];}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type {NormalDepreciationModel} from './irpc-normal-depreciation.ts';
|
||||
import type {NormalCostsModel} from './irpc-normal-costs.ts';
|
||||
import type {SchedulesModel} from './irpc-normal-schedules.ts';
|
||||
import {addFractions,decimalFraction,fraction,type FiscalFraction} from './fiscal-rational.ts';
|
||||
@@ -25,7 +26,7 @@ const zero=()=>fraction(0n,1n);
|
||||
function money(s:string){if(!/^-?\d+(?:\.\d{1,2})?$/.test(s))throw Error('Montante inválido');const v=decimalFraction(s.startsWith('-')?s.slice(1):s);return s.startsWith('-')?fraction(-BigInt(v.numerator),BigInt(v.denominator)):v;}
|
||||
const units=(f:FiscalFraction)=>BigInt(f.numerator)*100n/BigInt(f.denominator);
|
||||
const difference=(a:FiscalFraction,b:FiscalFraction)=>fraction(BigInt(a.numerator)*BigInt(b.denominator)-BigInt(b.numerator)*BigInt(a.denominator),BigInt(a.denominator)*BigInt(b.denominator));
|
||||
export function closeNormalProfit(input:NormalProfitInput,source:Source|null,rows:Array<{sourceCode:string;adjustment:FiscalFraction|null;status:string}>,accounting:FiscalFraction|null,additions:FiscalFraction,contextReady:boolean,schedules?:SchedulesModel,costs?:NormalCostsModel):NormalProfitModel{
|
||||
export function closeNormalProfit(input:NormalProfitInput,source:Source|null,rows:Array<{sourceCode:string;adjustment:FiscalFraction|null;status:string}>,accounting:FiscalFraction|null,additions:FiscalFraction,contextReady:boolean,schedules?:SchedulesModel,costs?:NormalCostsModel,depreciation?:NormalDepreciationModel):NormalProfitModel{
|
||||
validateNormalProfit(input);const pending:string[]=[],lines:NormalProfitModel['lines']=[],topics:NormalProfitModel['topics']=[];
|
||||
if(!contextReady)pending.push('Âmbito,exercício,moeda ou correspondência anual ainda insuficientes.');
|
||||
if(!input.completenessReference.trim())pending.push('Documente que o plano e os ajustamentos abrangem o sujeito passivo/exercício completo;uma oportunidade isolada não equivale automaticamente ao conjunto da empresa.');
|
||||
@@ -37,7 +38,7 @@ export function closeNormalProfit(input:NormalProfitInput,source:Source|null,row
|
||||
try{const amount=money(l.total);remainder=difference(amount,addedBack);if(units(amount)<0n||units(remainder)<0n)issues.push('Valor negativo ou acréscimos superiores à origem;reconcilie a linha sem compensação presumida.');
|
||||
if(!state||!reference.trim())issues.push('Conferência fiscal e suporte da linha em falta.');
|
||||
if(state==='absent'&&(units(amount)!==0n||units(addedBack)!==0n))issues.push('Ausência declarada contradiz o montante conhecido da origem.');
|
||||
if(state==='known'&&l.code==='depreciation'&&units(amount)!==0n&&schedules?.depreciation==null)issues.push('Depreciação contabilística conhecida;amortização fiscal por corresponder,não presumir igualdade.');
|
||||
if(state==='known'&&l.code==='depreciation'&&units(amount)!==0n&&(depreciation?depreciation.adjustment:schedules?.depreciation)==null)issues.push('Depreciação contabilística conhecida;amortização fiscal por corresponder,não presumir igualdade.');
|
||||
if(state==='known'&&l.code==='interest_expense'&&units(remainder)!==0n&&schedules?.interest==null)issues.push('Juros financeiros remanescentes conhecidos;dedutibilidade e eventuais limites por corresponder. Só os juros compensatórios já acrescidos estão cobertos.');
|
||||
if(!['revenue','cost_of_sales','operating_expenses','personnel','depreciation','interest_expense'].includes(l.code))issues.push('Linha financeira sem correspondência neste recorte.');
|
||||
}catch{issues.push('Montante da linha por reconciliar.');}
|
||||
@@ -58,5 +59,6 @@ export function closeNormalProfit(input:NormalProfitInput,source:Source|null,row
|
||||
}
|
||||
if(schedules)pending.push(...schedules.pending);
|
||||
if(costs)pending.push(...costs.pending);
|
||||
return{input:structuredClone(input),lines,topics,pending,taxableProfit:pending.length===0&&accounting?addFractions([accounting,additions,...(schedules?[schedules.net]:[]),...(costs?[costs.net]:[])]):null,source:schedules?'CIRPC17/18/20/21/22/24/36; Decreto72/2013 art3–7. Mapa complementar de efeitos com sinal reconciliado por tema, juros financeiros ordinários e quotas anuais constantes documentadas. Simulação emMZN/2026; benefícios e casos especiais separados emL7, saldo anual emL8.':'CIRPC17(1)/18/20/22/36(1)c;recorte anual2026,residente geral,MZN. Resultado antes de imposto + acréscimos36(1)c;demais correcções/variações sem efeito apenas quando ausência ou zero conhecidos expressamente suportados. Receita/custos ordinários classificados pelo utilizador;depreciação fiscal e juros financeiros remanescentes fora deste percurso. Lucro tributável simulado,não matéria colectável,colecta ou declaraçãoM20/22.'};
|
||||
if(depreciation&&!schedules)pending.push(...depreciation.pending);
|
||||
return{input:structuredClone(input),lines,topics,pending,taxableProfit:pending.length===0&&accounting?addFractions([accounting,additions,...(schedules?[schedules.net]:[]),...(costs?[costs.net]:[]),...(depreciation&&!schedules&&depreciation.adjustment?[depreciation.adjustment]:[])]):null,source:schedules?'CIRPC17/18/20/21/22/24/36; Decreto72/2013 art3–7. Mapa complementar de efeitos com sinal reconciliado por tema, juros financeiros ordinários e quotas anuais constantes documentadas. Simulação emMZN/2026; benefícios e casos especiais separados emL7, saldo anual emL8.':'CIRPC17(1)/18/20/22/36(1)c;recorte anual2026,residente geral,MZN. Resultado antes de imposto + acréscimos36(1)c;demais correcções/variações sem efeito apenas quando ausência ou zero conhecidos expressamente suportados. Receita/custos ordinários classificados pelo utilizador;depreciação fiscal e juros financeiros remanescentes fora deste percurso. Lucro tributável simulado,não matéria colectável,colecta ou declaraçãoM20/22.'};
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ const z=()=>fraction(0n);
|
||||
export const fiscalMoney=(s:string)=>{if(!/^\d{1,18}(?:\.\d{1,2})?$/.test(s))throw Error('Montante não negativo com até dois decimais necessário.');return decimalFraction(s);};
|
||||
const cmp=(a:FiscalFraction,b:FiscalFraction)=>BigInt(a.numerator)*BigInt(b.denominator)-BigInt(b.numerator)*BigInt(a.denominator);
|
||||
export type SchedulesModel=ReturnType<typeof calculateSchedules>;
|
||||
export function calculateSchedules(s:NormalSchedules,source:{accountingLines:Array<{code:string;total:string;accountingEffect:string}>}|null,oldRows:Array<{sourceCode:string;document:string;line:string;amount:string;adjustment:FiscalFraction|null}>){
|
||||
export function calculateSchedules(s:NormalSchedules,source:{accountingLines:Array<{code:string;total:string;accountingEffect:string}>}|null,oldRows:Array<{sourceCode:string;document:string;line:string;amount:string;adjustment:FiscalFraction|null}>,depreciationOverride?:{value:FiscalFraction|null;pending:string[]}){
|
||||
validateSchedules(s);const pending:string[]=[],effects:Array<{id:string;topic:string;amount:FiscalFraction|null;effect:FiscalFraction|null;reason:string}>=[];
|
||||
for(const r of s.rows){const issues:string[]=[],rule=r.rule?scheduleRules[r.rule]:null;let amount:FiscalFraction|null=null,effect:FiscalFraction|null=null;
|
||||
if(!rule||!r.reference.trim()||!r.document.trim()||!r.line.trim()||r.conditions!=='yes')issues.push('Regra, documento/linha, suporte e condições por identificar.');
|
||||
@@ -56,7 +56,7 @@ export function calculateSchedules(s:NormalSchedules,source:{accountingLines:Arr
|
||||
}
|
||||
let interest:FiscalFraction|null=null;const interestOrigin=source?.accountingLines.find(l=>l.code==='interest_expense');
|
||||
try{const corrected=s.rows.filter(r=>r.sourceCode==='interest_expense'&&r.rule&&(scheduleRules[r.rule][1]==='expenseCorrections'||r.rule==='future_cost'));const corrections=corrected.map(r=>{const e=effects.find(e=>e.id===r.id);if(!e?.effect)throw Error();return e.effect;});const remaining=subtractFractions(fiscalMoney(interestOrigin?.total??''),addFractions([...oldRows.filter(r=>r.sourceCode==='interest_expense').flatMap(r=>r.adjustment?[r.adjustment]:[]),...corrections]));interest=fiscalMoney(s.interest.amount);if(cmp(interest,remaining)!==0n||!s.interest.reference.trim()||s.interest.conditions!=='yes')throw Error();}catch{interest=null;}
|
||||
const assets=s.assets.map(a=>{const issues:string[]=[];let accounting:FiscalFraction|null=null,fiscal:FiscalFraction|null=null,adjustment:FiscalFraction|null=null;
|
||||
const assets=(depreciationOverride?[]:s.assets).map(a=>{const issues:string[]=[];let accounting:FiscalFraction|null=null,fiscal:FiscalFraction|null=null,adjustment:FiscalFraction|null=null;
|
||||
try{const cost=fiscalMoney(a.cost),residual=fiscalMoney(a.residual),previous=fiscalMoney(a.previousFiscal),rate=fiscalMoney(a.rate);accounting=fiscalMoney(a.accounting);const depreciable=subtractFractions(cost,residual);if(cmp(depreciable,z())<0n||cmp(previous,depreciable)>0n||cmp(rate,z())<=0n||cmp(rate,fraction(100n))>0n)throw Error();const annual=fraction(BigInt(depreciable.numerator)*BigInt(rate.numerator),BigInt(depreciable.denominator)*BigInt(rate.denominator)*100n);const available=subtractFractions(depreciable,previous);fiscal=cmp(annual,available)>0n?available:annual;
|
||||
if(cmp(fiscal,accounting)>0n)issues.push('Quota fiscal superior à contabilizada neste exercício; dedução de quotas anteriormente tributadas exige mapa próprio, sem presunção.');
|
||||
}catch{issues.push('Custo, residual, taxa, acumulado fiscal e depreciação contabilizada por reconciliar.');}
|
||||
@@ -64,8 +64,9 @@ export function calculateSchedules(s:NormalSchedules,source:{accountingLines:Arr
|
||||
if(!/^20\d{2}-\d{2}-\d{2}$/.test(a.inUseFrom)||!Number.isFinite(Date.parse(a.inUseFrom))||new Date(a.inUseFrom).toISOString().slice(0,10)!==a.inUseFrom||a.inUseFrom>(s.period.from||'2026-01-01')||a.inUseFrom<'2014-01-01'||!a.description.trim()||!a.tableReference.trim()||!a.reference.trim()||a.conditions!=='yes')issues.push('Identifique activo próprio, em uso no início do período, tabela/taxa aplicável, vida útil e condições do método constante anual; casos especiais/duodécimos separados.');
|
||||
if(['computer','server'].includes(a.category)&&/^20\d{2}-\d{2}-\d{2}$/.test(a.inUseFrom)){const limit=new Date(a.inUseFrom+'T00:00:00Z');if(Number.isFinite(+limit)){limit.setUTCFullYear(limit.getUTCFullYear()+(a.category==='computer'?8:10));if((s.period.from||'2026-01-01')>=limit.toISOString().slice(0,10))issues.push('Período máximo de vida útil excedido, artigo5; excepção específica não implementada.');}}
|
||||
if(!issues.length&&accounting&&fiscal)adjustment=subtractFractions(accounting,fiscal);if(issues.length)pending.push(a.description+': '+issues.join(' '));return{...structuredClone(a),accountingValue:accounting,fiscal,adjustment,reason:issues.join(' ')||'Decreto72/2013 art3–7; quota constante anual documentada, limitada ao saldo fiscal depreciável; diferença para a depreciação contabilística.'};});
|
||||
let depreciation:FiscalFraction|null=null;try{const total=addFractions(assets.map(a=>a.accountingValue??z()));if(s.assetsReference.trim()&&assets.every(a=>a.adjustment!==null)&&cmp(total,fiscalMoney(source?.accountingLines.find(l=>l.code==='depreciation')?.total??''))===0n)depreciation=addFractions(assets.map(a=>a.adjustment!));}catch{/* linha de origem desconhecida */}
|
||||
let depreciation:FiscalFraction|null=depreciationOverride?.value??null;if(!depreciationOverride)try{const total=addFractions(assets.map(a=>a.accountingValue??z()));if(s.assetsReference.trim()&&assets.every(a=>a.adjustment!==null)&&cmp(total,fiscalMoney(source?.accountingLines.find(l=>l.code==='depreciation')?.total??''))===0n)depreciation=addFractions(assets.map(a=>a.adjustment!));}catch{/* linha de origem desconhecida */}
|
||||
if(assets.length&&(oldRows.some(r=>r.sourceCode==='depreciation')||s.rows.some(r=>r.sourceCode==='depreciation'))){depreciation=null;pending.push('Depreciação repartida entre mapas de ajustamentos e activos: reconcilie as parcelas sem sobreposição antes de aplicar a quota fiscal.');}
|
||||
if(assets.length&&depreciation===null)pending.push('Mapa de activos sem reconciliação integral com a depreciação da origem; efeito fiscal não aplicado.');
|
||||
if(depreciationOverride)pending.push(...depreciationOverride.pending);
|
||||
return{input:structuredClone(s),effects,interest,depreciation,assets,pending,net: addFractions([...effects.flatMap(r=>r.effect?[r.effect]:[]),...(depreciation?[depreciation]:[])]),source:'CIRPC18/21/22/24/36; Decreto72/2013 art3–7. Entradas documentadas, sem taxas de activos sugeridas nem casos especiais presumidos.'};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import {normalFormPreparationVersion,validateNormalFormPreparation,prepareNormalForms,normalFormPreparationReport,type NormalFormPreparationInput,type NormalFormPreparationModel} from './irpc-normal-form-preparation.ts';
|
||||
import {normalPeriodFxVersion,validateNormalPeriodFx,mapNormalPeriodFx,normalPeriodFxDependency,normalPeriodFxReport,type NormalPeriodFxInput,type NormalPeriodFxModel} from './irpc-normal-period-fx.ts';
|
||||
import {normalDepreciationVersion,validateNormalDepreciation,calculateNormalDepreciation,normalDepreciationReport,type NormalDepreciationInput,type NormalDepreciationModel} from './irpc-normal-depreciation.ts';
|
||||
import {normalCostsVersion,validateNormalCosts,calculateNormalCosts,normalCostReport,type NormalCostsInput,type NormalCostsModel} from './irpc-normal-costs.ts';
|
||||
import {normalFormFields} from './irpc-normal-forms.ts';
|
||||
import {normalSchedulesVersion,normalSchedulePeriod,validateSchedules,calculateSchedules,type NormalSchedules,type SchedulesModel} from './irpc-normal-schedules.ts';
|
||||
@@ -12,18 +15,22 @@ import {addFractions,decimalFraction,fraction,fractionPresentation,type FiscalFr
|
||||
export const irpcNormalVersion='IRPC-normal-adjustments/1';
|
||||
export const irpcNormalSource='CIRPC Lei34/2007 art17(1),PDF6/p768(60):resultado e correcções;art36(1)c,PDF10/p768(64):multas e encargos por infracções sem origem contratual,incluindo juros compensatórios. Lei4/2012 PDF2 altera17(6)/acrescenta36(1)o;Lei19/2013 PDF2 mantém36(1)a–o e acrescenta p/q. Lei12/2025 altera3/5/20/62/67/75,adita61-A e revoga39/47/75(2)/76;sem alteração deste recorte36(1)c. Não é apuramento integral nem Modelo20/22.';
|
||||
export type NormalAdjustment={id:string;sourceCode:string;document:string;line:string;amount:string;rule:''|'noncontractual_fine'|'compensatory_interest'|'other';included:''|'yes'|'no';reference:string};
|
||||
export type NormalBook={version:1;year:string;mappingReference:string;scope:''|'resident_general'|'ep_general'|'other';scopeReference:string;rows:NormalAdjustment[];profit?:NormalProfitInput;base?:NormalBaseInput;assessment?:NormalAssessmentInput;schedules?:NormalSchedules;costs?:NormalCostsInput;result?:NormalRecord;history?:NormalRecord[]};
|
||||
type NormalModel={costs?:NormalCostsModel;fiscalPeriod?:ReturnType<typeof normalSchedulePeriod>;schedules?:SchedulesModel;currentLoss?:FiscalFraction|null;assessment?:NormalAssessmentModel;colecta?:FiscalFraction|null;base?:NormalBaseModel;profit?:NormalProfitModel;version:string;year:string;currency:string;origin:'projected';accountingProfit:FiscalFraction|null;additions:FiscalFraction;partialAdjustedResult:FiscalFraction|null;taxableProfit:FiscalFraction|null;taxableBase:FiscalFraction|null;tax:null;rows:Array<NormalAdjustment&{status:'calculated'|'indeterminate'|'outside_scope';adjustment:FiscalFraction|null;reason:string}>;pending:string[];source:string};
|
||||
export type NormalBook={version:1;year:string;mappingReference:string;scope:''|'resident_general'|'ep_general'|'other';scopeReference:string;rows:NormalAdjustment[];profit?:NormalProfitInput;base?:NormalBaseInput;assessment?:NormalAssessmentInput;schedules?:NormalSchedules;costs?:NormalCostsInput;depreciation?:NormalDepreciationInput;forms?:NormalFormPreparationInput;periodsFx?:NormalPeriodFxInput;result?:NormalRecord;history?:NormalRecord[]};
|
||||
type NormalModel={formPreparation?:NormalFormPreparationModel;periodsFx?:NormalPeriodFxModel;depreciation?:NormalDepreciationModel;costs?:NormalCostsModel;fiscalPeriod?:{kind:string;from:string;to:string;reference:string;pending:string[]};schedules?:SchedulesModel;currentLoss?:FiscalFraction|null;assessment?:NormalAssessmentModel;colecta?:FiscalFraction|null;base?:NormalBaseModel;profit?:NormalProfitModel;version:string;year:string;currency:string;origin:'projected';accountingProfit:FiscalFraction|null;additions:FiscalFraction;partialAdjustedResult:FiscalFraction|null;taxableProfit:FiscalFraction|null;taxableBase:FiscalFraction|null;tax:null;rows:Array<NormalAdjustment&{status:'calculated'|'indeterminate'|'outside_scope';adjustment:FiscalFraction|null;reason:string}>;pending:string[];source:string};
|
||||
type NormalRecord={key:string;createdAt:string;model:NormalModel};
|
||||
export const newNormalBook=():NormalBook=>({version:1,year:'',mappingReference:'',scope:'',scopeReference:'',rows:[]});
|
||||
export const newNormalAdjustment=(id:string):NormalAdjustment=>({id,sourceCode:'',document:'',line:'',amount:'',rule:'',included:'',reference:''});
|
||||
export function normalFinancialSource(d:Record<string,unknown>){
|
||||
export function rawNormalFinancialSource(d:Record<string,unknown>){
|
||||
const p=restoreBusinessPlanSnapshot(String(d.business_plan_snapshot??''));if(!p)return null;
|
||||
const forecast=forecastBusinessPlan(p),bridge=buildFinancialFiscalBridge(forecast);
|
||||
return{...bridge,periods:forecast.rows.length,origin:'projected' as const,scope:acquisitionScope(d)};
|
||||
}
|
||||
export function normalFinancialSource(d:Record<string,unknown>){const raw=rawNormalFinancialSource(d),input=normalBook(d).periodsFx;return input?mapNormalPeriodFx(d,input,raw).source:raw;}
|
||||
function store(d:Record<string,unknown>){const s=JSON.parse(String(d.modelo_a_snapshot||'{"version":1,"books":{}}'));if(s.version!==1||!s.books||typeof s.books!=='object'||Array.isArray(s.books))throw Error('Livros fiscais não recuperáveis;originais preservados.');return s;}
|
||||
export function validateNormal(b:NormalBook){
|
||||
if(b?.forms!==undefined)validateNormalFormPreparation(b.forms);
|
||||
if(b?.periodsFx!==undefined)validateNormalPeriodFx(b.periodsFx);
|
||||
if(b?.depreciation!==undefined)validateNormalDepreciation(b.depreciation);
|
||||
if(b?.costs!==undefined)validateNormalCosts(b.costs);
|
||||
if(b?.schedules!==undefined)validateSchedules(b.schedules);
|
||||
if(b?.assessment!==undefined)validateNormalAssessment(b.assessment);
|
||||
@@ -32,23 +39,27 @@ export function validateNormal(b:NormalBook){
|
||||
if(!b||b.version!==1||['year','mappingReference','scopeReference'].some(k=>typeof b[k as 'year']!=='string')||!['','resident_general','ep_general','other'].includes(b.scope)||!Array.isArray(b.rows)||b.history!==undefined&&!Array.isArray(b.history))throw Error('Preparação IRPC normal não recuperável.');
|
||||
for(const r of b.rows)if(!r||Object.keys(newNormalAdjustment('')).some(k=>typeof r[k as keyof NormalAdjustment]!=='string')||!['','noncontractual_fine','compensatory_interest','other'].includes(r.rule)||!['','yes','no'].includes(r.included))throw Error('Ajustamento IRPC não recuperável;original conservado.');
|
||||
if(new Set(b.rows.map(r=>r.id)).size!==b.rows.length)throw Error('Identificador de ajustamento duplicado.');
|
||||
for(const r of [b.result,...(b.history??[])])if(r&&(!r.model||![irpcNormalVersion,normalProfitVersion,normalBaseVersion,normalAssessmentVersion,normalSchedulesVersion,normalCostsVersion].includes(r.model.version)||typeof r.key!=='string'||typeof r.createdAt!=='string'||!Array.isArray(r.model.rows)||!Array.isArray(r.model.pending)))throw Error('Histórico IRPC normal não recuperável.');
|
||||
for(const r of [b.result,...(b.history??[])])if(r&&(!r.model||![irpcNormalVersion,normalProfitVersion,normalBaseVersion,normalAssessmentVersion,normalSchedulesVersion,normalCostsVersion,normalDepreciationVersion,normalPeriodFxVersion,normalFormPreparationVersion].includes(r.model.version)||typeof r.key!=='string'||typeof r.createdAt!=='string'||!Array.isArray(r.model.rows)||!Array.isArray(r.model.pending)))throw Error('Histórico IRPC normal não recuperável.');
|
||||
}
|
||||
export function normalBook(d:Record<string,unknown>):NormalBook{
|
||||
const m=store(d).irpcNormal;if(m===undefined)return newNormalBook();if(!m||typeof m!=='object'||Array.isArray(m))throw Error('Mapa IRPC normal não recuperável.');const b=m[acquisitionScope(d)]??newNormalBook();validateNormal(b);return b;
|
||||
}
|
||||
export function saveNormalBook(d:Record<string,unknown>,b:NormalBook){validateNormal(b);const s=store(d);if(s.irpcNormal!==undefined&&(!s.irpcNormal||typeof s.irpcNormal!=='object'||Array.isArray(s.irpcNormal)))throw Error('Mapa IRPC normal não recuperável.');return JSON.stringify({...s,irpcNormal:{...s.irpcNormal,[acquisitionScope(d)]:b}});}
|
||||
function key(d:Record<string,unknown>,b:NormalBook){return JSON.stringify([b.costs?normalCostsVersion:b.schedules?normalSchedulesVersion:b.assessment?normalAssessmentVersion:b.base?normalBaseVersion:b.profit?normalProfitVersion:irpcNormalVersion,acquisitionScope(d),d.business_plan_snapshot??'',b.year,b.mappingReference,b.scope,b.scopeReference,b.rows,...(b.profit?[b.profit]:[]),...(b.base?[b.base]:[]),...(b.assessment?[b.assessment]:[]),...(b.schedules?[b.schedules]:[]),...(b.costs?[b.costs]:[])]);}
|
||||
function key(d:Record<string,unknown>,b:NormalBook){return JSON.stringify([b.forms?normalFormPreparationVersion:b.periodsFx?normalPeriodFxVersion:b.depreciation?normalDepreciationVersion:b.costs?normalCostsVersion:b.schedules?normalSchedulesVersion:b.assessment?normalAssessmentVersion:b.base?normalBaseVersion:b.profit?normalProfitVersion:irpcNormalVersion,acquisitionScope(d),d.business_plan_snapshot??'',b.year,b.mappingReference,b.scope,b.scopeReference,b.rows,...(b.profit?[b.profit]:[]),...(b.base?[b.base]:[]),...(b.assessment?[b.assessment]:[]),...(b.schedules?[b.schedules]:[]),...(b.costs?[b.costs]:[]),...(b.depreciation?[b.depreciation]:[]),...(b.periodsFx?[b.periodsFx,normalPeriodFxDependency(d,b.periodsFx)]:[]),...(b.forms?[b.forms]:[])]);}
|
||||
export function normalCurrent(d:Record<string,unknown>,b:NormalBook){return !!b.result&&b.result.key===key(d,b);}
|
||||
const zero=()=>fraction(0n,1n);
|
||||
const cents=(s:string)=>{if(!/^-?\d+(?:\.\d{1,2})?$/.test(s))throw Error('Montante MZN inválido;máximo dois decimais.');const v=decimalFraction(s.startsWith('-')?s.slice(1):s);return s.startsWith('-')?fraction(-BigInt(v.numerator),BigInt(v.denominator)):v;};
|
||||
const integerCents=(v:FiscalFraction)=>BigInt(v.numerator)*100n/BigInt(v.denominator);
|
||||
export function calculateNormal(d:Record<string,unknown>,b:NormalBook):NormalModel{
|
||||
validateNormal(b);const pending:string[]=[],source=normalFinancialSource(d),fiscalPeriod=b.schedules?normalSchedulePeriod(b.schedules,b.scope):undefined;
|
||||
validateNormal(b);const pending:string[]=[],raw=rawNormalFinancialSource(d),periodsFx=b.periodsFx?mapNormalPeriodFx(d,b.periodsFx,raw):undefined,source=periodsFx?periodsFx.source:raw,fiscalPeriod=periodsFx?{kind:b.periodsFx!.kind,from:b.periodsFx!.from,to:b.periodsFx!.to,reference:b.periodsFx!.reference,pending:periodsFx.pending}:b.schedules?normalSchedulePeriod(b.schedules,b.scope):undefined;
|
||||
if(periodsFx&&b.periodsFx!.kind==='annual'&&b.periodsFx!.from!=='2026-01-01'){
|
||||
const formal=b.schedules?normalSchedulePeriod(b.schedules,b.scope):null;
|
||||
if(!formal||formal.pending.length||formal.from!==b.periodsFx!.from||formal.to!==b.periodsFx!.to)fiscalPeriod!.pending.push('Ano diferente do civil exige o período autorizado/comunicado já documentado nos mapas,com as mesmas datas.');
|
||||
}
|
||||
if(fiscalPeriod)pending.push(...fiscalPeriod.pending);
|
||||
if(!source)pending.push('Falta plano financeiro reconciliado.');
|
||||
if(source?.currency!=='MZN')pending.push('Ponte em MZN necessária;sem conversão fiscal anual presumida.');
|
||||
if(!/^2026$/.test(b.year)||!b.mappingReference.trim()||source?.periods!==12)pending.push('Documente correspondência dos 12 meses do plano ao exercício2026;outros exercícios/horizontes permanecem separados.');
|
||||
if(!/^2026$/.test(b.year)||!b.mappingReference.trim()||source?.periods!==(periodsFx?.months??12))pending.push('Documente correspondência dos 12 meses do plano ao exercício2026;outros exercícios/horizontes permanecem separados.');
|
||||
if(!(b.scope==='resident_general'||b.schedules&&b.scope==='ep_general')||!b.scopeReference.trim())pending.push('Identifique sujeito residente no regime geral,fora de transparência/regimes especiais;EP e outros âmbitos não abrangidos.');
|
||||
const rows:NormalModel['rows']=b.rows.map(r=>({...structuredClone(r),status:'indeterminate',adjustment:null,reason:''}));
|
||||
const amounts=new Map<string,FiscalFraction>(),groups=new Map<string,NormalModel['rows']>();
|
||||
@@ -69,23 +80,27 @@ export function calculateNormal(d:Record<string,unknown>,b:NormalBook):NormalMod
|
||||
for(const r of rows){if(!r.reason){r.status='calculated';r.adjustment=amounts.get(r.id)!;r.reason='Acréscimo integral de custo incluído no resultado de partida,qualificado pelo utilizador como '+(r.rule==='noncontractual_fine'?'multa/encargo de infracção sem origem contratual':'juros compensatórios')+';CIRPC36(1)c. Projecção pelos pressupostos do plano.';}else pending.push(r.document+': '+r.reason);}
|
||||
const additions=addFractions(rows.flatMap(r=>r.adjustment?[r.adjustment]:[]));
|
||||
const accountingProfit=source?.currency==='MZN'?cents(source.accountingProfitBeforeTax):null;
|
||||
const contextReady=(!fiscalPeriod||fiscalPeriod.pending.length===0)&&!!source&&source.currency==='MZN'&&source.periods===12&&b.year==='2026'&&!!b.mappingReference.trim()&&(b.scope==='resident_general'||!!b.schedules&&b.scope==='ep_general')&&!!b.scopeReference.trim();
|
||||
const contextReady=(!fiscalPeriod||fiscalPeriod.pending.length===0)&&!!source&&source.currency==='MZN'&&source.periods===(periodsFx?.months??12)&&b.year==='2026'&&!!b.mappingReference.trim()&&(b.scope==='resident_general'||!!b.schedules&&b.scope==='ep_general')&&!!b.scopeReference.trim();
|
||||
if(rows.length===0&&!b.profit)pending.push('Sem ajustamentos documentados;ausência de registos não prova inexistência de correcções.');
|
||||
if(!b.profit)pending.push('L6 parcial:resultado ajustado apenas destes acréscimos;restantes custos/variações patrimoniais,amortizações,perdas,deduções e colecta por tratar. Lucro tributável,matéria colectável e imposto indeterminados.');
|
||||
const schedules=b.schedules?calculateSchedules(b.schedules,source,rows):undefined;
|
||||
const costs=b.costs?calculateNormalCosts(b.costs,source,[...b.rows,...(b.schedules?.rows??[])],fiscalPeriod??{from:'2026-01-01',to:b.year+'-12-31'}):undefined;
|
||||
const profit=b.profit?closeNormalProfit(b.profit,source,rows,accountingProfit,additions,contextReady,schedules,costs):undefined;
|
||||
const depreciation=b.depreciation?calculateNormalDepreciation(b.depreciation,source,fiscalPeriod??{from:b.year+'-01-01',to:b.year+'-12-31'},[...b.rows,...(b.schedules?.rows??[]),...(b.costs?.rows??[])],!!periodsFx?.short):undefined;
|
||||
const schedules=b.schedules?calculateSchedules(b.schedules,source,rows,depreciation?{value:depreciation.adjustment,pending:depreciation.pending}:undefined):undefined;
|
||||
const costs=b.costs?calculateNormalCosts(b.costs,source,[...b.rows,...(b.schedules?.rows??[])],fiscalPeriod??{from:'2026-01-01',to:b.year+'-12-31'},!!periodsFx?.short):undefined;
|
||||
const profit=b.profit?closeNormalProfit(b.profit,source,rows,accountingProfit,additions,contextReady,schedules,costs,depreciation):undefined;
|
||||
if(profit&&periodsFx)profit.source='CIRPC17/18/20/21/22/24/36;'+periodsFx.rule+' L7 mantém benefícios/casos especiais;L8 mantém saldo final.';
|
||||
if(profit)pending.push(...profit.pending,...(!b.base?['Matéria colectável,perdas reportadas,deduções e imposto não apurados neste percurso.']:[]));
|
||||
const base=b.base?calculateNormalBase(b.base,profit?.taxableProfit??null,b.year,b.schedules?.currentLossReference):undefined;
|
||||
const base=b.base?calculateNormalBase(b.base,profit?.taxableProfit??null,b.year,b.schedules?.currentLossReference,!!periodsFx?.short):undefined;
|
||||
if(base)pending.push(...base.pending,...(!b.assessment?['Colecta/imposto a pagar,créditos anuais eM20/M22 não determinados;matéria colectável não é imposto.']:[]));
|
||||
const assessment=b.assessment?calculateNormalAssessment(b.assessment,base?.taxableBase??null,{year:b.year,scope:b.scope,extended:!!b.schedules,currency:source?.currency??'',...(fiscalPeriod?{period:{from:fiscalPeriod.from,to:fiscalPeriod.to}}:{})}):undefined;
|
||||
const assessment=b.assessment?calculateNormalAssessment(b.assessment,base?.taxableBase??null,{year:b.year,scope:b.scope,extended:!!b.schedules||!!periodsFx,short:!!periodsFx?.short,currency:source?.currency??'',...(fiscalPeriod?{period:{from:fiscalPeriod.from,to:fiscalPeriod.to}}:{})}):undefined;
|
||||
if(assessment)pending.push(...assessment.pending,assessment.finalBalance.reason);
|
||||
const currentLoss=b.schedules&&profit?.taxableProfit&&BigInt(profit.taxableProfit.numerator)<0n?fraction(-BigInt(profit.taxableProfit.numerator),BigInt(profit.taxableProfit.denominator)):null;
|
||||
return{...(costs?{costs}:{}),...(schedules?{schedules,currentLoss,fiscalPeriod}:{}),...(assessment?{assessment,colecta:assessment.colecta}:{}),...(base?{base}:{}),...(profit?{profit}:{}),version:b.costs?normalCostsVersion:b.schedules?normalSchedulesVersion:b.assessment?normalAssessmentVersion:b.base?normalBaseVersion:b.profit?normalProfitVersion:irpcNormalVersion,year:b.year,currency:source?.currency??'',origin:'projected',accountingProfit,additions,partialAdjustedResult:contextReady&&rows.length>0&&rows.every(r=>r.status==='calculated')&&accountingProfit?addFractions([accountingProfit,additions]):null,taxableProfit:profit?.taxableProfit??null,taxableBase:base?.taxableBase??null,tax:null,rows,pending,source:irpcNormalSource+(b.schedules?' Mapas complementares: CIRPC18/21/22/24/36; Decreto72/2013 art3–7. EP: CIRPC15(1)c, apenas resultado imputável ao estabelecimento no regime geral, anual2026/MZN.':'')};
|
||||
const model:NormalModel={...(periodsFx?{periodsFx}:{}),...(depreciation?{depreciation}:{}),...(costs?{costs}:{}),...(schedules?{schedules,currentLoss,fiscalPeriod}:{}),...(assessment?{assessment,colecta:assessment.colecta}:{}),...(base?{base}:{}),...(profit?{profit}:{}),version:b.forms?normalFormPreparationVersion:b.periodsFx?normalPeriodFxVersion:b.depreciation?normalDepreciationVersion:b.costs?normalCostsVersion:b.schedules?normalSchedulesVersion:b.assessment?normalAssessmentVersion:b.base?normalBaseVersion:b.profit?normalProfitVersion:irpcNormalVersion,year:b.year,currency:source?.currency??'',origin:'projected',accountingProfit,additions,partialAdjustedResult:contextReady&&rows.length>0&&rows.every(r=>r.status==='calculated')&&accountingProfit?addFractions([accountingProfit,additions]):null,taxableProfit:profit?.taxableProfit??null,taxableBase:base?.taxableBase??null,tax:null,rows,pending,source:irpcNormalSource+(periodsFx?' '+periodsFx.rule:b.schedules?' Mapas complementares: CIRPC18/21/22/24/36; Decreto72/2013 art3–7. EP: CIRPC15(1)c, apenas resultado imputável ao estabelecimento no regime geral, anual2026/MZN.':'')};
|
||||
if(b.forms){model.formPreparation=prepareNormalForms(b.forms,model,source);model.pending.push(...model.formPreparation.pending.map(p=>'Preparação M20/M22: '+p));}
|
||||
return model;
|
||||
}
|
||||
export function recordNormal(d:Record<string,unknown>,b:NormalBook,now=new Date().toISOString()):NormalBook{return{...b,history:b.result?[...(b.history??[]),structuredClone(b.result)]:b.history??[],result:{key:key(d,b),createdAt:now,model:calculateNormal(d,b)}};}
|
||||
const money=(v:FiscalFraction|null)=>v?fractionPresentation(v).decimal+' MZN':'Indeterminado';
|
||||
function normalBaseReport(m:NormalBaseModel):Array<[string,string,string,string]>{return[['IRPC normal — L6','Matéria colectável — deduções/perdas',`Lucro tributável:${money(m.taxableProfit)};perdas propostas elegíveis:${money(m.requestedLosses)};perdas deduzidas nesta simulação:${money(m.deductedLosses)};benefícios deduzidos:${money(m.benefitDeduction)};matéria colectável simulada:${money(m.taxableBase)};imposto a pagar:Indeterminado. ${m.pending.join(' | ')}`,`NUIT:${m.input.subjectNuit};${m.input.identityReference};método:${m.input.method==='direct'?'Declarativo/directo':m.input.method==='indirect'?'Indirecto':'Por identificar'};${m.input.methodReference};perdas:${m.input.lossesState==='absent'?'Ausência declarada':m.input.lossesState==='known'?'Conhecidas':'Informação em falta'};${m.input.lossesReference};objecto:${m.input.unchangedObject==='yes'?'Sem alteração declarada':m.input.unchangedObject==='no'?'Alteração declarada':'Por esclarecer'};${m.input.objectReference};${m.source}`],['IRPC normal — L6','Benefícios à matéria colectável',`Estado:${m.input.benefits.state==='known'?'Valor conhecido':m.input.benefits.state==='absent'?'Ausência declarada':'Informação em falta'};montante original:${m.input.benefits.amount||'Não preenchido'};dedução:${money(m.benefitDeduction)};não inclui créditos à colecta ou retenções sofridas.`,m.input.benefits.reference],...m.losses.map(l=>['IRPC normal — L6','Perda fiscal de '+l.year,`Estado:${l.status==='eligible'?'Elegível neste recorte':l.status==='excluded'?'Excluída neste exercício':'Indeterminada'};apurada:${l.assessed};utilizada antes:${l.previouslyUsed};saldo:${money(l.available)};utilização proposta:${l.requested};dedução nesta simulação:${money(l.deducted)};saldo aritmético posterior:${money(l.remaining)};último exercício ordinário:${l.lastOrdinaryYear??'Por determinar'};${l.reason}`,`NUIT:${l.subjectNuit};${l.reference};sem consumo do saldo oficial nem reporte automático.`]as[string,string,string,string])];}
|
||||
function normalAssessmentReport(m:NormalAssessmentModel):Array<[string,string,string,string]>{return[['IRPC normal — L6','Colecta — regime normal',`Matéria colectável:${money(m.taxableBase)};taxa:${m.rate?m.rate.numerator+'%':'Por identificar'};colecta simulada:${money(m.colecta)};${m.classification};${m.centExact===false?'Valor racional exacto com fracção de cêntimo;sem arredondamento fiscal aplicado.':''}${m.pending.join(' | ')}`,`Exercício:${m.period.year};período:${m.period.from}–${m.period.to};${m.input.reference};${m.rule};${m.source}`],['IRPC normal — L6','Saldo final — âmbito L8','A pagar:Indeterminado;a recuperar:Indeterminado. '+m.finalBalance.reason,'Sequência:lucro tributável → matéria colectável → colecta → saldo final;retenções,pagamentos e créditos anuais reservados aL8.']];}
|
||||
export function normalReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=normalBook(d),r=b.result;if(!r)return[];const m=r.model;return[['IRPC normal — L6','Ajustamentos ao resultado financeiro',`Exercício:${m.year};resultado contabilístico antes de imposto:${money(m.accountingProfit)};acréscimos demonstrados:${money(m.additions)};resultado parcial ajustado:${money(m.partialAdjustedResult)};${m.profit?'lucro tributável simulado:'+money(m.taxableProfit)+(m.base?';matéria colectável simulada:'+money(m.taxableBase)+(m.assessment?';colecta simulada:'+money(m.colecta??null)+';saldo final a pagar ou recuperar:Indeterminado (L8).':';imposto a pagar:Indeterminado.'):';matéria colectável/imposto:Indeterminados.'):'lucro tributável/matéria colectável/imposto:Indeterminados.'} Projecção com pressupostos;${m.pending.join(' | ')}`,`${normalCurrent(d,b)?'Entradas correspondentes':'Histórico;origem ou entradas alteradas'};${r.createdAt};${m.source}`],...(m.costs?normalCostReport(m.costs):[]),...(m.base?normalBaseReport(m.base):[]),...(m.assessment?normalAssessmentReport(m.assessment):[]),...(m.schedules?[['IRPC normal — L6','Mapas complementares',`Efeito líquido adicional:${money(m.schedules.net)};perda fiscal do próprio exercício:${money(m.currentLoss??null)};sem reporte ou consumo automático. ${m.schedules.pending.join(' | ')}`,m.schedules.source]as[string,string,string,string],...m.schedules.effects.map(e=>{const i=m.schedules!.input.rows.find(r=>r.id===e.id)!;return['IRPC normal — L6',i.document+'/'+i.line,`Montante:${money(e.amount)};efeito:${money(e.effect)};${e.reason}`,`${i.reference};exercício de imputação:${i.fiscalYear||'Não aplicável'}`]as[string,string,string,string];}),...m.schedules.assets.map(a=>['IRPC normal — L6',a.description,`Depreciação contabilística:${money(a.accountingValue)};quota fiscal:${money(a.fiscal)};acréscimo:${money(a.adjustment)};${a.reason}`,`${a.tableReference};${a.reference}`]as[string,string,string,string]),['IRPC normal — L6','Juros financeiros ordinários',`Remanescente aceite no recorte:${money(m.schedules.interest)};não corresponde a juros de mora/compensatórios L5.`,m.schedules.input.interest.reference]as[string,string,string,string]]:[]),...(m.schedules?[...normalFormFields(m).fields.map(f=>['IRPC normal — L6','M22 campo '+f.code+' — '+f.label,`${f.display};exacto:${f.value?f.value.numerator+'/'+f.value.denominator:'Indeterminado'};transcrição:${f.transcriptionReady?'Preparação parcial com valor exacto ao cêntimo':'Pendente ou sem aplicação'};${f.reason}`,normalFormFields(m).source]as[string,string,string,string]),...normalFormFields(m).m20.fields.map(f=>['IRPC normal — L6','M20 A.1 — conta '+f.account+' — '+f.label,`${f.display};representação em meticais inteiros:${f.transcriptionReady?'Exacta':'Pendente'};${f.reason}`,`${f.reference};${normalFormFields(m).m20.source}`]as[string,string,string,string]),['IRPC normal — L6','M20 — correspondência',normalFormFields(m).m20.reason,normalFormFields(m).m20.source]as[string,string,string,string]]:[]),...m.rows.map(r=>['IRPC normal — L6',r.document+'/'+r.line,`${({calculated:'Calculado neste recorte',indeterminate:'Indeterminado',outside_scope:'Fora deste recorte'})[r.status]};montante:${r.amount};acréscimo:${money(r.adjustment)};${r.reason}`,`Origem:${({revenue:'Receitas',cost_of_sales:'Custo das vendas',operating_expenses:'Despesas operacionais',personnel:'Pessoal',depreciation:'Depreciação contabilística',interest_expense:'Juros reconhecidos'}as Record<string,string>)[r.sourceCode]??'Por corresponder'};incluído:${r.included==='yes'?'Sim':r.included==='no'?'Não':'Por demonstrar'};suporte:${r.reference}`]as[string,string,string,string]),...(m.profit?[...m.profit.lines.map(l=>['IRPC normal — L6','Conferência: '+l.label,`Estado:${l.state==='known'?'Valor conhecido':l.state==='absent'?'Ausência declarada':'Informação em falta'};origem:${l.amount};acréscimos:${money(l.addedBack)};remanescente:${money(l.remainder)};${l.reason}`,l.reference]as[string,string,string,string]),...m.profit.topics.map(t=>['IRPC normal — L6',t.label,`Estado:${t.state==='known'?'Valor conhecido':t.state==='absent'?'Ausência declarada':'Informação em falta'};montante:${money(t.value)};${t.reason}`,t.reference]as[string,string,string,string]),['IRPC normal — L6','Âmbito da conferência',m.profit.input.completenessReference,m.profit.source]as[string,string,string,string]]:[])];}catch(e){return[['IRPC normal — L6','Não recuperável',String(e),'Originais conservados.']];}}
|
||||
export function normalReportRows(d:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=normalBook(d),r=b.result;if(!r)return[];const m=r.model;return[['IRPC normal — L6','Ajustamentos ao resultado financeiro',`Exercício:${m.year};resultado contabilístico antes de imposto:${money(m.accountingProfit)};acréscimos demonstrados:${money(m.additions)};resultado parcial ajustado:${money(m.partialAdjustedResult)};${m.profit?'lucro tributável simulado:'+money(m.taxableProfit)+(m.base?';matéria colectável simulada:'+money(m.taxableBase)+(m.assessment?';colecta simulada:'+money(m.colecta??null)+';saldo final a pagar ou recuperar:Indeterminado (L8).':';imposto a pagar:Indeterminado.'):';matéria colectável/imposto:Indeterminados.'):'lucro tributável/matéria colectável/imposto:Indeterminados.'} Projecção com pressupostos;${m.pending.join(' | ')}`,`${normalCurrent(d,b)?'Entradas correspondentes':'Histórico;origem ou entradas alteradas'};${r.createdAt};${m.source}`],...(m.formPreparation?normalFormPreparationReport(m.formPreparation):[]),...(m.periodsFx?normalPeriodFxReport(m.periodsFx):[]),...(m.depreciation?normalDepreciationReport(m.depreciation):[]),...(m.costs?normalCostReport(m.costs):[]),...(m.base?normalBaseReport(m.base):[]),...(m.assessment?normalAssessmentReport(m.assessment):[]),...(m.schedules?[['IRPC normal — L6','Mapas complementares',`Efeito líquido adicional:${money(m.schedules.net)};perda fiscal do próprio exercício:${money(m.currentLoss??null)};sem reporte ou consumo automático. ${m.schedules.pending.join(' | ')}`,m.schedules.source]as[string,string,string,string],...m.schedules.effects.map(e=>{const i=m.schedules!.input.rows.find(r=>r.id===e.id)!;return['IRPC normal — L6',i.document+'/'+i.line,`Montante:${money(e.amount)};efeito:${money(e.effect)};${e.reason}`,`${i.reference};exercício de imputação:${i.fiscalYear||'Não aplicável'}`]as[string,string,string,string];}),...m.schedules.assets.map(a=>['IRPC normal — L6',a.description,`Depreciação contabilística:${money(a.accountingValue)};quota fiscal:${money(a.fiscal)};acréscimo:${money(a.adjustment)};${a.reason}`,`${a.tableReference};${a.reference}`]as[string,string,string,string]),['IRPC normal — L6','Juros financeiros ordinários',`Remanescente aceite no recorte:${money(m.schedules.interest)};não corresponde a juros de mora/compensatórios L5.`,m.schedules.input.interest.reference]as[string,string,string,string]]:[]),...(m.schedules&&!m.formPreparation?[...normalFormFields(m).fields.map(f=>['IRPC normal — L6','M22 campo '+f.code+' — '+f.label,`${f.display};exacto:${f.value?f.value.numerator+'/'+f.value.denominator:'Indeterminado'};transcrição:${f.transcriptionReady?'Preparação parcial com valor exacto ao cêntimo':'Pendente ou sem aplicação'};${f.reason}`,normalFormFields(m).source]as[string,string,string,string]),...normalFormFields(m).m20.fields.map(f=>['IRPC normal — L6','M20 A.1 — conta '+f.account+' — '+f.label,`${f.display};representação em meticais inteiros:${f.transcriptionReady?'Exacta':'Pendente'};${f.reason}`,`${f.reference};${normalFormFields(m).m20.source}`]as[string,string,string,string]),['IRPC normal — L6','M20 — correspondência',normalFormFields(m).m20.reason,normalFormFields(m).m20.source]as[string,string,string,string]]:[]),...m.rows.map(r=>['IRPC normal — L6',r.document+'/'+r.line,`${({calculated:'Calculado neste recorte',indeterminate:'Indeterminado',outside_scope:'Fora deste recorte'})[r.status]};montante:${r.amount};acréscimo:${money(r.adjustment)};${r.reason}`,`Origem:${({revenue:'Receitas',cost_of_sales:'Custo das vendas',operating_expenses:'Despesas operacionais',personnel:'Pessoal',depreciation:'Depreciação contabilística',interest_expense:'Juros reconhecidos'}as Record<string,string>)[r.sourceCode]??'Por corresponder'};incluído:${r.included==='yes'?'Sim':r.included==='no'?'Não':'Por demonstrar'};suporte:${r.reference}`]as[string,string,string,string]),...(m.profit?[...m.profit.lines.map(l=>['IRPC normal — L6','Conferência: '+l.label,`Estado:${l.state==='known'?'Valor conhecido':l.state==='absent'?'Ausência declarada':'Informação em falta'};origem:${l.amount};acréscimos:${money(l.addedBack)};remanescente:${money(l.remainder)};${l.reason}`,l.reference]as[string,string,string,string]),...m.profit.topics.map(t=>['IRPC normal — L6',t.label,`Estado:${t.state==='known'?'Valor conhecido':t.state==='absent'?'Ausência declarada':'Informação em falta'};montante:${money(t.value)};${t.reason}`,t.reference]as[string,string,string,string]),['IRPC normal — L6','Âmbito da conferência',m.profit.input.completenessReference,m.profit.source]as[string,string,string,string]]:[])];}catch(e){return[['IRPC normal — L6','Não recuperável',String(e),'Originais conservados.']];}}
|
||||
export function normalHtml(d:Record<string,unknown>){const b=normalBook(d);if(!normalCurrent(d,b))throw Error('Calcule e guarde as entradas actuais antes de exportar.');const esc=(s:string)=>s.replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]!));return'<!doctype html><html lang="pt"><meta charset="utf-8"><title>IRPC normal — ajustamentos</title><h1>L6 — mapa parcial de ajustamentos</h1><p>Projecção pelos pressupostos do plano. Não é demonstração contabilística,liquidação ou Modelo20/22.</p>'+normalReportRows(d).map(r=>'<section><h2>'+esc(r[1])+'</h2><p>'+esc(r[2])+'</p><small>'+esc(r[3])+'</small></section>').join('')+'</html>';}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?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 IrpcNormalDepreciationDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_depreciation_parameters_quotas_prior_balances_and_results_recover_without_rewriting_legacy_assets():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()]));$id=null;$etag=null;
|
||||
$expected=['months'=>['6780','4480','7168'],'declining'=>['6630','4330','6928'],'prior'=>['6730','4430','7088'],'specific'=>['13535','8935','7148']];
|
||||
foreach(['months','declining','prior','specific','missing']as$case){
|
||||
$d=json_decode(file_get_contents(base_path('outputs/irpc-l6-depreciation/'.$case.'.json')),true,flags:JSON_THROW_ON_ERROR);$p=['person_type'=>$d['person_type']];unset($d['person_type']);
|
||||
if($id===null)$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>$p,'economic_snapshot'=>$d],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');else$this->putJson('/api/v1/simulation-drafts/'.$id,['personal_snapshot'=>$p,'economic_snapshot'=>$d],['If-Match'=>$etag])->assertOk();
|
||||
$r=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();$etag=$r->headers->get('ETag');$back=$r->json('data.economic_snapshot');$this->assertSame($d['modelo_a_snapshot'],$back['modelo_a_snapshot']);$this->assertSame($d['business_plan_snapshot'],$back['business_plan_snapshot']);
|
||||
$s=json_decode($back['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$b=$s['irpcNormal'][$d['context_opportunity']];$m=$b['result']['model'];$this->assertSame('IRPC-normal-adjustments/7',$m['version']);$this->assertSame($b['depreciation'],$m['depreciation']['input']);$this->assertSame($b['schedules']['assets'],$m['schedules']['input']['assets']);$this->assertSame([],$m['schedules']['assets']);$this->assertContains('IRPC-normal-adjustments/6',array_column(array_column($b['history'],'model'),'version'));$this->assertArrayHasKey('irpcWithholdings',$s);$this->assertNull($m['tax']);$this->assertNull($m['assessment']['finalBalance']['payable']);
|
||||
if($case==='missing'){$this->assertNull($m['depreciation']['adjustment']);$this->assertNull($m['taxableProfit']);$this->assertNull($m['colecta']);$this->assertSame('40',$m['costs']['rows'][1]['adjustment']['numerator']);}
|
||||
else{[$profit,$base,$colecta]=$expected[$case];$this->assertSame($profit,$m['taxableProfit']['numerator']);$this->assertSame($base,$m['taxableBase']['numerator']);$this->assertSame(['numerator'=>$colecta,'denominator'=>'5'],$m['colecta']);$this->assertSame([],$m['depreciation']['pending']);if($case==='prior'){$this->assertSame('20',$m['depreciation']['assets'][0]['priorUsed']['numerator']);$this->assertSame('40',$b['depreciation']['assets'][0]['priorAvailable']);}}
|
||||
file_put_contents(base_path('outputs/irpc-l6-depreciation/recovered-'.$case.'.json'),json_encode($back,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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 IrpcNormalFormPreparationDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_form_classification_exact_values_and_unmapped_fields_recover_without_changing_calculation_or_history():void
|
||||
{
|
||||
$db=DB::selectOne('select current_database() as name,inet_server_port() as port');$this->assertSame('simulator_fiscal_delta_20261021_test',$db->name);$this->assertSame(55441,(int)$db->port);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);$this->actingAs(User::factory()->create(['email_verified_at'=>now()]));$id=null;$etag=null;
|
||||
foreach(['mapped','fractional','missing','unmapped','booktax']as$case){
|
||||
$d=json_decode(file_get_contents(base_path('outputs/irpc-l6-values-forms/'.$case.'.json')),true,flags:JSON_THROW_ON_ERROR);$personal=['person_type'=>$d['person_type']];unset($d['person_type']);
|
||||
if($id===null)$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>$personal,'economic_snapshot'=>$d],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');else$this->putJson('/api/v1/simulation-drafts/'.$id,['personal_snapshot'=>$personal,'economic_snapshot'=>$d],['If-Match'=>$etag])->assertOk();
|
||||
$r=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();$etag=$r->headers->get('ETag');$back=$r->json('data.economic_snapshot');$this->assertSame($d['business_plan_snapshot'],$back['business_plan_snapshot']);$this->assertSame($d['modelo_a_snapshot'],$back['modelo_a_snapshot']);
|
||||
$s=json_decode($back['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$b=$s['irpcNormal'][$d['context_opportunity']];$m=$b['result']['model'];$f=$m['formPreparation'];$fields=array_column($f['fields'],null,'code');$this->assertSame('IRPC-normal-adjustments/9',$m['version']);$this->assertSame($b['forms'],$f['input']);$this->assertContains('IRPC-normal-adjustments/8',array_column(array_column($b['history'],'model'),'version'));$this->assertArrayHasKey('irpcWithholdings',$s);$this->assertSame('3130',$m['taxableProfit']['numerator']);$this->assertSame($m['taxableProfit'],$m['taxableBase']);$this->assertSame(['numerator'=>'5008','denominator'=>'5'],$m['colecta']);$this->assertNull($m['assessment']['finalBalance']['payable']);$this->assertNull($fields['309–316']['value']);$this->assertFalse($f['transport']['ready']);$this->assertSame($m['colecta'],$fields['300']['value']);
|
||||
if($case==='missing'){$this->assertNull($f['netResult']);$this->assertNull($fields['200']['value']);$this->assertSame($m['taxableProfit'],$fields['260']['value']);}
|
||||
elseif($case==='unmapped'||$case==='booktax'){$this->assertNull($fields['258']['value']);$this->assertNotEmpty($f['unmapped']);$this->assertFalse($f['reconciliation']['matches']);}
|
||||
else{$this->assertSame('3120',$f['netResult']['numerator']);$this->assertTrue($f['reconciliation']['matches']);$this->assertSame('0',$fields['213']['value']['numerator']);}
|
||||
if($case==='booktax'){$this->assertSame('2800',$f['netResult']['numerator']);$this->assertSame('0',$fields['213']['value']['numerator']);}
|
||||
if($case==='fractional'){$parts=array_column($f['m20'],null,'account');$this->assertSame(['numerator'=>'601','denominator'=>'2'],$parts['6.3']['value']);$this->assertFalse($parts['6.3']['representation']['ready']);$this->assertNull($parts['6.3']['representation']['represented']);}
|
||||
file_put_contents(base_path('outputs/irpc-l6-values-forms/recovered-'.$case.'.json'),json_encode($back,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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 IrpcNormalPeriodFxDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_period_and_currency_source_evidence_recover_with_original_plan_and_histories():void
|
||||
{
|
||||
$db=DB::selectOne('select current_database() as name,inet_server_port() as port');$this->assertSame('simulator_fiscal_delta_20261021_test',$db->name);$this->assertSame(55441,(int)$db->port);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);$this->actingAs(User::factory()->create(['email_verified_at'=>now()]));$id=null;$etag=null;
|
||||
foreach(['short','fx','missing','loss']as$case){
|
||||
$d=json_decode(file_get_contents(base_path('outputs/irpc-l6-periods-fx/'.$case.'.json')),true,flags:JSON_THROW_ON_ERROR);$personal=['person_type'=>$d['person_type']];unset($d['person_type']);
|
||||
if($id===null)$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>$personal,'economic_snapshot'=>$d],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');else$this->putJson('/api/v1/simulation-drafts/'.$id,['personal_snapshot'=>$personal,'economic_snapshot'=>$d],['If-Match'=>$etag])->assertOk();
|
||||
$r=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk();$etag=$r->headers->get('ETag');$back=$r->json('data.economic_snapshot');$this->assertSame($d['business_plan_snapshot'],$back['business_plan_snapshot']);$this->assertSame($d['modelo_a_snapshot'],$back['modelo_a_snapshot']);
|
||||
$s=json_decode($back['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$b=$s['irpcNormal'][$d['context_opportunity']];$m=$b['result']['model'];$this->assertSame('IRPC-normal-adjustments/8',$m['version']);$this->assertSame($b['periodsFx'],$m['periodsFx']['input']);$this->assertContains('IRPC-normal-adjustments/7',array_column(array_column($b['history'],'model'),'version'));$this->assertArrayHasKey('irpcWithholdings',$s);$this->assertNull($m['assessment']['finalBalance']['payable']);$this->assertNull($m['tax']);
|
||||
if($case==='short'||$case==='fx'){$this->assertSame($case==='short'?'3130':'200320',$m['taxableProfit']['numerator']);$this->assertSame($m['taxableProfit'],$m['taxableBase']);$this->assertSame(['numerator'=>$case==='short'?'5008':'320512','denominator'=>'5'],$m['colecta']);$this->assertSame(6,$m['periodsFx']['months']);$this->assertSame([],$m['periodsFx']['pending']);}
|
||||
else{$this->assertNull($m['colecta']);if($case==='loss'){$this->assertSame('3130',$m['taxableProfit']['numerator']);$this->assertNull($m['taxableBase']);}else{$this->assertNull($m['accountingProfit']);$this->assertNotEmpty($m['periodsFx']['pending']);}}
|
||||
file_put_contents(base_path('outputs/irpc-l6-periods-fx/recovered-'.$case.'.json'),json_encode($back,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
import test from'node:test';import assert from'node:assert/strict';import fs from'node:fs';
|
||||
import{normalBook,normalCurrent,calculateNormal,recordNormal,saveNormalBook,normalHtml,normalReportRows,normalFinancialSource}from'../../resources/js/lib/irpc-normal.ts';
|
||||
import{newNormalDepreciation,newDepreciationAsset,reuseNormalAssets,calculateNormalDepreciation,depreciationCatalog}from'../../resources/js/lib/irpc-normal-depreciation.ts';
|
||||
import{createProjectDossierReport}from'../../resources/js/lib/project-dossier-report.ts';
|
||||
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-costs-preserved.json',import.meta.url),'utf8')),val=f=>f?Number(f.numerator)/Number(f.denominator):null;
|
||||
function asset(p={}){return{...newDepreciationAsset('server'),assetKey:'asset-server',description:'Servidor QA',document:'QA-activo-servidor',line:'1',category:'server',valuation:'historical_new',specificRate:'no',method:'constant',rate:'20',timing:'annual',start:'2026-01-01',exitState:'absent',cost:'500',residual:'0',accounting:'120',previousAccounting:'0',previousFiscal:'0',priorAvailable:'0',previousReference:'QA ausência de quotas anteriores demonstrada,ou saldos anteriores conforme documentos',consistentMethod:'yes',conditions:'yes',reference:'QA factura/classificação/base histórica/período documentados,sem reavaliações ou regimes especiais',...p};}
|
||||
function setup(a=asset(),bookExpense=a.accounting){const d=seed(),b=normalBook(d),p=JSON.parse(d.business_plan_snapshot);p.plan.assetDepreciation=bookExpense;d.business_plan_snapshot=JSON.stringify(p);b.depreciation={...newNormalDepreciation(),completenessReference:'QA todos activos/quotas e plano reconciliados no sujeito/exercício2026',assets:[a]};return{d,b};}
|
||||
const model=(a,expense)=>{const{d,b}=setup(a,expense);return calculateNormal(d,b);};
|
||||
test('version7 reuses existing server and replaces20 legacy adjustment exactly once without changing financial result or old maps',()=>{const{d,b}=setup(),m=calculateNormal(d,b);assert.equal(val(m.accountingProfit),6240);assert.equal(val(m.depreciation.adjustment),20);assert.equal(val(m.taxableProfit),6730);assert.equal(val(m.taxableBase),4430);assert.equal(val(m.colecta),1417.6);assert.equal(m.version,'IRPC-normal-adjustments/7');assert.equal(val(m.schedules.net),520);assert.deepEqual(m.schedules.assets,[]);assert.deepEqual(m.schedules.input.assets,b.schedules.assets);assert.equal(m.tax,null);});
|
||||
test('documented first-year duodecimos include July and exclude month of exit',()=>{let m=model(asset({start:'2026-07-15',timing:'months'}));assert.equal(m.depreciation.assets[0].months,6);assert.equal(val(m.depreciation.assets[0].deduction),50);assert.equal(val(m.taxableProfit),6780);assert.equal(val(m.taxableBase),4480);assert.equal(val(m.colecta),1433.6);m=model(asset({start:'2026-07-15',timing:'months',exitState:'known',exitDate:'2026-10-15'}));assert.equal(m.depreciation.assets[0].months,3);assert.equal(val(m.depreciation.assets[0].deduction),25);assert.equal(val(m.colecta),1441.6);});
|
||||
test('annual first-year option remains distinct from duodecimos; contradictory dates and unknown event are not defaulted',()=>{assert.equal(val(model(asset({start:'2026-07-15'})).depreciation.assets[0].deduction),100);for(const p of[{exitState:''},{exitState:'known',exitDate:''},{exitState:'absent',exitDate:'2026-10-15'},{start:'2026-02-31'},{exitState:'known',exitDate:'2025-12-31'}])assert.equal(model(asset(p)).colecta,null);});
|
||||
test('declining method uses fiscal remaining basis and coefficient capped by minimum life; result differs from accounting depreciation',()=>{const m=model(asset({method:'declining',factor:'2',newTangible:'yes',switchToStraight:'no',accounting:'240'}));assert.equal(val(m.depreciation.assets[0].quota),200);assert.equal(val(m.depreciation.assets[0].deduction),200);assert.equal(val(m.depreciation.adjustment),40);assert.equal(val(m.taxableProfit),6630);assert.equal(val(m.taxableBase),4330);assert.equal(val(m.colecta),1385.6);for(const p of[{factor:'2.1'},{newTangible:'no'},{category:'furniture',factor:'2'},{category:'commercial_building',factor:'2'}])assert.equal(model(asset({method:'declining',factor:'2',newTangible:'yes',switchToStraight:'no',...p})).colecta,null);});
|
||||
test('coefficient bands1.5/2/2.5 and half-to-maximum constant-rate recorte are applied by catalog life',()=>{const c=model(asset({category:'computer',method:'declining',factor:'1.5',newTangible:'yes',switchToStraight:'no',accounting:'200'}));assert.equal(val(c.depreciation.assets[0].quota),187.5);assert.equal(model(asset({category:'computer',method:'declining',factor:'2',newTangible:'yes',switchToStraight:'no'})).colecta,null);const m=model(asset({category:'laundry_machine',specificRate:'yes',method:'declining',factor:'2.5',newTangible:'yes',switchToStraight:'no',accounting:'200'}));assert.equal(val(m.depreciation.assets[0].quota),156.25);assert.equal(val(model(asset({rate:'10'})).depreciation.assets[0].deduction),50);assert.equal(model(asset({rate:'9.99'})).colecta,null);});
|
||||
test('previous taxable accounting quotas are only recovered within current quota and documented available balance',()=>{const m=model(asset({start:'2025-01-01',accounting:'80',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40'}));assert.equal(val(m.depreciation.assets[0].deduction),100);assert.equal(val(m.depreciation.assets[0].priorUsed),20);assert.equal(val(m.depreciation.assets[0].priorRemaining),20);assert.equal(val(m.depreciation.adjustment),-20);assert.equal(val(m.taxableProfit),6730);assert.equal(m.depreciation.input.assets[0].priorAvailable,'40');for(const p of[{priorAvailable:''},{priorAvailable:'41'},{previousFiscal:'121'},{previousReference:''}])assert.equal(model(asset({start:'2025-01-01',accounting:'80',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40',...p})).colecta,null);});
|
||||
test('unrecorded quotas are not invented as a deduction; zero previous balance remains explicit',()=>{const m=model(asset({accounting:'0'}));assert.equal(val(m.depreciation.assets[0].quota),100);assert.equal(val(m.depreciation.assets[0].deduction),0);assert.equal(val(m.depreciation.adjustment),0);assert.equal(model(asset({previousAccounting:''})).colecta,null);});
|
||||
test('article8-2 option requires documented past condition and remaining minimum life,not an automatic switch',()=>{const a=asset({start:'2022-01-01',method:'declining',factor:'2',newTangible:'yes',switchToStraight:'yes',priorFullQuotas:'yes',previousAccounting:'500',previousFiscal:'420',priorAvailable:'80',accounting:'0'}),m=model(a);assert.equal(val(m.depreciation.assets[0].quota),80);assert.equal(val(m.depreciation.assets[0].deduction),80);assert.equal(val(m.depreciation.assets[0].priorUsed),80);assert.equal(model({...a,priorFullQuotas:''}).colecta,null);assert.equal(model({...a,priorFullQuotas:'no'}).colecta,null);});
|
||||
test('specific tableI prevails over generic tableII and known codes are never replaced with a free rate',()=>{const m=model(asset({category:'hotel_decoration',specificRate:'yes',rate:'20'}));assert.equal(m.depreciation.assets[0].catalog.table,'I');assert.equal(m.depreciation.assets[0].catalog.code,'84.2/B');assert.equal(val(m.depreciation.assets[0].quota),100);assert.equal(model(asset({specificRate:'yes'})).colecta,null);assert.equal(model(asset({category:'other'})).colecta,null);assert.equal(model(asset({category:'hotel_decoration',specificRate:'no'})).colecta,null);assert.equal(model(asset({valuation:'used'})).colecta,null);assert.equal(model(asset({valuation:''})).colecta,null);});
|
||||
test('commercial building excludes separately known land; zero land is not presumed when missing',()=>{const m=model(asset({category:'commercial_building',rate:'2',cost:'1000',land:'200',accounting:'30'}));assert.equal(val(m.depreciation.assets[0].basis),800);assert.equal(val(m.depreciation.assets[0].deduction),16);assert.equal(val(m.depreciation.adjustment),14);assert.equal(model(asset({category:'commercial_building',rate:'2',land:''})).colecta,null);});
|
||||
test('vehicle ceiling800000 and non-eligible accounting share are separate from prior recoverable quotas',()=>{let m=model(asset({category:'passenger_vehicle',rate:'25',vehicleException:'no',cost:'900000',accounting:'225000'}));assert.equal(val(m.depreciation.assets[0].taxBasis),800000);assert.equal(val(m.depreciation.assets[0].deduction),200000);assert.equal(val(m.depreciation.adjustment),25000);m=model(asset({category:'passenger_vehicle',rate:'25',vehicleException:'no',cost:'900000',accounting:'9000'}));assert.equal(val(m.depreciation.assets[0].eligibleCurrent),8000);assert.equal(val(m.depreciation.assets[0].deduction),8000);assert.equal(model(asset({category:'passenger_vehicle',rate:'25',vehicleException:''})).colecta,null);assert.equal(model(asset({category:'passenger_vehicle',rate:'25',vehicleException:'no',cost:'900000',start:'2025-01-01',previousAccounting:'225000',previousFiscal:'200000',priorAvailable:'25000'})).colecta,null);});
|
||||
test('low value is an explicit individual-asset option,not automatic just because cost is below20000',()=>{const m=model(asset({method:'low_value',individual:'yes',accounting:'500'}));assert.equal(val(m.depreciation.assets[0].deduction),500);assert.equal(val(m.colecta),1289.6);assert.equal(model(asset({method:'low_value',individual:'no'})).colecta,null);assert.equal(model(asset({category:'other',method:'low_value',individual:'yes'})).colecta,null);assert.equal(model(asset({method:'low_value',individual:'yes',cost:'20000.01'})).colecta,null);});
|
||||
test('software conditions and explicit nondepreciable category have distinct outcomes',()=>{assert.equal(model(asset({category:'software',rate:'25',limitedRights:''})).colecta,null);assert.equal(val(model(asset({category:'software',rate:'25',limitedRights:'yes'})).depreciation.assets[0].deduction),120);const m=model(asset({category:'nondepreciable',start:'',method:'',previousAccounting:''}));assert.equal(val(m.depreciation.assets[0].deduction),0);assert.equal(val(m.depreciation.adjustment),120);});
|
||||
test('maximum life exhaustion excludes current quota; first-use future period is known nondeduction',()=>{const m=model(asset({start:'2014-01-01',previousAccounting:'400',previousFiscal:'400',priorAvailable:'0'}));assert.equal(val(m.depreciation.assets[0].deduction),0);assert.match(m.depreciation.assets[0].reason,/vida útil máxima esgotada/);assert.equal(val(model(asset({start:'2027-01-01'})).depreciation.assets[0].deduction),0);assert.equal(model(asset({category:'computer',rate:'25',start:'2018-07-01'})).colecta,null);const clipped=model(asset({category:'computer',rate:'25',start:'2018-07-01',timing:'months'}));assert.equal(clipped.depreciation.assets[0].months,6);});
|
||||
test('duplicate asset/document and unreconciled accounting cannot create a second depreciation adjustment',()=>{const{d,b}=setup();b.depreciation.assets.push({...asset({id:'second'}),accounting:'0'});assert.equal(calculateNormal(d,b).colecta,null);const c=setup();c.b.depreciation.assets[0].document=c.b.rows[0].document;c.b.depreciation.assets[0].line=c.b.rows[0].line;assert.equal(calculateNormal(c.d,c.b).colecta,null);assert.equal(model(asset(),'121').colecta,null);});
|
||||
test('one unknown asset keeps independent quotas visible but total profit/base/colecta dependent on reconciliation remains unknown',()=>{const{d,b}=setup(asset({accounting:'60'}),'120');b.depreciation.assets.push(asset({id:'second',assetKey:'second',document:'QA-second',category:'other',accounting:'60'}));const m=calculateNormal(d,b);assert.equal(val(m.depreciation.assets[0].deduction),60);assert.equal(m.depreciation.assets[1].deduction,null);assert.equal(m.taxableProfit,null);assert.equal(m.taxableBase,null);assert.equal(m.colecta,null);});
|
||||
test('reuse copies declared values without inventing previous accounting/carry data; installed6 stays current and histories survive7',()=>{const d=seed(),old=normalBook(d);assert.equal(normalCurrent(d,old),true);assert.equal(old.result.model.version,'IRPC-normal-adjustments/6');const reused=reuseNormalAssets(old.schedules.assets);assert.equal(reused.assets[0].cost,old.schedules.assets[0].cost);assert.equal(reused.assets[0].previousAccounting,'');assert.equal(reused.assets[0].priorAvailable,'');const{d:next,b}=setup(asset({start:'2026-07-15',timing:'months'})),before=JSON.parse(next.modelo_a_snapshot);const oldAssets=structuredClone(b.schedules.assets);next.modelo_a_snapshot=saveNormalBook(next,recordNormal(next,b,'2026-10-26T10:00:00+02:00'));const back=normalBook(JSON.parse(JSON.stringify(next)));assert.equal(normalCurrent(next,back),true);assert.deepEqual(back.history.at(-1),old.result);assert.deepEqual(back.schedules.assets,oldAssets);assert.deepEqual(JSON.parse(next.modelo_a_snapshot).books,before.books);assert.deepEqual(JSON.parse(next.modelo_a_snapshot).irpcWithholdings,before.irpcWithholdings);assert.match(normalHtml(next),/1433.60/);assert.match(JSON.stringify(createProjectDossierReport(next,{selectedSections:new Set(['finance'])})),/quota anterior/);const changed=structuredClone(back);changed.depreciation.assets[0].priorAvailable='1';next.modelo_a_snapshot=saveNormalBook(next,changed);assert.equal(normalCurrent(next,normalBook(next)),false);assert.throws(()=>normalHtml(next));});
|
||||
test('recoverable scenarios generate five API/report proofs without recalculating accepted predecessor suites',()=>{const cases={months:setup(asset({start:'2026-07-15',timing:'months'})),declining:setup(asset({method:'declining',factor:'2',newTangible:'yes',switchToStraight:'no',accounting:'240'})),prior:setup(asset({start:'2025-01-01',accounting:'80',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40'})),specific:setup(asset({category:'laundry_machine',specificRate:'yes',rate:'12.5'})),missing:setup(asset({previousAccounting:''}))};fs.mkdirSync('outputs/irpc-l6-depreciation',{recursive:true});for(const[name,{d,b}]of Object.entries(cases)){d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T10:00:00+02:00'));fs.writeFileSync('outputs/irpc-l6-depreciation/'+name+'.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-depreciation/'+name+'.html',normalHtml(d));}assert.equal(val(normalBook(cases.specific.d).result.model.colecta),1429.6);});
|
||||
@@ -0,0 +1,25 @@
|
||||
import test from'node:test';import assert from'node:assert/strict';import fs from'node:fs';
|
||||
import{normalBook,normalCurrent,calculateNormal,recordNormal,saveNormalBook,normalHtml,normalFinancialSource}from'../../resources/js/lib/irpc-normal.ts';
|
||||
import{newNormalFormPreparation,reuseDirectFormAllocations,normalFormRepresentation,prepareNormalForms}from'../../resources/js/lib/irpc-normal-form-preparation.ts';
|
||||
import{fraction}from'../../resources/js/lib/fiscal-rational.ts';
|
||||
import{createProjectDossierReport}from'../../resources/js/lib/project-dossier-report.ts';
|
||||
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-periods-fx-preserved.json',import.meta.url),'utf8')),val=f=>f?Number(f.numerator)/Number(f.denominator):null;
|
||||
function setup(){const d=seed(),b=normalBook(d);b.forms={...newNormalFormPreparation(),edition:'accepted_pdf_and_m20a1',reference:'QA PDFM22/Q10–12 e M20A.1/Q4;edições inventariadas distintas',completenessReference:'QA contas completas da oportunidade/sujeito no período;sem outros custos ou proveitos omitidos',allocations:[...reuseDirectFormAllocations(normalFinancialSource(d)),{id:'revenue',sourceCode:'revenue',account:'7.2',amount:'6000',reference:'QA prestação de serviços documentada,sem vendas de bens'},{id:'opex',sourceCode:'operating_expenses',account:'6.3',amount:'600',reference:'QA fornecimentos e serviços de terceiros completos no período'}],incomeTax:{state:'absent',amount:'',placement:'',reference:'QA ausência documentada de imposto contabilizado;não inferir gasto da colecta'},reducedRate:{state:'absent',reference:'QA sujeito/período sem parcela de imposto a taxa reduzida'}};return{d,b};}
|
||||
const field=(m,code)=>m.formPreparation.fields.find(f=>f.code===code);
|
||||
test('exact representation keeps signedfraction,approximation,zero,missing and parentheses distinct without fiscal rounding',()=>{for(const f of[fraction(1n,3n),fraction(-1n,300n),fraction(123456789012345678901n,100n)]){const r=normalFormRepresentation(f,'whole');assert.equal(r.exact,f.numerator+'/'+f.denominator+' MZN');assert.equal(r.represented,null);assert.equal(r.ready,false);}assert.match(normalFormRepresentation(fraction(1n,3n)).display,/≈/);assert.equal(normalFormRepresentation(fraction(0n)).represented,'0');assert.equal(normalFormRepresentation(null).display,'Indeterminado');assert.equal(normalFormRepresentation(fraction(-20n),'whole',true).represented,'(20)');assert.match(normalFormRepresentation(fraction(1n,2n)).reason,/exacto ao cêntimo/);});
|
||||
test('optional9 prepares M20 classifications/netresult and M22 mappedadjustments while preserving all existing totals',()=>{const{d,b}=setup(),before=normalBook(d).result.model,m=calculateNormal(d,b);assert.equal(m.version,'IRPC-normal-adjustments/9');for(const k of['accountingProfit','taxableProfit','taxableBase','colecta'])assert.deepEqual(m[k],before[k]);assert.equal(val(m.formPreparation.netResult),3120);assert.equal(val(field(m,'200').value),3120);assert.equal(field(m,'200').transcriptionReady,false);assert.equal(val(field(m,'213').value),0);assert.equal(val(field(m,'206').value),10);assert.equal(val(field(m,'238').value),3130);assert.equal(val(field(m,'258').value),0);assert.equal(m.formPreparation.reconciliation.matches,true);assert.equal(field(m,'259').application,'not_applicable');assert.equal(field(m,'260').transcriptionReady,true);assert.equal(val(field(m,'302').value),1001.6);assert.equal(field(m,'302').representation.ready,true);assert.equal(field(m,'309–316').value,null);assert.equal(m.assessment.finalBalance.payable,null);});
|
||||
test('missingorcontradictory booktax blocks only netresult and its fieldreconciliation; knownnormalprofit/base/colecta preserved',()=>{for(const tax of[{state:'',amount:'',placement:'',reference:''},{state:'absent',amount:'320',placement:'outside',reference:'QA'},{state:'known',amount:'320',placement:'included',reference:'QA'}]){const{d,b}=setup();b.forms.incomeTax=tax;const m=calculateNormal(d,b);assert.equal(m.formPreparation.netResult,null);assert.equal(field(m,'200').value,null);assert.equal(val(field(m,'260').value),3130);assert.equal(val(field(m,'300').value),1001.6);assert.equal(val(m.colecta),1001.6);}});
|
||||
test('incomeTaxalreadyincluded reconciles account8.5 and mappedtax without subtracting it again',()=>{const{d,b}=setup();b.forms.incomeTax={state:'known',amount:'320',placement:'included',reference:'QA imposto reconhecido no plano'};b.forms.allocations.find(a=>a.id==='opex').amount='280';b.forms.allocations.push({id:'tax',sourceCode:'operating_expenses',account:'8.5',amount:'320',reference:'QA imposto já no custo de origem'});b.schedules.rows.push({id:'tax',rule:'tax_own',amount:'320',sourceCode:'operating_expenses',document:'QA-tax',line:'1',reference:'QA imposto sobre lucro já incluído',fiscalYear:'',conditions:'yes'});Object.assign(b.profit.topics.expenseCorrections,{state:'known',amount:'320',reference:'QA corresponde ao imposto contabilizado já no resultado'});const m=calculateNormal(d,b);assert.equal(val(m.formPreparation.netResult),3120);assert.equal(val(field(m,'213').value),0);assert.equal(val(m.taxableProfit),3450);assert.equal(field(m,'238').value,null);assert.equal(m.formPreparation.reconciliation.matches,false);assert.ok(m.formPreparation.unmapped.some(e=>e.reason.includes('213')));});
|
||||
test('incomplete revenueclassification does not erase directM20 fields or independentM22 calculations',()=>{const{d,b}=setup();b.forms.allocations.find(a=>a.id==='revenue').amount='5999';const m=calculateNormal(d,b);assert.equal(m.formPreparation.m20.find(f=>f.account==='7.2').value,null);assert.equal(val(m.formPreparation.m20.find(f=>f.account==='6.5').value),60);assert.equal(m.formPreparation.netResult,null);assert.equal(val(m.taxableProfit),3130);assert.equal(val(field(m,'300').value),1001.6);});
|
||||
test('fractional M20 allocations remain exact; no truncation or rounding changes source or net/taxable totals',()=>{const{d,b}=setup();b.forms.allocations.find(a=>a.id==='opex').amount='300.50';b.forms.allocations.push({id:'other',sourceCode:'operating_expenses',account:'6.8',amount:'299.50',reference:'QA gastos operacionais restantes documentados'});const m=calculateNormal(d,b),f=m.formPreparation.m20.find(f=>f.account==='6.3');assert.deepEqual(f.value,fraction(601n,2n));assert.equal(f.representation.ready,false);assert.equal(f.representation.represented,null);assert.equal(val(m.formPreparation.netResult),3120);assert.equal(val(m.colecta),1001.6);});
|
||||
test('lossuses positiveM22 lossfield and M20parentheses; no loss in profitfield and no negative taxablebase',()=>{const{d,b}=setup(),p=JSON.parse(d.business_plan_snapshot);p.plan.products[0].unitPrice='20';b.schedules.currentLossReference='QA perda fiscal corrente reconciliada no sujeito/período,sem reporte automático';d.business_plan_snapshot=JSON.stringify(p);b.forms.allocations.find(a=>a.id==='revenue').amount='1200';const m=calculateNormal(d,b);assert.equal(val(m.taxableProfit),-1670);assert.equal(val(field(m,'259').value),1670);assert.equal(field(m,'260').application,'not_applicable');assert.equal(m.formPreparation.netRepresentation.represented,'(1680)');assert.equal(val(m.taxableBase),0);assert.equal(val(m.colecta),0);});
|
||||
test('unmapped priorquota deduction keeps demonstrated effect and existing260/278/300; no guessedblankfield',()=>{const{d,b}=setup(),p=JSON.parse(d.business_plan_snapshot);p.plan.assetDepreciation='48';d.business_plan_snapshot=JSON.stringify(p);Object.assign(b.depreciation.assets[0],{accounting:'24',start:'2025-01-01',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40'});b.forms.allocations.find(a=>a.sourceCode==='depreciation').amount='24';const m=calculateNormal(d,b);assert.equal(val(m.depreciation.adjustment),-26);assert.equal(val(m.colecta),1001.6);assert.ok(m.formPreparation.unmapped.some(e=>e.origin.startsWith('Mapa de amortizações')));assert.equal(field(m,'258').value,null);assert.equal(field(m,'238').value,null);assert.equal(val(field(m,'260').value),3130);assert.equal(m.formPreparation.entries.find(e=>e.origin.startsWith('Mapa de amortizações')).code,null);});
|
||||
test('reducedrate unknown or knowninL7 blocks only302,notnormal300 and not finalL8 assumptions',()=>{for(const state of['','known']){const{d,b}=setup();b.forms.reducedRate.state=state;const m=calculateNormal(d,b);assert.equal(field(m,'302').value,null);assert.equal(val(field(m,'300').value),1001.6);assert.equal(val(m.colecta),1001.6);assert.equal(m.assessment.finalBalance.recoverable,null);}});
|
||||
test('save/recover/report preserves8history and originalplan and never claims officialrounding or crosseditiontransfer',()=>{const{d,b}=setup(),original=d.business_plan_snapshot,old=structuredClone(b.result);assert.equal(normalCurrent(d,normalBook(d)),true);d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T10:00:00+02:00'));const back=normalBook(JSON.parse(JSON.stringify(d)));assert.equal(normalCurrent(d,back),true);assert.deepEqual(back.history.at(-1),old);assert.equal(d.business_plan_snapshot,original);assert.match(normalHtml(d),/campo213/);assert.match(normalHtml(d),/transporte automático M20/);assert.match(normalHtml(d),/exacto ao cêntimo/);assert.match(JSON.stringify(createProjectDossierReport(d,{selectedSections:new Set(['finance'])})),/campo302/);back.forms.allocations[0].reference+='changed';assert.equal(normalCurrent(d,back),false);});
|
||||
test('fivenewcases write privateAPI proof inputs and reports without rerunning accepted functionalsuites',()=>{const cases={mapped:setup(),fractional:setup(),missing:setup(),unmapped:setup(),booktax:setup()};cases.booktax.b.forms.incomeTax={state:'known',amount:'320',placement:'outside',reference:'QA gasto contabilístico exterior ao plano;campo próprio pendente'};cases.fractional.b.forms.allocations.find(a=>a.id==='opex').amount='300.50';cases.fractional.b.forms.allocations.push({id:'other',sourceCode:'operating_expenses',account:'6.8',amount:'299.50',reference:'QA parcela contabilística com centavos'});cases.missing.b.forms.incomeTax={state:'',amount:'',placement:'',reference:''};const u=cases.unmapped,p=JSON.parse(u.d.business_plan_snapshot);p.plan.assetDepreciation='48';u.d.business_plan_snapshot=JSON.stringify(p);Object.assign(u.b.depreciation.assets[0],{accounting:'24',start:'2025-01-01',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40'});u.b.forms.allocations.find(a=>a.sourceCode==='depreciation').amount='24';fs.mkdirSync('outputs/irpc-l6-values-forms',{recursive:true});for(const[name,{d,b}]of Object.entries(cases)){d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T10:00:00+02:00'));fs.writeFileSync('outputs/irpc-l6-values-forms/'+name+'.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-values-forms/'+name+'.html',normalHtml(d));}});
|
||||
|
||||
test('newlyidentified includedtax mismatch blocks only dependent213/reconciliation without revising demonstratedtax',()=>{const{d,b}=setup();b.forms.incomeTax={state:'known',amount:'320',placement:'included',reference:'QA imposto reconhecido no plano'};b.forms.allocations.find(a=>a.id==='opex').amount='280';b.forms.allocations.push({id:'tax',sourceCode:'operating_expenses',account:'8.5',amount:'320',reference:'QA imposto reconhecido no custo da origem'});const m=calculateNormal(d,b);assert.equal(val(m.colecta),1001.6);assert.equal(val(m.formPreparation.netResult),3120);assert.equal(val(field(m,'213').value),0);assert.equal(field(m,'238').value,null);assert.equal(val(field(m,'260').value),3130);assert.ok(m.formPreparation.pending.some(s=>s.includes('tax_own')));});
|
||||
test('positive and negative asseteffects remain separate; netdepreciation is never all assignedto206',()=>{const{d,b}=setup(),a=structuredClone(b.depreciation.assets[0]);Object.assign(b.depreciation.assets[0],{cost:'60',accounting:'36'});Object.assign(a,{id:'other',assetKey:'other',document:'QA-other-asset',accounting:'24',start:'2025-01-01',previousAccounting:'120',previousFiscal:'80',priorAvailable:'40'});b.depreciation.assets.push(a);const m=calculateNormal(d,b);assert.equal(val(m.depreciation.adjustment),4);assert.equal(val(field(m,'206').value),30);assert.equal(val(m.taxableProfit),3124);assert.equal(m.formPreparation.unmapped.length,1);assert.equal(val(m.formPreparation.unmapped[0].value),-26);assert.equal(field(m,'238').value,null);});
|
||||
test('acceptedcostproof feeds independentM22 labels without applying printedpercentages again',()=>{const d=JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-costs-preserved.json',import.meta.url),'utf8')),b=normalBook(d),m=b.result.model,{b:input}=setup();const mapped=prepareNormalForms(input.forms,m,normalFinancialSource(d));for(const[rule,code]of [['allowance','219'],['representation','220'],['advertising','221'],['credit','209']]){const row=m.costs.rows.find(r=>r.rule===rule);assert.ok(row);const sum=m.costs.rows.filter(r=>r.rule===rule).reduce((s,r)=>s+val(r.adjustment),0);const value=mapped.fields.find(f=>f.code===code).value;if(code!=='209')assert.equal(val(value),sum);else assert.equal(val(value),m.costs.rows.filter(r=>['credit','stock','litigation'].includes(r.rule)).reduce((s,r)=>s+val(r.adjustment),0));}assert.deepEqual(b.result.model,m);});
|
||||
|
||||
test('absence of booktax cannot erase previouslyidentified ownincomeTax cost; independentknowncolecta remains',()=>{const{d,b}=setup();b.schedules.rows.push({id:'tax',rule:'tax_own',amount:'320',sourceCode:'operating_expenses',document:'QA-tax',line:'1',reference:'QA imposto já incluído',fiscalYear:'',conditions:'yes'});Object.assign(b.profit.topics.expenseCorrections,{state:'known',amount:'320',reference:'QA mapa do imposto próprio'});b.forms.incomeTax={state:'absent',amount:'',placement:'',reference:'QA ausência a conferir'};const m=calculateNormal(d,b);assert.equal(m.formPreparation.netResult,null);assert.ok(m.formPreparation.pending.some(s=>s.includes('contradiz imposto próprio')));assert.equal(val(field(m,'300').value),1104);});
|
||||
@@ -0,0 +1,33 @@
|
||||
import test from'node:test';import assert from'node:assert/strict';import fs from'node:fs';
|
||||
import{normalBook,normalCurrent,calculateNormal,recordNormal,saveNormalBook,normalHtml,normalFinancialSource}from'../../resources/js/lib/irpc-normal.ts';
|
||||
import{newNormalPeriodFx,mapNormalPeriodFx}from'../../resources/js/lib/irpc-normal-period-fx.ts';
|
||||
import{emptyFxSelection,selectFx}from'../../resources/js/lib/iva-fx-selection.ts';
|
||||
import{createProjectDossierReport}from'../../resources/js/lib/project-dossier-report.ts';
|
||||
import{newCurrencyEvent}from'../../resources/js/lib/iva-currency-events.ts';
|
||||
import{irpcFxSources}from'../../resources/js/lib/irpc-fx.ts';
|
||||
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/irpc-depreciation-preserved.json',import.meta.url),'utf8')),val=f=>f?Number(f.numerator)/Number(f.denominator):null;
|
||||
function fx(direction='mzn_per_unit',rate='64'){const s={...emptyFxSelection(),nature:'commercial',submissionDate:'2026-10-26',bulletinDate:'2026-10-26',bulletinRef:'QA pressuposto sintético64; não boletim real',source:'https://www.bancomoc.mz/',sourceCurrency:'USD',direction,mean:rate};return{...s,applied:selectFx(s)};}
|
||||
function setup(currency='MZN',kind='start'){
|
||||
const d=seed(),b=normalBook(d),p=JSON.parse(d.business_plan_snapshot);p.plan.currency=currency;d.business_plan_snapshot=JSON.stringify(p);
|
||||
b.rows=[];b.costs=undefined;b.schedules.rows=[];b.schedules.interest.amount=currency==='USD'?'7680':'120';
|
||||
for(const t of Object.values(b.profit.topics)){t.state='absent';t.amount='';t.reference='QA ausência declarada no período seleccionado';}
|
||||
b.base.losses=[];b.base.lossesState='absent';b.base.lossesReference='QA ausência de perdas reportadas';
|
||||
b.depreciation.assets[0].accounting=currency==='USD'?'3840':'60';b.depreciation.assets[0].cost=currency==='USD'?'32000':'500';b.depreciation.assets[0].start='2026-07-01';b.depreciation.assets[0].timing='months';
|
||||
b.periodsFx={...newNormalPeriodFx(),kind,from:'2026-07-01',to:'2026-12-31',planStart:'2026-01-01',reference:'QA início documentado1Julho;correspondência completa dos meses7–12 ao período do sujeito',mznMapsReference:'QA activo/quotas/juros e demais mapas reconciliados explicitamente emMZN no período',conversionReference:currency==='USD'?'QA pressuposto de simulação64MZN/USD aplicado aos meses seleccionados;sem regra fiscal anual confirmada':''};
|
||||
if(currency==='USD')b.periodsFx.months=Array.from({length:6},(_,n)=>({period:n+7,currency:'USD',fxSelection:fx()}));
|
||||
return{d,b};
|
||||
}
|
||||
test('installed7 stays current without opt-in; optional8 preserves original result and plan while selecting6 months',()=>{const d=seed(),old=normalBook(d);assert.equal(normalCurrent(d,old),true);const {d:next,b}=setup(),m=calculateNormal(next,b);assert.equal(m.version,'IRPC-normal-adjustments/8');assert.equal(val(m.accountingProfit),3120);assert.equal(val(m.depreciation.assets[0].quota),50);assert.equal(val(m.taxableProfit),3130);assert.equal(val(m.taxableBase),3130);assert.equal(val(m.colecta),1001.6);assert.equal(m.assessment.period.from,'2026-07-01');assert.equal(m.assessment.finalBalance.payable,null);assert.equal(JSON.parse(next.business_plan_snapshot).plan.periods,12);});
|
||||
test('monthly conversion reuses delivered selection and direction; originalUSD and assumptions remain reportable',()=>{const{d,b}=setup('USD'),m=calculateNormal(d,b);assert.equal(val(m.accountingProfit),199680);assert.equal(val(m.depreciation.adjustment),640);assert.equal(val(m.taxableProfit),200320);assert.equal(val(m.colecta),64102.4);assert.equal(m.periodsFx.originalCurrency,'USD');assert.equal(m.periodsFx.rows[0].evidence.column,'mean');assert.match(m.periodsFx.rule,/simulação/);const inv=setup('USD');for(const e of inv.b.periodsFx.months)e.fxSelection=fx('units_per_mzn','0.015625');assert.equal(val(calculateNormal(inv.d,inv.b).colecta),64102.4);});
|
||||
test('annual plan in another currency and shorterplan aligned to July both reconcile without annualising',()=>{const a=setup('USD');a.b.periodsFx.kind='annual';a.b.periodsFx.from='2026-01-01';a.b.periodsFx.months=Array.from({length:12},(_,n)=>({period:n+1,currency:'USD',fxSelection:fx()}));a.b.schedules.interest.amount='15360';a.b.depreciation.assets[0].accounting='7680';a.b.depreciation.assets[0].start='2026-01-01';assert.equal(val(calculateNormal(a.d,a.b).colecta),128204.8);const{d,b}=setup();const p=JSON.parse(d.business_plan_snapshot);p.plan.periods=6;p.plan.assetDepreciation='60';p.plan.depreciationLifeMonths=6;d.business_plan_snapshot=JSON.stringify(p);b.periodsFx.planStart='2026-07-01';assert.equal(val(calculateNormal(d,b).colecta),1001.6);});
|
||||
test('cessation and same-year start/end use documented dates; unsupportedpartial months and contradictions remain pending',()=>{const{d,b}=setup();b.periodsFx.kind='cessation';b.periodsFx.from='2026-01-01';b.periodsFx.to='2026-06-30';b.depreciation.assets[0].start='2026-01-01';assert.equal(val(calculateNormal(d,b).colecta),1001.6);b.periodsFx.kind='same_year';b.periodsFx.from='2026-04-01';b.periodsFx.to='2026-09-30';assert.equal(val(calculateNormal(d,b).colecta),1001.6);for(const patch of[{from:'2026-07-15'},{to:'2026-12-30'},{planStart:''},{from:'2026-01-01',kind:'start'},{to:'2026-02-31'},{reference:''},{mznMapsReference:''}]){const x=setup();Object.assign(x.b.periodsFx,patch);assert.equal(calculateNormal(x.d,x.b).colecta,null);}});
|
||||
test('missingmonth selection,sourcecurrency mismatch and editedapplied rate never become defaultFX',()=>{for(const mutate of[i=>i.months.pop(),i=>i.months[0].currency='EUR',i=>i.months[0].fxSelection.mean='65',i=>i.conversionReference='',i=>i.months.push({...i.months[0],period:1})]){const{d,b}=setup('USD');mutate(b.periodsFx);const m=calculateNormal(d,b);assert.equal(m.accountingProfit,null);assert.equal(m.colecta,null);assert.ok(m.periodsFx.rows.some(r=>r.value));}});
|
||||
test('fractionalcent conversion blocks dependenttotals while retaining exact source and otherknownmonths',()=>{const{d,b}=setup('USD');b.periodsFx.months[0].fxSelection=fx('mzn_per_unit','0.33333333');const m=calculateNormal(d,b);assert.equal(m.colecta,null);assert.ok(m.periodsFx.pending.some(s=>s.includes('fracção de cêntimo')));assert.equal(m.periodsFx.rows[0].originalCurrency,'USD');});
|
||||
test('shortperiod priorloss expiry and annualcaps stay explicitlypending; no change to independent arithmetic',()=>{const x=setup();x.b.base=structuredClone(normalBook(seed()).base);const m=calculateNormal(x.d,x.b);assert.equal(val(m.taxableProfit),3130);assert.equal(m.taxableBase,null);assert.ok(m.base.pending.some(s=>s.includes('41(5)')));const y=setup();y.b.costs=structuredClone(normalBook(seed()).costs);assert.equal(calculateNormal(y.d,y.b).colecta,null);assert.ok(calculateNormal(y.d,y.b).costs.pending.some(s=>s.includes('Limite anual neste período curto')));y.b.costs=undefined;y.b.depreciation.assets[0].timing='annual';assert.equal(calculateNormal(y.d,y.b).colecta,null);});
|
||||
test('save/recovery/report preserves7 history and sourceplan; changed period makes8 historical',()=>{const{d,b}=setup('USD'),original=d.business_plan_snapshot,old=structuredClone(b.result);d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T10:00:00+02:00'));const back=normalBook(JSON.parse(JSON.stringify(d)));assert.equal(normalCurrent(d,back),true);assert.deepEqual(back.history.at(-1),old);assert.equal(d.business_plan_snapshot,original);assert.match(normalHtml(d),/64102.40/);assert.match(normalHtml(d),/regra fiscal anual não confirmada/);assert.match(JSON.stringify(createProjectDossierReport(d,{selectedSections:new Set(['finance'])})),/USD/);back.periodsFx.reference+='changed';assert.equal(normalCurrent(d,back),false);});
|
||||
test('newcases prepare privateAPI persistence and dossiers including insufficientinformation',()=>{const cases={short:setup(),fx:setup('USD'),missing:setup('USD'),loss:setup()};cases.missing.b.periodsFx.months.pop();cases.loss.b.base=structuredClone(normalBook(seed()).base);fs.mkdirSync('outputs/irpc-l6-periods-fx',{recursive:true});for(const[name,{d,b}]of Object.entries(cases)){d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b,'2026-10-26T10:00:00+02:00'));fs.writeFileSync('outputs/irpc-l6-periods-fx/'+name+'.json',JSON.stringify(d));fs.writeFileSync('outputs/irpc-l6-periods-fx/'+name+'.html',normalHtml(d));}});
|
||||
test('different monthrates require MZN map reconciliation; UI source uses converted subset without relabellingUSD credit balances',()=>{const{d,b}=setup('USD');b.periodsFx.months[0].fxSelection=fx('mzn_per_unit','65');assert.equal(calculateNormal(d,b).colecta,null);b.depreciation.assets[0].accounting='3850';b.schedules.interest.amount='7700';assert.equal(val(calculateNormal(d,b).colecta),64272);d.modelo_a_snapshot=saveNormalBook(d,b);const source=normalFinancialSource(d);assert.equal(source.currency,'MZN');assert.equal(source.periods,6);assert.equal(source.accountingLines.find(l=>l.code==='depreciation').total,'3850.00');assert.deepEqual(source.cashSchedule,[]);});
|
||||
test('noncalendar annual mapping cannot bypass existing authorised period and unsupportedshortdates stay pending',()=>{const{d,b}=setup();b.periodsFx.kind='annual';b.periodsFx.from='2025-07-01';b.periodsFx.to='2026-06-30';b.periodsFx.planStart='2025-07-01';assert.equal(calculateNormal(d,b).colecta,null);assert.ok(calculateNormal(d,b).pending.some(s=>s.includes('autorizado/comunicado')));});
|
||||
test('reusedFX link invalidates current result when existing source changes and never changes historical result',()=>{const{d,b}=setup('USD'),snapshot=JSON.parse(d.modelo_a_snapshot);snapshot.currencyEvents??={};snapshot.currencyEvents[d.context_opportunity]={version:1,rows:[{...newCurrencyEvent('fx-shared','MZN'),sourceCurrency:'USD',fxSelection:fx()}]};d.modelo_a_snapshot=JSON.stringify(snapshot);const linked=irpcFxSources(d)[0];assert.ok(linked);b.periodsFx.months[0].fxLink={id:linked.id,fingerprint:linked.fingerprint};b.periodsFx.months[0].fxSelection=linked.selection;d.modelo_a_snapshot=saveNormalBook(d,recordNormal(d,b));const book=normalBook(d),old=structuredClone(book.result);assert.equal(normalCurrent(d,book),true);const changed=JSON.parse(d.modelo_a_snapshot);changed.currencyEvents[d.context_opportunity].rows[0].description='New source context';d.modelo_a_snapshot=JSON.stringify(changed);assert.equal(normalCurrent(d,normalBook(d)),false);assert.deepEqual(normalBook(d).result,old);assert.equal(calculateNormal(d,normalBook(d)).colecta,null);});
|
||||
|
||||
test('known nondepreciable asset in shortperiod keeps full addback without asking irrelevant timing',()=>{const{d,b}=setup();Object.assign(b.depreciation.assets[0],{category:'nondepreciable',timing:'',method:''});const m=calculateNormal(d,b);assert.equal(val(m.depreciation.adjustment),60);assert.equal(val(m.colecta),1017.6);});
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$root=dirname(__DIR__);
|
||||
$operation=$argv[1]??'';
|
||||
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-irpc-l6-depreciation-qa-20261026')throw new RuntimeException('Explicit reliefs QA workspace and routes/test operation required.');
|
||||
foreach(['storage/framework/views','storage/framework/cache','storage/framework/sessions','bootstrap/cache','storage/logs','outputs/irpc-l6-depreciation']as$dir)if(!is_dir($root.'/'.$dir))mkdir($root.'/'.$dir,0700,true);
|
||||
foreach(['APP_BASE_PATH'=>$root,'APP_ENV'=>'testing','APP_KEY'=>'base64:'.base64_encode(str_repeat('Q',32)),'DB_CONNECTION'=>'pgsql','DB_HOST'=>'127.0.0.1','DB_PORT'=>'55441','DB_DATABASE'=>'simulator_fiscal_delta_20261021_test','DB_USERNAME'=>'postgres','DB_PASSWORD'=>'','DB_URL'=>'','CACHE_STORE'=>'array','SESSION_DRIVER'=>'array','QUEUE_CONNECTION'=>'sync','MAIL_MAILER'=>'array'] as $k=>$v){putenv($k.'='.$v);$_ENV[$k]=$_SERVER[$k]=$v;}
|
||||
$loader=require $root.'/vendor/autoload.php';
|
||||
spl_autoload_register(function($class)use($root){foreach(['App\\'=>'app/','Tests\\'=>'tests/','Database\\Factories\\'=>'database/factories/']as$prefix=>$dir){if(str_starts_with($class,$prefix)){$file=$root.'/'.$dir.str_replace('\\','/',substr($class,strlen($prefix))).'.php';if(is_file($file)){require $file;return;}}}},true,true);
|
||||
$app=require $root.'/bootstrap/app.php';
|
||||
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
|
||||
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
|
||||
$c=config('database.connections.pgsql');
|
||||
if($app->configurationIsCached()||$c['host']!=='127.0.0.1'||(int)$c['port']!==55441||$c['database']!=='simulator_fiscal_delta_20261021_test'||!empty($c['url']))throw new RuntimeException('QA guard');
|
||||
$pdo=new PDO('pgsql:host=127.0.0.1;port=55441;dbname='.$c['database'],$c['username'],$c['password']);
|
||||
if($pdo->query('select current_database()')->fetchColumn()!==$c['database'])throw new RuntimeException('QA identity');
|
||||
if($operation==='routes'){
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$status=Illuminate\Support\Facades\Artisan::call('wayfinder:generate',['--with-form'=>true]);
|
||||
echo Illuminate\Support\Facades\Artisan::output();exit($status);
|
||||
}
|
||||
$argv=['phpunit','-c','phpunit.investmoz.xml','tests/Feature/IrpcNormalDepreciationDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$root=dirname(__DIR__);
|
||||
$operation=$argv[1]??'';
|
||||
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-irpc-l6-periods-fx-qa-20261026')throw new RuntimeException('Explicit reliefs QA workspace and routes/test operation required.');
|
||||
foreach(['storage/framework/views','storage/framework/cache','storage/framework/sessions','bootstrap/cache','storage/logs','outputs/irpc-l6-periods-fx']as$dir)if(!is_dir($root.'/'.$dir))mkdir($root.'/'.$dir,0700,true);
|
||||
foreach(['APP_BASE_PATH'=>$root,'APP_ENV'=>'testing','APP_KEY'=>'base64:'.base64_encode(str_repeat('Q',32)),'DB_CONNECTION'=>'pgsql','DB_HOST'=>'127.0.0.1','DB_PORT'=>'55441','DB_DATABASE'=>'simulator_fiscal_delta_20261021_test','DB_USERNAME'=>'postgres','DB_PASSWORD'=>'','DB_URL'=>'','CACHE_STORE'=>'array','SESSION_DRIVER'=>'array','QUEUE_CONNECTION'=>'sync','MAIL_MAILER'=>'array'] as $k=>$v){putenv($k.'='.$v);$_ENV[$k]=$_SERVER[$k]=$v;}
|
||||
$loader=require $root.'/vendor/autoload.php';
|
||||
spl_autoload_register(function($class)use($root){foreach(['App\\'=>'app/','Tests\\'=>'tests/','Database\\Factories\\'=>'database/factories/']as$prefix=>$dir){if(str_starts_with($class,$prefix)){$file=$root.'/'.$dir.str_replace('\\','/',substr($class,strlen($prefix))).'.php';if(is_file($file)){require $file;return;}}}},true,true);
|
||||
$app=require $root.'/bootstrap/app.php';
|
||||
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
|
||||
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
|
||||
$c=config('database.connections.pgsql');
|
||||
if($app->configurationIsCached()||$c['host']!=='127.0.0.1'||(int)$c['port']!==55441||$c['database']!=='simulator_fiscal_delta_20261021_test'||!empty($c['url']))throw new RuntimeException('QA guard');
|
||||
$pdo=new PDO('pgsql:host=127.0.0.1;port=55441;dbname='.$c['database'],$c['username'],$c['password']);
|
||||
if($pdo->query('select current_database()')->fetchColumn()!==$c['database'])throw new RuntimeException('QA identity');
|
||||
if($operation==='routes'){
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$status=Illuminate\Support\Facades\Artisan::call('wayfinder:generate',['--with-form'=>true]);
|
||||
echo Illuminate\Support\Facades\Artisan::output();exit($status);
|
||||
}
|
||||
$argv=['phpunit','-c','phpunit.investmoz.xml','tests/Feature/IrpcNormalPeriodFxDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
$root=dirname(__DIR__);
|
||||
$operation=$argv[1]??'';
|
||||
if(!in_array($operation,['routes','test'],true)||basename($root)!=='portal-investidor-irpc-l6-values-forms-qa-20261026')throw new RuntimeException('Explicit reliefs QA workspace and routes/test operation required.');
|
||||
foreach(['storage/framework/views','storage/framework/cache','storage/framework/sessions','bootstrap/cache','storage/logs','outputs/irpc-l6-values-forms']as$dir)if(!is_dir($root.'/'.$dir))mkdir($root.'/'.$dir,0700,true);
|
||||
foreach(['APP_BASE_PATH'=>$root,'APP_ENV'=>'testing','APP_KEY'=>'base64:'.base64_encode(str_repeat('Q',32)),'DB_CONNECTION'=>'pgsql','DB_HOST'=>'127.0.0.1','DB_PORT'=>'55441','DB_DATABASE'=>'simulator_fiscal_delta_20261021_test','DB_USERNAME'=>'postgres','DB_PASSWORD'=>'','DB_URL'=>'','CACHE_STORE'=>'array','SESSION_DRIVER'=>'array','QUEUE_CONNECTION'=>'sync','MAIL_MAILER'=>'array'] as $k=>$v){putenv($k.'='.$v);$_ENV[$k]=$_SERVER[$k]=$v;}
|
||||
$loader=require $root.'/vendor/autoload.php';
|
||||
spl_autoload_register(function($class)use($root){foreach(['App\\'=>'app/','Tests\\'=>'tests/','Database\\Factories\\'=>'database/factories/']as$prefix=>$dir){if(str_starts_with($class,$prefix)){$file=$root.'/'.$dir.str_replace('\\','/',substr($class,strlen($prefix))).'.php';if(is_file($file)){require $file;return;}}}},true,true);
|
||||
$app=require $root.'/bootstrap/app.php';
|
||||
(new Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables)->bootstrap($app);
|
||||
(new Illuminate\Foundation\Bootstrap\LoadConfiguration)->bootstrap($app);
|
||||
$c=config('database.connections.pgsql');
|
||||
if($app->configurationIsCached()||$c['host']!=='127.0.0.1'||(int)$c['port']!==55441||$c['database']!=='simulator_fiscal_delta_20261021_test'||!empty($c['url']))throw new RuntimeException('QA guard');
|
||||
$pdo=new PDO('pgsql:host=127.0.0.1;port=55441;dbname='.$c['database'],$c['username'],$c['password']);
|
||||
if($pdo->query('select current_database()')->fetchColumn()!==$c['database'])throw new RuntimeException('QA identity');
|
||||
if($operation==='routes'){
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$status=Illuminate\Support\Facades\Artisan::call('wayfinder:generate',['--with-form'=>true]);
|
||||
echo Illuminate\Support\Facades\Artisan::output();exit($status);
|
||||
}
|
||||
$argv=['phpunit','-c','phpunit.investmoz.xml','tests/Feature/IrpcNormalFormPreparationDraftTest.php'];$argc=count($argv);$_SERVER['argv']=$argv;$_SERVER['argc']=$argc;require $root.'/vendor/phpunit/phpunit/phpunit';
|
||||
Reference in New Issue
Block a user