1749 lines
74 KiB
TypeScript
1749 lines
74 KiB
TypeScript
import { inputText } from './input-text.ts';
|
||
import { acquisitionScope, readAcquisitionStore } from './iva-acquisitions.ts';
|
||
import {
|
||
fundraisingError,
|
||
type FundraisingContext,
|
||
} from './iva-fundraising.ts';
|
||
import {
|
||
pharmaAnnexEntry,
|
||
pharmaAnnexReference,
|
||
pharmaAnnexSource,
|
||
pharmaAnnexVersion,
|
||
} from './iva-pharma-annex.ts';
|
||
import {
|
||
annexIEntry,
|
||
annexIReference,
|
||
annexISource,
|
||
annexIVersion,
|
||
} from './iva-annex-i.ts';
|
||
import {
|
||
productionOptionCheck,
|
||
productionOptionVersion,
|
||
type ProductionTaxOption,
|
||
} from './iva-production-option.ts';
|
||
import {
|
||
temporaryBenefits,
|
||
temporaryBenefit,
|
||
temporaryBenefitNotice,
|
||
temporaryBenefitVersion,
|
||
} from './iva-temporary-benefits.ts';
|
||
import { directExportTreatments } from './iva-export-operations.ts';
|
||
import { exportBeneficiaryTreatments } from './iva-export-beneficiaries.ts';
|
||
import {
|
||
specialRegimeTreatments,
|
||
specialRegimeIssue,
|
||
specialRegimeOptions,
|
||
isSpecialRegime,
|
||
type SpecialRegime,
|
||
} from './iva-special-regimes.ts';
|
||
import { internationalAssetTreatments } from './iva-international-assets.ts';
|
||
import {
|
||
exportServiceTreatments,
|
||
isExportService,
|
||
} from './iva-export-services.ts';
|
||
import {
|
||
onboardSupplyTreatments,
|
||
onboardSupplyIssue,
|
||
supplyCategories,
|
||
isOnboardSupply,
|
||
type SupplyCategory,
|
||
} from './iva-onboard-supplies.ts';
|
||
import {
|
||
monthlySalesFromDraft,
|
||
readMonthlyIvaSettings,
|
||
} from './monthly-iva.ts';
|
||
import {
|
||
addFractions,
|
||
decimalFraction,
|
||
fraction,
|
||
fractionLabel,
|
||
fractionPresentation,
|
||
type FiscalFraction,
|
||
} from './fiscal-rational.ts';
|
||
|
||
export const operationVersion = 'iva-operations-l1/2';
|
||
// The accepted v2 full-catalogue key predates scoped keys. Freeze its members
|
||
// so later, independent catalogue additions cannot invalidate those snapshots.
|
||
const legacyV2TreatmentIds = [
|
||
'general',
|
||
'private_health',
|
||
'private_education',
|
||
'private_training',
|
||
'personal_lessons',
|
||
'export_goods',
|
||
'housing_rent',
|
||
'public_health',
|
||
'public_education',
|
||
'public_training',
|
||
'foreign_operation',
|
||
'other',
|
||
];
|
||
const source2022 =
|
||
'Lei22/2022, BR250, 2.º suplemento, PDF1–4 (2252(25)–(28)); arts.1/2/6; efeitos01/01/2023';
|
||
const source2016 =
|
||
'CIVA republicado Lei13/2016, BR156, 10.º suplemento, PDF9–15 (1426(267)–(273))';
|
||
const source2025 =
|
||
'Lei10/2025, BR248, suplemento1, PDF14–15 (2180(14)–(15)); arts.1/2/3/5; efeitos01/01/2026';
|
||
const healthGoodsSource =
|
||
'CIVA republicado Lei13/2016, PDF9, p.1426(267), art.9(1)(b–f); PDF15, p.1426(273), art.19(1); Lei22/2022 art.1, PDF1, p.2252(25), mantém9(1)(b–f). Âmbito técnico2026/MZN';
|
||
const internalOperation =
|
||
'Operação interna localizada em Moçambique; não é importação nem operação localizada no estrangeiro';
|
||
export const modeloA06Correspondence =
|
||
'Correspondência confirmada pela Tempo em25/10/2026 no exemplar VAT Return(1).pdf:05 — Vendas isentas com direito à dedução;06 — Operações do n.º1,alínea b),do artigo18.ºdoCIVA;07 — Operações que não conferem direito à dedução. Campo14=01+03+05+06+07,cada operação contada uma única vez no seu campo. O catálogo actual identifica os seus tratamentos por fundamento e destino exclusivo;nenhum está atribuído ao06. Operação no estrangeiro não significa06:enquadramento desconhecido permanece indeterminado. O06 só soma linhas que tenham correspondência própria identificada;zero exige período completo sem essas linhas. Originais e resultados históricos preservados.';
|
||
export type OperationTreatment = {
|
||
id: string;
|
||
label: string;
|
||
field: string | null;
|
||
rate: number | null;
|
||
article: string;
|
||
source: string;
|
||
conditions: string[];
|
||
logicVersion?: string;
|
||
};
|
||
const nonprofitConditions = [
|
||
'Não distribui nem disponibiliza lucros; os corpos gerentes não têm interesse directo ou indirecto nos resultados, por si ou interposta pessoa',
|
||
'Escrituração abrange todas as actividades e está à disposição dos serviços fiscais para comprovação',
|
||
'Preços homologados pela autoridade competente ou, quando não susceptíveis de homologação, inferiores aos de operações análogas das empresas comerciais sujeitas a imposto',
|
||
'Não entra em concorrência directa com sujeitos passivos do imposto',
|
||
];
|
||
function socialTreatment(
|
||
id: string,
|
||
label: string,
|
||
article: string,
|
||
conditions: string[],
|
||
nonprofit: boolean,
|
||
): OperationTreatment {
|
||
return {
|
||
id,
|
||
label,
|
||
field: '07',
|
||
rate: 0,
|
||
article: `CIVA arts.9(2)(${article}),10(${nonprofit ? '2' : '1'}),19(1)`,
|
||
source: 'CIVA republicado Lei13/2016, PDF9, p.1426(267), art.9(2)(a/c/d); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26),10(1–2). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
...(nonprofit
|
||
? nonprofitConditions
|
||
: [
|
||
'Entidade pública: órgão ou instituição da Administração Pública que desempenha funções administrativas do Estado, nos termos da legislação aplicável',
|
||
]),
|
||
...conditions,
|
||
],
|
||
};
|
||
}
|
||
const culturalSource =
|
||
'CIVA republicado Lei13/2016, PDF9, p.1426(267), art.9(2)(e–g); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF1, p.2252(25), mantém9(2)(e–g), e PDF2, p.2252(26),10(1–2). Âmbito técnico2026/MZN';
|
||
const culturalTreatments: OperationTreatment[] = (
|
||
[false, true] as const
|
||
).flatMap((nonprofit) => {
|
||
const suffix = nonprofit ? 'nonprofit' : 'public',
|
||
entity = nonprofit
|
||
? 'organismo sem finalidade lucrativa'
|
||
: 'entidade pública';
|
||
const make = (
|
||
id: string,
|
||
label: string,
|
||
article: string,
|
||
conditions: string[],
|
||
): OperationTreatment => ({
|
||
...socialTreatment(id, label, article, conditions, nonprofit),
|
||
source: culturalSource,
|
||
});
|
||
return [
|
||
make(
|
||
`cultural_visits_${suffix}`,
|
||
`Visitas culturais/naturais — ${entity}`,
|
||
'e',
|
||
[
|
||
'Visita, guiada ou não, a museu, galeria de arte, monumento, parque, perímetro florestal, jardim botânico/zoológico ou similar pertencente ao Estado, a entidade pública ou a entidade sem finalidade lucrativa',
|
||
'Serviço efectuado pela própria entidade, por intermédio dos seus próprios agentes; organismo sem finalidade lucrativa devidamente autorizado quando aplicável',
|
||
'Se houver bens, são transmissões estreitamente conexas com essa visita; não venda autónoma nem serviço de intermediário',
|
||
],
|
||
),
|
||
make(
|
||
`religious_personnel_${suffix}`,
|
||
`Cedência de pessoal religiosa/filosófica — ${entity}`,
|
||
'f',
|
||
[
|
||
'Cedência de pessoal efectuada por instituição religiosa ou filosófica identificada',
|
||
'Destino identificado: actividade isenta nos termos do CIVA, com fundamento específico, ou assistência espiritual; não qualquer fornecimento de mão-de-obra',
|
||
],
|
||
),
|
||
];
|
||
});
|
||
culturalTreatments.push({
|
||
id: 'statutory_membership',
|
||
label: 'Serviços colectivos aos associados por quota estatutária',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(2)(g),10(2),19(1)',
|
||
source: culturalSource,
|
||
conditions: [
|
||
internalOperation,
|
||
...nonprofitConditions,
|
||
'Serviços efectuados no interesse colectivo dos próprios associados por organismo sem finalidade lucrativa',
|
||
'Objectivos de natureza política, sindical, religiosa, patriótica, filantrópica, recreativa, desportiva, cultural, cívica ou de representação de interesses económicos, identificados',
|
||
'A única contraprestação é uma quota fixada nos respectivos estatutos; não pagamentos adicionais por serviços individuais',
|
||
],
|
||
});
|
||
const listedGoodsTreatments: OperationTreatment[] = [
|
||
['maize', 'Milho', 'Bem transmitido identificado como milho'],
|
||
[
|
||
'maize_flour',
|
||
'Farinha de milho',
|
||
'Bem transmitido identificado como farinha de milho',
|
||
],
|
||
['rice', 'Arroz', 'Bem transmitido identificado como arroz'],
|
||
['bread', 'Pão', 'Bem transmitido identificado como pão'],
|
||
[
|
||
'iodised_salt',
|
||
'Sal iodado',
|
||
'Sal identificado como iodado; não qualquer sal',
|
||
],
|
||
[
|
||
'infant_powdered_milk',
|
||
'Leite em pó para lactente até um ano',
|
||
'Leite em pó identificado como destinado a lactente até um ano; não qualquer leite ou alimento infantil',
|
||
],
|
||
['wheat', 'Trigo', 'Bem transmitido identificado como trigo'],
|
||
[
|
||
'wheat_flour',
|
||
'Farinha de trigo',
|
||
'Bem transmitido identificado como farinha de trigo',
|
||
],
|
||
[
|
||
'fresh_chilled_tomato',
|
||
'Tomate fresco ou refrigerado',
|
||
'Tomate identificado como fresco ou refrigerado; não alargar automaticamente a conserva ou preparação',
|
||
],
|
||
['potato', 'Batata', 'Bem transmitido identificado como batata'],
|
||
['onion', 'Cebola', 'Bem transmitido identificado como cebola'],
|
||
[
|
||
'frozen_horse_mackerel',
|
||
'Carapau congelado',
|
||
'Peixe identificado como carapau congelado; não qualquer espécie ou estado de conservação',
|
||
],
|
||
[
|
||
'lighting_kerosene',
|
||
'Petróleo de iluminação',
|
||
'Produto identificado como petróleo de iluminação; não qualquer combustível',
|
||
],
|
||
[
|
||
'domestic_lpg',
|
||
'Gás doméstico — GPL',
|
||
'Produto identificado como gás doméstico GPL; não qualquer gás industrial',
|
||
],
|
||
[
|
||
'jet_fuel',
|
||
'Jet fuel',
|
||
'Produto identificado como jet fuel; não qualquer combustível de transporte',
|
||
],
|
||
[
|
||
'eligible_bicycles',
|
||
'Bicicletas comuns e de ferro até 4 velocidades',
|
||
'Produto identificado na enumeração legal «bicicletas comuns e de ferro até 4 velocidades», com características sustentadas; não qualquer veículo',
|
||
],
|
||
[
|
||
'condoms',
|
||
'Preservativos',
|
||
'Bem transmitido identificado como preservativo',
|
||
],
|
||
[
|
||
'insecticides',
|
||
'Insecticidas',
|
||
'Bem transmitido identificado como insecticida; não qualquer produto químico',
|
||
],
|
||
].map(([id, label, condition]) => ({
|
||
id: `listed_${id}`,
|
||
label: `Lista legal — ${label}`,
|
||
field: '05',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(10),19(1)(b)(v), redacção Lei22/2022',
|
||
source: 'Lei22/2022 art.1, PDF2, p.2252(26),9(10); CIVA republicado Lei13/2016, PDF15, p.1426(273),19(1)(b)(v). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão do próprio bem enumerado, identificado; não um serviço autónomo nem extensão por sector, marca ou destino',
|
||
condition,
|
||
],
|
||
}));
|
||
export const operationTreatments: OperationTreatment[] = [
|
||
...exportServiceTreatments,
|
||
...onboardSupplyTreatments,
|
||
...internationalAssetTreatments,
|
||
...directExportTreatments,
|
||
...exportBeneficiaryTreatments,
|
||
...specialRegimeTreatments,
|
||
...temporaryBenefits.map((item) => ({
|
||
id: item.id,
|
||
label: `Benefício terminado31/12/2025 — ${item.label}`,
|
||
field: null,
|
||
rate: null,
|
||
article: `CIVA art.${item.article}`,
|
||
source: `${item.source} CIVA republicado Lei13/2016,9(14),PDF10/p.1426(268),apenas para9(13)(b/f). Âmbito:cessação nas operações internas2026/MZN;sem cálculo histórico.`,
|
||
conditions: [],
|
||
logicVersion: temporaryBenefitVersion,
|
||
})),
|
||
{
|
||
id: 'annexI_goods',
|
||
label: 'Equipamento, sementes e outros bens do Anexo I — artigo9(12)(h)',
|
||
field: '07',
|
||
rate: 0,
|
||
logicVersion: annexIVersion,
|
||
article: 'CIVA arts.9(12)(h),19(1); Lei22/2022 art.3(1)',
|
||
source: `CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(h), e PDF15, p.1426(273),19(1); rectificação BR89/2017, PDF1, p.569; ${annexISource}. Âmbito técnico2026/MZN`,
|
||
conditions: [
|
||
internalOperation,
|
||
'O bem transmitido corresponde ao código e à designação da posição escolhida no Anexo I; não basta a actividade agrícola, a marca ou uma indicação genérica de equipamento',
|
||
'Qualificadores expressos na posição conferidos e fundamentados, incluindo natureza, destino, certificação, peso, capacidade ou potência quando exigidos nessa linha; não acrescentar condições a linhas que não as exigem',
|
||
],
|
||
},
|
||
{
|
||
id: 'pharma_manufacturing_inputs',
|
||
label: 'Bens do Anexo III para fabrico de medicamentos — estabelecimento integrado no SNS',
|
||
field: '07',
|
||
rate: 0,
|
||
logicVersion: pharmaAnnexVersion,
|
||
article: 'CIVA arts.9(1)(g),19(1); Lei22/2022 art.3(1)',
|
||
source: `CIVA republicado Lei13/2016, PDF9, p.1426(267),9(1)(g), e PDF15, p.1426(273),19(1); rectificação BR89/2017, PDF1, p.569; ${pharmaAnnexSource}. Âmbito técnico2026/MZN`,
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão efectuada pelo próprio estabelecimento público ou privado identificado, integrado no Serviço Nacional de Saúde; não basta que o adquirente ou destino seja um hospital',
|
||
'Bens a utilizar como matérias-primas, produtos intermédios ou componentes para o fabrico de medicamentos, com utilização/fundamento identificados; não mera venda de produto acabado ou utilização genérica',
|
||
'Bem corresponde cumulativamente ao código e à designação da linha seleccionada no Anexo III; confirmar as especificações aplicáveis sem corrigir ou completar a pauta por suposição',
|
||
],
|
||
},
|
||
...([false, true] as const).map((nonprofit) => ({
|
||
...socialTreatment(
|
||
`fundraising_${nonprofit ? 'nonprofit' : 'public'}`,
|
||
`Angariação ocasional de fundos — ${nonprofit ? 'organismo sem finalidade lucrativa' : 'entidade pública'}`,
|
||
'h',
|
||
[
|
||
'Actividade habitual isenta identificada:9(1)(a),9(2)(a/b/c/d/e/g) ou9(3)(a/b), com condições dessa isenção e fundamento identificados',
|
||
'Manifestação ocasional destinada à angariação de fundos em proveito exclusivo da entidade organizadora',
|
||
'Total anual declarado inclui esta manifestação e as realizadas fora deste dossier; não superior a oito no ano',
|
||
],
|
||
nonprofit,
|
||
),
|
||
logicVersion: 'fundraising/1',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(2)(h); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF1, p.2252(25),9(1–3), e PDF2, p.2252(26),10(1–2). Âmbito técnico2026/MZN',
|
||
})),
|
||
...(['agriculture', 'forestry', 'livestock', 'fishing'] as const).flatMap(
|
||
(kind, index) => {
|
||
const label = [
|
||
'Produção agrícola, avicultura e apicultura',
|
||
'Produção silvícola',
|
||
'Produção pecuária',
|
||
'Pesca',
|
||
][index],
|
||
letter = ['a', 'b', 'c', 'd'][index];
|
||
const conditions = [
|
||
internalOperation,
|
||
'Operação no âmbito da própria produção identificada, utilizando os próprios recursos; não mera revenda/comércio',
|
||
'Se houver transformação, é acessória, pelo próprio produtor sobre produtos da sua produção, por meios geralmente utilizados nas respectivas explorações',
|
||
];
|
||
const source = `CIVA republicado Lei13/2016, PDF10, p.1426(268),9(7)(${letter}); PDF11, p.1426(269),11; PDF15, p.1426(273),19(1)(b)(v); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(7). Âmbito técnico2026/MZN`;
|
||
const exempt: OperationTreatment = {
|
||
id: `production_${kind}`,
|
||
label: `${label} — isenção`,
|
||
field: kind === 'agriculture' ? '05' : '07',
|
||
rate: 0,
|
||
article: `CIVA arts.9(7)(${letter}),19(1)${kind === 'agriculture' ? '(b)(v)' : ',11'}`,
|
||
source,
|
||
conditions: [
|
||
...conditions,
|
||
...(kind === 'agriculture'
|
||
? []
|
||
: [
|
||
'Regime de isenção aplicável nesta operação: não existe opção de tributação eficaz do art.11 no período; fundamento/histórico identificado',
|
||
]),
|
||
],
|
||
};
|
||
return kind === 'agriculture'
|
||
? [exempt]
|
||
: [
|
||
exempt,
|
||
{
|
||
id: `production_${kind}_opted`,
|
||
logicVersion: productionOptionVersion,
|
||
label: `${label} — opção de tributação eficaz`,
|
||
field: '01',
|
||
rate: 16,
|
||
article: `CIVA arts.9(7)(${letter}),11,15(1),17(1),19(1)(a),34`,
|
||
source: `${source}; ${source2022}; ${source2025}`,
|
||
conditions: [
|
||
...conditions,
|
||
'Opção declarada à Direcção da Área Fiscal competente, na declaração adequada; se opção no início, consta da declaração de início da actividade',
|
||
'Operação no regime normal por opção eficaz; contraprestação completa, sem base especial do art.15(2) ou outro tratamento próprio',
|
||
],
|
||
},
|
||
];
|
||
},
|
||
),
|
||
...listedGoodsTreatments,
|
||
{
|
||
...socialTreatment(
|
||
'public_security',
|
||
'Bens e serviços ligados à segurança por entidade pública',
|
||
'b',
|
||
[
|
||
'Transmissão de bens ou prestação ligada à segurança identificada e efectuada pela própria entidade pública; não presumir segurança social nem extensão a prestador privado contratado',
|
||
],
|
||
false,
|
||
),
|
||
source: 'CIVA republicado Lei13/2016, PDF9, p.1426(267),9(2)(b); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF1, p.2252(25), mantém9(2)(b); PDF2, p.2252(26),10(1). Âmbito técnico2026/MZN',
|
||
},
|
||
{
|
||
id: 'official_military_supplies',
|
||
label: 'Fornecimentos oficiais militares/paramilitares elegíveis',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(11),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(11); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(11). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Bem/serviço no fornecimento de material de guerra e de aquartelamento ou fardamento militar/paramilitar, identificado',
|
||
'Destino de utilização oficial das Forças de Defesa e Segurança Nacional; actividade efectuada exclusivamente para aqueles serviços',
|
||
'Estabelecimento reconhecido pelo Ministério competente, com reconhecimento identificado; não basta o cliente ser militar',
|
||
],
|
||
},
|
||
{
|
||
id: 'sisa_subject_operation',
|
||
label: 'Operação sujeita a SISA, ainda que dela isenta',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(d),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(d); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(d). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Operação e fundamento de sujeição a SISA identificados, ainda que exista isenção desse imposto; não qualquer operação imobiliária',
|
||
'Esta linha corresponde à operação sujeita a SISA, não a locação, mediação ou outro serviço autónomo; este bloco não calcula SISA',
|
||
],
|
||
},
|
||
{
|
||
id: 'exempt_activity_asset_sale',
|
||
label: 'Alienação de bem exclusivamente afecto a actividade isenta',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(e),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(e); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(e). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de bem identificado, com histórico de afectação disponível',
|
||
'Bem afecto exclusivamente a sector de actividade isento; não mera afectação parcial ou existência de alguma operação isenta na entidade',
|
||
],
|
||
},
|
||
{
|
||
id: 'no_deduction_right_asset_sale',
|
||
label: 'Alienação de bem sem direito anterior à dedução',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(e),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(e); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(e). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de bem identificado, com histórico fiscal da aquisição/afectação disponível',
|
||
'Bem não foi objecto de direito à dedução, com fundamento identificado; não basta não ter exercido um direito existente nem desconhecer o histórico',
|
||
],
|
||
},
|
||
{
|
||
id: 'excluded_deduction_asset_sale',
|
||
label: 'Alienação de bem adquirido com exclusão do artigo 20',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(e),20,19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(e); PDF15, p.1426(273),19(1)/20(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(e), e PDF3, p.2252(27),20(2–3). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de bem identificado e aquisição correspondente documentada ou expressamente pressuposta',
|
||
'Aquisição feita com exclusão do direito à dedução nos termos do artigo20: identificar número/alínea aplicável e conferir as excepções do20(3); não assumir exclusão apenas por falta de documento ou dedução não exercida',
|
||
],
|
||
},
|
||
{
|
||
id: 'special_tax_games',
|
||
label: 'Jogos e operações sujeitos ao imposto especial sobre o jogo',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(8),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(8); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(8). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Exploração/prática de jogo de fortuna ou azar ou diversão social nos termos da legislação própria, respectiva comissão, título de aposta ou bilhete de acesso/ingresso à área de jogo, identificado',
|
||
'Esta operação está sujeita ao imposto especial sobre o jogo, com fundamento identificado; não qualquer entretenimento ou serviço acessório autónomo; o imposto especial não é calculado neste bloco',
|
||
],
|
||
},
|
||
{
|
||
id: 'stamped_values',
|
||
label: 'Transmissão de valores selados',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(a),19(1), redacção Lei22/2022',
|
||
source: 'Lei22/2022 art.1, PDF2, p.2252(26),9(12)(a); CIVA republicado Lei13/2016, PDF15, p.1426(273),19(1). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Bem transmitido identificado como valor selado, com natureza sustentada pelo documento ou pressuposto da operação',
|
||
'A linha corresponde à transmissão do valor selado; comissão de venda ou serviço autónomo não é abrangido automaticamente por esta opção',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_passenger_transport',
|
||
label: 'Serviço de transporte público de passageiros',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(j),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(j); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(j). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Serviço identificado como transporte público de passageiros, com natureza da prestação sustentada; não inferido apenas da natureza pública do prestador',
|
||
'A linha corresponde ao transporte de passageiros; não transporte de mercadorias, aluguer autónomo de veículo ou outro serviço acessório sem enquadramento próprio',
|
||
],
|
||
},
|
||
{
|
||
id: 'industrial_feed',
|
||
label: 'Rações de produção industrial para animais elegíveis',
|
||
field: '05',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(f),19(1)(b)(v)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(f); PDF15, p.1426(273),19(1)(b)(v); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(f). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de bens resultantes de actividade industrial de produção de rações, identificada; não presumir isenção de qualquer alimento para animais',
|
||
'Rações destinadas à alimentação de animais de reprodução e abate para consumo humano, com destino identificado',
|
||
],
|
||
},
|
||
{
|
||
id: 'industrial_feed_inputs',
|
||
label: 'Insumos enumerados para produção industrial de rações',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(g),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(g); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(g). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Bem identificado na enumeração: grão de soja, bagaço de soja, soja integral, farinha de peixe, farinha de carne, pós de osso, monofosfato de cálcio, lisina ou metionina',
|
||
'Utilização como matéria-prima na actividade industrial de produção de rações destinadas à alimentação de animais de reprodução e abate para consumo humano, identificada',
|
||
],
|
||
},
|
||
{
|
||
id: 'veterinary_goods',
|
||
label: 'Agulhas, seringas e medicamentos de uso veterinário',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(12)(i),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(12)(i); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(12)(i). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de agulha, seringa ou medicamento identificado; não qualquer equipamento ou serviço veterinário',
|
||
'Uso veterinário identificado para os bens da operação',
|
||
],
|
||
},
|
||
{
|
||
id: 'agricultural_services',
|
||
label: 'Serviços agrícolas de canais, regadio, drenagem e pulverização',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(15),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(15); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(15). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Serviço identificado: abertura de canais, ceifa, drenagem, fornecimento de água para irrigação, limpeza de valas de drenagem ou pulverização da terra',
|
||
'Serviço efectuado no âmbito da actividade agrícola, com afectação identificada; não qualquer obra, limpeza ou fornecimento de água',
|
||
],
|
||
},
|
||
{
|
||
id: 'author_rights',
|
||
label: 'Direitos de autor pelo autor, herdeiro ou legatário',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(9)(a),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(9)(a); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(9). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Operação identificada como transmissão de direitos de autor ou autorização para utilização de obra intelectual',
|
||
'Efectuada pelo próprio autor, seu herdeiro ou legatário, com qualidade identificada; não presumir isenção de revenda/intermediação por terceiro',
|
||
],
|
||
},
|
||
{
|
||
id: 'cultural_publications',
|
||
label: 'Jornais, revistas e livros de natureza elegível',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(9)(b),19(1)',
|
||
source: 'CIVA republicado Lei13/2016, PDF10, p.1426(268),9(9)(b); PDF15, p.1426(273),19(1); Lei22/2022 art.1, PDF2, p.2252(26), mantém9(9). Âmbito técnico2026/MZN',
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de jornal, revista ou livro identificado; não qualquer material escolar, serviço editorial ou fornecimento autónomo',
|
||
'Natureza cultural, educativa, técnica ou recreativa da publicação identificada e sustentada; não inferida apenas do nome do fornecedor ou sector',
|
||
],
|
||
},
|
||
...culturalTreatments,
|
||
{
|
||
id: 'general',
|
||
label: 'Operação interna geral',
|
||
field: '01',
|
||
rate: 16,
|
||
article: 'CIVA arts.1,6,15(1),17(1–2); art.34 (Lei10/2025)',
|
||
source: `${source2016}; ${source2022}; ${source2025}`,
|
||
conditions: [
|
||
'Operação localizada e tributável em Moçambique, no regime normal',
|
||
'Contraprestação completa; sem base especial do art.15(2), isenção ou taxa reduzida',
|
||
],
|
||
},
|
||
{
|
||
id: 'private_health',
|
||
label: 'Serviços médicos e sanitários privados',
|
||
field: '03',
|
||
rate: 5,
|
||
article: 'CIVA arts.15(10),17-A(a)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Serviço médico/sanitário ou operação estreitamente conexa',
|
||
'Prestador hospitalar privado, clínica, dispensário ou similar',
|
||
],
|
||
},
|
||
{
|
||
id: 'private_education',
|
||
label: 'Ensino privado reconhecido',
|
||
field: '03',
|
||
rate: 5,
|
||
article: 'CIVA arts.15(10),17-A(b)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Ensino ou bens/serviços conexos prestados pelo estabelecimento',
|
||
'Estabelecimento privado integrado no Sistema Nacional de Ensino e reconhecido pelo Ministério competente',
|
||
],
|
||
},
|
||
{
|
||
id: 'private_training',
|
||
label: 'Formação profissional privada',
|
||
field: '03',
|
||
rate: 5,
|
||
article: 'CIVA arts.15(10),17-A(c)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Formação profissional ou fornecimentos conexos pelo prestador, não venda autónoma de materiais',
|
||
'Prestador privado',
|
||
],
|
||
},
|
||
{
|
||
id: 'personal_lessons',
|
||
label: 'Lições ministradas a título pessoal',
|
||
field: '03',
|
||
rate: 5,
|
||
article: 'CIVA arts.15(10),17-A(d)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Lições ministradas a título pessoal',
|
||
'Matérias de ensino escolar ou superior',
|
||
],
|
||
},
|
||
{
|
||
id: 'export_goods',
|
||
label: 'Exportação de bens pelo vendedor',
|
||
field: '05',
|
||
rate: 0,
|
||
article: 'CIVA arts.13(1)(a),19(1)(b)(i)',
|
||
source: source2016,
|
||
conditions: [
|
||
'Bens expedidos/transportados para o estrangeiro pelo vendedor ou terceiro por sua conta',
|
||
'Saída documentada, ou pressuposto expresso da projecção; não mera venda a cliente estrangeiro',
|
||
],
|
||
},
|
||
{
|
||
id: 'housing_rent',
|
||
label: 'Locação de imóvel para habitação',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA art.9(5), redacção Lei22/2022; art.19(1)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Locação de imóvel para fins de habitação',
|
||
'Não é hospedagem nem locação comercial/industrial ou serviço autónomo',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_health',
|
||
label: 'Serviços médicos e sanitários públicos',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(a),10(1),19(1)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Serviço médico/sanitário ou operação estreitamente conexa',
|
||
'Estabelecimento hospitalar público, dispensário ou similar; natureza pública confirmada',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_education',
|
||
label: 'Ensino público reconhecido',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(3)(a),10(1),19(1)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Ensino ou fornecimentos conexos pelo estabelecimento',
|
||
'Estabelecimento público integrado no Sistema Nacional de Ensino e reconhecido pelo Ministério competente',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_training',
|
||
label: 'Formação profissional pública',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(3)(b),10(1),19(1)',
|
||
source: source2022,
|
||
conditions: [
|
||
'Formação profissional ou fornecimentos conexos pelo prestador',
|
||
'Prestador público',
|
||
],
|
||
},
|
||
{
|
||
id: 'financial_stamp',
|
||
label: 'Operação bancária/financeira sujeita a Selo',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA art.9(4), redacção Lei22/2022; art.19(1)',
|
||
source: `${source2022}; ${source2016}`,
|
||
conditions: [
|
||
'A operação é bancária ou financeira, não um serviço acessório autónomo',
|
||
'Sujeição a Imposto do Selo identificada para esta operação; não depende de calcular o Selo neste bloco',
|
||
],
|
||
},
|
||
{
|
||
id: 'insurance_stamp',
|
||
label: 'Seguro/resseguro ou mediação conexa sujeitos a Selo',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA art.9(6), redacção Lei22/2022; art.19(1)',
|
||
source: `${source2022}; ${source2016}`,
|
||
conditions: [
|
||
'Seguro/resseguro ou serviço conexo efectuado por corrector ou outro mediador de seguros',
|
||
'Sujeição a Imposto do Selo identificada para esta operação',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_waste',
|
||
label: 'Remoção de lixo pública ou por entidade contratada',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA art.9(12)(b), redacção Lei22/2022; art.19(1)',
|
||
source: `${source2022}; ${source2016}`,
|
||
conditions: [
|
||
'Prestação de serviço de remoção de lixo, não qualquer serviço de limpeza',
|
||
'Efectuada por entidade pública ou por entidade por ela contratada, com relação identificada',
|
||
],
|
||
},
|
||
{
|
||
id: 'public_funeral',
|
||
label: 'Serviços funerários/cremação públicos e bens acessórios',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA art.9(12)(c), redacção Lei22/2022; art.19(1)',
|
||
source: `${source2022}; ${source2016}`,
|
||
conditions: [
|
||
'Serviço funerário/cremação ou transmissão de bem acessório a esse serviço',
|
||
'Efectuado por entidade pública; não aplicar automaticamente a funerária privada',
|
||
],
|
||
},
|
||
{
|
||
id: 'health_assistive',
|
||
label: 'Bens de compensação da deficiência, próteses e braille',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(b),19(1)',
|
||
source: healthGoodsSource,
|
||
conditions: [
|
||
internalOperation,
|
||
'Bem identificado na enumeração: cadeira de rodas/veículo semelhante, aparelho/máquina/impressora braille, prótese ou material de compensação',
|
||
'Destino documentado conforme a categoria: deficiência, substituição de membro/órgão, fractura, pessoa invisual ou correcção da audição; não qualquer equipamento médico',
|
||
],
|
||
},
|
||
{
|
||
id: 'human_materials',
|
||
label: 'Órgãos, sangue e leite humanos',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(c),19(1)',
|
||
source: healthGoodsSource,
|
||
conditions: [
|
||
internalOperation,
|
||
'Transmissão de órgãos, sangue ou leite de origem humana, identificada; não produto de origem animal nem serviço autónomo',
|
||
],
|
||
},
|
||
{
|
||
id: 'patient_transport',
|
||
label: 'Transporte de doentes/feridos por organismo autorizado',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(d),19(1)',
|
||
source: healthGoodsSource,
|
||
conditions: [
|
||
internalOperation,
|
||
'Transporte de doente ou ferido em ambulância ou outro veículo apropriado',
|
||
'Organismo que efectua o transporte devidamente autorizado, com autorização identificada; não basta o destino hospitalar',
|
||
],
|
||
},
|
||
{
|
||
id: 'mosquito_nets',
|
||
label: 'Transmissão de redes mosquiteiras',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(e),19(1)',
|
||
source: healthGoodsSource,
|
||
conditions: [
|
||
internalOperation,
|
||
'Bem identificado como rede mosquiteira; não extensão automática a outros produtos de prevenção',
|
||
],
|
||
},
|
||
{
|
||
id: 'medicines',
|
||
label: 'Medicamentos e artigos terapêuticos/profilácticos elegíveis',
|
||
field: '07',
|
||
rate: 0,
|
||
article: 'CIVA arts.9(1)(f),19(1)',
|
||
source: healthGoodsSource,
|
||
conditions: [
|
||
internalOperation,
|
||
'Medicamento/especialidade farmacêutica ou produto farmacêutico exclusivamente terapêutico/profiláctico; ou pasta, gaze, algodão hidrófilo, tira/penso adesivo ou análogo',
|
||
'Finalidade elegível identificada: terapêutica/profiláctica nos produtos farmacêuticos; higiénica, medicinal ou cirúrgica nos artigos enumerados, mesmo impregnados/revestidos; não qualquer produto vendido por farmácia',
|
||
],
|
||
},
|
||
...([false, true] as const).flatMap((nonprofit) => {
|
||
const suffix = nonprofit ? 'nonprofit' : 'public',
|
||
entity = nonprofit
|
||
? 'organismo sem finalidade lucrativa'
|
||
: 'entidade pública';
|
||
return [
|
||
socialTreatment(
|
||
`social_assistance_${suffix}`,
|
||
`Assistência social — ${entity}`,
|
||
'a',
|
||
[
|
||
'Prestação de assistência social ou transmissão de bens estreitamente conexa, identificada',
|
||
'Fins e objecto da entidade reconhecidos pelas autoridades competentes, com fundamento identificado',
|
||
],
|
||
nonprofit,
|
||
),
|
||
socialTreatment(
|
||
`social_care_${suffix}`,
|
||
`Equipamentos sociais/creches/lares — ${entity}`,
|
||
'c',
|
||
[
|
||
'Prestação ou bem estreitamente conexo no exercício da actividade habitual de creche, jardim de infância, centro de tempos livres, lar ou outro equipamento social enumerado no art.9(2)(c)',
|
||
'Equipamento pertencente à entidade e fins/objecto reconhecidos pelas autoridades competentes; não qualquer alojamento ou empresa de cuidados',
|
||
],
|
||
nonprofit,
|
||
),
|
||
socialTreatment(
|
||
`activity_facilities_${suffix}`,
|
||
`Prática artística/desportiva/recreativa — ${entity}`,
|
||
'd',
|
||
[
|
||
'Serviço efectuado pela própria entidade que explora o estabelecimento ou instalação',
|
||
'Instalação destinada à prática artística, desportiva, recreativa ou de educação física e serviço prestado a quem a pratica; não isenção automática de bilhetes para espectadores',
|
||
],
|
||
nonprofit,
|
||
),
|
||
];
|
||
}),
|
||
{
|
||
id: 'foreign_operation',
|
||
label: 'Operação no estrangeiro — analisar territorialidade',
|
||
field: null,
|
||
rate: null,
|
||
article:
|
||
'CIVA art.6: localização por natureza da operação; art.19(1)(b)(ii) não determina sozinho a caixa06 do formulário',
|
||
source: source2016,
|
||
conditions: [],
|
||
},
|
||
{
|
||
id: 'other',
|
||
label: 'Outra operação / enquadramento ainda desconhecido',
|
||
field: null,
|
||
rate: null,
|
||
article:
|
||
'Fundamento específico ainda necessário; não presumir taxa ou isenção',
|
||
source: 'Sem fonte operacional determinada para esta linha',
|
||
conditions: [],
|
||
},
|
||
];
|
||
export type SalesOperation = {
|
||
id: string;
|
||
period: string;
|
||
description: string;
|
||
establishment: string;
|
||
amount: string;
|
||
basis: '' | 'net' | 'included';
|
||
treatment: string;
|
||
confirmed: number[];
|
||
evidence: string;
|
||
sourceAmount: string | null;
|
||
sourcePeriod: string;
|
||
sourceCurrency: string;
|
||
source: string;
|
||
taxOption?: ProductionTaxOption;
|
||
fundraiser?: FundraisingContext;
|
||
pharmaAnnexItem?: string;
|
||
annexIItem?: string;
|
||
annexIOnly?: boolean;
|
||
temporaryBenefitOrigin?: string;
|
||
relatedExport?: { id: string; description: string; treatment: string };
|
||
supplyCategory?: SupplyCategory;
|
||
specialRegime?: SpecialRegime;
|
||
splitSource?: {
|
||
id: string;
|
||
amount: string | null;
|
||
period: string;
|
||
currency: string;
|
||
basis: string;
|
||
};
|
||
};
|
||
export type SalesBook = {
|
||
currency: string;
|
||
rows: SalesOperation[];
|
||
periods: Array<{ period: string; complete: boolean }>;
|
||
result?: SalesResult;
|
||
history?: SalesResult[];
|
||
};
|
||
export type SalesLineResult = {
|
||
id: string;
|
||
field: string | null;
|
||
net: string | null;
|
||
vat: string | null;
|
||
gross: string | null;
|
||
rate: number | null;
|
||
status: 'calculated' | 'exempt' | 'indeterminate';
|
||
reason: string;
|
||
article: string;
|
||
source: string;
|
||
exact?: { net: FiscalFraction; vat: FiscalFraction; gross: FiscalFraction };
|
||
};
|
||
export type SalesResult = {
|
||
version: string;
|
||
revision: number;
|
||
calculatedAt: string;
|
||
opportunity: string;
|
||
location: string;
|
||
currency: string;
|
||
key: string;
|
||
inputs: SalesOperation[];
|
||
rows: SalesLineResult[];
|
||
periods: Array<{
|
||
period: string;
|
||
fields: Record<string, string | null>;
|
||
exactFields?: Record<string, FiscalFraction | null>;
|
||
reasons: string[];
|
||
}>;
|
||
rounding: string;
|
||
};
|
||
export const salesBaseFields = ['01', '03', '05', '06', '07'] as const;
|
||
/** One result identity belongs to at most one base field; this does not classify a new tax treatment. */
|
||
export function salesBaseMembership(
|
||
rows: readonly SalesLineResult[],
|
||
): Record<(typeof salesBaseFields)[number], SalesLineResult[]> {
|
||
const groups: Record<(typeof salesBaseFields)[number], SalesLineResult[]> =
|
||
{ '01': [], '03': [], '05': [], '06': [], '07': [] };
|
||
const seen = new Set<string>();
|
||
for (const row of rows) {
|
||
if (seen.has(row.id))
|
||
throw new Error(
|
||
'Mesma operação repetida no apuramento; não somar em05 e06 nem duas vezes no mesmo campo.',
|
||
);
|
||
seen.add(row.id);
|
||
if (row.field === null) continue;
|
||
if (
|
||
!salesBaseFields.includes(
|
||
row.field as (typeof salesBaseFields)[number],
|
||
)
|
||
)
|
||
throw new Error(
|
||
'Campo de base sem correspondência identificada no exemplar; conservar a operação antes de a somar.',
|
||
);
|
||
groups[row.field as (typeof salesBaseFields)[number]].push(row);
|
||
}
|
||
return groups;
|
||
}
|
||
|
||
type Store = ReturnType<typeof readAcquisitionStore> & {
|
||
salesOperations?: Record<string, SalesBook>;
|
||
};
|
||
export function newSalesOperation(
|
||
id: string,
|
||
period: string,
|
||
currency: string,
|
||
): SalesOperation {
|
||
return {
|
||
id,
|
||
period,
|
||
description: '',
|
||
establishment: '',
|
||
amount: '',
|
||
basis: '',
|
||
treatment: 'other',
|
||
confirmed: [],
|
||
evidence: '',
|
||
sourceAmount: null,
|
||
sourcePeriod: period,
|
||
sourceCurrency: currency,
|
||
source: 'Introdução directa; projecção, não declaração submetida',
|
||
};
|
||
}
|
||
export function continueAnnexIOperation(
|
||
row: SalesOperation,
|
||
targetId: string,
|
||
): SalesOperation {
|
||
const entry = annexIEntry(row.annexIItem),
|
||
target = operationTreatments.find((t) => t.id === targetId);
|
||
if (
|
||
row.treatment !== 'annexI_goods' ||
|
||
!entry?.relatedTreatments?.includes(targetId) ||
|
||
!target
|
||
)
|
||
throw new Error(
|
||
'O tratamento de continuidade não corresponde à posição escolhida; dados conservados.',
|
||
);
|
||
return {
|
||
...row,
|
||
treatment: targetId,
|
||
confirmed: [],
|
||
annexIOnly: false,
|
||
source: `${row.source}; continuidade expressamente escolhida a partir de ${annexIReference(entry)} para ${target.label}; rever condições do tratamento, sem alterar montante/moeda/contexto`,
|
||
};
|
||
}
|
||
export function continueTemporaryOperation(
|
||
row: SalesOperation,
|
||
targetId: string,
|
||
): SalesOperation {
|
||
const benefit = temporaryBenefit(row.treatment),
|
||
target = operationTreatments.find((item) => item.id === targetId);
|
||
if (!benefit || !target || temporaryBenefit(targetId))
|
||
throw new Error(
|
||
'Escolha um enquadramento actual distinto do benefício terminado; dados conservados.',
|
||
);
|
||
return {
|
||
...row,
|
||
treatment: targetId,
|
||
confirmed: [],
|
||
temporaryBenefitOrigin: benefit.id,
|
||
source: `${row.source}; continuidade expressamente escolhida de ${benefit.id}/${benefit.branch}, CIVA art.${benefit.article}, termo31/12/2025, ${temporaryBenefitVersion}, para ${target.label}; ${benefit.source}; montante/moeda/período conservados; rever condições, sem cálculo automático`,
|
||
};
|
||
}
|
||
export function referenceExportOperation(
|
||
row: SalesOperation,
|
||
related: SalesOperation,
|
||
): SalesOperation {
|
||
if (!isExportService(row.treatment) || row.id === related.id)
|
||
throw new Error(
|
||
'Escolha outra operação para referenciar neste serviço; dados conservados.',
|
||
);
|
||
return {
|
||
...row,
|
||
relatedExport: {
|
||
id: related.id,
|
||
description: related.description,
|
||
treatment: related.treatment,
|
||
},
|
||
confirmed: [],
|
||
};
|
||
}
|
||
export function salesBook(draft: Record<string, unknown>): SalesBook {
|
||
const store = readAcquisitionStore(draft.modelo_a_snapshot) as Store;
|
||
if (
|
||
store.salesOperations !== undefined &&
|
||
(!store.salesOperations ||
|
||
typeof store.salesOperations !== 'object' ||
|
||
Array.isArray(store.salesOperations))
|
||
)
|
||
throw new Error(
|
||
'Formato das operações IVA não recuperável; dados preservados, sem substituição.',
|
||
);
|
||
const book = store.salesOperations?.[acquisitionScope(draft)];
|
||
if (!book)
|
||
return {
|
||
currency: inputText(draft.currency ?? ''),
|
||
rows: [],
|
||
periods: [],
|
||
};
|
||
if (
|
||
typeof book.currency !== 'string' ||
|
||
!Array.isArray(book.rows) ||
|
||
!Array.isArray(book.periods) ||
|
||
book.rows.some(
|
||
(r) =>
|
||
!r ||
|
||
Object.keys(newSalesOperation('', '', '')).some((k) =>
|
||
k === 'confirmed'
|
||
? !Array.isArray(r.confirmed) ||
|
||
r.confirmed.some((c) => !Number.isInteger(c))
|
||
: k === 'sourceAmount'
|
||
? !(
|
||
r.sourceAmount === null ||
|
||
typeof r.sourceAmount === 'string'
|
||
)
|
||
: typeof r[k as keyof SalesOperation] !== 'string',
|
||
),
|
||
) ||
|
||
book.periods.some(
|
||
(p) =>
|
||
!p ||
|
||
!/^\d{4}-\d{2}$/.test(p.period) ||
|
||
typeof p.complete !== 'boolean',
|
||
)
|
||
)
|
||
throw new Error(
|
||
'Operações IVA não recuperáveis; os dados originais foram preservados.',
|
||
);
|
||
return book;
|
||
}
|
||
export function saveSalesBook(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
): string {
|
||
const store = readAcquisitionStore(draft.modelo_a_snapshot) as Store;
|
||
return JSON.stringify({
|
||
...store,
|
||
salesOperations: {
|
||
...store.salesOperations,
|
||
[acquisitionScope(draft)]: book,
|
||
},
|
||
});
|
||
}
|
||
export function reuseSales(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
): SalesBook {
|
||
const source = monthlySalesFromDraft(draft),
|
||
settings = readMonthlyIvaSettings(
|
||
inputText(draft.monthly_iva_settings ?? ''),
|
||
);
|
||
if (
|
||
!/^2026-(0[1-9]|1[0-2])$/.test(settings.start) ||
|
||
Number(settings.start.slice(5)) + source.amounts.length > 13
|
||
)
|
||
throw new Error(
|
||
'Defina o período da projecção dentro de2026 antes de reutilizar vendas.',
|
||
);
|
||
if (book.currency !== draft.currency)
|
||
throw new Error(
|
||
'Moeda da origem divergente; valores preservados, sem conversão.',
|
||
);
|
||
const rows = [...book.rows],
|
||
periods = [...book.periods];
|
||
source.amounts.forEach((value, index) => {
|
||
const period = `2026-${String(Number(settings.start.slice(5)) + index).padStart(2, '0')}`;
|
||
if (!periods.some((p) => p.period === period))
|
||
periods.push({ period, complete: false });
|
||
if (
|
||
!rows.some(
|
||
(r) =>
|
||
r.sourcePeriod === period &&
|
||
r.source !==
|
||
'Introdução directa; projecção, não declaração submetida',
|
||
)
|
||
)
|
||
rows.push({
|
||
...newSalesOperation(`sales-${period}`, period, book.currency),
|
||
description: `Vendas do plano — ${period}`,
|
||
amount: value ?? '',
|
||
basis: settings.basis === 'unknown' ? '' : settings.basis,
|
||
sourceAmount: value,
|
||
source: source.source,
|
||
evidence: source.source,
|
||
});
|
||
});
|
||
return { ...book, rows, periods };
|
||
}
|
||
export function splitSales(
|
||
book: SalesBook,
|
||
id: string,
|
||
newId: string,
|
||
): SalesBook {
|
||
const row = book.rows.find((r) => r.id === id);
|
||
if (!row) throw new Error('Operação de origem não encontrada.');
|
||
if (row.sourceAmount === null && !row.splitSource)
|
||
throw new Error(
|
||
'Reparta uma venda reutilizada com valor de origem determinado; para entradas directas acrescente as operações em falta.',
|
||
);
|
||
const source = row.splitSource ?? {
|
||
id: row.id,
|
||
amount: row.sourceAmount ?? row.amount,
|
||
period: row.period,
|
||
currency: row.sourceCurrency,
|
||
basis: row.basis,
|
||
};
|
||
if (
|
||
!source.basis ||
|
||
source.amount === null ||
|
||
amount(source.amount) === null
|
||
)
|
||
throw new Error(
|
||
'Determine o valor e a representação do preço antes de repartir a origem.',
|
||
);
|
||
const first = {
|
||
...row,
|
||
amount: '',
|
||
sourceAmount: null,
|
||
splitSource: source,
|
||
source: `Repartição explícita de ${source.id}: ${source.amount} ${source.currency}; ${row.source}`,
|
||
};
|
||
return {
|
||
...book,
|
||
rows: [
|
||
...book.rows.map((r) => (r.id === id ? first : r)),
|
||
{
|
||
...first,
|
||
id: newId,
|
||
description: 'Parcela adicional da mesma venda',
|
||
treatment: 'other',
|
||
confirmed: [],
|
||
evidence: '',
|
||
},
|
||
],
|
||
periods: book.periods.map((p) =>
|
||
p.period === row.period ? { ...p, complete: false } : p,
|
||
),
|
||
};
|
||
}
|
||
export function recordSales(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
): SalesBook {
|
||
return {
|
||
...book,
|
||
history: book.result
|
||
? [...(book.history ?? []), book.result]
|
||
: book.history,
|
||
result: calculateSales(draft, book),
|
||
};
|
||
}
|
||
function amount(raw: unknown): bigint | null {
|
||
if (typeof raw !== 'string' || !/^\d{1,15}(\.\d{1,6})?$/.test(raw.trim()))
|
||
return null;
|
||
const [n, d = ''] = raw.trim().split('.');
|
||
return BigInt(n) * 1000000n + BigInt(d.padEnd(6, '0'));
|
||
}
|
||
function money(n: bigint): string {
|
||
return `${n / 1000000n}.${(n % 1000000n).toString().padStart(6, '0').replace(/0+$/, '').padEnd(2, '0')}`;
|
||
}
|
||
function sum(values: Array<string | null>): string | null {
|
||
const parsed = values.map(amount);
|
||
return parsed.some((v) => v === null)
|
||
? null
|
||
: money((parsed as bigint[]).reduce((a, b) => a + b, 0n));
|
||
}
|
||
export function evaluateSalesOperation(
|
||
row: SalesOperation,
|
||
currency: string,
|
||
): SalesLineResult {
|
||
const rule = operationTreatments.find((r) => r.id === row.treatment),
|
||
n = amount(row.amount);
|
||
const annex =
|
||
row.treatment === 'pharma_manufacturing_inputs'
|
||
? pharmaAnnexEntry(row.pharmaAnnexItem)
|
||
: undefined;
|
||
const annexI =
|
||
row.treatment === 'annexI_goods'
|
||
? annexIEntry(row.annexIItem)
|
||
: undefined;
|
||
const result: SalesLineResult = {
|
||
id: row.id,
|
||
field: rule?.field ?? null,
|
||
net: null,
|
||
vat: null,
|
||
gross: null,
|
||
rate: rule?.rate ?? null,
|
||
status: 'indeterminate',
|
||
reason: '',
|
||
article: rule?.article ?? 'Tratamento desconhecido',
|
||
source:
|
||
(rule?.source ?? 'Fonte em falta') +
|
||
(annex ? `; ${pharmaAnnexReference(annex)}` : '') +
|
||
(annexI ? `; ${annexIReference(annexI)}` : ''),
|
||
};
|
||
const stop = (reason: string) => ({ ...result, reason });
|
||
if (currency !== 'MZN' || row.sourceCurrency !== currency)
|
||
return stop(
|
||
'Moeda fora de MZN ou divergente da origem; sem conversão silenciosa.',
|
||
);
|
||
if (!/^2026-(0[1-9]|1[0-2])$/.test(row.period))
|
||
return stop(
|
||
'Período fora da cobertura de2026; sem cálculo histórico presumido.',
|
||
);
|
||
if (temporaryBenefit(row.treatment))
|
||
return stop(temporaryBenefitNotice(row.treatment, row.period));
|
||
const regimeIssue = specialRegimeIssue(row.treatment, row.specialRegime);
|
||
if (regimeIssue) return { ...stop(regimeIssue), field: null, rate: null };
|
||
const supplyIssue = onboardSupplyIssue(row.treatment, row.supplyCategory);
|
||
if (supplyIssue) return { ...stop(supplyIssue), field: null, rate: null };
|
||
if (!rule || rule.rate === null || !rule.field)
|
||
return stop(
|
||
'Operação não calculada: delimite incidência, territorialidade e fundamento desta linha. A remissão do campo06 não constitui uma regra de classificação de todas as operações no estrangeiro.',
|
||
);
|
||
if (n === null || !['net', 'included'].includes(row.basis))
|
||
return stop(
|
||
'Montante ou indicação de IVA incluído/sem IVA em falta; vazio não significa zero.',
|
||
);
|
||
if (!row.description.trim() || !row.establishment.trim())
|
||
return stop(
|
||
'Indique a operação e o estabelecimento a que pertence; não inferidos da localização do investimento.',
|
||
);
|
||
if (
|
||
rule.conditions.some((_, i) => !row.confirmed.includes(i)) ||
|
||
!row.evidence.trim()
|
||
)
|
||
return stop(
|
||
'Condições da operação ou fundamento/pressuposto em falta; o sector não prova o tratamento fiscal.',
|
||
);
|
||
if (row.sourceAmount !== null && amount(row.sourceAmount) !== n)
|
||
return stop(
|
||
'Divergência com o valor reutilizado: conserve a origem e prepare a repartição explicitamente; não substituir silenciosamente.',
|
||
);
|
||
if (row.treatment === 'pharma_manufacturing_inputs' && !annex)
|
||
return stop(
|
||
'Escolha a linha aplicável do Anexo III/2022, com código e designação; um código livre, desconhecido ou de outro anexo não comprova a isenção.',
|
||
);
|
||
if (row.treatment === 'annexI_goods') {
|
||
if (!annexI)
|
||
return stop(
|
||
'Escolha a posição aplicável do Anexo I/2022, com código, designação e qualificadores; não presumir isenção por sector ou código livre.',
|
||
);
|
||
if (annexI.relatedRequired)
|
||
return stop(
|
||
'Esta designação tem tratamento específico já disponível no artigo9(10), com direito à dedução nas condições do19(1)(b)(v). Escolha a continuidade indicada e confirme as suas condições; não atribuir07 apenas pela presença no Anexo I.',
|
||
);
|
||
if (annexI.relatedTreatments?.length && row.annexIOnly !== true)
|
||
return stop(
|
||
'A natureza ou o destino deste bem pode corresponder a outro tratamento já disponível. Escolha a continuidade aplicável ou fundamente expressamente por que a operação pertence apenas a9(12)(h); desconhecido não significa ausência desse enquadramento.',
|
||
);
|
||
}
|
||
if (
|
||
row.treatment === 'fundraising_public' ||
|
||
row.treatment === 'fundraising_nonprofit'
|
||
) {
|
||
const error = fundraisingError(
|
||
row.fundraiser,
|
||
row.treatment === 'fundraising_nonprofit',
|
||
);
|
||
if (error) return stop(error);
|
||
}
|
||
const option =
|
||
row.treatment.startsWith('production_') &&
|
||
row.treatment.endsWith('_opted')
|
||
? productionOptionCheck(row.taxOption, row.period)
|
||
: {};
|
||
if (option.error) return stop(option.error);
|
||
if (row.basis === 'included' && rule.rate === 5) {
|
||
const exact = {
|
||
net: fraction(n * 20n, 21000000n),
|
||
vat: fraction(n, 21000000n),
|
||
gross: fraction(n, 1000000n),
|
||
};
|
||
return {
|
||
...result,
|
||
net: fractionPresentation(exact.net).decimal,
|
||
vat: fractionPresentation(exact.vat).decimal,
|
||
gross: money(n),
|
||
exact,
|
||
status: 'calculated',
|
||
reason: 'Base e IVA conservados como fracções exactas (preço×20/21 e preço/21). Decimal a6casas é apresentação técnica, metade para cima; não é regra de arredondamento da declaração nem imposto final a pagar.',
|
||
};
|
||
}
|
||
let net = n,
|
||
vat = 0n;
|
||
if (rule.rate === 16 && row.basis === 'included') {
|
||
net = ((n * 100n + 58000000n) / 116000000n) * 1000000n;
|
||
vat = n - net;
|
||
if (vat < 0n)
|
||
return stop(
|
||
'Arredondamento à unidade produz IVA negativo para este montante; reveja a linha e a documentação.',
|
||
);
|
||
} else vat = (n * BigInt(rule.rate) + 50n) / 100n;
|
||
return {
|
||
...result,
|
||
net: money(net),
|
||
vat: money(vat),
|
||
gross: money(row.basis === 'included' ? n : net + vat),
|
||
status: rule.rate === 0 ? 'exempt' : 'calculated',
|
||
reason:
|
||
(rule.rate === 0
|
||
? rule.field === '05'
|
||
? 'Isenção com direito à dedução nas condições legais; não é taxa zero nem dedução automática de compras.'
|
||
: 'Isenção sem direito à dedução para esta operação; não é isenção geral da entidade.'
|
||
: 'IVA da operação projectada; não é imposto final a pagar nem declaração submetida.') +
|
||
(option.summary ? ' ' + option.summary : ''),
|
||
};
|
||
}
|
||
function key(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
catalog = operationTreatments.filter((t) =>
|
||
book.rows.some((r) => r.treatment === t.id),
|
||
),
|
||
): string {
|
||
return JSON.stringify([
|
||
operationVersion,
|
||
catalog,
|
||
acquisitionScope(draft),
|
||
draft.currency,
|
||
draft.province_id,
|
||
draft.district_id,
|
||
draft.business_plan_snapshot,
|
||
draft.cashflow_snapshot,
|
||
draft.monthly_iva_settings,
|
||
book.currency,
|
||
book.rows,
|
||
book.periods,
|
||
]);
|
||
}
|
||
export function salesCurrent(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
): boolean {
|
||
return (
|
||
!!book.result &&
|
||
book.result.version === operationVersion &&
|
||
book.result.opportunity === acquisitionScope(draft) &&
|
||
(book.result.key === key(draft, book) ||
|
||
book.result.key ===
|
||
key(
|
||
draft,
|
||
book,
|
||
operationTreatments.filter((t) =>
|
||
legacyV2TreatmentIds.includes(t.id),
|
||
),
|
||
))
|
||
);
|
||
}
|
||
export function calculateSales(
|
||
draft: Record<string, unknown>,
|
||
book: SalesBook,
|
||
now = new Date().toISOString(),
|
||
): SalesResult {
|
||
if (book.currency !== 'MZN' || draft.currency !== book.currency)
|
||
throw new Error(
|
||
'Confirme a moeda original MZN; sem conversão ou reinterpretação.',
|
||
);
|
||
if (
|
||
new Set(book.rows.map((r) => r.id)).size !== book.rows.length ||
|
||
new Set(book.periods.map((p) => p.period)).size !== book.periods.length
|
||
)
|
||
throw new Error(
|
||
'Identificadores/períodos duplicados; corrija sem somar em duplicado.',
|
||
);
|
||
if (
|
||
!book.periods.length ||
|
||
book.rows.some(
|
||
(r) => !book.periods.some((p) => p.period === r.period),
|
||
) ||
|
||
book.periods.some((p) => !/^2026-(0[1-9]|1[0-2])$/.test(p.period))
|
||
)
|
||
throw new Error(
|
||
'Cada operação deve pertencer a um período válido de2026 incluído na cobertura.',
|
||
);
|
||
let live: ReturnType<typeof monthlySalesFromDraft> | null = null;
|
||
try {
|
||
live = monthlySalesFromDraft(draft);
|
||
} catch {
|
||
/* Explicitly block stale imported values below. */
|
||
}
|
||
const start = readMonthlyIvaSettings(
|
||
inputText(draft.monthly_iva_settings ?? ''),
|
||
).start;
|
||
const rows = book.rows.map((r) => {
|
||
const result = evaluateSalesOperation(r, book.currency);
|
||
if (isExportService(r.treatment) && r.relatedExport !== undefined) {
|
||
const ref = r.relatedExport,
|
||
target =
|
||
ref && typeof ref === 'object'
|
||
? book.rows.find(
|
||
(item) => item.id === ref.id && item.id !== r.id,
|
||
)
|
||
: undefined;
|
||
if (
|
||
!target ||
|
||
ref.description !== target.description ||
|
||
ref.treatment !== target.treatment
|
||
)
|
||
return {
|
||
...result,
|
||
net: null,
|
||
vat: null,
|
||
gross: null,
|
||
status: 'indeterminate' as const,
|
||
reason: 'A operação relacionada foi removida ou alterada. Actualize a referência e reveja as condições; preço do serviço e resultado anterior conservados.',
|
||
};
|
||
}
|
||
const fundraiser = r.fundraiser;
|
||
if (
|
||
r.treatment.startsWith('fundraising_') &&
|
||
fundraiser &&
|
||
book.rows.some(
|
||
(v) =>
|
||
v.id !== r.id &&
|
||
v.treatment.startsWith('fundraising_') &&
|
||
v.period.slice(0, 4) === r.period.slice(0, 4) &&
|
||
String(v.fundraiser?.entityReference ?? '').trim() ===
|
||
String(fundraiser.entityReference ?? '').trim() &&
|
||
v.fundraiser?.annualEvents !== fundraiser.annualEvents,
|
||
)
|
||
)
|
||
return {
|
||
...result,
|
||
net: null,
|
||
vat: null,
|
||
gross: null,
|
||
status: 'indeterminate' as const,
|
||
reason: 'Totais anuais divergentes para a mesma entidade; resolva a divergência sem substituir dados de outra linha.',
|
||
};
|
||
if (r.splitSource) {
|
||
const source = r.splitSource,
|
||
group = book.rows.filter(
|
||
(v) => v.splitSource?.id === source.id,
|
||
),
|
||
index = Number(source.period.slice(5)) - Number(start.slice(5));
|
||
if (
|
||
group.some(
|
||
(v) =>
|
||
v.period !== source.period ||
|
||
v.basis !== source.basis ||
|
||
v.sourceCurrency !== source.currency ||
|
||
JSON.stringify(v.splitSource) !==
|
||
JSON.stringify(source),
|
||
) ||
|
||
sum(group.map((v) => v.amount)) !==
|
||
(amount(source.amount) === null
|
||
? null
|
||
: money(amount(source.amount)!)) ||
|
||
!live ||
|
||
live.amounts[index] !== source.amount
|
||
)
|
||
return {
|
||
...result,
|
||
exact: undefined,
|
||
net: null,
|
||
vat: null,
|
||
gross: null,
|
||
status: 'indeterminate' as const,
|
||
reason: 'Repartição não reconciliada com o total, período, moeda e representação do preço da origem; nenhuma parcela foi presumida.',
|
||
};
|
||
}
|
||
if (
|
||
r.sourceAmount !== null ||
|
||
r.source.startsWith('Vendas por produto') ||
|
||
r.source.startsWith('Receita mensal')
|
||
) {
|
||
const index =
|
||
Number(r.sourcePeriod.slice(5)) - Number(start.slice(5));
|
||
if (
|
||
!live ||
|
||
!start.startsWith('2026-') ||
|
||
r.sourcePeriod !== r.period ||
|
||
live.amounts[index] !== r.sourceAmount
|
||
)
|
||
return {
|
||
...result,
|
||
exact: undefined,
|
||
net: null,
|
||
vat: null,
|
||
gross: null,
|
||
status: 'indeterminate' as const,
|
||
reason: 'A origem/período das vendas mudou. Reveja e confirme a nova origem; o resultado anterior foi conservado.',
|
||
};
|
||
}
|
||
return result;
|
||
});
|
||
const periods = book.periods.map((p) => {
|
||
const matching = book.rows
|
||
.map((r, i) => ({ input: r, result: rows[i] }))
|
||
.filter((r) => r.input.period === p.period),
|
||
fields: Record<string, string | null> = {},
|
||
exactFields: Record<string, FiscalFraction | null> = {},
|
||
reasons = matching
|
||
.filter((r) => r.result.status === 'indeterminate')
|
||
.map(
|
||
(r) =>
|
||
`${r.input.description || r.input.id}: ${r.result.reason}`,
|
||
);
|
||
const known =
|
||
p.complete &&
|
||
!matching.some((r) => r.result.status === 'indeterminate');
|
||
const membership = salesBaseMembership(matching.map((r) => r.result));
|
||
for (const f of salesBaseFields)
|
||
fields[f] = known ? sum(membership[f].map((r) => r.net)) : null;
|
||
for (const [f, base] of [
|
||
['02', '01'],
|
||
['04', '03'],
|
||
])
|
||
fields[f] = known
|
||
? sum(
|
||
matching
|
||
.filter((r) => r.result.field === base)
|
||
.map((r) => r.result.vat),
|
||
)
|
||
: null;
|
||
if (matching.some((r) => r.result.exact)) {
|
||
for (const [f, base, part] of [
|
||
['01', '01', 'net'],
|
||
['02', '01', 'vat'],
|
||
['03', '03', 'net'],
|
||
['04', '03', 'vat'],
|
||
['05', '05', 'net'],
|
||
['06', '06', 'net'],
|
||
['07', '07', 'net'],
|
||
] as const) {
|
||
exactFields[f] = known
|
||
? addFractions(
|
||
matching
|
||
.filter((r) => r.result.field === base)
|
||
.map(
|
||
(r) =>
|
||
r.result.exact?.[part] ??
|
||
decimalFraction(r.result[part]!),
|
||
),
|
||
)
|
||
: null;
|
||
fields[f] = exactFields[f]
|
||
? fractionPresentation(exactFields[f]!).decimal
|
||
: null;
|
||
}
|
||
}
|
||
if (!p.complete)
|
||
reasons.push(
|
||
'Confirme que todas as operações do período foram incluídas; uma lista vazia só é zero após essa confirmação.',
|
||
);
|
||
return {
|
||
period: p.period,
|
||
fields,
|
||
...(Object.keys(exactFields).length ? { exactFields } : {}),
|
||
reasons,
|
||
};
|
||
});
|
||
const labels = draft.selection_labels as
|
||
| Record<string, unknown>
|
||
| undefined;
|
||
return {
|
||
version: operationVersion,
|
||
revision: (book.result?.revision ?? 0) + 1,
|
||
calculatedAt: now,
|
||
opportunity: acquisitionScope(draft),
|
||
location: ['province_id', 'district_id']
|
||
.filter((k) => draft[k])
|
||
.map((k) => String(labels?.[k] ?? draft[k]))
|
||
.join(' / '),
|
||
currency: book.currency,
|
||
key: key(draft, book),
|
||
inputs: structuredClone(book.rows),
|
||
rows,
|
||
periods,
|
||
rounding:
|
||
'Base líquida: precisão técnica6decimais, metade para cima, sem arredondamento final da declaração. Preço geral com IVA: base=preço×100/116, arredondada à unidade mais próxima (metade para cima), IVA=preço−base, CIVA art.34 redacção Lei10/2025. Preço incluído5%: fracções exactas preço×20/21 e preço/21 conservadas; decimal a6casas/metade para cima apenas na apresentação. A regra declarativa não é presumida.',
|
||
};
|
||
}
|
||
export function salesReportRows(
|
||
draft: Record<string, unknown>,
|
||
): Array<[string, string, string, string]> {
|
||
try {
|
||
const book = salesBook(draft),
|
||
r = book.result;
|
||
if (!r) return [];
|
||
const context = `${r.currency}; oportunidade ${r.opportunity}; ${r.location || 'localização não indicada'}; ${r.version}; revisão ${r.revision}; ${r.calculatedAt}`;
|
||
const out: Array<[string, string, string, string]> = [
|
||
[
|
||
'IVA mensal',
|
||
'Operações IVA L1',
|
||
salesCurrent(draft, book)
|
||
? 'Resultado correspondente às entradas'
|
||
: 'Histórico desactualizado; não aplicar à projecção actual',
|
||
context,
|
||
],
|
||
[
|
||
'IVA mensal',
|
||
'Método e limites L1',
|
||
r.rounding,
|
||
'Sem apuramento integral, dedução automática ou submissão AT',
|
||
],
|
||
];
|
||
for (const row of r.inputs) {
|
||
const value = r.rows.find((v) => v.id === row.id)!;
|
||
out.push([
|
||
'IVA mensal',
|
||
`${row.period} — ${row.description} — ${row.establishment}`,
|
||
`Declarado ${row.amount || 'em falta'} ${row.sourceCurrency}; ${row.basis === 'included' ? 'IVA incluído' : row.basis === 'net' ? 'sem IVA' : 'base desconhecida'}; líquido ${value.net ?? 'Indeterminado'}; IVA ${value.vat ?? 'Indeterminado'}; bruto ${value.gross ?? 'Indeterminado'}; campo ${value.field ?? 'não determinado'}; ${value.reason}`,
|
||
`${context}; ${value.article}; ${value.source}; origem: ${row.source}; pressuposto/documento: ${row.evidence || 'em falta'}; condições confirmadas: ${row.confirmed.join(',') || 'nenhuma'}`,
|
||
]);
|
||
}
|
||
for (const row of r.inputs.filter((v) => v.fundraiser))
|
||
out.push([
|
||
'IVA mensal',
|
||
`Manifestação ocasional — ${row.description}`,
|
||
`Entidade ${row.fundraiser!.entityReference}; ano ${row.period.slice(0, 4)}; total anual declarado ${row.fundraiser!.annualEvents}; manifestação ${row.fundraiser!.eventNumber}; actividade ${row.fundraiser!.activity}`,
|
||
`${context}; dados declarados para a entidade, incluindo manifestações fora deste dossier; não é contagem automática das vendas.`,
|
||
]);
|
||
for (const row of r.inputs.filter(
|
||
(v) => isExportService(v.treatment) && v.relatedExport,
|
||
))
|
||
out.push([
|
||
'IVA mensal',
|
||
`Operação relacionada — ${row.description}`,
|
||
`${row.relatedExport!.description}; ID ${row.relatedExport!.id}; tratamento ${row.relatedExport!.treatment}`,
|
||
`${context}; referência escolhida expressamente nesta oportunidade,sem copiar valor para preço do serviço nem provar automaticamente o enquadramento subjacente`,
|
||
]);
|
||
for (const row of r.inputs.filter((v) => isOnboardSupply(v.treatment)))
|
||
out.push([
|
||
'IVA mensal',
|
||
`Natureza do abastecimento — ${row.description}`,
|
||
supplyCategories.find(
|
||
([value]) => value === row.supplyCategory,
|
||
)?.[1] ?? 'Por identificar',
|
||
`${context}; CIVA13(2–3); natureza conservada no cálculo/relatório, sem aplicar dedução ou restituição automaticamente`,
|
||
]);
|
||
for (const row of r.inputs.filter((v) => isSpecialRegime(v.treatment)))
|
||
out.push([
|
||
'IVA mensal',
|
||
`Regime especial — ${row.description}`,
|
||
specialRegimeOptions.find(
|
||
([value]) => value === row.specialRegime,
|
||
)?.[1] ?? 'Por identificar',
|
||
`${context}; CIVA14(1)(b/c/d),19(1)(b)(iv); regime declarado/documentado ou pressuposto expresso da projecção; não certifica admissão ou permanência, nem liquida importação ou saída do regime`,
|
||
]);
|
||
for (const value of r.rows.filter((v) => v.exact))
|
||
out.push([
|
||
'IVA mensal',
|
||
`Precisão exacta — ${value.id}`,
|
||
`Líquido ${fractionLabel(value.exact!.net)} MZN; IVA ${fractionLabel(value.exact!.vat)} MZN; bruto ${fractionLabel(value.exact!.gross)} MZN`,
|
||
`${context}; apresentação decimal a6casas, aproximação quando a fracção não termina; não aplicar à declaração sem a sua regra própria.`,
|
||
]);
|
||
for (const p of r.periods) {
|
||
out.push([
|
||
'IVA mensal',
|
||
`Operações L1 — ${p.period}`,
|
||
Object.entries(p.fields)
|
||
.map(([f, v]) => `${f}: ${v ?? 'Indeterminado'}`)
|
||
.join('; '),
|
||
`${context}; ${p.reasons.join(' ')}`,
|
||
]);
|
||
if (p.exactFields)
|
||
out.push([
|
||
'IVA mensal',
|
||
`Precisão exacta do período — ${p.period}`,
|
||
Object.entries(p.exactFields)
|
||
.map(
|
||
([f, v]) =>
|
||
`${f}: ${v ? fractionLabel(v) : 'Indeterminado'}`,
|
||
)
|
||
.join('; '),
|
||
`${context}; totais em fracções MZN, calculados antes da apresentação decimal, sem regra declarativa presumida.`,
|
||
]);
|
||
}
|
||
return out;
|
||
} catch {
|
||
return [
|
||
[
|
||
'IVA mensal',
|
||
'Operações IVA L1',
|
||
'Não foi possível recuperar as operações guardadas',
|
||
'Originais preservados; sem substituição por zero',
|
||
],
|
||
];
|
||
}
|
||
}
|