32 lines
6.4 KiB
TypeScript
32 lines
6.4 KiB
TypeScript
import {acquisitionScope} from './iva-acquisitions.ts';
|
||
import {operationTreatments,salesBook,salesCurrent,type SalesOperation,type SalesLineResult} from './iva-operations.ts';
|
||
export const acquisitionRightVersion='acquisition-operation-right/1';
|
||
export const acquisitionRightSource='L2-A — CIVA19(1)(a)/(b)(i/iv/v),19(2),Lei13/2016 PDF15/p1426(273);Lei10/2025 PDF13/p2180(13):18(2),19(2),exclusão17-A. Condições da aquisição18/20/21 continuam próprias. Sem resolverP00105/06,importações,afectação parcial,excepções ou regularizações.';
|
||
export type AcquisitionRightLink={version:1;scopeId:string;operationId:string;fingerprint:string;scopeConfirmed:''|'yes'|'no';allocationRef:string;noExcludedPayments:''|'yes'|'no'};
|
||
export type AcquisitionEligibility={status:'eligible'|'no_right'|'indeterminate';reason:string;source:string;operationId?:string;operationPeriod?:string;operationTreatment?:string;operationDescription?:string;operationArticle?:string;scopeId?:string};
|
||
export function validAcquisitionRightLink(value:unknown):value is AcquisitionRightLink{if(!value||typeof value!=='object'||Array.isArray(value))return false;const v=value as AcquisitionRightLink;return v.version===1&&['scopeId','operationId','fingerprint','allocationRef'].every(k=>typeof v[k as keyof AcquisitionRightLink]==='string')&&['','yes','no'].includes(v.scopeConfirmed)&&['','yes','no'].includes(v.noExcludedPayments);}
|
||
function fingerprint(row:SalesOperation,result:SalesLineResult|undefined){return JSON.stringify({version:acquisitionRightVersion,input:row,classification:result?{article:result.article,source:result.source,status:result.status}:null});}
|
||
export function linkedBeverageDeductionRight(draft:Record<string,unknown>,link:AcquisitionRightLink,notCoastalFishing:boolean):AcquisitionEligibility{
|
||
const base=linkedOperationRight(draft,link),b=salesBook(draft),row=b.rows.find(r=>r.id===link.operationId),line=b.result?.rows.find(r=>r.id===link.operationId),rule=operationTreatments.find(t=>t.id===row?.treatment);
|
||
if(link.scopeId!==acquisitionScope(draft)||!salesCurrent(draft,b)||!row||!line||!rule||link.fingerprint!==fingerprint(row,line)||row.supplyCategory!=='beverages'||!['onboard_seagoing_ship','onboard_international_aircraft','onboard_rescue_coastal_ship'].includes(row.treatment)||!row.description.trim()||!row.establishment.trim()||!row.evidence.trim()||rule.conditions.some((_,i)=>!row.confirmed.includes(i))||!/^2026-(0[1-9]|1[0-2])$/.test(row.period)||row.sourceCurrency!=='MZN'||!/^\d{1,15}(\.\d{1,6})?$/.test(row.amount)||!['net','included'].includes(row.basis)||row.treatment==='onboard_rescue_coastal_ship'&&!notCoastalFishing)return {...base,status:'indeterminate',reason:'Correspondência própria13(2) por demonstrar em origemL1 actual:c/d/h,bebidas,condições e documento;pesca costeira conserva exclusão de provisões.'};
|
||
return {...base,status:'eligible',reason:'Via de dedução13(2) de bebidas em13(1)(c/d/h),nas condições demonstradas;20(1)(d) não se aplica para este efeito. Não concede isenção directa nem restituição.',source:base.source+';CIVA13(2–3),Lei13/2016 PDF12/p1426(270)'};
|
||
}
|
||
export function acquisitionOperationChoices(draft:Record<string,unknown>){const book=salesBook(draft);return book.rows.map(row=>({id:row.id,label:`${row.description||row.id} — ${row.period} — ${operationTreatments.find(t=>t.id===row.treatment)?.label??row.treatment}`,row,current:salesCurrent(draft,book)}));}
|
||
export function createAcquisitionRightLink(draft:Record<string,unknown>,operationId:string):AcquisitionRightLink{const b=salesBook(draft),row=b.rows.find(r=>r.id===operationId);if(!row)throw Error('Operação de origem não encontrada nesta oportunidade;dados preservados.');return{version:1,scopeId:acquisitionScope(draft),operationId,fingerprint:fingerprint(row,b.result?.rows.find(r=>r.id===operationId)),scopeConfirmed:'',allocationRef:'',noExcludedPayments:''};}
|
||
export function linkedOperationRight(draft:Record<string,unknown>,link:AcquisitionRightLink):AcquisitionEligibility{
|
||
const base:AcquisitionEligibility={status:'indeterminate',reason:'',source:acquisitionRightSource,operationId:link.operationId,scopeId:link.scopeId};const stop=(reason:string,status:AcquisitionEligibility['status']='indeterminate')=>({...base,status,reason});
|
||
if(!validAcquisitionRightLink(link)||link.scopeId!==acquisitionScope(draft))return stop('Vínculo inválido ou pertencente a outra oportunidade;sem direito presumido.');
|
||
const b=salesBook(draft),row=b.rows.find(r=>r.id===link.operationId),line=b.result?.rows.find(r=>r.id===link.operationId);if(!row)return stop('Operação ligada removida/não recuperável;escolha uma origem disponível,sem apagar o histórico.');
|
||
base.operationPeriod=row.period;base.operationTreatment=row.treatment;base.operationDescription=row.description;base.operationArticle=line?.article;base.source+=';'+(line?.source??'Fonte da classificação por recuperar');
|
||
if(!salesCurrent(draft,b)||!line||line.status==='indeterminate')return stop('Classificação L1 de origem incompleta ou alterada;recupere/actualize a classificação,sem dedução por rótulo.');
|
||
if(link.fingerprint!==fingerprint(row,line))return stop('Operação de origem alterada:reveja e adopte explicitamente a origem actual;montantes e histórico da aquisição conservados.');
|
||
const rule=operationTreatments.find(t=>t.id===row.treatment);if(!rule)return stop('Correspondência do direito ainda não identificada para esta operação.');
|
||
if(/17-A/.test(rule.article))return stop('Operação abrangida por17-A:19(2),redacçãoLei10/2025,não permite dedução apesar de ter IVA liquidado.','no_right');
|
||
if(/19\(1\)\(b\)\((i|iv|v)\)/.test(rule.article))return stop('Direito da operação identificado em'+rule.article+';a aquisição continua sujeita às suas condições próprias.','eligible');
|
||
if(line.status==='calculated'&&rule.rate===16)return stop('Operação sujeita e não isenta:direito19(1)(a),sujeito a19(2) e condições próprias da aquisição.','eligible');
|
||
// This is a bounded match to the already implemented article9 catalogue, after
|
||
// checking its express article19 exceptions. Never use the ModeloA box as law.
|
||
if(line.status==='exempt'&&/\b9\(/.test(rule.article))return stop('Operação isenta do artigo9 fora das correspondências com direito identificadas19(1)(b);não confere dedução neste fundamento.','no_right');
|
||
return stop('Classificação disponível,mas correspondência do direito não implementada neste percurso;não inferir direito pela caixa05/06/07 ou por IVAzero.');
|
||
}
|