feat(irpc): demonstrate ordinary economic credit and compatible carry in QA

This commit is contained in:
2026-10-28 14:29:55 +02:00
parent 6ededb597a
commit aae67c169d
21 changed files with 882 additions and 37 deletions
@@ -0,0 +1,10 @@
import type {FiscalProofParcel} from '@/lib/fiscal-proof-parcel';
export function FiscalProofParcelFields({value, change, label}: {value: FiscalProofParcel; change: (value: FiscalProofParcel) => void; label: string}) {
return <details className="rounded border p-2" open={!!value.parcel || !!value.parcelReference}>
<summary>Identificar parcela quando o documento suporta várias parcelas — {label}</summary>
<p>Sem identificação específica, o comprovativo existente abrange o documento inteiro. Para distinguir impostos ou parcelas, indique a linha/identificador exacto e o suporte dessa separação. Os montantes comprovado e seleccionado devem corresponder à parcela identificada. A mudança do identificador não comprova uma parcela diferente.</p>
<label className="block">Identificador da parcela<input aria-label={label + ' — Parcela documental'} value={value.parcel ?? ''} onChange={e => change({parcel: e.target.value})}/></label>
<label className="block">Suporte da separação das parcelas<input aria-label={label + ' — Suporte da parcela'} value={value.parcelReference ?? ''} onChange={e => change({parcelReference: e.target.value})}/></label>
</details>;
}
@@ -1,3 +1,5 @@
import {SimulationIrpcEconomic} from './simulation-irpc-economic';
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
import {SimulationIrpcCarry} from './simulation-irpc-carry';
import {SimulationIrpcInternational} from './simulation-irpc-international';
import {SimulationIrpcPec} from './simulation-irpc-pec';
@@ -18,10 +20,10 @@ export function SimulationIrpcAnnual({draft,onChange}:Props){
<p>Colecta disponível:{a.colecta?a.colecta.numerator+'/'+a.colecta.denominator+' MZN':'Indeterminada'};origem:{a.kind==='special'?'L7 qualificado':'L6 normal'};NUIT:{a.subjectNuit||'Por identificar'};exercício:{a.year||'Por identificar'}.</p><button type="button" onClick={()=>{try{save(reuseAnnualAssessment(draft,b));}catch(e){setError(String(e));}}}>Reutilizar colecta corrente L6/L7</button>{field('assessmentReference','Suporte da colecta reutilizada L8')}
<fieldset className="space-y-2 rounded border p-3"><legend>Retenções efectivamente sofridas por conta</legend>{group('withholdings','Retenções sofridas')}
{options.length===0?<p>Não há afectações de comprovativosL5 neste contexto. Reutilize e reconcilie a retenção recebida emL5 ou declare ausência com suporte.</p>:options.map(o=><div key={o.rowId+'|'+o.paymentId} className="rounded border p-2"><p>{o.document||'Prova por identificar'};emitente:{o.issuer};data:{o.date};parcelaL5:{o.amount}MZN;{o.reason||'Retenção recebida/efectiva por conta reconciliada emL5.'}</p><button type="button" disabled={!o.ready||b.withholdings.rows.some(r=>r.rowId===o.rowId&&r.paymentId===o.paymentId)} onClick={()=>{try{save(reuseAnnualWithholding(draft,b,o.rowId,o.paymentId,crypto.randomUUID()));}catch(e){setError(String(e));}}}>Seleccionar prova L5 {o.document||o.paymentId}</button></div>)}
{b.withholdings.rows.map((r,index)=>{const change=(p:Partial<typeof r>)=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>Retenção seleccionada {index+1}</legend><p>Rendimento:{r.rowId};prova:{r.paymentId};NUIT:{r.subjectNuit};exercício:{r.year}. Fonte conservada. Se mudou, retire esta selecção e seleccione a prova actual. O histórico mantém-se.</p><label className="block">Parcela a abater<input aria-label={'Parcela da retenção L8 '+(index+1)} value={r.amount} onChange={e=>change({amount:e.target.value})}/></label><label className="block">O próprio sujeito é beneficiário e o rendimento está incluído na base deste exercício<select aria-label={'Rendimento incluído L8 '+(index+1)} value={r.incomeIncluded} onChange={e=>change({incomeIncluded:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável neste fecho</option></select></label><label className="block">Suporte da titularidade e inclusão<input aria-label={'Suporte da retenção L8 '+(index+1)} value={r.reference} onChange={e=>change({reference:e.target.value})}/></label><button type="button" onClick={()=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.filter(x=>x.id!==r.id)}})}>Retirar selecção L8 da retenção {index+1}</button></fieldset>;})}</fieldset>
{b.withholdings.rows.map((r,index)=>{const change=(p:Partial<typeof r>)=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>Retenção seleccionada {index+1}</legend><p>Rendimento:{r.rowId};prova:{r.paymentId};NUIT:{r.subjectNuit};exercício:{r.year}. Fonte conservada. Se mudou, retire esta selecção e seleccione a prova actual. O histórico mantém-se.</p><label className="block">Parcela a abater<input aria-label={'Parcela da retenção L8 '+(index+1)} value={r.amount} onChange={e=>change({amount:e.target.value})}/></label><label className="block">O próprio sujeito é beneficiário e o rendimento está incluído na base deste exercício<select aria-label={'Rendimento incluído L8 '+(index+1)} value={r.incomeIncluded} onChange={e=>change({incomeIncluded:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — crédito não aplicável neste fecho</option></select></label><label className="block">Suporte da titularidade e inclusão<input aria-label={'Suporte da retenção L8 '+(index+1)} value={r.reference} onChange={e=>change({reference:e.target.value})}/></label><FiscalProofParcelFields value={r} change={change} label={"Retenção L8 "+(index+1)}/><button type="button" onClick={()=>patch({withholdings:{...b.withholdings,rows:b.withholdings.rows.filter(x=>x.id!==r.id)}})}>Retirar selecção L8 da retenção {index+1}</button></fieldset>;})}</fieldset>
<fieldset className="space-y-2 rounded border p-3"><legend>PPC efectivamente pagos</legend>{group('ppc','PPC efectuados')}<p>Regista comprovativos de pagamentos por conta; não calcula automaticamente a obrigação PPC nem usa pagamentos previstos no plano.</p><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:[...b.ppc.rows,{...newAnnualPpc(crypto.randomUUID()),subjectNuit:b.subjectNuit,year:b.year}]}})}>Registar comprovativo PPC</button>
{b.ppc.rows.map((r,index)=>{const change=(p:Partial<AnnualPpc>)=>patch({ppc:{...b.ppc,rows:b.ppc.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>PPC {index+1}</legend>{([['document','Comprovativo'],['issuer','Emitente'],['date','Data (AAAA-MM-DD)'],['subjectNuit','NUIT beneficiário'],['year','Exercício do comprovativo'],['currency','Moeda do comprovativo'],['proofAmount','Montante comprovado'],['amount','Parcela a abater'],['reference','Suporte do pagamento e afectação']]as const).map(([key,label])=><label key={key} className="block">{label}<input aria-label={'PPC L8 '+(index+1)+' — '+label} value={r[key]} onChange={e=>change({[key]:e.target.value})}/></label>)}<label className="block">Pagamento efectivamente comprovado<select aria-label={'PPC L8 '+(index+1)+' — Efectivo'} value={r.effective} onChange={e=>change({effective:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — comprovativo reconciliado</option><option value="no">Não — apenas previsão/outro estado</option></select></label><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:b.ppc.rows.filter(x=>x.id!==r.id)}})}>Retirar PPC {index+1}</button></fieldset>;})}</fieldset>
<SimulationIrpcInternational book={b} save={save}/><SimulationIrpcPec book={b} save={save}/><SimulationIrpcCarry book={b} save={save}/>{([...(b.pecPayments?[]:[['pec','PEC']as const]),...(b.internationalCredits?[]:[['credits','Créditos com regras próprias']as const])]).map(([key,label])=><fieldset key={key} className="rounded border p-3"><legend>{label} — tratamento separado</legend><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada com suporte</option><option value="known">Valor conhecido — positivo exige regra própria</option></select></label>{(b[key].state==='known'||b[key].amount)&&<label className="block">Montante declarado<input aria-label={'Montante L8 — '+label} value={b[key].amount} onChange={e=>patch({[key]:{...b[key],amount:e.target.value}})}/></label>}<label className="block">Suporte<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></fieldset>)}
{b.ppc.rows.map((r,index)=>{const change=(p:Partial<AnnualPpc>)=>patch({ppc:{...b.ppc,rows:b.ppc.rows.map(x=>x.id===r.id?{...x,...p}:x)}});return<fieldset key={r.id} className="rounded border p-2"><legend>PPC {index+1}</legend>{([['document','Comprovativo'],['issuer','Emitente'],['date','Data (AAAA-MM-DD)'],['subjectNuit','NUIT beneficiário'],['year','Exercício do comprovativo'],['currency','Moeda do comprovativo'],['proofAmount','Montante comprovado'],['amount','Parcela a abater'],['reference','Suporte do pagamento e afectação']]as const).map(([key,label])=><label key={key} className="block">{label}<input aria-label={'PPC L8 '+(index+1)+' — '+label} value={r[key]} onChange={e=>change({[key]:e.target.value})}/></label>)}<label className="block">Pagamento efectivamente comprovado<select aria-label={'PPC L8 '+(index+1)+' — Efectivo'} value={r.effective} onChange={e=>change({effective:e.target.value as ''|'yes'|'no'})}><option value="">Informação em falta</option><option value="yes">Sim — comprovativo reconciliado</option><option value="no">Não — apenas previsão/outro estado</option></select></label><FiscalProofParcelFields value={r} change={change} label={"PPC L8 "+(index+1)}/><button type="button" onClick={()=>patch({ppc:{...b.ppc,rows:b.ppc.rows.filter(x=>x.id!==r.id)}})}>Retirar PPC {index+1}</button></fieldset>;})}</fieldset>
<SimulationIrpcEconomic book={b} save={save} residentConfirmed={a.current&&a.book.scope==='resident_general'&&b.subjectNuit===a.subjectNuit&&b.year===a.year&&b.assessmentKey===a.key}/><SimulationIrpcInternational book={b} save={save}/><SimulationIrpcPec book={b} save={save}/><SimulationIrpcCarry book={b} save={save}/>{([...(b.pecPayments?[]:[['pec','PEC']as const]),...(b.internationalCredits?[]:[['credits','Créditos com regras próprias']as const])]).map(([key,label])=><fieldset key={key} className="rounded border p-3"><legend>{label} — tratamento separado</legend><label className="block">Estado — {label}<select aria-label={'Estado L8 — '+label} value={b[key].state} onChange={e=>patch({[key]:{...b[key],state:e.target.value as ''|'known'|'absent'}})}><option value="">Informação em falta</option><option value="absent">Ausência declarada com suporte</option><option value="known">Valor conhecido — positivo exige regra própria</option></select></label>{(b[key].state==='known'||b[key].amount)&&<label className="block">Montante declarado<input aria-label={'Montante L8 — '+label} value={b[key].amount} onChange={e=>patch({[key]:{...b[key],amount:e.target.value}})}/></label>}<label className="block">Suporte<input aria-label={'Suporte L8 — '+label} value={b[key].reference} onChange={e=>patch({[key]:{...b[key],reference:e.target.value}})}/></label></fieldset>)}
<button type="button" onClick={()=>{try{save(recordAnnual(draft,b));}catch(e){setError(String(e));}}}>Calcular e guardar saldo anual L8</button><button type="button" onClick={()=>{try{const html=annualHtml(draft),url=URL.createObjectURL(new Blob([html],{type:'text/html;charset=utf-8'})),link=document.createElement('a');link.href=url;link.download='IRPC-L8-saldo-simulado.html';link.click();URL.revokeObjectURL(url);setError('');}catch(e){setError(String(e));}}}>Exportar saldo anual L8</button>
{annualReportRows(draft).map((r,n)=><p key={n}>{r[1]} — {r[2]} — {r[3]}</p>)}</section>;
}catch(e){return<p role="alert">PreparaçãoL8 não recuperável:{String(e)}. Originais e históricos conservados.</p>;}
@@ -0,0 +1,41 @@
import {prepareEconomicBook, type AnnualBook} from '@/lib/irpc-annual-settlement';
import {economicSource, newEconomicPosition, type EconomicPosition} from '@/lib/irpc-economic-credit';
export function SimulationIrpcEconomic({book, save, residentConfirmed}: {book: AnnualBook; save: (book: AnnualBook) => void; residentConfirmed: boolean}) {
const c = book.economicCredits;
if (!c) return <fieldset className="rounded border p-3"><legend>Crédito por dupla tributação económica</legend><p>Para lucros distribuídos por entidade moçambicana sujeita e não isenta de IRPC, distinga o imposto correspondente aos lucros da retenção sofrida e do imposto estrangeiro. Reutiliza a colecta e conserva o resumo de créditos anterior.</p><button type="button" onClick={() => save(prepareEconomicBook(book))}>Preparar crédito económico</button></fieldset>;
const update = (next: typeof c) => save({...book, economicCredits: next});
const rowChange = (r: EconomicPosition) => update({...c, rows: c.rows.map(x => x.id === r.id ? r : x)});
return <fieldset className="space-y-3 rounded border p-3"><legend>Crédito económico — lucros distribuídos</legend>
<p>{economicSource}</p>
<p>O detalhe substitui nesta execução o resumo económico anterior, que permanece conservado: {book.internationalCredits?.economic.state || 'informação em falta'}; montante {book.internationalCredits?.economic.amount || 'não preenchido'}. Os benefícios e o crédito internacional mantêm os seus estados próprios.</p>
<label className="block">Estado do crédito económico<select aria-label="Estado do crédito económico L8" value={c.state} onChange={e => update({...c, state: e.target.value as typeof c.state})}><option value="">Informação em falta</option><option value="known">Lucros e IRPC conhecidos</option><option value="absent">Ausência documentada</option></select></label>
<label className="block">Suporte do inventário económico<input aria-label="Suporte económico L8" value={c.reference} onChange={e => update({...c, reference: e.target.value})}/></label>
{(c.state === 'known' || c.rows.length > 0) && <button type="button" onClick={() => update({...c, state: 'known', rows: [...c.rows, {...newEconomicPosition(crypto.randomUUID()), subjectNuit: book.subjectNuit, year: book.year, recipientResident: residentConfirmed ? 'yes' : ''}]})}>Registar lucros distribuídos</button>}
{c.rows.map((r, i) => {
const patch = (v: Partial<EconomicPosition>) => rowChange({...r, ...v});
const prefix = 'Crédito económico ' + (i + 1) + ' — ';
const field = (key: Exclude<keyof EconomicPosition, 'previousUse'>, label: string) => <label key={key} className="block">{label}<input aria-label={prefix + label} value={r[key]} onChange={e => patch({[key]: e.target.value})}/></label>;
const choice = (key: 'recipientResident' | 'payerResident' | 'payerIrpc' | 'payerExempt' | 'article40' | 'incomeIncluded', label: string) => <label key={key} className="block">{label}<select aria-label={prefix + label} value={r[key]} onChange={e => patch({[key]: e.target.value})}><option value="">Informação em falta</option><option value="yes">Sim — documentado</option><option value="no">Não — documentado</option></select></label>;
const outside = !!r.kind && r.kind !== 'ordinary_profit' || r.article40 === 'yes';
const detail = <>
{([['distributionDocument', 'Documento da distribuição'], ['distributionParcel', 'Parcela dos lucros no documento'], ['distributionDate', 'Data da distribuição (AAAA-MM-DD)'], ['income', 'Lucros incluídos antes do crédito — MZN, ilíquidos de retenção'], ['incomeReference', 'Suporte da inclusão e do valor ilíquido na base corrente'], ['taxIssuer', 'Emitente do comprovativo de IRPC correspondente'], ['taxDocument', 'Documento do IRPC correspondente aos lucros'], ['taxParcel', 'Parcela desse IRPC no documento'], ['parcelReference', 'Suporte da identificação da parcela de imposto'], ['currency', 'Moeda comprovada'], ['proofAmount', 'IRPC comprovado nesta parcela — MZN'], ['taxAmount', 'IRPC correspondente aos lucros seleccionados — MZN'], ['attributionReference', 'Suporte da correspondência entre imposto e lucros'], ['previouslyAttributed', 'IRPC desta parcela já atribuído a outros lucros — MZN'], ['priorAttributionReference', 'Suporte das atribuições anteriores, incluindo zero conhecido'], ['grossUpIncluded', 'Crédito nominal já acrescido à base (art.53) — MZN'], ['grossUpReference', 'Suporte do acréscimo art.53 já incluído em L6'], ['nationalLimit', 'Quota proporcional da colecta após acréscimo — MZN'], ['nationalLimitReference', 'Suporte da quota proporcional da colecta']] as const).map(([key, label]) => field(key, label))}
{choice('incomeIncluded', 'Lucros ilíquidos e acréscimo incluídos na base corrente')}
<p>O cálculo confere 60% do IRPC correspondente com o acréscimo já incluído em L6 e a quota proporcional documentada. Se a base estiver incompleta, reconcilie-a no percurso existente. Esta acção conserva os valores e não altera a colecta automaticamente.</p>
<button type="button" onClick={() => patch({assessmentKey: book.assessmentKey})}>Confirmar inclusão e quota na colecta seleccionada</button>
<label className="block">Utilização anterior efectiva do crédito<select aria-label={prefix + 'Estado do uso anterior'} value={r.previousUse.state} onChange={e => patch({previousUse: {...r.previousUse, state: e.target.value as EconomicPosition['previousUse']['state']}})}><option value="">Informação em falta</option><option value="known">Montante conhecido</option><option value="absent">Ausência documentada</option></select></label>
{(r.previousUse.state === 'known' || r.previousUse.amount) && <label className="block">Crédito já utilizado — MZN<input aria-label={prefix + 'Uso anterior MZN'} value={r.previousUse.amount} onChange={e => patch({previousUse: {...r.previousUse, amount: e.target.value}})}/></label>}
<label className="block">Suporte do uso anterior ou da sua ausência<input aria-label={prefix + 'Suporte do uso anterior'} value={r.previousUse.reference} onChange={e => patch({previousUse: {...r.previousUse, reference: e.target.value}})}/></label>
</>;
return <fieldset key={r.id} className="space-y-2 rounded border p-3"><legend>Lucros {i + 1}</legend>
{field('subjectNuit', 'NUIT beneficiário')}{field('payerNuit', 'NUIT distribuidor')}{field('year', 'Exercício')}
<label className="block">Natureza<select aria-label={prefix + 'Natureza'} value={r.kind} onChange={e => patch({kind: e.target.value as EconomicPosition['kind']})}><option value="">Por identificar</option><option value="ordinary_profit">Lucros ordinários — art.64(1–2)</option><option value="liquidation">Partilha em liquidação — art.64(3)</option><option value="association">Associação — art.64(4)</option></select></label>
{residentConfirmed && r.recipientResident === 'yes' ? <p>Residência do beneficiário reutilizada da colecta corrente no regime geral. NUIT e exercício conservados.</p> : choice('recipientResident', 'Beneficiário com sede/direcção efectiva em Moçambique')}{choice('payerResident', 'Distribuidor com sede/direcção efectiva em Moçambique')}{choice('payerIrpc', 'Distribuidor sujeito a IRPC')}{choice('payerExempt', 'Distribuidor isento de IRPC')}{field('qualificationReference', 'Suporte da qualificação das entidades e dos lucros')}
{choice('article40', 'Esta parcela está abrangida pela exclusão do art.40(1)')}{field('article40Reference', 'Suporte da correspondência do art.40(1) e seu período')}
<p>O art.40(1), na redacção de 2009, contempla as entidades e condições próprias, incluindo participação directa de pelo menos20% e prazo de dois anos, nos termos da fonte. Uma parcela excluída da base não recebe também o crédito64. A simples percentagem de participação não decide todas as condições.</p>
{outside ? <><p>Esta parcela exige o tratamento específico identificado. O crédito ordinário não é aplicado; os valores introduzidos ficam conservados.</p><details><summary>Consultar ou reconciliar dados conservados</summary>{detail}</details></> : detail}
<button type="button" onClick={() => update({...c, rows: c.rows.filter(x => x.id !== r.id)})}>Retirar lucros {i + 1}</button>
</fieldset>;
})}
</fieldset>;
}
@@ -1,3 +1,4 @@
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
import CountrySelect, {type Country} from '@/components/country-select';
import {countryIsoCodes} from '@/lib/country-iso-codes';
import {newInternationalProof, prepareInternationalBook, type AnnualBook} from '@/lib/irpc-annual-settlement';
@@ -22,9 +23,9 @@ export function SimulationIrpcInternational({book, save}: {book: AnnualBook; sav
<p>Reúna os comprovativos de cada país numa posição por exercício. Indique a fracção nacional e a convenção conforme o suporte do caso. Limites admitem decimal ou fracção exacta n/d; a apresentação não constitui arredondamento fiscal. Valores MZN exigem correspondência documental; uma previsão de câmbio ou de pagamento não comprova o crédito.</p>
<label className="block">Estado internacional<select aria-label="Estado do crédito internacional L8" value={c.state} onChange={e => update({...c, state: e.target.value as InternationalCredits['state']})}><option value="">Informação em falta</option><option value="known">Posições conhecidas</option><option value="absent">Ausência declarada</option></select></label>
<label className="block">Suporte do inventário<input aria-label="Suporte do crédito internacional L8" value={c.reference} onChange={e => update({...c, reference: e.target.value})}/></label>
<Evidence label="Crédito económico — regra própria" value={c.economic} change={economic => update({...c, economic})}/>
{book.economicCredits ? <p>Resumo económico anterior conservado: {c.economic.state || "Informação em falta"}; {c.economic.amount || "Montante não preenchido"}. Nesta execução é usado o detalhe económico acima.</p> : <Evidence label="Crédito económico — regra própria" value={c.economic} change={economic => update({...c, economic})}/>}
<Evidence label="Benefícios/créditos próprios — regra própria" value={c.benefits} change={benefits => update({...c, benefits})}/>
<p>Créditos económicos e benefícios positivos exigem regras próprias; informação em falta mantém o saldo dependente indeterminado.</p>
<p>{book.economicCredits ? "Benefícios positivos exigem regras próprias; o crédito económico tem o seu detalhe separado. Informação em falta mantém o saldo dependente indeterminado." : "Créditos económicos e benefícios positivos exigem regras próprias; informação em falta mantém o saldo dependente indeterminado."}</p>
<button type="button" onClick={() => update({...c, state: 'known', positions: [...c.positions, {...newInternationalPosition(crypto.randomUUID()), subjectNuit: book.subjectNuit, year: book.year, assessmentKey: book.assessmentKey}]})}>Registar posição internacional</button>
{c.positions.map((p, index) => {
const patch = (v: Partial<InternationalPosition>) => positionChange({...p, ...v});
@@ -47,7 +48,7 @@ export function SimulationIrpcInternational({book, save}: {book: AnnualBook; sav
{(r.exerciseReference || r.date && !r.date.startsWith(p.year + '-')) && <label className="block">Suporte da correspondência da data ao exercício<input aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Correspondência temporal'} value={r.exerciseReference} onChange={e => change({exerciseReference: e.target.value})}/></label>}
<label className="block">Pagamento efectivo<select aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Efectivo'} value={r.effective} onChange={e => change({effective: e.target.value as InternationalTaxProof['effective']})}><option value="">Informação em falta</option><option value="yes">Sim — comprovado</option><option value="no">Não — previsão/outra situação</option></select></label>
<label className="block">Imposto sobre o rendimento elegível<select aria-label={'Imposto estrangeiro ' + (index + 1) + '.' + (ri + 1) + ' — Natureza'} value={r.incomeTax} onChange={e => change({incomeTax: e.target.value as InternationalTaxProof['incomeTax']})}><option value="">Informação em falta</option><option value="yes">Sim — natureza documentada</option><option value="no">Não — outra natureza, sem crédito presumido</option></select></label>
<button type="button" onClick={() => patch({payments: p.payments.filter(x => x.id !== r.id)})}>Retirar imposto estrangeiro {index + 1}.{ri + 1}</button>
<FiscalProofParcelFields value={r} change={change} label={"Imposto estrangeiro " + (index + 1) + "." + (ri + 1)}/><button type="button" onClick={() => patch({payments: p.payments.filter(x => x.id !== r.id)})}>Retirar imposto estrangeiro {index + 1}.{ri + 1}</button>
</fieldset>;
})}
<button type="button" onClick={() => update({...c, positions: c.positions.filter(x => x.id !== p.id)})}>Retirar posição internacional {index + 1}</button>
@@ -1,3 +1,4 @@
import {FiscalProofParcelFields} from './fiscal-proof-parcel';
import {newPecPayment, pecSource, type PecPayment} from '@/lib/irpc-pec';
import type {AnnualBook} from '@/lib/irpc-annual-settlement';
@@ -15,7 +16,7 @@ export function SimulationIrpcPec({book, save}: {book: AnnualBook; save: (book:
return <fieldset key={r.id} className="rounded border p-2"><legend>PEC {index + 1}</legend>
{([['document', 'Comprovativo PEC'], ['issuer', 'Emitente'], ['date', 'Data (AAAA-MM-DD)'], ['subjectNuit', 'NUIT beneficiário'], ['year', 'Exercício de origem'], ['currency', 'Moeda'], ['proofAmount', 'Montante comprovado'], ['previouslyUsed', 'Já utilizado fora destas parcelas'], ['priorUseReference', 'Suporte das utilizações anteriores ou do zero conhecido'], ['amount', 'Parcela disponível seleccionada'], ['reference', 'Suporte do pagamento PEC e afectação']] as const).map(([key, label]) => <label key={key} className="block">{label}<input aria-label={'PEC L8 ' + (index + 1) + ' — ' + label} value={r[key]} onChange={e => change({[key]: e.target.value})}/></label>)}
<label className="block">Pagamento PEC efectivamente comprovado<select aria-label={'PEC L8 ' + (index + 1) + ' — Efectivo'} value={r.effective} onChange={e => change({effective: e.target.value as PecPayment['effective']})}><option value="">Informação em falta</option><option value="yes">Sim — natureza PEC e pagamento reconciliados</option><option value="no">Não — previsão ou outra natureza</option></select></label>
<button type="button" onClick={() => update({...detail, rows: detail.rows.filter(x => x.id !== r.id)})}>Retirar selecção PEC {index + 1}</button>
<FiscalProofParcelFields value={r} change={change} label={"PEC L8 " + (index + 1)}/><button type="button" onClick={() => update({...detail, rows: detail.rows.filter(x => x.id !== r.id)})}>Retirar selecção PEC {index + 1}</button>
</fieldset>;
})}
<p>O detalhe substitui o resumo PEC apenas nesta execução. Os comprovativos e resultados de execuções anteriores permanecem no histórico; não há consumo automático de saldos oficiais.</p>