Files
SGF/resources/views/accounting/general-ledger.blade.php
2026-11-04 09:37:20 +02:00

34 lines
12 KiB
PHP

@php $ctx=app(\App\Shared\Context\CompanyContext::class);$supplierAccess=app(\App\Modules\Identity\Services\Permissions::class)->has(auth()->id(),$ctx->identifiers()['tenant_id'],$ctx->identifiers()['company_id'],'supplier_payables.view',$ctx->unitId());$money=static fn($n)=>($n<0?'-':'').number_format(intdiv(abs($n),100),0,',',' ').','.str_pad((string)(abs($n)%100),2,'0',STR_PAD_LEFT).' MZN'; @endphp
<!doctype html><html lang="pt"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Lançamento sintético — SGF</title><style>body{font:17px system-ui;color:#142b28;background:#f3f6f5;margin:0}main{max-width:1100px;margin:auto;padding:24px}section,header{background:white;border:1px solid #d5e0dc;border-radius:12px;margin:18px 0;padding:24px}label{display:block;margin:14px 0}input,select,textarea,button{font:inherit;padding:10px;max-width:100%}textarea{display:block;width:90%}.notice{background:#fff2cc;padding:16px}pre{white-space:pre-wrap;overflow-wrap:anywhere}a{color:#125a4b}table{border-collapse:collapse;width:100%}td,th{text-align:left;border-bottom:1px solid #ddd;padding:10px}</style></head><body><main>
<header><a href="/">Voltar à navegação SGF / mudar módulo</a>@if(config('sales_ledger.synthetic_enabled')===true||config('cost_ledger.synthetic_enabled')===true)<h1>Contabilidade — diário de teste por origem</h1><p class="notice">EXCLUSIVAMENTE SINTÉTICO. Contas, regras e períodos artificiais. Sem uso real, contas PGC aprovadas ou execução monetária.</p><p>Fontes técnicas, comerciais e financeiras são efeitos distintos, com fotografia e revisão próprias. O lançamento não cria nem altera a dívida financeira.</p>@else<h1>Contabilidade — primeiro lançamento de ensaio</h1><p class="notice">EXCLUSIVAMENTE SINTÉTICO. Duas contas e um período artificiais. Sem uso real, contas PGC aprovadas, emissão, execução bancária ou fecho operacional.</p><p>Origem: obrigação reconhecida pelo Payables. Aplicar o lançamento não cria nem altera a dívida financeira. Vencimento e período contabilístico são factos separados.</p>@endif</header>
@if(config('general_ledger.composition_enabled')===true)<p><a href="/general-ledger/workspace">Painel conjunto de vendas, fornecedores e custos QA</a></p>@endif
<p id="result" role="status"></p><p id="error" role="alert"></p>
@if(config('cost_ledger.synthetic_enabled')===true)<p><a href="/general-ledger/costs">Oficina → custo directo validado → diário e relatórios QA</a></p>@endif
<p><a href="/general-ledger/trial-balance?from_on=2026-10-01&amp;to_on=2026-10-31">Balancete e razão por conta — Outubro de 2026 (QA)</a></p>
@if(config('general_ledger.cycle_enabled')===true&&$supplierAccess)<p><a href="/general-ledger/cycle">Ciclo integrado: afectação, reversão, correcção técnica e fecho QA</a></p>@endif
@if(config('sales_ledger.synthetic_enabled')===true)<p><a href="/general-ledger/sales">Comercial → recebíveis → contabilidade: origem, decisões e extracto</a></p>@endif
@if(session('ledger_demo'))<p><a href="/qa/ledger-demo">Trocar função no ensaio sintético</a></p>@endif
@if($data['permissions']['prepare']&&$supplierAccess)<section><h2>Preparar da origem reconhecida</h2><form data-action="prepare"><label>ID da obrigação de fornecedor<input name="obligation_id" type="number" min="1" value="{{ $data['query']['obligation_id']??'' }}" required></label><label>Data explícita<input name="journal_on" type="date" value="2026-10-29" required></label><label>Período de ensaio<input name="period_key" value="QA-2026-10" required></label><label>Regra de ensaio<input name="rule_key" value="QA-PAYABLE-JOURNAL" required></label><label>Fundamento<textarea name="reason" required minlength="3" maxlength="2000"></textarea></label><button>Capturar proposta e impacto — sem lançar</button></form></section>@endif
<section><h2>Propostas e conferência individual</h2>
@foreach($data['proposals'] as $p)
@php $snapshot=json_decode($p->snapshot,true);$isPart=in_array($snapshot['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement'],true);$review=collect($data['reviews'])->firstWhere('proposal_id',$p->id); @endphp
@if(isset($snapshot['source']['industrial_lot_id']))<p><a href="/general-ledger/industrial?lot_id={{ $snapshot['source']['industrial_lot_id'] }}">Conferir lote e resultado industrial</a></p>@elseif(isset($snapshot['source']['cost_order_id']))<p><a href="/general-ledger/costs?order_id={{ $snapshot['source']['cost_order_id'] }}">Conferir ordem e custo directo seleccionado</a></p>@elseif(isset($snapshot['source']['invoice_id']))<p><a href="/general-ledger/sales?invoice_id={{ $snapshot['source']['invoice_id'] }}">Conferir percurso e origem comercial</a></p>@else<p><a href="/general-ledger/flow?source={{ $snapshot['source']['local_source_id'] }}">Conferir percurso e origem</a></p>@endif
<article><h3>Proposta #{{ $p->id }} · captura {{ $p->capture_version }}</h3><p>@if(isset($snapshot['source']['industrial_lot_id']))Lote #{{ $snapshot['source']['industrial_lot_id'] }}@elseif(isset($snapshot['source']['cost_order_id']))Ordem #{{ $snapshot['source']['cost_order_id'] }}@else Obrigação #{{ $p->obligation_id }}@endif · preparador #{{ $p->prepared_by }} · {{ $p->created_at }}</p><p>{{ $p->reason }}</p><p>Data {{ $snapshot['journal_on'] }} · período {{ $snapshot['period']['period_key'] }} v{{ $snapshot['period']['version'] }} · regra v{{ $snapshot['rule']['version'] }}</p>
<ul>@foreach($snapshot['lines'] as $line)<li>{{ $line['account_code'] }} — débito {{ $money($line['debit_minor']) }} / crédito {{ $money($line['credit_minor']) }}</li>@endforeach</ul><p>Impacto: lançamento equilibrado de {{ $money($snapshot['impact']['debit_minor']) }}. Sem novo pagamento ou alteração do saldo financeiro.</p><p>Revisão exacta <code>{{ $p->revision }}</code>@unless(isset($snapshot['source']['cost_order_id'])||isset($snapshot['source']['industrial_lot_id'])) · Vencimento da origem: {{ $snapshot['source']['due_on'] ?? 'desconhecido' }}@endunless</p>
<details><summary>Fotografia da fonte, regra e período</summary><pre>{{ json_encode($snapshot,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) }}</pre></details>
@if($review)<p>Decisão {{ $review->decision==='approved'?'aprovada':'recusada' }} por #{{ $review->reviewed_by }} · {{ $review->reason }}</p>@endif
@if($p->journal_id)<p>Aplicada uma vez no lançamento #{{ $p->journal_id }}. Consulte o comprovativo abaixo.</p>@endif
@if(!$isPart&&$data['permissions']['validate']&&!$review&&$p->prepared_by!==auth()->id())<form data-action="validate"><input type="hidden" name="proposal_id" value="{{ $p->id }}"><input type="hidden" name="revision" value="{{ $p->revision }}"><label>Decisão explícita<select name="decision"><option value="approved">Aprovar versão exacta</option><option value="rejected">Recusar</option></select></label><label>Fundamento independente<textarea name="reason" required minlength="3" maxlength="2000"></textarea></label><button>Registar conferência — sem lançar</button></form>@endif
@if(!$isPart&&$data['permissions']['post']&&$review&&$review->decision==='approved'&&$p->prepared_by===auth()->id()&&!$p->journal_id)<form data-action="post"><input type="hidden" name="proposal_id" value="{{ $p->id }}"><input type="hidden" name="revision" value="{{ $p->revision }}"><label>Fundamento da aplicação<textarea name="reason" required minlength="3" maxlength="2000"></textarea></label><button>Revalidar e aplicar este impacto uma vez</button></form>@endif
</article>@endforeach</section>
<section><h2>Lançamentos e extracto sintético</h2><form method="get"><label>Período<input name="period_key" value="{{ $data['query']['period_key']??'QA-2026-10' }}"></label><label>Conta<select name="account_id"><option value="">Todas as contas</option>@foreach($data['accounts'] as $account)<option value="{{ $account->id }}" @selected(($data['query']['account_id']??'')==$account->id)>{{ $account->code }}</option>@endforeach</select></label><button>Consultar</button></form>
<p>Débitos {{ $money($data['totals']['debit_minor']) }} · Créditos {{ $money($data['totals']['credit_minor']) }} · Débito menos crédito {{ $money($data['totals']['net_minor']) }}</p><p>Os totais incluem todos os lançamentos do filtro; as contrapartidas dos lançamentos da página aparecem abaixo. Não são um balancete legal ou uma posição financeira por liquidar.</p>
@foreach($data['journals'] as $j)@php $journalSource=json_decode($j->snapshot,true)['source']; @endphp<article><h3>Lançamento #{{ $j->id }} · {{ $j->journal_on }}</h3><p>@if(isset($journalSource['industrial_lot_id']))Lote #{{ $journalSource['industrial_lot_id'] }}@elseif(isset($journalSource['cost_order_id']))Ordem #{{ $journalSource['cost_order_id'] }}@else Obrigação #{{ $j->obligation_id }}@endif · Proposta #{{ $j->proposal_id }} · Revisão #{{ $j->review_id }} · Aplicador #{{ $j->posted_by }}</p><p>{{ $j->reason }} · {{ $j->created_at }}</p><ul>@foreach($data['lines'] as $l)@if($l->journal_id===$j->id)<li>Conta #{{ $l->account_id }}: D {{ $money($l->debit_minor) }} / C {{ $money($l->credit_minor) }}</li>@endif @endforeach</ul><details><summary>Fotografia imutável da origem, regra e período aplicados</summary><pre>{{ json_encode(json_decode($j->snapshot,true),JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) }}</pre></details>@if(isset($journalSource['cost_order_id']))<p><a href="/general-ledger/costs?order_id={{ $journalSource['cost_order_id'] }}">Conferir a ordem, o custo e as decisões</a>. A fotografia acima conserva o estado aplicado.</p>@else<p>Para conferir a posição financeira actual, volte à navegação, seleccione Compras e use a referência local da origem. A fotografia acima conserva o estado aplicado.</p>@endif</article>@endforeach
<nav aria-label="Paginação do diário"><p>Página {{ $data['pagination']['page'] }} de {{ $data['pagination']['last_page'] }} · {{ $data['pagination']['total'] }} lançamentos</p>@foreach([-1=>'Anterior',1=>'Seguinte'] as $delta=>$label)@php $target=$data['pagination']['page']+$delta; @endphp @if($target>=1&&$target<=$data['pagination']['last_page'])<a href="?{{ http_build_query(array_replace($data['query'],['page'=>$target])) }}">{{ $label }}</a>@endif @endforeach</nav></section>
<section><h2>Histórico do ensaio</h2>@foreach($data['events'] as $event)<details><summary>#{{ $event->id }} · {{ $event->kind }} · actor #{{ $event->actor_id }} · {{ $event->created_at }}</summary><p>{{ $event->reason }}</p><pre>{{ $event->previous }}</pre><pre>{{ $event->subsequent }}</pre></details>@endforeach</section>
@include('accounting.receipt-recovery')
</main><script>
const token=@json(csrf_token()),context=@json(session('company_context.token'));
for(const form of document.querySelectorAll('form[data-action]'))form.onsubmit=async e=>{e.preventDefault();const body=Object.fromEntries(new FormData(form));for(const key of ['obligation_id','proposal_id'])if(key in body)body[key]=Number(body[key]);const signature=JSON.stringify(body);if(form.dataset.signature!==signature){form.dataset.signature=signature;form.dataset.key=crypto.randomUUID();}const button=form.querySelector('button');button.disabled=true;document.querySelector('#error').textContent='';try{const r=await fetch('/api/general-ledger/'+form.dataset.action,{method:'POST',credentials:'same-origin',headers:{Accept:'application/json','Content-Type':'application/json','X-CSRF-TOKEN':token,'X-SGF-Context':context,'Idempotency-Key':form.dataset.key},body:signature});const data=await r.json();if(!r.ok)throw new Error(data.message||'Não foi possível concluir.');location.reload();}catch(err){document.querySelector('#error').textContent=err.message+' Chave do pedido: '+form.dataset.key+'. Confira o histórico antes de repetir.';}finally{button.disabled=false;}};
</script></body></html>