Files
Investidor-V2/resources/js/components/simulation-irpc-economic.tsx
T

42 lines
8.5 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 {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>;
}