Files
Investidor-V2/resources/js/lib/iva-digital.ts
T

85 lines
17 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {acquisitionScope,readAcquisitionStore} from './iva-acquisitions.ts';
export const digitalVersion='digital-operations/1';
export const digitalSource='CIVA: Lei10/2025, PDF12/p2180(12), artigos3(2)/(7),6(7)k/(8)/(9); enumeração6(7)k i–v mantida de Lei13/2016 PDF8/p1426(266). Obrigações26(1–3),2016PDF17/p1426(275), e23(2),25(7),26(4),32(1)c e33(2),2025PDF13–14. Recorte2026. Natureza, localização e responsável separados de isenção, taxa, base, IVA e dedução. Modelos/formalidades remetidos para regulamentação não são presumidos.';
export const digitalTreatments=[
{id:'web',kind:'service',branch:'P121',clause:'6(7)k i',label:'Sítios, alojamento, manutenção remota e acesso a plataformas/cloud'},
{id:'software_service',kind:'service',branch:'P121',clause:'6(7)k ii/6(9)',label:'Programa/actualização como serviço, incluindo SaaS'},
{id:'information',kind:'service',branch:'P121',clause:'6(7)k iii',label:'Imagens, textos, informação e acesso a bases de dados'},
{id:'media',kind:'service',branch:'P121',clause:'6(7)k iv/6(9)',label:'Música, filmes, jogos, emissões e streaming'},
{id:'teaching',kind:'service',branch:'P121',clause:'6(7)k v',label:'Ensino à distância por meios electrónicos'},
{id:'analogous',kind:'service',branch:'P121',clause:'6(7)k vi/6(9)',label:'Outro serviço digital análogo, financeiro ou de intermediação digital'},
{id:'software_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Software como activo digital'},
{id:'content_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Conteúdo digital como activo'},
{id:'data_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Dados digitais com finalidade económica'},
{id:'crypto_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Criptomoeda como activo digital'},
{id:'ebook_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'E-book como activo digital'},
{id:'profile_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Perfil em rede social como activo'},
{id:'access_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Conta, acesso ou identificador digital equiparável'},
{id:'other_asset',kind:'goods',branch:'P130',clause:'3(2)/(7)',label:'Outro activo virtual equiparável, com natureza fundamentada'},
] as const;
type Choice=''|'yes'|'no';
export type DigitalRow={id:string;kind:string;treatment:string;description:string;period:string;origin:'projected'|'effective';operationDate:string;operationRef:string;documentRef:string;documentDate:string;subjectRef:string;useRef:string;natureRef:string;scope:string;evidence:string;providerPlace:string;providerCountry:string;providerRef:string;buyerPlace:string;buyerCountry:string;buyerRef:string;buyerTaxable:string;buyerTaxRef:string;providerTaxable:string;providerTaxRef:string;representative:string;representativeRef:string;communicationPrior:string;communicationRef:string;businessUse:string;businessRef:string;goodsPlace:string;goodsPlaceRef:string;conditions:Partial<Record<string,Choice>>};
type LineResult={id:string;status:'determined'|'not_applicable'|'indeterminate';kind:string;territory:'national'|'outside'|null;responsible:'acquirer'|'provider'|'representative'|'outside'|'indeterminate';obligationReason:string;declarationReferenceDate:string|null;procedure:'regulation_pending'|'general'|'outside'|'indeterminate';baseAmount:null;vat:null;rate:null;deduction:null;reason:string};
export type DigitalCalculation={version:string;revision:number;calculatedAt:string;opportunity:string;sourceKey:string;source:string;inputs:DigitalRow[];rows:LineResult[]};
export type DigitalBook={rows:DigitalRow[];result?:DigitalCalculation;history?:DigitalCalculation[]};
type Store=ReturnType<typeof readAcquisitionStore>&{digitalOperations?:Record<string,DigitalBook>};
export function newDigitalRow(id:string):DigitalRow{return{id,kind:'',treatment:'',description:'',period:'',origin:'projected',operationDate:'',operationRef:'',documentRef:'',documentDate:'',subjectRef:'',useRef:'',natureRef:'',scope:'',evidence:'',providerPlace:'',providerCountry:'',providerRef:'',buyerPlace:'',buyerCountry:'',buyerRef:'',buyerTaxable:'',buyerTaxRef:'',providerTaxable:'',providerTaxRef:'',representative:'',representativeRef:'',communicationPrior:'',communicationRef:'',businessUse:'',businessRef:'',goodsPlace:'',goodsPlaceRef:'',conditions:{}};}
export function digitalConditions(kind:string):Array<readonly[string,string]>{return kind==='goods'?[
['digitalAsset','Activo intangível em formato electrónico, com valor económico e susceptível de apropriação, titularidade, controlo, transferência ou licenciamento —3(7)'],
['assetTransfer','Esta operação transmite o bem digital identificado; a qualificação como activo distingue-se da prestação de um serviço'],
]:kind==='service'?[
['digitalService','Prestação intangível por meios electrónicos/software/plataforma/rede, nos termos de6(9); não exige ausência total de intervenção humana'],
['serviceSupply','A operação identificada é prestação de serviço, distinguida da transmissão de um activo digital'],
]:[];}
export function switchDigitalKind(row:DigitalRow,kind:string):DigitalRow{return{...row,kind,treatment:'',conditions:{}};}
export function switchDigitalTreatment(row:DigitalRow,treatment:string):DigitalRow{return{...row,treatment,conditions:{}};}
function readStore(draft:Record<string,unknown>):Store{
const s=readAcquisitionStore(draft.modelo_a_snapshot) as Store;
if(s.digitalOperations!==undefined&&(!s.digitalOperations||typeof s.digitalOperations!=='object'||Array.isArray(s.digitalOperations)))throw new Error('Operações digitais não recuperáveis; originais conservados.');
const fields=Object.keys(newDigitalRow('')).filter(k=>k!=='conditions');
for(const b of Object.values(s.digitalOperations??{}))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 DigitalRow]!=='string')||!r.conditions||typeof r.conditions!=='object'||Array.isArray(r.conditions)))throw new Error('Linhas digitais não recuperáveis; sem lista vazia por defeito.');
return s;
}
export function digitalBook(draft:Record<string,unknown>):DigitalBook{return readStore(draft).digitalOperations?.[acquisitionScope(draft)]??{rows:[]};}
export function saveDigitalBook(draft:Record<string,unknown>,book:DigitalBook):string{const s=readStore(draft),encoded=JSON.stringify({...s,digitalOperations:{...s.digitalOperations,[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;}
function declarationDate(period:string):string{const[y,m]=period.split('-').map(Number);return new Date(Date.UTC(y,m,10)).toISOString().slice(0,10);}
export function evaluateDigital(row:DigitalRow):LineResult{
const result:LineResult={id:row.id,status:'indeterminate',kind:row.kind,territory:null,responsible:'indeterminate',obligationReason:'Responsável por esclarecer; nenhuma obrigação ou quantia presumida.',declarationReferenceDate:null,procedure:'indeterminate',baseAmount:null,vat:null,rate:null,deduction:null,reason:''};
const stop=(reason:string,status:LineResult['status']='indeterminate')=>({...result,status,reason});
const rule=digitalTreatments.find(t=>t.id===row.treatment&&t.kind===row.kind);
if(!rule)return stop('Distinga transmissão de bem digital de prestação de serviço e identifique o tratamento. Software, licenciamento ou acesso não decidem essa distinção apenas pelo nome.');
if(!/^2026-(0[1-9]|1[0-2])$/.test(row.period)||!['projected','effective'].includes(row.origin)||!date(row.operationDate)||row.operationDate.slice(0,7)!==row.period)return stop('Identifique mês2026 e data correspondente, efectivos ou pressupostos expressos.');
const conditions=digitalConditions(row.kind);
if(conditions.some(([k])=>row.conditions[k]==='no'))return stop('A natureza não corresponde a este percurso digital; conservar dados e utilizar o tratamento aplicável. Não significa isenção ou IVA zero.','not_applicable');
if(conditions.some(([k])=>row.conditions[k]!=='yes'))return stop('Natureza digital ou distinção bem/serviço por esclarecer; categoria comercial não substitui os factos da operação.');
if(!row.description.trim()||!row.operationRef.trim()||!row.documentRef.trim()||!date(row.documentDate)||!row.subjectRef.trim()||!row.useRef.trim()||!row.natureRef.trim()||!row.scope.trim()||!row.evidence.trim())return stop('Identifique objecto, direitos/funcionalidade, destinatário/utilização, fundamento da natureza, operação, documento ou pressuposto, âmbito e período. Reutilize referências existentes; sem certificado ou aprovador adicional.');
if(!['MZ','foreign'].includes(row.buyerCountry)||!row.buyerPlace.trim()||!row.buyerRef.trim())return stop('Identifique estabelecimento ou domicílio do adquirente e referência; nacionalidade, moeda, IP ou servidor isolado não substituem esse facto.');
if(row.buyerCountry==='foreign')return{...result,status:'determined',territory:'outside',responsible:'outside',procedure:'outside',obligationReason:'6(8): fora do âmbito territorial nacional desta operação; não determina imposto estrangeiro.',reason:`${rule.branch},${rule.clause}: adquirente estabelecido/domiciliado no estrangeiro,6(8), ainda que transmitente/prestador nacional. Não é benefício fiscal, isenção ou IVA zero.`};
if(!['MZ','foreign'].includes(row.providerCountry)||!row.providerPlace.trim()||!row.providerRef.trim())return stop('Identifique sede/estabelecimento/domicílio a partir do qual o transmitente/prestador fornece, com país e referência.');
if(row.providerCountry==='foreign'){
result.territory='national';result.status='determined';result.reason=`${rule.branch},${rule.clause}: transmitente/prestador sem sede/EP/domicílio nacional a partir do qual fornece e adquirente nacional;6(7)k na redacção2025. A qualidade fiscal do adquirente não é presumida e distingue-se da localização.`;
if(row.buyerTaxable==='yes'&&row.buyerTaxRef.trim())return{...result,responsible:'acquirer',procedure:'regulation_pending',declarationReferenceDate:declarationDate(row.period),obligationReason:'26(4): obrigações a cargo do adquirente sujeito passivo nacional;25(7): declaração mensal e mapa de retenções em modelos apropriados, nos termos a regulamentar.32(1)c: referência legal até dia10 do mês seguinte. Sem ajuste de calendário/prorrogação presumidos, sem submissão ou pagamento.23(2)exclui6(7)k;33(2)refere26(1–3), não substituir este percurso digital por operações isoladas.'};
if(row.buyerTaxable==='no'&&row.buyerTaxRef.trim()&&row.providerTaxable==='yes'&&row.providerTaxRef.trim()){
if(row.representative==='yes'&&row.representativeRef.trim()&&row.communicationPrior==='yes'&&row.communicationRef.trim())return{...result,responsible:'representative',procedure:'general',obligationReason:'26(1)/(2): representante residente identificado, com procuração/poderes e comunicação à contraparte antes da operação; responsabilidade solidária com o representado.33(2) remete26(1–3) para declaração de operações isoladas. Não aplicar automaticamente prazo digital32(1)c ou atribuir retenção a consumidor.'};
if(row.representative==='no'&&row.businessUse==='yes'&&row.businessRef.trim())return{...result,responsible:'acquirer',procedure:'general',obligationReason:'26(3): falta de representante e aquisição no exercício de actividade comercial, industrial ou profissional expressamente identificadas; obrigações pelo adquirente.33(2) e23(2) quanto26(3), sem substituir pelo percurso26(4) de sujeito passivo nacional ou presumir montante/prazo.'};
}
return{...result,obligationReason:row.buyerTaxable==='no'?'26(4) não atribui por este fundamento as obrigações ao adquirente não sujeito passivo. Conferir26(1–3): qualidade do fornecedor não residente, representante com poderes/comunicação ou aquisição no exercício de actividade. Localização nacional mantida; nenhuma retenção automaticamente imputada ao consumidor.':'Qualidade fiscal do adquirente ou referência por esclarecer; localização nacional mantida. Não atribuir automaticamente26(4) nem obrigação de retenção a consumidor.'};
}
if(row.kind==='goods'){
if(!['MZ','foreign'].includes(row.goodsPlace)||!row.goodsPlaceRef.trim())return stop('Bem digital equiparado a corpóreo3(2)/(7): para o percurso doméstico6(1), identificar onde se encontra na colocação à disposição, com fundamento próprio. Não inferir do servidor, nacionalidade ou domicílio do adquirente.');
result.territory=row.goodsPlace==='MZ'?'national':'outside';
if(result.territory==='outside')return{...result,status:'determined',responsible:'outside',procedure:'outside',obligationReason:'6(1): localização exterior expressamente fundamentada; sem imposto estrangeiro presumido.',reason:`P130,3(2)/(7) e6(1): local do bem na colocação à disposição identificado fora do território; classificação não concede isenção.`};
}else result.territory='national';
return{...result,status:'determined',responsible:row.providerTaxable==='yes'&&!!row.providerTaxRef.trim()?'provider':'indeterminate',procedure:row.providerTaxable==='yes'&&!!row.providerTaxRef.trim()?'general':'indeterminate',obligationReason:row.providerTaxable==='yes'&&!!row.providerTaxRef.trim()?'Transmitente/prestador sujeito passivo nacional: obrigações gerais identificadas23(1)/25.26(4) e prazo digital32(1)c não se aplicam por este fundamento. Sem cálculo, submissão ou pagamento.':'Qualidade fiscal do transmitente/prestador ou fundamento por esclarecer; não atribuir obrigação automaticamente.',reason:`${rule.branch},${rule.clause}: operação doméstica;${row.kind==='service'?'localização6(3), estabelecimento nacional a partir do qual o serviço é prestado':'localização6(1), bem na colocação à disposição em Moçambique segundo referência expressa'}. Não confirma isenção, taxa, base, IVA ou dedução.`};
}
function key(draft:Record<string,unknown>,book:DigitalBook):string{return JSON.stringify({version:digitalVersion,scope:acquisitionScope(draft),location:draft.selection_labels??{},rows:book.rows});}
export function digitalCurrent(draft:Record<string,unknown>,book:DigitalBook):boolean{return!!book.result&&book.result.sourceKey===key(draft,book);}
export function recordDigital(draft:Record<string,unknown>,book:DigitalBook):DigitalBook{
if(!book.rows.length||new Set(book.rows.map(r=>r.id)).size!==book.rows.length)throw new Error('Identifique operações distintas; originais conservados.');
const result:DigitalCalculation={version:digitalVersion,revision:(book.result?.revision??0)+1,calculatedAt:new Date().toISOString(),opportunity:acquisitionScope(draft),sourceKey:key(draft,book),source:digitalSource,inputs:structuredClone(book.rows),rows:book.rows.map(evaluateDigital)};
const next={...book,result,history:[...(book.history??[]),...(book.result?[book.result]:[])]};saveDigitalBook(draft,next);return next;
}
export function digitalReportRows(draft:Record<string,unknown>):Array<[string,string,string,string]>{try{const b=digitalBook(draft),r=b.result;if(!r)return[];return r.inputs.map((row,i)=>{const line=r.rows[i],rule=digitalTreatments.find(t=>t.id===row.treatment);return['IVA mensal',`${rule?.branch??'Digital'} — ${row.period} — ${row.id}`,`${row.description};natureza:${row.kind==='goods'?'Bem digital':'Serviço digital'};localização:${line.territory==='national'?'Moçambique':line.territory==='outside'?'Fora do território':'Indeterminada'};${line.reason};responsável:${line.responsible};${line.obligationReason};${line.declarationReferenceDate?'Data de referência32(1)c:'+line.declarationReferenceDate+';':''}base/IVA/dedução:Indeterminados`,`${r.opportunity};${digitalCurrent(draft,b)?'Resultado actual':'Entradas alteradas;resultado histórico conservado'};versão${r.revision};${r.calculatedAt};${r.source};${row.origin==='projected'?'Pressupostos projectados':'Dados efectivos declarados'};${rule?.label??''};operação${row.operationRef},${row.operationDate};objecto${row.subjectRef};natureza${row.natureRef};utilização${row.useRef};fornecedor${row.providerPlace},${row.providerCountry},${row.providerRef};adquirente${row.buyerPlace},${row.buyerCountry},${row.buyerRef};${row.buyerCountry==='MZ'&&row.providerCountry==='foreign'?'qualidade fiscal adquirente'+row.buyerTaxable+','+row.buyerTaxRef+';':''}${row.buyerCountry==='MZ'&&row.providerCountry==='MZ'?'qualidade fiscal fornecedor'+row.providerTaxable+','+row.providerTaxRef+';':''}${row.kind==='goods'&&row.buyerCountry==='MZ'&&row.providerCountry==='MZ'?'local do bem6(1)'+row.goodsPlace+','+row.goodsPlaceRef+';':''}${row.buyerCountry==='MZ'&&row.providerCountry==='foreign'&&row.buyerTaxable==='no'?'fornecedor não residente sujeito passivo'+row.providerTaxable+','+row.providerTaxRef+';representante'+row.representative+','+row.representativeRef+';comunicação anterior'+row.communicationPrior+','+row.communicationRef+';actividade comercial/industrial/profissional'+row.businessUse+','+row.businessRef+';':''}âmbito${row.scope};documento/pressuposto${row.documentRef},${row.documentDate};factos${row.evidence};${digitalConditions(row.kind).map(([k,label])=>label+':'+(row.conditions[k]??'Por esclarecer')).join(';')};sem lançamento fiscal, dedução/campo11, regularização ou totaisModeloA.`];});}catch{return[['IVA mensal','Digital','Dados não recuperáveis','Originais conservados; nenhuma classificação por defeito.']];}}