85 lines
17 KiB
TypeScript
85 lines
17 KiB
TypeScript
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.']];}}
|