import { acquisitionScope, readAcquisitionStore } from './iva-acquisitions.ts'; export const goodsLocationVersion = 'goods-territoriality/1'; export const goodsLocationSource = 'CIVA republicado Lei13/2016,6(1)/(2),PDF7/p1426(265):localização dos bens no território nacional ao iniciar transporte/expedição para o adquirente ou,sem transporte/expedição,ao pôr à sua disposição. Não obstante6(1),são também tributáveis transmissões pelo importador e subsequentes de bens transportados/expedidos do estrangeiro,antes da importação. Lei10/2025PDF12mantém6(1)/(2),altera6(7–9)quanto a digitais;recorte2026,bens corpóreos,não digitais. Endereço/nacionalidade do cliente não substituem localização dos bens. Classificação territorial apenas,não isenção/taxa/IVA/dedução/campo11/direitos aduaneiros ou totaisModeloA.'; export const goodsLocationTreatments = [ { id: 'goods_with_transport', label: 'Bens com transporte ou expedição para o adquirente', }, { id: 'goods_without_transport', label: 'Bens sem transporte ou expedição', }, { id: 'goods_before_import', label: 'Transmissão pelo importador ou subsequente,antes da importação', }, ] as const; export const goodsLocationConditions = [ [ 'goodsSupply', 'Está identificada uma transmissão de bens corpóreos,não uma prestação de serviços ou bens digitais abrangidos por regras próprias', ], [ 'factsCorrespond', 'Os factos,referências e âmbito correspondem a esta operação e período,não apenas ao endereço ou nacionalidade do adquirente', ], ] as const; export const goodsOwnConditions = { goods_with_transport: [ [ 'transportToBuyer', 'Os bens são transportados ou expedidos para o adquirente', ], [ 'notBeforeImport', 'Não corresponde à transmissão pré-importação abrangida pelo artigo6(2)', ], ], goods_without_transport: [ [ 'noTransport', 'Não há expedição ou transporte dos bens para esta transmissão', ], [ 'notBeforeImport', 'Não corresponde à transmissão pré-importação abrangida pelo artigo6(2)', ], ], goods_before_import: [ ['fromAbroad', 'Os bens são transportados ou expedidos do estrangeiro'], [ 'nationalImport', 'Está identificada a importação desses bens para o território nacional', ], [ 'importerChain', 'A transmissão é feita pelo importador ou é subsequente na cadeia desses bens', ], [ 'beforeImport', 'Esta transmissão ocorre antes da importação identificada', ], ], } as const; export function ownGoodsConditions( treatment: string, ): ReadonlyArray { return ( goodsOwnConditions[treatment as keyof typeof goodsOwnConditions] ?? [] ); } type Choice = '' | 'yes' | 'no'; export type GoodsLocationRow = { id: string; treatment: string; description: string; period: string; origin: 'projected' | 'effective'; operationDate: string; operationRef: string; documentRef: string; documentDate: string; goodsPlace: string; placeCountry: string; placeRef: string; transportRef: string; importRef: string; chainRef: string; scope: string; evidence: string; conditions: Partial>; }; type LineResult = { id: string; status: 'determined' | 'not_applicable' | 'indeterminate'; territory: 'national' | 'outside' | null; baseAmount: null; vat: null; rate: null; reason: string; }; export type GoodsLocationCalculation = { version: string; revision: number; calculatedAt: string; opportunity: string; location: string; sourceKey: string; source: string; inputs: GoodsLocationRow[]; rows: LineResult[]; }; export type GoodsLocationBook = { rows: GoodsLocationRow[]; result?: GoodsLocationCalculation; history?: GoodsLocationCalculation[]; }; type Store = ReturnType & { goodsLocations?: Record; }; export function newGoodsLocation(id: string): GoodsLocationRow { return { id, treatment: '', description: '', period: '', origin: 'projected', operationDate: '', operationRef: '', documentRef: '', documentDate: '', goodsPlace: '', placeCountry: '', placeRef: '', transportRef: '', importRef: '', chainRef: '', scope: '', evidence: '', conditions: {}, }; } export function switchGoodsTreatment( row: GoodsLocationRow, treatment: string, ): GoodsLocationRow { return { ...row, treatment, conditions: Object.fromEntries( goodsLocationConditions.map(([key]) => [ key, row.conditions[key] ?? '', ]), ), }; } function readStore(draft: Record): Store { const store = readAcquisitionStore(draft.modelo_a_snapshot) as Store; if ( store.goodsLocations !== undefined && (!store.goodsLocations || typeof store.goodsLocations !== 'object' || Array.isArray(store.goodsLocations)) ) throw new Error( 'Bases de contraprestação não recuperáveis;originais preservados.', ); const fields = Object.keys(newGoodsLocation('')).filter( (key) => key !== 'conditions', ); for (const book of Object.values(store.goodsLocations ?? {})) if ( !book || !Array.isArray(book.rows) || (book.history !== undefined && !Array.isArray(book.history)) || book.rows.some( (row) => !row || fields.some( (key) => typeof row[key as keyof GoodsLocationRow] !== 'string', ) || !row.conditions || typeof row.conditions !== 'object' || Array.isArray(row.conditions), ) ) throw new Error( 'Linhas de contraprestação não recuperáveis;originais preservados,sem substituição por lista vazia.', ); return store; } export function goodsLocationBook( draft: Record, ): GoodsLocationBook { return ( readStore(draft).goodsLocations?.[acquisitionScope(draft)] ?? { rows: [], } ); } export function saveGoodsLocationBook( draft: Record, book: GoodsLocationBook, ): string { const current = readStore(draft), encoded = JSON.stringify({ ...current, goodsLocations: { ...current.goodsLocations, [acquisitionScope(draft)]: book, }, }); readStore({ ...draft, modelo_a_snapshot: encoded }); return encoded; } function validDate(value: string): boolean { const d = new Date(value + 'T00:00:00Z'); return ( /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(d.getTime()) && d.toISOString().slice(0, 10) === value ); } export function evaluateGoodsLocation(row: GoodsLocationRow): LineResult { const result: LineResult = { id: row.id, status: 'indeterminate', territory: null, baseAmount: null, vat: null, rate: null, reason: '', }; const stop = ( reason: string, status: LineResult['status'] = 'indeterminate', ) => ({ ...result, status, reason }); const rule = goodsLocationTreatments.find((t) => t.id === row.treatment); if (!rule) return stop( 'Identifique transporte,ausência de transporte ou transmissão antes da importação;desconhecido não determina território.', ); if ( !/^2026-(0[1-9]|1[0-2])$/.test(row.period) || !['projected', 'effective'].includes(row.origin) || !validDate(row.operationDate) || row.operationDate.slice(0, 7) !== row.period ) return stop( 'Identifique mês em2026 e data da operação correspondentes,com dados efectivos declarados ou pressupostos expressos de projecção.', ); const required = [ ...goodsLocationConditions, ...ownGoodsConditions(row.treatment), ]; const failed = required.filter(([key]) => row.conditions[key] === 'no'); if (failed.length) return stop( 'Este percurso não se aplica: ' + failed.map(([, label]) => label).join('; ') + '. Rever o percurso,não classificar automaticamente como fora do território ou isento.', 'not_applicable', ); if (required.some(([key]) => row.conditions[key] !== 'yes')) return stop( 'Confirme os factos próprios deste percurso;desconhecido não é fora do território,isenção ou IVA zero.', ); if ( !row.description.trim() || !row.operationRef.trim() || !row.documentRef.trim() || !validDate(row.documentDate) || !row.scope.trim() || !row.evidence.trim() ) return stop( 'Identifique bens/operação,documento/data,âmbito e factos ou pressupostos expressos;reutilize referências existentes,sem certificado/aprovador novo.', ); if (row.treatment === 'goods_before_import') { if ( !row.transportRef.trim() || !row.importRef.trim() || !row.chainRef.trim() ) return stop( 'Identifique transporte/expedição do estrangeiro,importação nacional e posição da transmissão na cadeia,ou pressupostos expressos;não inferir a condição pelo endereço do adquirente.', ); return { ...result, status: 'determined', territory: 'national', reason: `Art6(2):transmissão pelo importador ou subsequente de bens transportados/expedidos do estrangeiro,antes da importação nacional identificada;abrangida territorialmente emMoçambique,${row.origin === 'projected' ? 'nas condições expressamente assumidas na projecção' : 'nos dados efectivos declarados'}. A localização física anterior no estrangeiro não afasta por si esta regra. Classificação não determina isenção,base,taxa,IVA,dedução/campo11,direitos ou totaisModeloA.`, }; } if ( !row.goodsPlace.trim() || !row.placeRef.trim() || !['MZ', 'foreign'].includes(row.placeCountry) ) return stop( 'Identifique onde os bens se encontram no momento relevante e respectiva referência:início do transporte/expedição ou colocação à disposição sem transporte;endereço do cliente não substitui esse facto.', ); if (row.treatment === 'goods_with_transport' && !row.transportRef.trim()) return stop( 'Identifique o transporte/expedição correspondente ao início referido,ou pressuposto expresso.', ); const territory = row.placeCountry === 'MZ' ? 'national' : 'outside'; return { ...result, status: 'determined', territory, reason: `Art6(1):bens ${territory === 'national' ? 'situados emMoçambique' : 'situados fora deMoçambique'} ${row.treatment === 'goods_with_transport' ? 'ao iniciar o transporte/expedição para o adquirente' : 'ao serem postos à disposição do adquirente,sem transporte/expedição'},${row.origin === 'projected' ? 'nas condições expressamente assumidas na projecção' : 'nos dados efectivos declarados'}. Factos de6(2)conferidos como não aplicáveis a este percurso. ${territory === 'outside' ? 'Fora deste âmbito territorial não equivale a isenção ou taxa/IVA zero.' : 'Abrangência territorial não confirma isenção,taxa ou IVA.'} Classificação não determina base,dedução/campo11,direitos ou totaisModeloA.`, }; } function key(draft: Record, book: GoodsLocationBook): string { return JSON.stringify({ version: goodsLocationVersion, scope: acquisitionScope(draft), location: draft.selection_labels ?? {}, rows: book.rows, }); } export function goodsLocationCurrent( draft: Record, book: GoodsLocationBook, ): boolean { return !!book.result && book.result.sourceKey === key(draft, book); } export function recordGoodsLocations( draft: Record, book: GoodsLocationBook, ): GoodsLocationBook { if ( !book.rows.length || new Set(book.rows.map((row) => row.id)).size !== book.rows.length ) throw new Error( 'Indique operações com identificadores distintos;originais conservados.', ); const result: GoodsLocationCalculation = { version: goodsLocationVersion, revision: (book.result?.revision ?? 0) + 1, calculatedAt: new Date().toISOString(), opportunity: acquisitionScope(draft), location: JSON.stringify(draft.selection_labels ?? {}), sourceKey: key(draft, book), source: goodsLocationSource, inputs: structuredClone(book.rows), rows: book.rows.map((row) => evaluateGoodsLocation(row)), }; const next = { ...book, result, history: [ ...(book.history ?? []), ...(book.result ? [book.result] : []), ], }; saveGoodsLocationBook(draft, next); return next; } export function goodsLocationReportRows( draft: Record, ): Array<[string, string, string, string]> { try { const book = goodsLocationBook(draft), result = book.result; if (!result) return []; return result.inputs.map((row, index) => [ 'IVA mensal', `Localização P105 — ${row.period} — ${row.id}`, `${row.description};classificação territorial: ${result.rows[index].territory === 'national' ? 'Moçambique' : result.rows[index].territory === 'outside' ? 'Fora do território nacional' : 'Indeterminada'};IVA: Indeterminado;${result.rows[index].reason}`, `${result.opportunity};${result.location};${goodsLocationCurrent(draft, book) ? 'Resultado actual' : 'Entradas alteradas;resultado histórico conservado'};versão${result.revision};${result.calculatedAt};${result.source};${row.origin === 'projected' ? 'Projecção' : 'Dados efectivos declarados'};${goodsLocationTreatments.find((t) => t.id === row.treatment)?.label ?? 'Percurso por indicar'};operação ${row.operationRef},${row.operationDate};${row.treatment === 'goods_before_import' ? `transporte ${row.transportRef};importação ${row.importRef};cadeia ${row.chainRef}` : `local dos bens ${row.goodsPlace},${row.placeCountry};referência ${row.placeRef}${row.treatment === 'goods_with_transport' ? `;transporte ${row.transportRef}` : ''}`};âmbito ${row.scope};documento ${row.documentRef},${row.documentDate};condições ${[...goodsLocationConditions, ...ownGoodsConditions(row.treatment)].map(([key, label]) => label + ': ' + (row.conditions[key] === 'yes' ? 'Sim' : row.conditions[key] === 'no' ? 'Não' : 'Por indicar')).join('; ')};fundamento factual ${row.evidence};não determina isenção/IVA/campo11/totaisModeloA.`, ]); } catch { return [ [ 'IVA mensal', 'Localização de bens', 'Dados não recuperáveis', 'Originais preservados;sem classificar por defeito.', ], ]; } }