feat(iva): prepare documented P059 miner import paths in QA
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import {useState} from 'react';
|
||||
import {minerBook,minerCurrent,minerReportRows,minerSource,minerTreatments,minerCommon,minerVehicle,minerReturn,minerNeedsDeadlineExtension,minerNeedsStayException,newMiner,recordMiners,saveMinerBook,type MinerBook,type MinerRow} from '@/lib/iva-miners';
|
||||
export function SimulationMiners({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}){
|
||||
const[error,setError]=useState('');let book:MinerBook;try{book=minerBook(draft);}catch(e){return<p role="alert">{String(e)}</p>;}
|
||||
const save=(next:MinerBook)=>{try{onChange('modelo_a_snapshot',saveMinerBook(draft,next));setError('');}catch(e){setError(String(e));}};
|
||||
const change=(id:string,patch:Partial<MinerRow>)=>save({...book,rows:book.rows.map(r=>r.id===id?{...r,...patch}:r)});
|
||||
return<details className="rounded border p-3"><summary>Importações dos mineiros — regime da África do Sul (P059)</summary><p>Lei n.º2/2011,artigos1–4:página1 doPDF;artigo5:página2/p110-(8). O exemplar reúne vários diplomas;«Lei do Voluntariado» é o nome do ficheiro original de uma das ligações,não a base deste benefício. Regulamento:Decreto3/2011,artigos1–8,páginas2–3/p152–153.</p><p>Prepare apenas o percurso dos bens desta importação. A redução de50% pertence ao veículo separado da bagagem,não a todas as importações dos mineiros. O simulador não concede autorizações,nem emite declaração aduaneira. Importação temporária em férias tem licença/prazos próprios;use o percurso aduaneiro existente,não a transforme em isenção definitiva.</p>{error&&<p role="alert">{error}</p>}
|
||||
<button type="button" onClick={()=>{const next=newMiner(crypto.randomUUID(),String(draft.currency??''));const candidates=book.rows.filter(r=>r.beneficiary&&r.contractRef&&r.origin===next.origin);const keys=new Set(candidates.map(r=>r.beneficiary+'\u0000'+r.contractRef));if(keys.size===1){const source=candidates[0];next.beneficiary=source.beneficiary;next.contractRef=source.contractRef;for(const k of ['national','rsa','contract'] as const)next.conditions[k]=source.conditions[k];}save({...book,rows:[...book.rows,next]});}}>Acrescentar importação de mineiro</button>
|
||||
{book.rows.map(row=>{const rule=minerTreatments.find(t=>t.id===row.treatment);const text=(field:Exclude<keyof MinerRow,'conditions'>,label:string,type='text')=><label key={field} className="block">{label}<input type={type} value={row[field]} onChange={e=>change(row.id,{[field]:e.target.value})}/></label>;const choice=(key:keyof MinerRow['conditions'],label:string)=><label key={key} className="block">{label}<select value={row.conditions[key]??''} onChange={e=>change(row.id,{conditions:{...row.conditions,[key]:e.target.value}})}><option value="">Por esclarecer</option><option value="yes">Sim,segundo factos ou pressuposto identificado</option><option value="no">Não</option></select></label>;
|
||||
return<details key={row.id}><summary>{row.description||'Importação de mineiro por preparar'} — {row.period||'período por indicar'}</summary>
|
||||
<label>Percurso dos bens<select value={row.treatment} onChange={e=>change(row.id,{treatment:e.target.value,conditions:Object.fromEntries(minerCommon.map(([k])=>[k,row.conditions[k]]))})}><option value="">Escolher percurso</option>{minerTreatments.map(t=><option key={t.id} value={t.id}>{t.label}</option>)}</select></label>
|
||||
{text('description','Bens desta importação')}{text('beneficiary','Mineiro beneficiário originário')}{text('contractRef','Contrato e referência do visto de trabalho')}{text('period','Período da importação','month')}{text('importDate','Data da importação ou previsão','date')}
|
||||
<label>Natureza dos dados<select value={row.origin} onChange={e=>change(row.id,{origin:e.target.value as MinerRow['origin']})}><option value="projected">Pressupostos de projecção</option><option value="effective">Factos efectivos declarados</option></select></label>
|
||||
{text('documentRef','Documento disponível ou previsão')}{text('documentDate','Data do documento ou previsão','date')}{text('scope','Âmbito:bens,operação,período e condições')}{text('evidence','Factos ou pressupostos expressos')}
|
||||
<label>Titular do direito<select value={row.rightHolder} onChange={e=>change(row.id,{rightHolder:e.target.value as MinerRow['rightHolder']})}><option value="">Por indicar</option><option value="miner">Mineiro</option><option value="heir">Herdeiro legal,direito não usado</option></select></label>
|
||||
{minerCommon.map(([k,label])=>choice(k,label))}{rule?.conditions.map(([k,label])=>choice(k,label))}
|
||||
{row.rightHolder==='heir'&&<>{text('heirRef','Herdeiro e fundamento da transmissão do direito')}{text('deathDate','Data do óbito','date')}{text('requestDate','Data do requerimento','date')}{choice('heir','Qualidade de herdeiro legal identificada')}{choice('unusedRight','Direito não usado pelo beneficiário originário')}{choice('acquiredAlive','Bens adquiridos pelo mineiro em vida')}</>}
|
||||
{['baggage','return_baggage','monthly'].includes(row.treatment)&&<label>Via dos bens<select value={row.route} onChange={e=>change(row.id,{route:e.target.value as MinerRow['route']})}><option value="">Por indicar</option><option value="accompanied">Acompanhados pelo mineiro</option><option value="company">Empresa transportadora/distribuidora</option></select></label>}
|
||||
{text('customsRef','Referência aduaneira aplicável ou pressuposto da projecção')}{text('customsScope','Âmbito do enquadramento aduaneiro nesta importação')}
|
||||
{['monthly','accumulated'].includes(row.treatment)&&<>{text('value','Valor desta remessa emMZN')}{text('periodTotal','Total do âmbito mensal/acumulado,incluindo esta e outras remessas')}{text('quotaRef','Registo completo ou pressuposto do saldo,incluindo fora do dossier')}<p>Cobertura dos exemplares:15.000MZN por mês. Revisão do limite2(5) não é presumida. Não misturar saldos mensais com meses já acumulados.</p></>}
|
||||
{row.treatment==='accumulated'&&<>{text('accumulationStart','Primeiro mês acumulado','month')}{text('accumulationMonths','Número de meses acumulados:1a6')}</>}
|
||||
{row.treatment==='appliance'&&text('kind','Tipo de electrodoméstico para o limite anual')}
|
||||
{minerVehicle(row.treatment)&&<><label>Direito de veículo identificado<select value={row.kind} onChange={e=>change(row.id,{kind:e.target.value})}><option value="">Por indicar</option><option value="vehicle">Veículo automóvel</option><option value="tractor">Tractor agrícola e respectivas alfaias</option></select></label><label>Benefício anterior aplicável a este direito<select value={row.previousBenefit} onChange={e=>change(row.id,{previousBenefit:e.target.value as MinerRow['previousBenefit']})}><option value="">Por esclarecer</option><option value="none">Nenhum,segundo histórico identificado</option><option value="known">Sim,data conhecida</option></select></label>{row.previousBenefit==='known'&&text('previousBenefitDate','Data do benefício anterior aplicável','date')}</>}
|
||||
{(row.treatment==='appliance'||minerVehicle(row.treatment))&&<>{text('quantity','Quantidade abrangida nesta linha')}{text('quotaRef','Histórico do direito,incluindo benefícios fora do dossier')}</>}
|
||||
{minerReturn(row.treatment)&&<>{text('arrivalDate','Data da chegada no regresso definitivo','date')}{row.rightHolder!=='heir'&&text('requestDate','Data do requerimento do benefício','date')}{minerNeedsDeadlineExtension(row)&&<>{choice('deadlineExtension','Invoca prorrogação excepcional do prazo de90dias,por motivo justificado?')}{row.conditions.deadlineExtension==='yes'&&text('extensionRef','Prorrogação e motivo justificado,âmbito e prazo concedido ou pressuposto expresso')}</>}</>}
|
||||
{['return_vehicle','separate_vehicle'].includes(row.treatment)&&<>{text('stayStart','Início da estadia naRSA','date')}{text('stayEnd','Fim da estadia naRSA','date')}{minerNeedsStayException(row)&&<>{choice('stayException','Invoca autorização excepcional6(7) por estadia inferior a um ano?')}{row.conditions.stayException==='yes'&&text('exceptionRef','Autorização/âmbito da excepção6(7)ou pressuposto expresso')}</>}</>}
|
||||
{row.treatment==='separate_vehicle'&&<>{text('normalVat','IVA informado sem o benefício de50%,emMZN')}{text('normalVatRef','Referência do IVA normal ou pressuposto expresso')}</>}
|
||||
<p>Trocar percurso conserva os dados ocultos no rascunho e limpa as suas confirmações específicas. Referências aduaneiras disponíveis podem ser reutilizadas;nenhum procedimento específico daKAWENA é imposto às outras vias.</p>
|
||||
</details>;})}
|
||||
{!!book.rows.length&&<button type="button" onClick={()=>{try{save(recordMiners(draft,book));}catch(e){setError(String(e));}}}>Conferir importações dos mineiros e guardar versão</button>}
|
||||
{book.result&&<><p role="status">{minerCurrent(draft,book)?'Resultado corresponde às entradas guardadas.':'Entradas alteradas;resultado histórico conservado.'} {book.history?.length??0} versões anteriores.Sem dedução/campo11 ou declaração aduaneira emitida.</p><dl>{minerReportRows(draft).map(([,label,value,source],i)=><div key={i}><dt>{label}</dt><dd>{value}<small className="block">{source}</small></dd></div>)}</dl></>}
|
||||
<details><summary>Fonte e âmbito documental</summary><p>{minerSource}</p></details>
|
||||
</details>;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { SimulationObjectiveImports } from './simulation-objective-imports';
|
||||
import { SimulationMiners } from './simulation-miners';
|
||||
import { SimulationDigital } from './simulation-digital';
|
||||
import { SimulationTravel } from './simulation-travel';
|
||||
import { SimulationTravelEvents } from './simulation-travel-events';
|
||||
@@ -115,6 +116,7 @@ export function SimulationMonthlyIva({draft, busy, onCalculate, onChange}: {
|
||||
<SimulationAcquisitions draft={draft} onChange={onChange} />
|
||||
<SimulationImportExemptions draft={draft} onChange={onChange} />
|
||||
<SimulationObjectiveImports draft={draft} onChange={onChange} />
|
||||
<SimulationMiners draft={draft} onChange={onChange} />
|
||||
<SimulationPublicAuthorityBase draft={draft} onChange={onChange} />
|
||||
<SimulationCommissionBase draft={draft} onChange={onChange} />
|
||||
<SimulationActSaleBase draft={draft} onChange={onChange} />
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
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.']];}}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { digitalReportRows } from './iva-digital.ts';
|
||||
import { objectiveImportReportRows } from './iva-objective-imports.ts';
|
||||
import { minerReportRows } from './iva-miners.ts';
|
||||
import { travelReportRows } from './iva-travel.ts';
|
||||
import { travelEventReportRows } from './iva-travel-events.ts';
|
||||
import { foreignOperationReportRows } from './iva-foreign-operations.ts';
|
||||
@@ -666,6 +667,7 @@ export function buildProjectDossierRows(
|
||||
rows.push(...foreignOperationReportRows(draft));
|
||||
rows.push(...digitalReportRows(draft));
|
||||
rows.push(...objectiveImportReportRows(draft));
|
||||
rows.push(...minerReportRows(draft));
|
||||
rows.push(...travelReportRows(draft));
|
||||
rows.push(...travelEventReportRows(draft));
|
||||
rows.push(...offerPreparationReportRows(draft));
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace Tests\Feature;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\TestCase;
|
||||
final class IvaMinerDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_miner_imports_recover_with_P046_and_all_accepted_books_untouched(): void
|
||||
{
|
||||
$target=DB::selectOne('select current_database() as name,inet_server_port() as port');$this->assertSame('simulator_fiscal_delta_20261021_test',$target->name);$this->assertSame(55441,(int)$target->port);
|
||||
config(['investor-path.enabled'=>true,'suitecrm.enabled'=>false]);$this->actingAs(User::factory()->create(['email_verified_at'=>now()]));
|
||||
$draft=json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-miners.json')),true,flags:JSON_THROW_ON_ERROR);
|
||||
$id=$this->postJson('/api/v1/simulation-drafts',['personal_snapshot'=>[],'economic_snapshot'=>$draft],['Idempotency-Key'=>(string)Str::uuid()])->assertCreated()->json('data.id');$recovered=$this->getJson('/api/v1/simulation-drafts/'.$id)->assertOk()->json('data.economic_snapshot');
|
||||
$this->assertSame($draft['modelo_a_snapshot'],$recovered['modelo_a_snapshot']);$this->assertSame('opportunity:362',$recovered['context_opportunity']);$s=json_decode($recovered['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$b=$s['minerImports']['opportunity:362'];$this->assertCount(9,$b['rows']);$this->assertCount(1,$b['history']);$this->assertSame($b['rows'],$b['result']['inputs']);
|
||||
foreach($b['result']['rows'] as $row){$this->assertContains($row['status'],['exempt','reduced']);$this->assertNull($row['deduction']);$this->assertStringContainsString('arts1–4PDF1',$row['source']);}
|
||||
$this->assertSame('80.061728',$b['result']['rows'][7]['importVat']);$this->assertSame('exempt',$b['result']['rows'][8]['status']);
|
||||
$frozen=json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-miners-preserved.json')),true,flags:JSON_THROW_ON_ERROR);$prior=json_decode($frozen['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$this->assertSame($prior,array_diff_key($s,['minerImports'=>true]));$this->assertCount(28,$s['objectiveImports']['opportunity:361']['result']['rows']);$this->assertSame('regulation_pending',$s['digitalOperations']['opportunity:359']['result']['rows'][0]['procedure']);foreach($s['travelOperations']['opportunity:360']['result']['periods'] as $period){$this->assertNull($period['vat']);$this->assertNull($period['deduction']);}
|
||||
file_put_contents(base_path('outputs/iva-l1-miners-returnees-20261025/recovered.json'),json_encode($recovered,JSON_THROW_ON_ERROR|JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
|
||||
import {minerTreatments,minerCommon,newMiner,evaluateMiner,recordMiners,saveMinerBook,minerBook,minerCurrent,minerReportRows} from '../../resources/js/lib/iva-miners.ts';
|
||||
const frozen=JSON.parse(fs.readFileSync('tests/frontend/fixtures/iva-miners-preserved.json','utf8')),prior=JSON.parse(frozen.modelo_a_snapshot),draft={...frozen,context_opportunity:'opportunity:362',project_title:'SYNTHETIC QA — Mineiros / Manica'};
|
||||
function row(treatment,id=treatment,patch={}){const rule=minerTreatments.find(t=>t.id===treatment);return{...newMiner(id,'MZN'),treatment,description:'SYNTHETIC QA bens '+id,period:'2026-10',beneficiary:'SYNTHETIC QA mineiro '+id,contractRef:'SYNTHETIC QA contrato/visto',documentRef:'SYNTHETIC QA documento/previsão',documentDate:'2026-10-01',scope:'SYNTHETIC QA bens/direito/operação identificados',evidence:'SYNTHETIC QA factos ou pressupostos expressos',importDate:'2026-10-10',route:'accompanied',customsRef:'SYNTHETIC QA DU/registo/pressuposto',customsScope:'SYNTHETIC QA direito e bens desta importação',rightHolder:'miner',conditions:Object.fromEntries([...minerCommon,...(rule?.conditions??[])].map(([k])=>[k,'yes'])),quantity:'1',kind:treatment==='appliance'?'frigorífico':'vehicle',quotaRef:'SYNTHETIC QA histórico global,inclui esta e operações externas',previousBenefit:'none',value:'15000',periodTotal:'15000',accumulationStart:'2026-05',accumulationMonths:'6',arrivalDate:'2026-09-01',requestDate:'2026-09-20',stayStart:'2024-09-01',stayEnd:'2026-08-31',normalVat:'160.123456',normalVatRef:'SYNTHETIC QA IVA normal informado',...patch};}
|
||||
const rows=minerTreatments.map(t=>row(t.id));rows.find(r=>r.treatment==='accumulated').periodTotal='90000';
|
||||
for(const r of rows)test('P059 '+r.treatment+' scoped entitlement without duties or deduction',()=>{const v=evaluateMiner(r,'MZN');assert.equal(v.status,r.treatment==='separate_vehicle'?'reduced':'exempt');assert.equal(v.importVat,r.treatment==='separate_vehicle'?'80.061728':'0.00');assert.equal(v.deduction,null);assert.match(v.source,/arts1–4PDF1/);assert.match(v.source,/PDF2–3\/p152–153/);});
|
||||
test('P059 unknowns never become exemption; alcohol/tobacco and other failed facts remain distinct',()=>{
|
||||
const r=rows[0];for(const patch of [{currency:'USD'},{period:'2025-10'},{importDate:'2026-11-10'},{importDate:'2026-02-30'},{beneficiary:''},{contractRef:''},{documentRef:''},{documentDate:''},{scope:''},{evidence:''},{customsRef:''},{customsScope:''},{rightHolder:''},{route:''},{conditions:{...r.conditions,rsa:''}},{treatment:'temporary_vehicle'}]){const v=evaluateMiner({...r,...patch},'MZN');assert.equal(v.status,'indeterminate');assert.equal(v.importVat,null);}
|
||||
for(const [k] of [...minerCommon,...minerTreatments[0].conditions])assert.equal(evaluateMiner({...r,conditions:{...r.conditions,[k]:'no'}},'MZN').status,'not_applicable');
|
||||
for(const r of rows)for(const[k]of [...minerCommon,...minerTreatments.find(t=>t.id===r.treatment).conditions])assert.equal(evaluateMiner({...r,conditions:{...r.conditions,[k]:''}},'MZN').status,'indeterminate');
|
||||
});
|
||||
test('P059 monthly amount and accumulated window including year boundary,without reuse of expired months',()=>{
|
||||
const m=row('monthly');for(const patch of [{value:''},{periodTotal:''},{value:'15001',periodTotal:'15000'},{quotaRef:''},{periodTotal:'15000.000001'}])assert.equal(evaluateMiner({...m,...patch},'MZN').status,'indeterminate');
|
||||
const a=rows.find(r=>r.treatment==='accumulated');assert.equal(evaluateMiner({...a,periodTotal:'90000.000001'},'MZN').status,'indeterminate');for(const patch of [{accumulationStart:'2026-04'},{accumulationMonths:'7'},{accumulationMonths:'0'},{accumulationStart:''}])assert.equal(evaluateMiner({...a,...patch},'MZN').status,'indeterminate');
|
||||
const year={...a,period:'2026-01',importDate:'2026-01-10',accumulationStart:'2025-08'};assert.equal(evaluateMiner(year,'MZN').status,'exempt');
|
||||
});
|
||||
test('P059 appliance and five-year vehicle entitlement use identified external history,not dossier absence',()=>{
|
||||
const a=row('appliance');for(const patch of [{kind:''},{quantity:'2'},{quotaRef:''}])assert.equal(evaluateMiner({...a,...patch},'MZN').status,'indeterminate');
|
||||
const v=row('vehicle');for(const patch of [{kind:''},{kind:'boat'},{previousBenefit:''},{quantity:'0'},{quotaRef:''},{previousBenefit:'known',previousBenefitDate:''}])assert.equal(evaluateMiner({...v,...patch},'MZN').status,'indeterminate');
|
||||
assert.equal(evaluateMiner({...v,previousBenefit:'known',previousBenefitDate:'2021-10-10'},'MZN').status,'exempt');assert.equal(evaluateMiner({...v,previousBenefit:'known',previousBenefitDate:'2021-10-11'},'MZN').status,'not_applicable');assert.equal(evaluateMiner({...v,previousBenefit:'known',previousBenefitDate:'2026-10-11'},'MZN').status,'not_applicable');
|
||||
});
|
||||
test('P059 return deadlines and one-year stay; exceptions are referenced,never presumed',()=>{
|
||||
const v=row('return_vehicle');assert.equal(evaluateMiner({...v,requestDate:'2026-11-30'},'MZN').status,'exempt');assert.equal(evaluateMiner({...v,requestDate:'2026-12-01'},'MZN').status,'indeterminate');
|
||||
const ext={...v,requestDate:'2027-03-01',extensionRef:'SYNTHETIC QA prorrogação',conditions:{...v.conditions,deadlineExtension:'yes'}};assert.equal(evaluateMiner(ext,'MZN').status,'exempt');assert.equal(evaluateMiner({...ext,requestDate:'2027-03-02'},'MZN').status,'indeterminate');
|
||||
assert.equal(evaluateMiner({...v,stayStart:'2026-02-01'},'MZN').status,'indeterminate');assert.equal(evaluateMiner({...v,stayEnd:'2026-09-02'},'MZN').status,'indeterminate');
|
||||
const sep=row('separate_vehicle');const except={...sep,stayStart:'2026-02-01',exceptionRef:'SYNTHETIC QA âmbito/autorização6(7)',conditions:{...sep.conditions,stayException:'yes'}};assert.equal(evaluateMiner(except,'MZN').status,'reduced');assert.equal(evaluateMiner({...except,exceptionRef:''},'MZN').status,'indeterminate');
|
||||
for(const patch of [{normalVat:''},{normalVat:'-1'},{normalVatRef:''}])assert.equal(evaluateMiner({...sep,...patch},'MZN').status,'indeterminate');assert.equal(evaluateMiner({...sep,normalVat:'0.000001'},'MZN').importVat,'0.0000005');
|
||||
});
|
||||
test('P059 heirs:unused right,goods acquired alive and request within one year;not automatic succession',()=>{
|
||||
const r=row('baggage','heir',{rightHolder:'heir',heirRef:'SYNTHETIC QA herdeiro/direito',deathDate:'2025-10-01',requestDate:'2026-10-01'});r.conditions={...r.conditions,heir:'yes',unusedRight:'yes',acquiredAlive:'yes'};assert.equal(evaluateMiner(r,'MZN').status,'exempt');for(const patch of [{requestDate:'2026-10-02'},{requestDate:'2025-09-30'},{heirRef:''},{deathDate:''},{conditions:{...r.conditions,acquiredAlive:''}}])assert.notEqual(evaluateMiner({...r,...patch},'MZN').status,'exempt');assert.equal(evaluateMiner({...r,conditions:{...r.conditions,unusedRight:'no'}},'MZN').status,'not_applicable');
|
||||
});
|
||||
test('P059 saved scope detects double annual/vehicle rights and overlapping monthly balances without deleting lines',()=>{
|
||||
for(const t of ['appliance','vehicle','monthly','accumulated']){const first=row(t,'first'),second={...first,id:'second'};if(t==='accumulated'){first.periodTotal='90000';second.periodTotal='90000';}const b=recordMiners(draft,{rows:[first,second]});assert.deepEqual(b.result.rows.map(r=>r.status),['indeterminate','indeterminate']);assert.equal(b.rows.length,2);}
|
||||
const first=row('monthly','m1',{value:'5000'}),second={...first,id:'m2',value:'10000'};assert.deepEqual(recordMiners(draft,{rows:[first,second]}).result.rows.map(r=>r.status),['exempt','exempt']);second.value='';assert.equal(recordMiners(draft,{rows:[first,second]}).result.rows[0].status,'indeterminate');
|
||||
const v=row('vehicle','v1'),later={...v,id:'v2',kind:'tractor'};assert.deepEqual(recordMiners(draft,{rows:[v,later]}).result.rows.map(r=>r.status),['exempt','exempt']);
|
||||
});
|
||||
const heir=row('baggage','heir',{rightHolder:'heir',heirRef:'SYNTHETIC QA herdeiro/direito',deathDate:'2025-10-01',requestDate:'2026-10-01'});heir.conditions={...heir.conditions,heir:'yes',unusedRight:'yes',acquiredAlive:'yes'};rows.push(heir);
|
||||
let book=recordMiners(draft,{rows});draft.modelo_a_snapshot=saveMinerBook(draft,book);
|
||||
test('P059 history,opportunity isolation,P046 QA and all125 accepted books retained',()=>{
|
||||
assert.equal(minerCurrent(draft,book),true);const changed={...book,rows:book.rows.map((r,i)=>i?r:{...r,scope:'changed'})};assert.equal(minerCurrent(draft,changed),false);book=recordMiners(draft,changed);draft.modelo_a_snapshot=saveMinerBook(draft,book);assert.equal(book.history.length,1);assert.equal(minerBook({...draft,context_opportunity:'opportunity:999'}).rows.length,0);assert.deepEqual(prior,Object.fromEntries(Object.entries(JSON.parse(draft.modelo_a_snapshot)).filter(([k])=>k!=='minerImports')));assert.throws(()=>minerBook({...draft,modelo_a_snapshot:'{"version":1,"books":{},"minerImports":[]}'}));assert.match(minerReportRows(draft)[0][3],/versão2/);assert.doesNotMatch(minerReportRows(draft)[0][3],/IVA normal/);assert.match(minerReportRows(draft).find(r=>r[1].includes('separate_vehicle'))[3],/IVA normal/);
|
||||
if(process.env.IVA_MINER_FIXTURE)fs.writeFileSync(process.env.IVA_MINER_FIXTURE,JSON.stringify(draft));
|
||||
});
|
||||
Reference in New Issue
Block a user