feat(iva): prepare P046 objective import correspondence in QA
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { SimulationObjectiveImports } from './simulation-objective-imports';
|
||||
import { SimulationDigital } from './simulation-digital';
|
||||
import { SimulationTravel } from './simulation-travel';
|
||||
import { SimulationTravelEvents } from './simulation-travel-events';
|
||||
@@ -113,6 +114,7 @@ export function SimulationMonthlyIva({draft, busy, onCalculate, onChange}: {
|
||||
<SimulationOperations draft={draft} onChange={onChange} />
|
||||
<SimulationAcquisitions draft={draft} onChange={onChange} />
|
||||
<SimulationImportExemptions draft={draft} onChange={onChange} />
|
||||
<SimulationObjectiveImports draft={draft} onChange={onChange} />
|
||||
<SimulationPublicAuthorityBase draft={draft} onChange={onChange} />
|
||||
<SimulationCommissionBase draft={draft} onChange={onChange} />
|
||||
<SimulationActSaleBase draft={draft} onChange={onChange} />
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {useState} from 'react';
|
||||
import {objectiveImportBook,objectiveImportCurrent,objectiveImportReportRows,objectiveImportSource,objectiveImportSources,objectiveImportTreatments,newObjectiveImport,reuseObjectiveImport,recordObjectiveImports,saveObjectiveImportBook,type ObjectiveImportBook,type ObjectiveImportRow} from '@/lib/iva-objective-imports';
|
||||
import {annexIEntries} from '@/lib/iva-annex-i';
|
||||
import {pharmaAnnexEntries} from '@/lib/iva-pharma-annex';
|
||||
export function SimulationObjectiveImports({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}){
|
||||
const[error,setError]=useState('');let book:ObjectiveImportBook;try{book=objectiveImportBook(draft);}catch(e){return<p role="alert">{String(e)}</p>;}
|
||||
const save=(next:ObjectiveImportBook)=>{try{onChange('modelo_a_snapshot',saveObjectiveImportBook(draft,next));setError('');}catch(e){setError(String(e));}};
|
||||
const change=(id:string,patch:Partial<ObjectiveImportRow>)=>save({...book,rows:book.rows.map(r=>r.id===id?{...r,...patch}:r)});
|
||||
let sources:ReturnType<typeof objectiveImportSources>=[];try{sources=objectiveImportSources(draft);}catch{/* Preserve unrecoverable source; direct entry remains independent. */}
|
||||
return<details className="rounded border p-3"><summary>Importações definitivas — isenção objectiva interna (P046)</summary><p>Analise a transmissão interna hipotética dos mesmos bens. Não é necessário criar uma venda interna. A enumeração legal é exemplificativa; este percurso cobre as correspondências expressas implementadas. Outros fundamentos permanecem por enquadrar.</p><p>{objectiveImportSource}</p>{error&&<p role="alert">{error}</p>}
|
||||
<button type="button" onClick={()=>save({...book,rows:[...book.rows,newObjectiveImport(crypto.randomUUID(),String(draft.currency??''))]})}>Acrescentar importação objectiva</button>
|
||||
{book.rows.map(row=>{const rule=objectiveImportTreatments.find(t=>t.id===row.reference.treatment);return<details key={row.id}><summary>{row.description||'Importação por preparar'} — {row.period||'período por indicar'}</summary>
|
||||
{!!sources.length&&<label>Preencher campos em falta a partir de bens já preparados<select value="" onChange={e=>{const source=sources.find(s=>s.id===e.target.value);if(source)change(row.id,reuseObjectiveImport(row,source));}}><option value="">Escolher origem sem sobrescrever edições</option>{sources.map(s=><option key={s.id} value={s.id}>{s.description} — {s.period}</option>)}</select></label>}
|
||||
{(['description','period','importer','documentRef','documentDate','scope','evidence'] as const).map((field,i)=><label key={field} className="block">{['Bens desta importação','Período da importação','Importador','Documento aduaneiro ou previsão','Data do documento ou previsão','Âmbito: bens,operação,período e condições','Factos ou pressupostos expressos'][i]}<input type={field==='period'?'month':field==='documentDate'?'date':'text'} value={row[field]} onChange={e=>change(row.id,{[field]:e.target.value})}/></label>)}
|
||||
<label>Natureza dos dados<select value={row.origin} onChange={e=>change(row.id,{origin:e.target.value as ObjectiveImportRow['origin']})}><option value="projected">Pressupostos de projecção</option><option value="effective">Factos efectivos declarados</option></select></label>
|
||||
{(['definitive','goods','sameGoods','internalApplicable'] as const).map((field,i)=><label key={field} className="block">{['Importação definitiva','A operação importa bens,não um serviço','Correspondência refere-se aos mesmos bens e âmbito desta importação','Isenção objectiva interna aplicável na hipótese destes bens'][i]}<select value={row[field]} onChange={e=>change(row.id,{[field]:e.target.value})}><option value="">Por esclarecer</option><option value="yes">Sim</option><option value="no">Não</option></select></label>)}
|
||||
<label>Correspondência da isenção interna<select value={row.reference.treatment} onChange={e=>change(row.id,{reference:{...row.reference,treatment:e.target.value,confirmed:[]},internalApplicable:''})}><option value="other">Escolher correspondência</option>{objectiveImportTreatments.map(t=><option key={t.id} value={t.id}>{t.label}</option>)}</select></label>
|
||||
{rule&&<><p>{rule.article}. As condições seguintes referem-se à hipótese de transmissão no território nacional dos bens identificados; não ao fornecedor estrangeiro. Trocar correspondência conserva anexos em rascunho e limpa confirmações.</p>{rule.conditions.map((condition,i)=><label className="block" key={i}><input type="checkbox" checked={row.reference.confirmed.includes(i)} onChange={e=>change(row.id,{reference:{...row.reference,confirmed:e.target.checked?[...row.reference.confirmed,i]:row.reference.confirmed.filter(n=>n!==i)}})}/>{i===0?'A transmissão interna hipotética estaria localizada em Moçambique; a operação real analisada continua a ser a importação':condition}</label>)}</>}
|
||||
{row.reference.treatment==='annexI_goods'&&<><label>Posição do Anexo I<select value={row.reference.annexIItem??''} onChange={e=>change(row.id,{reference:{...row.reference,annexIItem:e.target.value,confirmed:[]}})}><option value="">Por indicar</option>{annexIEntries.map(a=><option key={a.id} value={a.id}>{a.code} — {a.label}</option>)}</select></label><label>Após conferir os outros tratamentos,corresponde apenas a9(12)h?<select value={row.reference.annexIOnly===undefined?'':row.reference.annexIOnly?'yes':'no'} onChange={e=>change(row.id,{reference:{...row.reference,annexIOnly:e.target.value===''?undefined:e.target.value==='yes'}})}><option value="">Por esclarecer</option><option value="yes">Sim,com fundamento nos factos indicados</option><option value="no">Não;escolher correspondência específica</option></select></label></>}
|
||||
{row.reference.treatment==='pharma_manufacturing_inputs'&&<label>Posição do Anexo III<select value={row.reference.pharmaAnnexItem??''} onChange={e=>change(row.id,{reference:{...row.reference,pharmaAnnexItem:e.target.value,confirmed:[]}})}><option value="">Por indicar</option>{pharmaAnnexEntries.map(a=><option key={a.id} value={a.id}>{a.code} — {a.label}</option>)}</select></label>}
|
||||
</details>;})}
|
||||
{!!book.rows.length&&<button type="button" onClick={()=>{try{save(recordObjectiveImports(draft,book));}catch(e){setError(String(e));}}}>Conferir importações objectivas e guardar versão</button>}
|
||||
{book.result&&<><p role="status">{objectiveImportCurrent(draft,book)?'Resultado corresponde às entradas guardadas.':'Entradas alteradas;resultado histórico conservado.'} {book.history?.length??0} versões anteriores. Sem dedução ou soma aos livros internos.</p><dl>{objectiveImportReportRows(draft).map(([,label,value,origin],i)=><div key={i}><dt>{label}</dt><dd>{value}<small className="block">{origin}</small></dd></div>)}</dl></>}
|
||||
</details>;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import {acquisitionScope,readAcquisitionStore} from './iva-acquisitions.ts';
|
||||
import {operationTreatments,newSalesOperation,evaluateSalesOperation,salesBook,type SalesOperation} from './iva-operations.ts';
|
||||
export const objectiveImportVersion='objective-imports/1';
|
||||
export const objectiveImportSource='CIVA12(1)a,Lei10/2025,BR248-I/suplemento,PDF12/p2180(12),efeitos01/01/2026:importações definitivas de bens cuja transmissão interna beneficie de isenção objectiva;enumeração exemplificativa. Correspondências9(1)b/c/e/f/g,9(9)b,9(10),9(11),9(12)g/h/i;catálogo interno e anexosLei22/2022 já conferidos. Sem dedução/campo11,direitos ou alteração dos livros internos.';
|
||||
const fixed=['health_assistive','human_materials','mosquito_nets','medicines','pharma_manufacturing_inputs','cultural_publications','official_military_supplies','industrial_feed_inputs','annexI_goods','veterinary_goods'];
|
||||
export const objectiveImportTreatments=operationTreatments.filter(t=>fixed.includes(t.id)||t.id.startsWith('listed_'));
|
||||
type Choice=''|'yes'|'no';
|
||||
export type ObjectiveImportRow={id:string;description:string;period:string;currency:string;origin:'projected'|'effective';importer:string;documentRef:string;documentDate:string;scope:string;evidence:string;definitive:Choice;goods:Choice;sameGoods:Choice;internalApplicable:Choice;reference:SalesOperation;reusedFrom:string};
|
||||
type Line={id:string;status:'exempt'|'not_applicable'|'indeterminate';importVat:string|null;deduction:null;source:string;reason:string};
|
||||
export type ObjectiveImportCalculation={version:string;revision:number;calculatedAt:string;opportunity:string;sourceKey:string;source:string;inputs:ObjectiveImportRow[];rows:Line[]};
|
||||
export type ObjectiveImportBook={rows:ObjectiveImportRow[];result?:ObjectiveImportCalculation;history?:ObjectiveImportCalculation[]};
|
||||
type Store=ReturnType<typeof readAcquisitionStore>&{objectiveImports?:Record<string,ObjectiveImportBook>};
|
||||
export function newObjectiveImport(id:string,currency:string):ObjectiveImportRow{return{id,description:'',period:'',currency,origin:'projected',importer:'',documentRef:'',documentDate:'',scope:'',evidence:'',definitive:'',goods:'',sameGoods:'',internalApplicable:'',reference:newSalesOperation(id+'-reference','',currency),reusedFrom:''};}
|
||||
export function objectiveImportSources(draft:Record<string,unknown>):SalesOperation[]{return salesBook(draft).rows.filter(r=>objectiveImportTreatments.some(t=>t.id===r.treatment));}
|
||||
export function reuseObjectiveImport(row:ObjectiveImportRow,source:SalesOperation):ObjectiveImportRow{
|
||||
const reference={...row.reference};
|
||||
// This explicit action fills missing values only; confirmations belong to this import.
|
||||
if(!reference.treatment||reference.treatment==='other')reference.treatment=source.treatment;
|
||||
if(reference.treatment===source.treatment){reference.annexIItem ||= source.annexIItem;reference.pharmaAnnexItem ||= source.pharmaAnnexItem;reference.annexIOnly ??= source.annexIOnly;}
|
||||
return{...row,description:row.description||source.description,period:row.period||source.period,evidence:row.evidence||source.evidence,reference,reusedFrom:source.id};
|
||||
}
|
||||
function readStore(draft:Record<string,unknown>):Store{const s=readAcquisitionStore(draft.modelo_a_snapshot) as Store;const books=s.objectiveImports;if(books!==undefined&&(!books||typeof books!=='object'||Array.isArray(books)))throw new Error('Importações objectivas não recuperáveis;originais preservados.');const fields=Object.keys(newObjectiveImport('','MZN')).filter(k=>k!=='reference');for(const b of Object.values(books??{}))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 ObjectiveImportRow]!=='string')||!r.reference||typeof r.reference.treatment!=='string'||!Array.isArray(r.reference.confirmed)))throw new Error('Linhas de importação objectiva não recuperáveis;sem substituição por lista vazia.');return s;}
|
||||
export function objectiveImportBook(draft:Record<string,unknown>):ObjectiveImportBook{return readStore(draft).objectiveImports?.[acquisitionScope(draft)]??{rows:[]};}
|
||||
export function saveObjectiveImportBook(draft:Record<string,unknown>,book:ObjectiveImportBook):string{const s=readStore(draft),encoded=JSON.stringify({...s,objectiveImports:{...s.objectiveImports,[acquisitionScope(draft)]:book}});readStore({...draft,modelo_a_snapshot:encoded});return encoded;}
|
||||
function date(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 evaluateObjectiveImport(row:ObjectiveImportRow,currency:unknown):Line{
|
||||
const rule=objectiveImportTreatments.find(t=>t.id===row.reference.treatment),result:Line={id:row.id,status:'indeterminate',importVat:null,deduction:null,source:objectiveImportSource+(rule?';'+rule.source:''),reason:''};const stop=(reason:string,status:Line['status']='indeterminate')=>({...result,status,reason});
|
||||
if(currency!=='MZN'||row.currency!==currency||!/^2026-(0[1-9]|1[0-2])$/.test(row.period))return stop('Indique período2026 e moedaMZN coerente com o projecto;sem conversão presumida.');
|
||||
if(!['projected','effective'].includes(row.origin)||![row.description,row.importer,row.documentRef,row.scope,row.evidence].every(v=>!!v.trim())||!date(row.documentDate))return stop('Identifique bens,importador,documento/previsão,data,âmbito e factos/pressupostos.Reutilize os elementos disponíveis.');
|
||||
if([row.definitive,row.goods,row.sameGoods,row.internalApplicable].some(v=>v==='no'))return stop('Este fundamento12(1)a não se aplica aos factos indicados;IVA fora deste fundamento permanece por enquadrar,sem taxa presumida.','not_applicable');
|
||||
if([row.definitive,row.goods,row.sameGoods,row.internalApplicable].some(v=>v!=='yes'))return stop('Confirme importação definitiva de bens,a correspondência dos mesmos bens e a aplicabilidade da isenção objectiva interna.Desconhecido não significa IVA zero.');
|
||||
if(!rule)return stop('Escolha correspondência interna documentada.A lista implementada cobre as remissões expressas12(1)a;é exemplificativa e não declara excluídos outros fundamentos.Não basta descrição,isenção de serviços,exportação,taxa reduzida ou benefício terminado.');
|
||||
// Evaluate the legal hypothetical domestic transmission, never create a domestic sale or use its amount/totals.
|
||||
const reference={...row.reference,id:row.id+'-reference',description:row.description,period:row.period,sourcePeriod:row.period,sourceCurrency:row.currency,sourceAmount:null,establishment:row.importer,amount:'0',basis:'net' as const,evidence:row.evidence};
|
||||
const v=evaluateSalesOperation(reference,row.currency);result.source=objectiveImportSource+';'+v.source;
|
||||
if(v.status!=='exempt')return stop('Correspondência interna ainda não demonstrada: '+v.reason);
|
||||
return{...result,status:'exempt',importVat:'0.00',reason:`Isenção12(1)a por correspondência a${rule.article},nas condições ${row.origin==='projected'?'expressamente assumidas da projecção':'efectivas declaradas'}. IVA na importação:0.00MZN. Hipótese de transmissão interna,não venda criada nem montante somado.Sem direitos,base aduaneira,dedução/campo11 ou concessão administrativa automática.`};
|
||||
}
|
||||
function key(draft:Record<string,unknown>,book:ObjectiveImportBook):string{return JSON.stringify({version:objectiveImportVersion,catalog:objectiveImportTreatments,scope:acquisitionScope(draft),currency:draft.currency,location:draft.selection_labels??{},rows:book.rows});}
|
||||
export function objectiveImportCurrent(draft:Record<string,unknown>,book:ObjectiveImportBook):boolean{return!!book.result&&book.result.sourceKey===key(draft,book);}
|
||||
export function recordObjectiveImports(draft:Record<string,unknown>,book:ObjectiveImportBook):ObjectiveImportBook{if(!book.rows.length||new Set(book.rows.map(r=>r.id)).size!==book.rows.length)throw new Error('Indique linhas com identidades distintas;originais conservados.');const result:ObjectiveImportCalculation={version:objectiveImportVersion,revision:(book.result?.revision??0)+1,calculatedAt:new Date().toISOString(),opportunity:acquisitionScope(draft),sourceKey:key(draft,book),source:objectiveImportSource,inputs:structuredClone(book.rows),rows:book.rows.map(r=>evaluateObjectiveImport(r,draft.currency))};const next={...book,result,history:[...(book.history??[]),...(book.result?[book.result]:[])]};saveObjectiveImportBook(draft,next);return next;}
|
||||
export function objectiveImportReportRows(draft:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=objectiveImportBook(draft),r=b.result;if(!r)return[];return r.inputs.map((row,i)=>['IVA mensal',`P046 — ${row.period} — ${row.id}`,`${row.description};${r.rows[i].reason};IVA importação:${r.rows[i].importVat??'Indeterminado'};dedução:Não apurada`,`${r.opportunity};${objectiveImportCurrent(draft,b)?'Resultado actual':'Entradas alteradas;resultado histórico conservado'};versão${r.revision};${r.calculatedAt};${row.origin};importador${row.importer};documento${row.documentRef},${row.documentDate};âmbito${row.scope};factos${row.evidence};reutilização${row.reusedFrom||'Introdução directa'};correspondência${row.reference.treatment};condições internas confirmadas${row.reference.confirmed.join(',')}${row.reference.treatment==='annexI_goods'?`;AnexoI${row.reference.annexIItem??''},apenasAnexoI${String(row.reference.annexIOnly??'Por esclarecer')}`:row.reference.treatment==='pharma_manufacturing_inputs'?`;AnexoIII${row.reference.pharmaAnnexItem??''}`:''};${r.rows[i].source};sem alteração dos livros internos,ModeloA,campo11 ou totais.`]);}catch{return[['IVA mensal','P046','Dados não recuperáveis','Originais preservados;sem classificação por defeito.']];}}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { digitalReportRows } from './iva-digital.ts';
|
||||
import { objectiveImportReportRows } from './iva-objective-imports.ts';
|
||||
import { travelReportRows } from './iva-travel.ts';
|
||||
import { travelEventReportRows } from './iva-travel-events.ts';
|
||||
import { foreignOperationReportRows } from './iva-foreign-operations.ts';
|
||||
@@ -664,6 +665,7 @@ export function buildProjectDossierRows(
|
||||
rows.push(...currencyEventReportRows(draft));
|
||||
rows.push(...foreignOperationReportRows(draft));
|
||||
rows.push(...digitalReportRows(draft));
|
||||
rows.push(...objectiveImportReportRows(draft));
|
||||
rows.push(...travelReportRows(draft));
|
||||
rows.push(...travelEventReportRows(draft));
|
||||
rows.push(...offerPreparationReportRows(draft));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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 IvaObjectiveImportDraftTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
public function test_objective_imports_recover_without_altering_any_accepted_book(): 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-objective-imports.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:361',$recovered['context_opportunity']);
|
||||
$s=json_decode($recovered['modelo_a_snapshot'],true,flags:JSON_THROW_ON_ERROR);$b=$s['objectiveImports']['opportunity:361'];$this->assertCount(28,$b['rows']);$this->assertCount(1,$b['history']);$this->assertSame($b['rows'],$b['result']['inputs']);
|
||||
foreach($b['result']['rows'] as $line){$this->assertSame('exempt',$line['status']);$this->assertSame('0.00',$line['importVat']);$this->assertNull($line['deduction']);}
|
||||
$frozen=json_decode(file_get_contents(base_path('tests/frontend/fixtures/iva-objective-imports-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,['objectiveImports'=>true]));
|
||||
$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-import-source-review-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,33 @@
|
||||
import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';
|
||||
import {newObjectiveImport,objectiveImportTreatments,evaluateObjectiveImport,reuseObjectiveImport,recordObjectiveImports,saveObjectiveImportBook,objectiveImportBook,objectiveImportCurrent,objectiveImportReportRows} from '../../resources/js/lib/iva-objective-imports.ts';
|
||||
import {annexIEntries} from '../../resources/js/lib/iva-annex-i.ts';
|
||||
const frozen=JSON.parse(fs.readFileSync('tests/frontend/fixtures/iva-objective-imports-preserved.json','utf8')),prior=JSON.parse(frozen.modelo_a_snapshot);
|
||||
const draft={...frozen,context_opportunity:'opportunity:361',project_title:'SYNTHETIC QA — Importações objectivas / Manica'};
|
||||
function row(treatment,id=treatment){const r={...newObjectiveImport(id,'MZN'),description:'SYNTHETIC QA bens '+treatment,period:'2026-10',importer:'SYNTHETIC QA importador',documentRef:'SYNTHETIC QA DU/previsão',documentDate:'2026-10-01',scope:'SYNTHETIC QA bens/operação/período',evidence:'SYNTHETIC QA factos/pressupostos da correspondência',definitive:'yes',goods:'yes',sameGoods:'yes',internalApplicable:'yes'};const rule=objectiveImportTreatments.find(t=>t.id===treatment);r.reference={...r.reference,treatment,confirmed:rule?.conditions.map((_,i)=>i)??[],annexIItem:'annexI-2022-001',annexIOnly:true,pharmaAnnexItem:'annexIII-2022-001'};return r;}
|
||||
const rows=objectiveImportTreatments.map(t=>row(t.id));
|
||||
for(const r of rows)test('P046 '+r.reference.treatment+' explicit correspondence,without domestic sale or deduction',()=>{const v=evaluateObjectiveImport(r,'MZN');assert.equal(v.status,'exempt');assert.equal(v.importVat,'0.00');assert.equal(v.deduction,null);assert.match(v.source,/12\(1\)a/);assert.match(v.source,/9\(/);});
|
||||
test('P046 documentary and factual unknowns remain indeterminate; explicit failed prerequisites remain distinct',()=>{
|
||||
for(const patch of [{period:'2025-12'},{period:'2026-13'},{currency:'USD'},{origin:''},{description:''},{importer:''},{documentRef:''},{documentDate:'2026-02-30'},{scope:''},{evidence:''},{definitive:''},{goods:''},{sameGoods:''},{internalApplicable:''}]){const v=evaluateObjectiveImport({...rows[0],...patch},'MZN');assert.equal(v.status,'indeterminate');assert.equal(v.importVat,null);}
|
||||
for(const field of ['definitive','goods','sameGoods','internalApplicable']){const v=evaluateObjectiveImport({...rows[0],[field]:'no'},'MZN');assert.equal(v.status,'not_applicable');assert.equal(v.importVat,null);}
|
||||
for(const t of ['export_goods','public_health','private_education','production_agriculture','temporary_sugar','unknown'])assert.equal(evaluateObjectiveImport(row(t),'MZN').status,'indeterminate');
|
||||
});
|
||||
test('P046 does not infer conditions,annex qualifiers or extend another reference to the goods',()=>{
|
||||
for(const r of rows)for(const condition of r.reference.confirmed){const v=evaluateObjectiveImport({...r,reference:{...r.reference,confirmed:r.reference.confirmed.filter(n=>n!==condition)}},'MZN');assert.equal(v.status,'indeterminate');assert.equal(v.importVat,null);}
|
||||
for(const field of ['annexIItem','pharmaAnnexItem']){const t=field==='annexIItem'?'annexI_goods':'pharma_manufacturing_inputs';assert.equal(evaluateObjectiveImport({...row(t),reference:{...row(t).reference,[field]:'missing'}},'MZN').status,'indeterminate');}
|
||||
const overlap=row('annexI_goods');overlap.reference.annexIItem='annexI-2022-013';overlap.reference.annexIOnly=undefined;assert.equal(evaluateObjectiveImport(overlap,'MZN').status,'indeterminate');overlap.reference.annexIOnly=true;assert.equal(evaluateObjectiveImport(overlap,'MZN').status,'exempt');
|
||||
const required=row('annexI_goods');required.reference.annexIItem=annexIEntries.find(a=>a.relatedRequired).id;assert.equal(evaluateObjectiveImport(required,'MZN').status,'indeterminate');
|
||||
});
|
||||
test('P046 reuse fills only missing fields and never copies confirmations or overwrites deliberate edits',()=>{
|
||||
const source={...rows[0].reference,id:'source',description:'source description',period:'2026-11',evidence:'source evidence',treatment:'annexI_goods',annexIItem:'annexI-2022-001'};
|
||||
const empty=newObjectiveImport('reuse','MZN'),copy=reuseObjectiveImport(empty,source);assert.equal(copy.description,source.description);assert.equal(copy.period,source.period);assert.equal(copy.reference.treatment,'annexI_goods');assert.deepEqual(copy.reference.confirmed,[]);assert.equal(copy.internalApplicable,'');
|
||||
const original=structuredClone(rows[0]),edited=reuseObjectiveImport(original,source);assert.equal(edited.description,original.description);assert.equal(edited.evidence,original.evidence);assert.deepEqual(edited.reference,original.reference);assert.equal(edited.reusedFrom,'source');
|
||||
});
|
||||
let book=recordObjectiveImports(draft,{rows});draft.modelo_a_snapshot=saveObjectiveImportBook(draft,book);
|
||||
test('P046 versions,history,project isolation and every accepted book remain intact',()=>{
|
||||
assert.equal(objectiveImportCurrent(draft,book),true);const changed={...book,rows:book.rows.map((r,i)=>i? r:{...r,scope:'changed'})};assert.equal(objectiveImportCurrent(draft,changed),false);const next=recordObjectiveImports(draft,changed);assert.equal(next.history.length,1);assert.deepEqual(next.history[0],book.result);assert.notEqual(next.result.sourceKey,book.result.sourceKey);assert.equal(objectiveImportBook({...draft,context_opportunity:'opportunity:999'}).rows.length,0);
|
||||
assert.deepEqual(prior,Object.fromEntries(Object.entries(JSON.parse(draft.modelo_a_snapshot)).filter(([key])=>key!=='objectiveImports')));assert.throws(()=>objectiveImportBook({...draft,modelo_a_snapshot:'{"version":1,"books":{},"objectiveImports":[]}'}));
|
||||
assert.match(objectiveImportReportRows(draft)[0][3],/versão1/);assert.match(objectiveImportReportRows(draft)[0][3],/sem alteração dos livros internos/);book=recordObjectiveImports(draft,book);draft.modelo_a_snapshot=saveObjectiveImportBook(draft,book);
|
||||
});
|
||||
test('P046 final fixture keeps digital formalities and travel fiscal limitations visible',()=>{
|
||||
const s=JSON.parse(draft.modelo_a_snapshot);assert.equal(s.digitalOperations['opportunity:359'].result.rows[0].procedure,'regulation_pending');for(const p of s.travelOperations['opportunity:360'].result.periods){assert.equal(p.vat,null);assert.equal(p.deduction,null);}assert.equal(book.result.rows.length,28);if(process.env.IVA_OBJECTIVE_IMPORT_FIXTURE)fs.writeFileSync(process.env.IVA_OBJECTIVE_IMPORT_FIXTURE,JSON.stringify(draft));
|
||||
});
|
||||
Reference in New Issue
Block a user