import { acquisitionScope, readAcquisitionStore } from './iva-acquisitions.ts'; export const ownNameVersion = 'own-name-chains/1'; export const ownNameSource = 'CIVA3(3)c,Lei13/2016PDF6/p1426(264):transferências em execução de contrato de comissão,incluindo consignação;comissário considerado comprador na comissão de venda,comitente na de compra. Art4(3),PDF7/p1426(265):mandatário emnome próprio é adquirente eprestador do serviço sucessivamente. Lei10/2025PDF12mantém3(3)c e não altera4. Base de bens15(2)e/P091já entregue,não recalculada ou somada neste percurso. Recorte2026:circuito/contraprestações originais separados,sem localização,isenção,taxa,IVA,dedução/campo11 ou totaisModeloA presumidos.'; export const ownNameTreatments = [ { id: 'goods_sale_commission', branch: 'P122', label: 'P122 — Comissão de venda de bens', kind: 'goods', direction: 'sale', }, { id: 'goods_purchase_commission', branch: 'P122', label: 'P122 — Comissão de compra de bens', kind: 'goods', direction: 'purchase', }, { id: 'service_sale_mandate', branch: 'P128', label: 'P128 — Mandato de venda/prestação de serviços', kind: 'services', direction: 'sale', }, { id: 'service_purchase_mandate', branch: 'P128', label: 'P128 — Mandato de aquisição de serviços', kind: 'services', direction: 'purchase', }, ] as const; export const ownNameConditions = [ [ 'ownName', 'O comissário/mandatário actua emnome próprio,não apenas emnome do mandante', ], [ 'forAnother', 'Actua por conta de outrem identificado ecom relação documentada ou pressuposto expresso', ], [ 'factsCorrespond', 'Objecto,partes,referências eperíodo correspondem à operação identificada', ], ] as const; const goodsConditions = [ [ 'goodsNature', 'São transferências de bens,não serviço autónomo de intermediação', ], [ 'commissionContract', 'As transferências executam contrato de comissão identificado,conforme3(3)c,incluindo consignação quando aplicável', ], [ 'directionConfirmed', 'Papéis e duas relações correspondem ao sentido de venda ou compra escolhido', ], ] as const; const serviceConditions = [ [ 'serviceNature', 'Está identificada uma prestação de serviços,não transferência de bens', ], [ 'mandateContract', 'A prestação é efectuada por intervenção de mandatário emnome próprio,com mandato identificado4(3)', ], [ 'successiveService', 'Mandatário é adquirente eprestador do serviço sucessivamente;referências distinguem as duas prestações', ], [ 'directionConfirmed', 'Papéis e duas relações correspondem ao sentido de venda/prestação ou aquisição escolhido', ], ] as const; export function ownNameSpecificConditions( treatment: string, ): ReadonlyArray { const rule = ownNameTreatments.find((t) => t.id === treatment); return rule?.kind === 'goods' ? goodsConditions : rule?.kind === 'services' ? serviceConditions : []; } type Choice = '' | 'yes' | 'no'; export type OwnNameRow = { id: string; treatment: string; description: string; period: string; origin: 'projected' | 'effective'; operationDate: string; operationRef: string; documentRef: string; documentDate: string; principalRef: string; agentRef: string; thirdPartyRef: string; contractRef: string; subjectRef: string; currency: string; firstAmount: string; secondAmount: string; firstLegRef: string; secondLegRef: string; existingBaseRef: string; scope: string; evidence: string; conditions: Partial>; }; type Leg = { supplier: string; acquirer: string; consideration: string; currency: string; reference: string; }; type LineResult = { id: string; status: 'determined' | 'not_applicable' | 'indeterminate'; legs: Leg[]; baseAmount: null; vat: null; rate: null; reason: string; }; export type OwnNameCalculation = { version: string; revision: number; calculatedAt: string; opportunity: string; location: string; sourceKey: string; source: string; inputs: OwnNameRow[]; rows: LineResult[]; }; export type OwnNameBook = { rows: OwnNameRow[]; result?: OwnNameCalculation; history?: OwnNameCalculation[]; }; type Store = ReturnType & { ownNames?: Record; }; export function newOwnName(id: string, currency = ''): OwnNameRow { return { id, treatment: '', description: '', period: '', origin: 'projected', operationDate: '', operationRef: '', documentRef: '', documentDate: '', principalRef: '', agentRef: '', thirdPartyRef: '', contractRef: '', subjectRef: '', currency, firstAmount: '', secondAmount: '', firstLegRef: '', secondLegRef: '', existingBaseRef: '', scope: '', evidence: '', conditions: {}, }; } export function switchOwnNameTreatment( row: OwnNameRow, treatment: string, ): OwnNameRow { return { ...row, treatment, conditions: Object.fromEntries( ownNameConditions.map(([key]) => [key, row.conditions[key] ?? '']), ), }; } function readStore(draft: Record): Store { const store = readAcquisitionStore(draft.modelo_a_snapshot) as Store; if ( store.ownNames !== undefined && (!store.ownNames || typeof store.ownNames !== 'object' || Array.isArray(store.ownNames)) ) throw new Error( 'Circuitos emnome próprio não recuperáveis;originais preservados.', ); const fields = Object.keys(newOwnName('')).filter( (key) => key !== 'conditions', ); for (const book of Object.values(store.ownNames ?? {})) 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 OwnNameRow] !== 'string', ) || !row.conditions || typeof row.conditions !== 'object' || Array.isArray(row.conditions), ) ) throw new Error( 'Linhas de circuitos emnome próprio não recuperáveis;originais preservados,sem substituição por lista vazia.', ); return store; } export function ownNameBook(draft: Record): OwnNameBook { return readStore(draft).ownNames?.[acquisitionScope(draft)] ?? { rows: [] }; } export function saveOwnNameBook( draft: Record, book: OwnNameBook, ): string { const current = readStore(draft), encoded = JSON.stringify({ ...current, ownNames: { ...current.ownNames, [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 evaluateOwnName( row: OwnNameRow, currency: unknown, ): LineResult { const result: LineResult = { id: row.id, status: 'indeterminate', legs: [], baseAmount: null, vat: null, rate: null, reason: '', }; const stop = ( reason: string, status: LineResult['status'] = 'indeterminate', ) => ({ ...result, status, reason }); const rule = ownNameTreatments.find((t) => t.id === row.treatment); if (!rule) return stop( 'Identifique bens/serviços e o sentido da comissão/mandato;sem inferir pelo pagamento de uma comissão.', ); 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ês2026,data correspondente e dados efectivos ou pressupostos expressos.', ); const required = [ ...ownNameConditions, ...ownNameSpecificConditions(row.treatment), ]; if (required.some(([key]) => row.conditions[key] === 'no')) return stop( 'Este circuito emnome próprio não se aplica aos factos declarados;rever mandato/comissão/natureza/papéis,sem classificar mera intermediação como duas operações ou presumir isenção/IVAzero.', 'not_applicable', ); if (required.some(([key]) => row.conditions[key] !== 'yes')) return stop( 'Natureza,actuação emnome próprio ou papéis por esclarecer;desconhecido não é não aplicável ou zero.', ); if ( !row.description.trim() || !row.operationRef.trim() || !row.documentRef.trim() || !validDate(row.documentDate) || !row.scope.trim() || !row.evidence.trim() || !row.subjectRef.trim() || !row.contractRef.trim() || ![ row.principalRef, row.agentRef, row.thirdPartyRef, row.firstLegRef, row.secondLegRef, ].every((v) => !!v.trim()) ) return stop( 'Identifique objecto,mandante/comitente,comissário/mandatário,terceiro,contrato,duas relações,operação/documento/data eâmbito;reutilize referências ou pressupostos expressos,sem certificado/aprovador novo.', ); if ( typeof currency !== 'string' || !/^[A-Z]{3}$/.test(currency) || row.currency !== currency ) return stop( 'Identifique moeda original coerente com o contexto;sem converter ou reinterpretarUSDcomoMZN.', ); const amount = (v: string): string | null => /^\d{1,15}(\.\d{1,6})?$/.test(v.trim()) ? v.trim() : null; const first = amount(row.firstAmount), second = amount(row.secondAmount); if (first === null || second === null) return stop( 'Identifique separadamente as contraprestações originais das duas relações;desconhecido não é zero. Não inferir uma delas da comissão ou somá-las como uma única base.', ); const before = rule.direction === 'sale' ? row.principalRef : row.thirdPartyRef, after = rule.direction === 'sale' ? row.thirdPartyRef : row.principalRef; const legs = [ { supplier: before, acquirer: row.agentRef, consideration: first, currency: row.currency, reference: row.firstLegRef, }, { supplier: row.agentRef, acquirer: after, consideration: second, currency: row.currency, reference: row.secondLegRef, }, ]; return { ...result, status: 'determined', legs, reason: `${rule.branch}:${rule.kind === 'goods' ? `art3(3)c,comissão de${rule.direction === 'sale' ? 'venda:comissário considerado comprador' : 'compra:comitente considerado comprador'}` : 'art4(3),mandatário emnome próprio considerado adquirente eprestador do serviço sucessivamente'}. ${row.origin === 'projected' ? 'Circuito sob pressupostos expressos.' : 'Circuito segundo dados efectivos declarados.'} Duas relações com objecto/partes/contraprestações originais separadas,sem soma ou compensação automática. ${rule.kind === 'goods' ? 'BaseP091/15(2)e já entregue permanece separada e não é recalculada.' : 'Não transformar este circuito emcomissão autónoma ou aplicar15(2)e de bens aos serviços.'} Sem determinar localização,isenção,base,taxa,IVA,dedução/campo11 ou totaisModeloA.`, }; } function key(draft: Record, book: OwnNameBook): string { return JSON.stringify({ version: ownNameVersion, scope: acquisitionScope(draft), location: draft.selection_labels ?? {}, currency: draft.currency, rows: book.rows, }); } export function ownNameCurrent( draft: Record, book: OwnNameBook, ): boolean { return !!book.result && book.result.sourceKey === key(draft, book); } export function recordOwnNames( draft: Record, book: OwnNameBook, ): OwnNameBook { 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: OwnNameCalculation = { version: ownNameVersion, revision: (book.result?.revision ?? 0) + 1, calculatedAt: new Date().toISOString(), opportunity: acquisitionScope(draft), location: JSON.stringify(draft.selection_labels ?? {}), sourceKey: key(draft, book), source: ownNameSource, inputs: structuredClone(book.rows), rows: book.rows.map((row) => evaluateOwnName(row, draft.currency)), }; const next = { ...book, result, history: [ ...(book.history ?? []), ...(book.result ? [book.result] : []), ], }; saveOwnNameBook(draft, next); return next; } export function ownNameReportRows( draft: Record, ): Array<[string, string, string, string]> { try { const book = ownNameBook(draft), result = book.result; if (!result) return []; return result.inputs.map((row, index) => { const line = result.rows[index], rule = ownNameTreatments.find((t) => t.id === row.treatment); return [ 'IVA mensal', `Circuito ${rule?.branch ?? 'emnome próprio'} — ${row.period} — ${row.id}`, `${row.description};${line.status === 'determined' ? 'Circuito identificado' : line.status === 'not_applicable' ? 'Circuito não aplicável' : 'Circuito indeterminado'};IVA: Indeterminado;${line.reason};${line.legs.map((leg, i) => `Relação${i + 1}:${leg.supplier} → ${leg.acquirer},contraprestação original ${leg.consideration} ${leg.currency},referência ${leg.reference}`).join('; ')};sem soma das duas relações`, `${result.opportunity};${result.location};${ownNameCurrent(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'};${rule?.label ?? 'Percurso por indicar'};operação ${row.operationRef},${row.operationDate};objecto ${row.subjectRef};contrato ${row.contractRef};mandante/comitente ${row.principalRef};agente ${row.agentRef};terceiro ${row.thirdPartyRef};relações ${row.firstLegRef}/${row.secondLegRef};${rule?.kind === 'goods' ? `referênciaP091existente ${row.existingBaseRef || 'não indicada;sem nova base calculada'};` : ''}âmbito ${row.scope};documento ${row.documentRef},${row.documentDate};condições ${[...ownNameConditions, ...ownNameSpecificConditions(row.treatment)].map(([key, label]) => label + ': ' + (row.conditions[key] === 'yes' ? 'Sim' : row.conditions[key] === 'no' ? 'Não' : 'Por indicar')).join('; ')};fundamento ${row.evidence};sem IVA/dedução/campo11/totaisModeloA.`, ]; }); } catch { return [ [ 'IVA mensal', 'Circuito emnome próprio', 'Dados não recuperáveis', 'Originais preservados;sem circuito ou valor por defeito.', ], ]; } }