Files
Investidor-V2/resources/js/components/simulation-offer-preparations.tsx
T

50 lines
13 KiB
TypeScript

import {SimulationOfferWorkflow} from './simulation-offer-workflow';
import { useState } from 'react';
import { newOfferPreparation,offerPreparationBook,offerPreparationSource,offerReusableData,recordOfferPreparations,reuseOfferData,saveOfferPreparationBook,emptyOfferRequest,recordOfferRequest,offerRequestLabels,offerScenarios,type OfferRequest,type OfferPreparation,type OfferPreparationBook } from '@/lib/iva-offer-preparations';
export function SimulationOfferPreparations({draft,onChange}:{draft:Record<string,unknown>;onChange:(field:string,value:string)=>void}) {
const [error,setError]=useState('');let book:OfferPreparationBook;let reusable:ReturnType<typeof offerReusableData>;
try{book=offerPreparationBook(draft);reusable=offerReusableData(draft);}catch(cause){return <p role="alert">{String(cause)}</p>;}
const run=(action:()=>void)=>{try{action();setError('');}catch(cause){setError(String(cause));}};
const save=(next:OfferPreparationBook)=>onChange('modelo_a_snapshot',saveOfferPreparationBook(draft,next));
const change=(id:string,patch:Partial<OfferPreparation>)=>run(()=>save({...book,rows:book.rows.map(row=>row.id===id?{...row,...patch}:row)}));
const question=(row:OfferPreparation,key:'nationalInstitution'|'publicSocialInstitution'|'goodsSuitable'|'publicInterestUse'|'mitigation',label:string)=><label className="block" key={key}>{label}<select className="block rounded border p-2" value={row[key]} onChange={e=>change(row.id,{[key]:e.target.value})}><option value="">Por indicar</option><option value="yes">Sim</option><option value="no">Não</option></select></label>;
return <details className="space-y-3 rounded border p-3"><summary>Ofertas a instituições e para calamidades — preparar rascunhos</summary>
<p>Prepare apenas as ofertas relevantes para a oportunidade {String(draft.context_opportunity||'entrada geral')}. Pode guardar dados incompletos e reutilizar referências existentes. A reutilização preenche apenas campos vazios; as suas edições mantêm-se.</p>
<p>Procedimento operacional indicado pela Tempo:prepare o pedido escrito,registe a apresentação externa,a decisão com despacho e o formulário aplicável. Antes do deferimento,compare o cenário normal com uma hipótese condicionada. Apresentar o pedido não concede isenção. Estes cenários não alteram a dedução no campo11; os restantes enquadramentos continuam disponíveis.</p>
{error&&<p role="alert">{error}</p>}
<button type="button" className="underline" onClick={()=>run(()=>save({...book,rows:[...book.rows,newOfferPreparation(crypto.randomUUID(),'P084',String(draft.currency??''))]}))}>Preparar oferta a instituição</button>{' '}
<button type="button" className="underline" onClick={()=>run(()=>save({...book,rows:[...book.rows,newOfferPreparation(crypto.randomUUID(),'P085',String(draft.currency??''))]}))}>Preparar oferta para calamidade</button>
{book.rows.map(row=><details key={row.id} className="rounded border p-3"><summary>{row.branch} — {row.description||'Bens por identificar'}</summary>
<p>{row.branch==='P084'?'Artigo14(2)(a): aquisição ou importação de bens para ofertas a instituições nacionais de interesse público e relevantes fins sociais.':'Artigo14(2)(b): aquisição de bens para ofertas destinadas a atenuar efeitos de calamidades naturais.'} {offerPreparationSource}</p>
{row.branch==='P084'&&<label>Operação<select className="block rounded border p-2" value={row.operation} onChange={e=>change(row.id,{operation:e.target.value as OfferPreparation['operation']})}><option value="">Por indicar</option><option value="acquisition">Aquisição</option><option value="import">Importação</option></select></label>}
<label>Reutilizar dados desta oportunidade<select className="block rounded border p-2" value="" onChange={e=>run(()=>{const source=reusable.find(item=>item.key===e.target.value);if(source)save({...book,rows:book.rows.map(item=>item.id===row.id?reuseOfferData(item,source):item)});})}><option value="">Escolher referência existente</option>{reusable.filter(item=>(row.branch==='P084'||item.operation==='acquisition')&&(!row.operation||item.operation===row.operation)).map(item=><option key={item.key} value={item.key}>{item.label}</option>)}</select></label>
{row.reusedFrom&&<p>Referência reutilizada: {row.reusedFrom.key}. A origem foi conservada; a aquisição/importação original não foi alterada.</p>}
<div className="grid gap-3 md:grid-cols-2">{(['description','period','currency','amount','counterparty','documentRef','documentDate','beneficiary','purpose','evidence'] as const).map((key,index)=><label key={key}>{['Bens destinados à oferta','Período previsto / efectivo','Moeda','Valor de referência, sem cálculo fiscal','Fornecedor / importador','Documento / referência da previsão','Data do documento / prevista','Instituição / destinatário da oferta','Finalidade concreta da oferta','Fundamento factual / pressupostos'][index]}<input className="block rounded border p-2" type={key==='period'?'month':key==='documentDate'?'date':'text'} value={row[key]} onChange={e=>change(row.id,{[key]:e.target.value})}/></label>)}</div>
{row.branch==='P084'?<fieldset><legend>Condições próprias da oferta à instituição</legend>{question(row,'nationalInstitution','A instituição beneficiária é nacional')}{question(row,'publicSocialInstitution','A instituição tem interesse público e relevantes fins sociais')}{question(row,'goodsSuitable','Os bens são inteiramente adequados à natureza da instituição')}{question(row,'publicInterestUse','A instituição utilizará os bens em actividades de evidente interesse público')}</fieldset>:<fieldset><legend>Finalidade própria da oferta para calamidade</legend><label>Calamidade e contexto afectados<input className="block rounded border p-2" value={row.disaster} onChange={e=>change(row.id,{disaster:e.target.value})}/></label><label>Natureza da calamidade<input className="block rounded border p-2" value={row.disasterKind} placeholder="Cheias, tempestades, secas, ciclones, sismos ou outra de idêntica natureza" onChange={e=>change(row.id,{disasterKind:e.target.value})}/></label>{question(row,'mitigation','Os bens destinam-se a ofertas para atenuar os efeitos da calamidade identificada')}</fieldset>}
{row.branch==='P085'&&<label>Finalidade humanitária da resposta à emergência<textarea className="block w-full rounded border p-2" value={row.humanitarianPurpose??''} onChange={e=>change(row.id,{humanitarianPurpose:e.target.value})}/></label>}
<label>Documentos disponíveis e respectivas referências<textarea className="block w-full rounded border p-2" value={row.availableDocuments??''} onChange={e=>change(row.id,{availableDocuments:e.target.value})}/></label>
{!!row.documentRef&&<button type="button" className="underline" onClick={()=>{const reference=[row.documentRef,row.documentDate].filter(Boolean).join(', ');if(!(row.availableDocuments??'').includes(reference))change(row.id,{availableDocuments:[row.availableDocuments,reference].filter(Boolean).join('\n')});}}>Juntar referência do documento já indicado</button>}
{(()=>{const request=row.request??emptyOfferRequest();const edit=(patch:Partial<OfferRequest>)=>change(row.id,{request:{...request,...patch}});return <fieldset className="rounded border p-3"><legend>Pedido e âmbito da decisão</legend>
<p>Registe o estado declarado e os elementos disponíveis. Este registo não apresenta um pedido à autoridade nem concede o benefício. Conserve o âmbito exacto de cada decisão, mesmo que cubra apenas parte dos bens ou períodos.</p>
<label>Estado declarado do pedido<select className="block rounded border p-2" value={request.state} onChange={e=>edit({state:e.target.value as OfferRequest['state']})}>{Object.entries(offerRequestLabels).map(([value,label])=><option key={value} value={value}>{label}</option>)}</select></label>
{(['reference','presentedOn','decisionRef','decisionOn','goods','periodFrom','periodTo','conditions'] as const).map((key,index)=><label className="block" key={key}>{['Referência do pedido','Data de apresentação, se disponível','Documento / referência da decisão, se disponível','Data da decisão, se disponível','Bens abrangidos pela decisão','Início do período abrangido','Fim do período abrangido','Condições, limites ou fundamento da decisão'][index]}<input className="block rounded border p-2" type={key==='presentedOn'||key==='decisionOn'?'date':'text'} placeholder={key==='periodFrom'||key==='periodTo'?'AAAA-MM ou AAAA-MM-DD, conforme a decisão':undefined} value={request[key]} onChange={e=>edit({[key]:e.target.value})}/></label>)}
<label>Operação abrangida pela decisão<select className="block rounded border p-2" value={request.operation} onChange={e=>edit({operation:e.target.value as OfferRequest['operation']})}><option value="">Por indicar</option><option value="acquisition">Aquisição</option><option value="import">Importação</option></select></label>
<label>Âmbito de operações literal, se a decisão abranger mais do que a opção indicada<input className="block rounded border p-2" value={request.operationScope??''} onChange={e=>edit({operationScope:e.target.value})}/></label>
<label>Âmbito temporal literal, se não estiver limitado pelas datas indicadas<input className="block rounded border p-2" placeholder="Conservar, por exemplo, a indicação sem termo ou até revogação do documento" value={request.periodScope??''} onChange={e=>edit({periodScope:e.target.value})}/></label>
<button type="button" className="underline" onClick={()=>edit({goods:request.goods||row.description,operation:request.operation||row.operation,periodFrom:request.periodFrom||row.period,periodTo:request.periodTo||row.period})}>Reutilizar contexto nos campos de âmbito vazios</button>{' '}
<button type="button" className="rounded border p-2" onClick={()=>run(()=>save({...book,rows:book.rows.map(item=>item.id===row.id?recordOfferRequest(item):item)}))}>Registar estado declarado e âmbito</button>
{(row.requestRecords??[]).map((record,index)=><p key={index}>{offerRequestLabels[record.request.state]} — {record.recordedAt}; bens: {record.request.goods||'por indicar'}; operação: {record.request.operationScope||record.request.operation||'por indicar'}; período: {record.request.periodScope||`${record.request.periodFrom||'por indicar'} a ${record.request.periodTo||'por indicar'}`}; condições: {record.request.conditions||'por indicar'}; decisão: {record.request.decisionRef||'por indicar'}. Este âmbito não se estende automaticamente à restante operação ou oportunidade.</p>)}
</fieldset>;})()}
<SimulationOfferWorkflow row={row} draft={draft} onChange={patch=>change(row.id,patch)}/>
<fieldset className="rounded border p-3"><legend>Cenário normal e cenário condicionado à isenção</legend>
<label>IVA no cenário normal, indicado em documento ou pressuposto<input className="block rounded border p-2" inputMode="decimal" value={row.normalVat??''} onChange={e=>change(row.id,{normalVat:e.target.value})}/></label>
<label>Documento ou pressuposto do IVA normal<input className="block rounded border p-2" value={row.normalVatSource??''} onChange={e=>change(row.id,{normalVatSource:e.target.value})}/></label>
<label>Assumir, apenas para comparar, a hipótese condicionada de isenção<select className="block rounded border p-2" value={row.conditionalAssumption??''} onChange={e=>change(row.id,{conditionalAssumption:e.target.value as ''|'yes'|'no'})}><option value="">Por indicar</option><option value="yes">Sim, como hipótese condicionada</option><option value="no">Não</option></select></label>
{(()=>{const scenarios=offerScenarios(row);return <><table><thead><tr><th>Cenário</th><th>IVA ({row.currency})</th></tr></thead><tbody><tr><td>Normal — valor indicado, sem taxa presumida</td><td>{scenarios.normalVat??'Indeterminado'}</td></tr><tr><td>Condicionado à isenção — hipótese, não concessão</td><td>{scenarios.conditionalVat??'Indeterminado'}</td></tr>{scenarios.applied&&<tr><td>Tratamento deferido apenas no âmbito do despacho</td><td>{scenarios.appliedVat}</td></tr>}</tbody></table><p>{scenarios.conditionalReason}</p><p>O cenário condicionado não altera imposto,dedução,totais ou o estado do pedido. O tratamento deferido exige despacho registado e correspondência expressa desta linha ao âmbito;alterar os dados exige nova conferência.</p></>;})()}
</fieldset>
</details>)}
{!!book.rows.length&&<><button type="button" className="rounded border p-2" onClick={()=>run(()=>save(recordOfferPreparations(book)))}>Guardar versão dos rascunhos de ofertas</button><p role="status">{book.saved?'Versão de preparação conservada; alterações seguintes não substituem essa versão.':'Preparação em curso; pode guardar mesmo com informação em falta.'} Estado e âmbito do tratamento indicados em cada linha. {book.history?.length??0} versões anteriores.</p></>}
</details>;
}