101 lines
22 KiB
TypeScript
101 lines
22 KiB
TypeScript
import {acquisitionScope,readAcquisitionStore} from './iva-acquisitions.ts';
|
||
export const minerVersion='miner-imports/1';
|
||
export const minerSource='Lei n.º2/2011,de11/01:Regime Aduaneiro Aplicável aos Mineiros na República da África do Sul;BR1-I,4.ºsuplemento,arts1–4PDF1(sem número impresso),art5PDF2/p110-(8). OriginalMCNet149:Lei-2-2011-BR-1-4-suplemento-MCNet.pdf;ficheiro conjunto,não regra da Lei do Voluntariado. Decreto3/2011,de16/03,arts1–8,BR11-I,PDF2–3/p152–153. CIVA12(2)b,2016PDF11/p1426(269),mantido2022/2025:IVA na proporção da redução de direitos. Âmbito demonstradoRSA/2026/MZN;sem direitos,base aduaneira ou dedução.';
|
||
type Choice=''|'yes'|'no';
|
||
export const minerTreatments=[
|
||
{id:'baggage',label:'Bagagem sem fins comerciais',article:'Lei2/2011,2(1)a;Decreto3/2011,1b/3(1)a',conditions:[['personalBaggage','Objectos pessoais pertencentes ao mineiro durante a estadia na RSA,em quantidades razoáveis e sem fins comerciais']]},
|
||
{id:'monthly',label:'Remessa mensal',article:'Lei2/2011,2(1)c;Decreto3/2011,1e/3(1)b',conditions:[['ownedRSA','Bens pertencentes ao mineiro,adquiridos na RSA,para uso próprio ou da família'],['route','Remessa por empresa licenciada ou por meios próprios quando acompanhada pelo mineiro']]},
|
||
{id:'accumulated',label:'Remessas acumuladas até seis meses',article:'Decreto3/2011,4',conditions:[['ownedRSA','Bens pertencentes ao mineiro,adquiridos na RSA,para a família'],['licensedCompany','Remessa acumulada por empresa transportadora/distribuidora autorizada'],['unusedMonths','Meses e saldo indicados não foram usados noutras remessas;conferir o registo completo']]},
|
||
{id:'appliance',label:'Um electrodoméstico de cada tipo por ano',article:'Lei2/2011,2(1)b;Decreto3/2011,3(1)c/8(3)',conditions:[['oneAppliance','Um único electrodoméstico do tipo identificado nesta linha'],['annualQuota','Não foi usado este direito para o mesmo tipo/ano,incluindo fora deste dossier']]},
|
||
{id:'vehicle',label:'Veículo ou tractor e alfaias — direito em cinco anos',article:'Lei2/2011,2(1)d/2;Decreto3/2011,3(1)d/6(3)/(8)/(9)/8(3)',conditions:[['oneVehicle','Um veículo automóvel OU um tractor agrícola com respectivas alfaias,identificado nesta linha'],['vehicleQuota','Direito identificado disponível:um veículo e um tractor/alfaias em cada cinco anos,considerando benefícios usados fora deste dossier'],['noAlienation','Sem alienação antes de cinco anos da importação;incumprimento exige pagamento das imposições,não regularização automática pelo simulador']]},
|
||
{id:'return_baggage',label:'Bagagem no fim do contrato e regresso definitivo',article:'Lei2/2011,2(3);Decreto3/2011,6(1)',conditions:[['endContract','Contrato de trabalho terminado e regresso definitivo ao País'],['personalBaggage','Bens abrangidos pelo conceito de bagagem,identificados e sem finalidade comercial']]},
|
||
{id:'return_vehicle',label:'Veículo/tractor no regresso,como bagagem',article:'Decreto3/2011,6(1)–(3)/(5)–(9)',conditions:[['endContract','Contrato terminado e regresso definitivo'],['oneVehicle','Um veículo OU tractor agrícola e alfaias,identificado como bagagem'],['vehicleQuota','Direito disponível,sem nova isenção/redução antes de cinco anos do benefício anterior aplicável'],['noAlienation','Respeitar a restrição de alienação durante cinco anos da importação']]},
|
||
{id:'separate_vehicle',label:'Veículo separado da bagagem — redução de 50%',article:'Decreto3/2011,6(3)–(9)',conditions:[['endContract','Contrato terminado e regresso definitivo'],['oneVehicle','Veículo novo ou usado separado da bagagem identificado como substituição do benefício do regresso'],['substitution','Tratamento excepcional de separado de bagagem concedido para este veículo/âmbito,ou pressuposto expresso da projecção;pedido não equivale a concessão'],['vehicleQuota','Direito disponível,sem repetir benefício de veículo antes de cinco anos'],['noAlienation','Respeitar restrição de alienação durante cinco anos']]},
|
||
] as const;
|
||
export const minerCommon=[['national','Beneficiário originário de nacionalidade moçambicana'],['rsa','Serviço nas minas da República da África do Sul abrangido pelos acordos de trabalho migratório'],['contract','Contrato regular visado pelo Ministério do Trabalho de Moçambique,com referência identificada'],['noAlcoholTobacco','Linha não inclui bebidas alcoólicas nem tabaco;separar esses bens,excluídos pela Lei2/2011art3']] as const;
|
||
type Condition=typeof minerCommon[number][0]|typeof minerTreatments[number]['conditions'][number][0]|'heir'|'unusedRight'|'acquiredAlive'|'stayException'|'deadlineExtension';
|
||
export type MinerRow={id:string;treatment:string;description:string;period:string;currency:string;origin:'projected'|'effective';beneficiary:string;contractRef:string;documentRef:string;documentDate:string;scope:string;evidence:string;conditions:Partial<Record<Condition,Choice>>;rightHolder:''|'miner'|'heir';deathDate:string;requestDate:string;heirRef:string;importDate:string;route:''|'accompanied'|'company'|'declaration';customsRef:string;customsScope:string;quantity:string;kind:string;value:string;periodTotal:string;quotaRef:string;accumulationStart:string;accumulationMonths:string;arrivalDate:string;stayStart:string;stayEnd:string;exceptionRef:string;extensionRef:string;previousBenefit:''|'none'|'known';previousBenefitDate:string;normalVat:string;normalVatRef:string;};
|
||
type Line={id:string;status:'exempt'|'reduced'|'not_applicable'|'indeterminate';importVat:string|null;deduction:null;source:string;reason:string;};
|
||
export type MinerCalculation={version:string;revision:number;calculatedAt:string;opportunity:string;sourceKey:string;source:string;inputs:MinerRow[];rows:Line[]};
|
||
export type MinerBook={rows:MinerRow[];result?:MinerCalculation;history?:MinerCalculation[]};
|
||
type Store=ReturnType<typeof readAcquisitionStore>&{minerImports?:Record<string,MinerBook>};
|
||
export function newMiner(id:string,currency:string):MinerRow{return{id,treatment:'',description:'',period:'',currency,origin:'projected',beneficiary:'',contractRef:'',documentRef:'',documentDate:'',scope:'',evidence:'',conditions:{},rightHolder:'',deathDate:'',requestDate:'',heirRef:'',importDate:'',route:'',customsRef:'',customsScope:'',quantity:'',kind:'',value:'',periodTotal:'',quotaRef:'',accumulationStart:'',accumulationMonths:'',arrivalDate:'',stayStart:'',stayEnd:'',exceptionRef:'',extensionRef:'',previousBenefit:'',previousBenefitDate:'',normalVat:'',normalVatRef:''};}
|
||
export function minerVehicle(id:string):boolean{return['vehicle','return_vehicle','separate_vehicle'].includes(id);}
|
||
export function minerReturn(id:string):boolean{return['return_baggage','return_vehicle','separate_vehicle'].includes(id);}
|
||
function readStore(draft:Record<string,unknown>):Store{const s=readAcquisitionStore(draft.modelo_a_snapshot) as Store;const fields=Object.keys(newMiner('','')).filter(k=>k!=='conditions');if(s.minerImports!==undefined&&(!s.minerImports||typeof s.minerImports!=='object'||Array.isArray(s.minerImports)))throw new Error('Importações dos mineiros não recuperáveis;originais preservados.');for(const b of Object.values(s.minerImports??{}))if(!b||!Array.isArray(b.rows)||(b.history!==undefined&&!Array.isArray(b.history))||b.rows.some(r=>!r||fields.some(k=>typeof r[k as keyof MinerRow]!=='string')||!r.conditions||typeof r.conditions!=='object'||Array.isArray(r.conditions)))throw new Error('Linhas dos mineiros não recuperáveis;sem substituição por lista vazia.');return s;}
|
||
export function minerBook(draft:Record<string,unknown>):MinerBook{return readStore(draft).minerImports?.[acquisitionScope(draft)]??{rows:[]};}
|
||
export function saveMinerBook(draft:Record<string,unknown>,book:MinerBook):string{const s=readStore(draft),encoded=JSON.stringify({...s,minerImports:{...s.minerImports,[acquisitionScope(draft)]:book}});readStore({...draft,modelo_a_snapshot:encoded});return encoded;}
|
||
function date(raw:string):Date|null{const d=new Date(raw+'T00:00:00Z');return /^\d{4}-\d{2}-\d{2}$/.test(raw)&&Number.isFinite(d.getTime())&&d.toISOString().slice(0,10)===raw?d:null;}
|
||
function anniversary(raw:string,years:number):string{const d=date(raw)!;d.setUTCFullYear(d.getUTCFullYear()+years);return d.toISOString().slice(0,10);}
|
||
export function minerNeedsDeadlineExtension(row:MinerRow):boolean{const a=date(row.arrivalDate),r=date(row.requestDate);return minerReturn(row.treatment)&&row.rightHolder==='miner'&&!!a&&!!r&&(r.getTime()-a.getTime())/86400000>90;}
|
||
export function minerNeedsStayException(row:MinerRow):boolean{return row.treatment==='separate_vehicle'&&!!date(row.stayStart)&&!!date(row.stayEnd)&&row.stayEnd<anniversary(row.stayStart,1);}
|
||
function decimal(raw:string):bigint|null{if(!/^\d{1,15}(\.\d{1,6})?$/.test(raw))return null;const[w,f='']=raw.split('.');return BigInt(w)*1000000n+BigInt(f.padEnd(6,'0'));}
|
||
function half(raw:bigint):string{const n=raw*5n;return`${n/10000000n}.${(n%10000000n).toString().padStart(7,'0').replace(/0+$/,'').padEnd(2,'0')}`;}
|
||
export function evaluateMiner(row:MinerRow,currency:unknown):Line{
|
||
const rule=minerTreatments.find(t=>t.id===row.treatment),result:Line={id:row.id,status:'indeterminate',importVat:null,deduction:null,source:minerSource+(rule?';'+rule.article:''),reason:''};const stop=(reason:string,status:Line['status']='indeterminate')=>({...result,status,reason});
|
||
if(!rule)return stop('Escolha percurso documentado do regimeRSA;outros países,importações temporárias ou benefício diferente permanecem por enquadrar,não são automaticamente isentos.');
|
||
if(currency!=='MZN'||row.currency!==currency||!/^2026-(0[1-9]|1[0-2])$/.test(row.period)||!date(row.importDate)||row.importDate.slice(0,7)!==row.period)return stop('Indique moedaMZN e data/período coerentes da importação2026;sem conversão ou datas presumidas.');
|
||
if(!['projected','effective'].includes(row.origin)||![row.beneficiary,row.description,row.contractRef,row.documentRef,row.scope,row.evidence,row.customsRef,row.customsScope].every(v=>!!v.trim())||!date(row.documentDate))return stop('Identifique beneficiário,bens,contrato,documento/previsão,data,âmbito e referência/pressuposto aduaneiro.Reutilize documentos,não é exigido novo certificado.');
|
||
const conditions=[...minerCommon,...rule.conditions];const failed=conditions.filter(([k])=>row.conditions[k]==='no');if(failed.length)return stop('Não se aplica este fundamento aos factos indicados: '+failed.map(([,label])=>label).join('; ')+'. Outros enquadramentos permanecem por apurar.','not_applicable');if(conditions.some(([k])=>row.conditions[k]!=='yes'))return stop('Condições do beneficiário,bens ou percurso por esclarecer;desconhecido não é IVA zero.');
|
||
const route=['appliance','vehicle','return_vehicle','separate_vehicle'].includes(row.treatment)?'declaration':row.treatment==='accumulated'?'company':row.route;
|
||
if(!['accompanied','company','declaration'].includes(route)||(['monthly','accumulated','baggage','return_baggage'].includes(row.treatment)&&route==='declaration'))return stop('Identifique via acompanhada ou empresa nos bens/bagagem;electrodomésticos e veículos seguem declaração aduaneira. Não exigir procedimentoKAWENA a outras vias.');
|
||
if(!['miner','heir'].includes(row.rightHolder))return stop('Indique titular originário ou transmissão do direito a herdeiro legal.');
|
||
if(row.rightHolder==='heir'){
|
||
if(['heir','unusedRight','acquiredAlive'].some(k=>row.conditions[k as Condition]==='no'))return stop('Transmissão a herdeiro não demonstrada:apenas direito não usado e bens adquiridos em vida pelo mineiro.','not_applicable');
|
||
if(['heir','unusedRight','acquiredAlive'].some(k=>row.conditions[k as Condition]!=='yes')||!row.heirRef.trim()||!date(row.deathDate)||!date(row.requestDate))return stop('Identifique qualidade de herdeiro,direito não usado,bens adquiridos em vida,óbito e requerimento;sem conceder direito sucessório automaticamente.');
|
||
if(row.requestDate<row.deathDate||row.requestDate>anniversary(row.deathDate,1))return stop('Requerimento do herdeiro fora do período de um ano contado do óbito,ou anterior ao mesmo;este fundamento não se aplica.','not_applicable');
|
||
}
|
||
if(['monthly','accumulated'].includes(row.treatment)){
|
||
const amount=decimal(row.value),total=decimal(row.periodTotal);if(amount===null||total===null||amount>total||!row.quotaRef.trim())return stop('Indique valor desta remessa e total do âmbito mensal/acumulado,incluindo outras remessas e esta,com registo ou pressuposto expresso;vazio não significa zero.');
|
||
let months=1;
|
||
if(row.treatment==='accumulated'){
|
||
months=Number(row.accumulationMonths);if(!/^[1-6]$/.test(row.accumulationMonths)||!/^\d{4}-(0[1-9]|1[0-2])$/.test(row.accumulationStart))return stop('Indique início e duração de um a seis meses acumulados;sem acumular indefinidamente.');
|
||
const start=Number(row.accumulationStart.slice(0,4))*12+Number(row.accumulationStart.slice(5)),end=Number(row.period.slice(0,4))*12+Number(row.period.slice(5));if(end-start+1!==months)return stop('Meses indicados não correspondem ao início/fim do saldo acumulado;conserve origem e reveja a janela,sem reutilização automática.');
|
||
}
|
||
if(total>15000000000n*BigInt(months))return stop('Total excede a cobertura de15.000MZN por mês prevista nos exemplaresLei2/2011/Decreto3/2011.Não se concede benefício ao excesso nem se presume revisão do limite ao abrigo2(5);conferir disposição específica se invocada.');
|
||
}
|
||
if(row.treatment==='appliance'&&(!row.kind.trim()||row.quantity!=='1'||!row.quotaRef.trim()))return stop('Identifique tipo de electrodoméstico,uma unidade e registo/pressuposto do direito anual,incluindo operações externas ao dossier.');
|
||
if(minerVehicle(row.treatment)){
|
||
if(!['vehicle','tractor'].includes(row.kind)||row.quantity!=='1'||!row.quotaRef.trim()||!['none','known'].includes(row.previousBenefit))return stop('Identifique veículo ou tractor/alfaias,uma unidade e histórico do direito abrangido,incluindo benefícios fora do dossier;desconhecido não é ausência de benefício.');
|
||
if(row.previousBenefit==='known'){if(!date(row.previousBenefitDate))return stop('Indique data do benefício anterior aplicável ao direito identificado.');if(row.importDate<anniversary(row.previousBenefitDate,5))return stop('Ainda não decorreram cinco anos desde o benefício anterior aplicável indicado;sem nova isenção ou redução neste direito.','not_applicable');}
|
||
}
|
||
if(minerReturn(row.treatment)){
|
||
const arrival=date(row.arrivalDate),request=date(row.requestDate);if(!arrival||!request||row.requestDate<row.arrivalDate)return stop('Indique chegada e requerimento do benefício no regresso;pedido não equivale a decisão.');
|
||
if(minerNeedsDeadlineExtension(row)){const max=new Date(arrival),day=max.getUTCDate();max.setUTCDate(1);max.setUTCMonth(max.getUTCMonth()+6);const end=new Date(max);end.setUTCMonth(end.getUTCMonth()+1);end.setUTCDate(0);max.setUTCDate(Math.min(day,end.getUTCDate()));if(row.conditions.deadlineExtension!=='yes'||!row.extensionRef.trim()||request>max)return stop('Prazo normal de90dias ultrapassado;identifique prorrogação excepcional justificada até seis meses,não presumida pelo simulador.');}
|
||
if(minerVehicle(row.treatment)){
|
||
if(!date(row.stayStart)||!date(row.stayEnd)||row.stayEnd<row.stayStart||row.stayEnd>row.arrivalDate)return stop('Indique estadia naRSA para o benefício de veículo no regresso,coerente com a data da chegada.');
|
||
if(row.stayEnd<anniversary(row.stayStart,1)&&!(row.treatment==='separate_vehicle'&&row.conditions.stayException==='yes'&&row.exceptionRef.trim()))return stop('Estadia inferior a um ano;excepção6(7) só no separado de bagagem com autorização/âmbito identificados ou pressuposto expresso,não automática.');
|
||
}
|
||
}
|
||
const moment=route==='company'?'saída do armazém de regime para entrega às famílias':route==='accompanied'?'entrada no território na estância aduaneira':'desembaraço mediante Documento Único';
|
||
const context=`${rule.article};${row.origin==='projected'?'pressupostos expressos de projecção':'factos efectivos declarados'}. Momento/procedimento8:${moment}. Sem despacho ou DU emitido pelo portal,direitos,base aduaneira ou dedução/campo11.`;
|
||
if(row.treatment==='separate_vehicle'){
|
||
const normal=decimal(row.normalVat);if(normal===null||!row.normalVatRef.trim())return stop('Indique IVA normal desta importação e referência/pressuposto para aplicar redução proporcional de50%;sem presumir base ou taxa normal.');
|
||
return{...result,status:'reduced',importVat:half(normal),reason:`Redução50% das imposições no cenário específico de veículo separado da bagagem6(4),não taxa deIVA nem benefício geral. IVA informado${row.normalVat}×50%=${half(normal)}MZN,aritmética exacta sem arredondamento fiscal presumido.${context}`};
|
||
}
|
||
return{...result,status:'exempt',importVat:'0.00',reason:`Classificação de isenção no âmbito documentado de direitos/imposições,IVA12(2)b na proporção do benefício integral indicado. IVA importação:0.00MZN;sem concessão administrativa automática.${context}`};
|
||
}
|
||
function key(draft:Record<string,unknown>,b:MinerBook):string{return JSON.stringify({version:minerVersion,scope:acquisitionScope(draft),currency:draft.currency,location:draft.selection_labels??{},rows:b.rows});}
|
||
export function minerCurrent(draft:Record<string,unknown>,b:MinerBook):boolean{return!!b.result&&b.result.sourceKey===key(draft,b);}
|
||
export function recordMiners(draft:Record<string,unknown>,book:MinerBook):MinerBook{
|
||
if(!book.rows.length||new Set(book.rows.map(r=>r.id)).size!==book.rows.length)throw new Error('Identifique linhas distintas;originais preservados.');
|
||
const rows=book.rows.map(r=>evaluateMiner(r,draft.currency));
|
||
// Declarative quotas cover external operations too; detect contradictions inside this saved scope.
|
||
book.rows.forEach((row,i)=>{if(!['exempt','reduced'].includes(rows[i].status))return;
|
||
const peers=book.rows.filter(r=>r.beneficiary===row.beneficiary&&r.origin===row.origin&&evaluateMiner(r,draft.currency).status!=='not_applicable');
|
||
let conflict=false;
|
||
if(row.treatment==='appliance')conflict=peers.filter(r=>r.treatment==='appliance'&&r.kind===row.kind&&r.period.slice(0,4)===row.period.slice(0,4)).length>1;
|
||
if(minerVehicle(row.treatment))conflict=peers.filter(r=>minerVehicle(r.treatment)&&r.kind===row.kind&&date(r.importDate)&&(r.importDate<=row.importDate?row.importDate<anniversary(r.importDate,5):r.importDate<anniversary(row.importDate,5))).length>1;
|
||
if(['monthly','accumulated'].includes(row.treatment)){
|
||
const start=row.treatment==='accumulated'?row.accumulationStart:row.period;
|
||
const relevant=peers.filter(r=>['monthly','accumulated'].includes(r.treatment)&&(r.treatment==='accumulated'?r.accumulationStart:r.period)<=row.period&&r.period>=start);
|
||
if(relevant.some(r=>r.treatment==='accumulated'||row.treatment==='accumulated')&&relevant.length>1)conflict=true;
|
||
if(relevant.some(r=>decimal(r.value)===null))conflict=true;
|
||
const sum=relevant.reduce((s,r)=>s+(decimal(r.value)??0n),0n),total=decimal(row.periodTotal);if(total!==null&&sum>total)conflict=true;
|
||
}
|
||
if(conflict)rows[i]={...rows[i],status:'indeterminate',importVat:null,reason:'Linhas guardadas sobrepõem ou excedem o mesmo direito/período/saldo declarado;reveja o conjunto e o registo externo,sem dupla utilização nem exclusão silenciosa.'};
|
||
});
|
||
const result:MinerCalculation={version:minerVersion,revision:(book.result?.revision??0)+1,calculatedAt:new Date().toISOString(),opportunity:acquisitionScope(draft),sourceKey:key(draft,book),source:minerSource,inputs:structuredClone(book.rows),rows};const next={...book,result,history:[...(book.history??[]),...(book.result?[book.result]:[])]};saveMinerBook(draft,next);return next;
|
||
}
|
||
export function minerReportRows(draft:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=minerBook(draft),r=b.result;if(!r)return[];return r.inputs.map((row,i)=>['IVA mensal',`P059 — ${row.period} — ${row.id}`,`${row.description};${r.rows[i].reason};IVA:${r.rows[i].importVat??'Indeterminado'};dedução:Não apurada`,`${r.opportunity};${minerCurrent(draft,b)?'Resultado actual':'Entradas alteradas;resultado histórico conservado'};versão${r.revision};${r.calculatedAt};${row.origin};beneficiário${row.beneficiary};contrato${row.contractRef};documento${row.documentRef},${row.documentDate};âmbito${row.scope};factos${row.evidence};condições${JSON.stringify(Object.fromEntries([...minerCommon,...(minerTreatments.find(t=>t.id===row.treatment)?.conditions??[])].map(([k])=>[k,row.conditions[k]??'Por esclarecer'])))};importação${row.importDate};aduaneiro${row.customsRef},${row.customsScope};titular${row.rightHolder}${row.rightHolder==='heir'?`;herança${row.heirRef};óbito${row.deathDate};pedido${row.requestDate}`:''}${['monthly','accumulated'].includes(row.treatment)?`;remessa${row.value};total${row.periodTotal};registo${row.quotaRef}`:''}${row.treatment==='accumulated'?`;janela${row.accumulationStart}/${row.accumulationMonths}meses`:''}${row.treatment==='appliance'||minerVehicle(row.treatment)?`;tipo${row.kind};quantidade${row.quantity};histórico${row.quotaRef}`:''}${minerVehicle(row.treatment)?`;benefício anterior${row.previousBenefit},${row.previousBenefit==='known'?row.previousBenefitDate:'nenhum declarado'}`:''}${minerReturn(row.treatment)?`;chegada${row.arrivalDate};pedido${row.requestDate}${minerNeedsDeadlineExtension(row)?`;prorrogação${row.extensionRef||'Por identificar'}`:''}`:''}${row.treatment==='return_vehicle'||row.treatment==='separate_vehicle'?`;estadia${row.stayStart}/${row.stayEnd}${minerNeedsStayException(row)?`;excepção${row.exceptionRef||'Por identificar'}`:''}`:''}${row.treatment==='separate_vehicle'?`;IVA normal${row.normalVat};referência${row.normalVatRef}`:''};${r.rows[i].source};sem lançamento automático nos totaisModeloA/campo11.`]);}catch{return[['IVA mensal','P059','Dados não recuperáveis','Originais preservados;sem substituição porzero.']];}}
|