Files
compras/integrations/sgf/operational/budget-setup.php
T
2026-11-02 20:27:11 +02:00

21 lines
5.1 KiB
PHP

<!doctype html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Preparação orçamental · Compras internas</title></head><body><main>
<h1>Preparação orçamental · Compras internas</h1><p>Empresa <?= (int) $scope['company_id'] ?> · Unidade <?= (int) $scope['unit_id'] ?></p>
<nav><a href="/purchasing/internal-requisitions">Requisições e parametrização</a> · <a href="/">Navegação SGF</a></nav>
<p>Cadastre apenas centros autorizados. Seleccione os centros já existentes; novas dotações exigem aprovação por outra identidade. Nenhum valor está predefinido.</p>
<?php if ($contract['canCentre']) { ?><form id="centre"><h2>Novo centro</h2><label>Código <input name="code" maxlength="100" required></label><label>Fundamento <textarea name="reason" minlength="10" maxlength="500" required></textarea></label><button>Cadastrar centro</button></form><?php } ?>
<?php if ($contract['canPrepare']) { ?><form id="budget"><h2>Preparar dotação</h2><label>Centro <select name="cost_center_id" id="centres" required></select></label><label>Exercício <input name="year" type="number" min="2026" max="2100" required></label><label>Natureza <select name="kind"><option value="operating">Funcionamento</option><option value="investment">Investimento</option></select></label><label>Montante MZN <input name="amount" inputmode="decimal" required></label><label><input type="checkbox" name="includes_tax"> Inclui imposto</label><label>Fundamento <textarea name="reason" minlength="3" maxlength="4000" required></textarea></label><button>Guardar dotação para aprovação</button></form><?php } ?>
<h2>Dotações da unidade</h2><div id="budgets"></div><p id="result" role="status" aria-live="polite"></p>
<script type="application/json" id="setup-contract"><?= json_encode($contract, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_THROW_ON_ERROR) ?></script>
<script>
const contract=JSON.parse(document.getElementById('setup-contract').textContent);const pending=new Map();
async function api(path,body,key){const response=await fetch(path,{method:body?'POST':'GET',headers:{Accept:'application/json','Content-Type':'application/json','X-CSRF-TOKEN':contract.csrf,'X-SGF-Context':contract.token,'X-Purchasing-Context':'procurement.internal','Idempotency-Key':key||''},...(body?{body:JSON.stringify(body)}:{})});const data=await response.json();if(!response.ok)throw new Error(data.message||'Não foi possível concluir.');return data;}
async function command(path,body){const content=JSON.stringify([path,body]);let key=pending.get(content);if(!key){key=crypto.randomUUID();pending.set(content,key);}const result=await api(path,path.endsWith('/centres')?{...body,operation_key:key}:body,key);pending.delete(content);return result;}
async function refresh(){const data=await api('/api/purchasing-budget');const centres=document.getElementById('centres');if(centres){centres.replaceChildren();for(const centre of data.centres){const option=document.createElement('option');option.value=centre.id;option.textContent=centre.code;centres.append(option);}}
const money=value=>{const amount=BigInt(value);return `${amount/100n}.${(amount%100n).toString().padStart(2,'0')} MZN`;};
const list=document.getElementById('budgets');list.replaceChildren();for(const budget of data.budgets){const row=document.createElement('p');const centre=data.centres.find(item=>Number(item.id)===Number(budget.cost_center_id));row.textContent=`Dotação ${budget.id} · ${centre?.code||budget.cost_center_id} · ${budget.year} · ${money(budget.amount_minor)} · ${budget.includes_tax?'Inclui imposto':'Não inclui imposto'} · ${budget.kind} · Preparada por ${data.actors[budget.created_by]||budget.created_by} · ${budget.reason} · ${budget.approved_by?'Aprovada':'Por aprovar'}`;if(contract.canApprove&&!budget.approved_by&&Number(budget.created_by)!==Number(contract.actor_id)){const button=document.createElement('button');button.textContent='Aprovar';button.onclick=()=>perform(async()=>{await command('/api/purchasing-budget/approve',{budget_id:budget.id});await refresh();});row.append(button);}list.append(row);}}
async function perform(action){try{await action();document.getElementById('result').textContent='Operação concluída.';}catch(error){document.getElementById('result').textContent=error.message;}}
const centre=document.getElementById('centre');if(centre)centre.onsubmit=event=>{event.preventDefault();void perform(async()=>{const form=new FormData(centre);await command('/api/internal-requisitions/centres',{code:form.get('code'),reason:form.get('reason')});centre.reset();await refresh();});};
const budget=document.getElementById('budget');if(budget)budget.onsubmit=event=>{event.preventDefault();void perform(async()=>{const form=new FormData(budget);await command('/api/purchasing-budget/create',{cost_center_id:Number(form.get('cost_center_id')),year:Number(form.get('year')),kind:form.get('kind'),amount:form.get('amount'),includes_tax:form.has('includes_tax'),reason:form.get('reason')});budget.reset();await refresh();});};
void perform(refresh);
</script></main></body></html>