commit melhorias1
This commit is contained in:
@@ -8,6 +8,7 @@ use Tempo\Finance\Payables\Payables;
|
||||
final class PayableJournal
|
||||
{
|
||||
use LedgerCycle;
|
||||
use LedgerAssets;
|
||||
use LedgerSales;
|
||||
use LedgerReports;
|
||||
public const POLICY='QA-PAYABLE-JOURNAL-v1';
|
||||
@@ -37,8 +38,9 @@ final class PayableJournal
|
||||
if(!preg_match('/^(\d{4})-(\d{2})-(\d{2})$/D',$date,$m)||!checkdate((int)$m[2],(int)$m[3],(int)$m[1]))throw new \InvalidArgumentException('Data de lançamento explícita inválida.');
|
||||
$sales=in_array($kind,['sales.invoice','receivable.allocated','receivable.reversed','receivable.reduced','receivable.reduction_reversed','cash.receipt_classified','cash.expense_confirmed'],true);$cost=in_array($kind,['workshop.direct_cost.validated','workshop.direct_cost.reversal','workshop.direct_cost.replacement'],true);
|
||||
$industrial=in_array($kind,['industry.cost.validated','industry.cost.reversal','industry.cost.replacement'],true);
|
||||
$asset=in_array($kind,['assets.admitted','assets.depreciation','assets.writeoff','assets.depreciation.reversal','assets.depreciation.replacement'],true);
|
||||
$stock=in_array($kind,['stock.receipt','stock.issue','stock.return','stock.adjustment','stock.supplier_return'],true);
|
||||
$s=$sales||$cost||$industrial||$stock?($this->additionalSource!==null?($this->additionalSource)($kind,$obligation):throw new \DomainException('Origem adicional não composta neste host.')):($kind==='supplier_payable.recognised'?($this->source)($obligation):$this->cycleSource($kind,$obligation));
|
||||
$s=$sales||$cost||$industrial||$stock||$asset?($this->additionalSource!==null?($this->additionalSource)($kind,$obligation):throw new \DomainException('Origem adicional não composta neste host.')):($kind==='supplier_payable.recognised'?($this->source)($obligation):$this->cycleSource($kind,$obligation));
|
||||
$amount=$s['amount_minor']??$s['principal_minor']??null;
|
||||
if(($s['effect_type']??'')!==$kind||!($s['compatible']??false)||($s['currency']??'')!=='MZN'||!is_int($amount)||$amount<=0||(!$sales&&($s['tax_minor']??null)!==0)||($s['history_imported']??true))throw new \DomainException('Fonte incompatível com o ensaio contabilístico.');
|
||||
$period=$this->latest('periods','period_key',$periodKey);$rule=$this->latest('rules','rule_key',$ruleKey);
|
||||
@@ -47,6 +49,7 @@ final class PayableJournal
|
||||
if($stock){if(!$this->db->getSchemaBuilder()->hasTable('gl_stock_books')||($s['stock_movement_on']??'')!==$date)throw new \DomainException('Valorização não composta ou data física incompatível.');}
|
||||
if($industrial&&($s['period_start']<$period->starts_on||$s['period_end']>$period->ends_on))throw new \DomainException('Período não contém o lote seleccionado.');
|
||||
if($period->status!=='open'||$date<$period->starts_on||$date>$period->ends_on)throw new \DomainException('Período fechado, incompatível ou alterado.');
|
||||
if($asset)return $this->captureAssets($s,$date,$period,$rule,$amount);
|
||||
if($rule->policy!==(str_starts_with($kind,'cash.')?'QA-CASH-JOURNAL-v1':($stock?'QA-STOCK-VALUATION-v1':($sales?'QA-SALES-JOURNAL-v1':($cost?'QA-DIRECT-COST-JOURNAL-v1':($industrial?'QA-INDUSTRIAL-JOURNAL-v1':self::POLICY)))))||$rule->effect_type!==$s['effect_type']||$rule->currency!=='MZN'||$date<$rule->valid_from||$date>$rule->valid_to)throw new \DomainException('Regra contabilística ausente, incompatível ou fora de vigência.');
|
||||
if(str_starts_with($kind,'cash.')&&($s['permitted_period_key']!==$periodKey||$s['cash_effect_on']!==$date))throw new \DomainException('Data ou período de Caixa incompatível.');if($sales)return $this->captureSales($s,$date,$period,$rule,$amount);
|
||||
$accounts=$this->q('accounts')->whereIn('id',[$rule->debit_account_id,$rule->credit_account_id])->get()->keyBy('id');
|
||||
@@ -83,7 +86,7 @@ final class PayableJournal
|
||||
if($old){if(!hash_equals($old->request_hash,$hash))throw new \DomainException('Chave repetida com conteúdo diferente.');return json_decode($old->result,true,64,JSON_THROW_ON_ERROR)+['replayed'=>true];}
|
||||
if(in_array($action,['validate','post'],true)){
|
||||
$candidate=$this->proposal($this->id($data['proposal_id']));
|
||||
if(in_array(json_decode($candidate->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement'],true))throw new \DomainException('Use a decisão/aplicação do par de correcção completo.');
|
||||
if(in_array(json_decode($candidate->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement'],true))throw new \DomainException('Use a decisão/aplicação do par de correcção completo.');
|
||||
}
|
||||
if($action==='prepare')$result=$this->prepare($data,$reason);
|
||||
elseif($action==='validate')$result=$this->review($data,$reason);
|
||||
@@ -107,7 +110,7 @@ final class PayableJournal
|
||||
{
|
||||
if(!is_string($d['revision'])||!in_array($d['decision'],['approved','rejected'],true))throw new \InvalidArgumentException('Decisão/revisão explícita inválida.');
|
||||
$p=$this->proposal($this->id($d['proposal_id']));
|
||||
if(in_array(json_decode($p->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement'],true))($this->authorize)('correct_validate');
|
||||
if(in_array(json_decode($p->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement'],true))($this->authorize)('correct_validate');
|
||||
if((int)$p->prepared_by===$this->actor||$p->prepared_person===$this->person)throw new \DomainException('Revisão exige outra identidade e pessoa.');
|
||||
$this->current($p,$d['revision']);$old=$this->q('reviews')->where('proposal_id',$p->id)->first();
|
||||
if($old){if($old->decision!==$d['decision']||$old->reason!==$reason)throw new \DomainException('Decisão já registada; não pode ser alterada.');return ['state'=>$old->decision,'proposal_id'=>$p->id,'review_id'=>$old->id];}
|
||||
@@ -118,7 +121,7 @@ final class PayableJournal
|
||||
private function post(array $d,string $reason):array
|
||||
{
|
||||
if(!is_string($d['revision']))throw new \InvalidArgumentException('Revisão inválida.');$p=$this->proposal($this->id($d['proposal_id']));
|
||||
if(in_array(json_decode($p->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement'],true))($this->authorize)('correct_post');
|
||||
if(in_array(json_decode($p->snapshot,true)['source']['effect_type'],['journal.reversal','journal.replacement','workshop.direct_cost.reversal','workshop.direct_cost.replacement','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement'],true))($this->authorize)('correct_post');
|
||||
if((int)$p->prepared_by!==$this->actor||$p->prepared_person!==$this->person)throw new \DomainException('Só o preparador autorizado aplica esta versão.');
|
||||
$existing=$this->q('journals')->where('effect_key',$p->effect_key)->first();
|
||||
if($existing){if((int)$existing->proposal_id!==(int)$p->id||!hash_equals($p->revision,$d['revision'])||$existing->reason!==$reason)throw new \DomainException('Efeito já lançado com conteúdo diferente.');return ['state'=>'posted','proposal_id'=>$p->id,'journal_id'=>$existing->id];}
|
||||
@@ -153,12 +156,12 @@ final class PayableJournal
|
||||
$lines=$this->q('lines')->whereIn('journal_id',$ids)->orderBy('line_no')->get();
|
||||
$totals=$this->q('lines')->whereIn('journal_id',(clone $journals)->select('id'));if($account)$totals->where('account_id',$account->id);
|
||||
$sum=$totals->selectRaw('coalesce(sum(debit_minor),0) AS debit_minor,coalesce(sum(credit_minor),0) AS credit_minor')->first();
|
||||
$proposalQuery=$this->q('proposals');if($industrialLot)$proposalQuery->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement']);if($costOrder)$proposalQuery->where('snapshot->source->cost_order_id',$costOrder);if($salesInvoice)$proposalQuery->where('snapshot->source->invoice_id',$salesInvoice);if($focus)$proposalQuery->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS);$proposals=$proposalQuery->orderByDesc('id')->paginate(25,['*'],'page',$page);$proposalIds=array_map(fn($p)=>$p->id,$proposals->items());$reviews=$this->q('reviews')->whereIn('proposal_id',$proposalIds)->get();
|
||||
$proposalQuery=$this->q('proposals');if($industrialLot)$proposalQuery->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement']);if($costOrder)$proposalQuery->where('snapshot->source->cost_order_id',$costOrder);if($salesInvoice)$proposalQuery->where('snapshot->source->invoice_id',$salesInvoice);if($focus)$proposalQuery->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS);$proposals=$proposalQuery->orderByDesc('id')->paginate(25,['*'],'page',$page);$proposalIds=array_map(fn($p)=>$p->id,$proposals->items());$reviews=$this->q('reviews')->whereIn('proposal_id',$proposalIds)->get();
|
||||
$posted=$this->q('journals')->whereIn('proposal_id',$proposalIds)->pluck('id','proposal_id');foreach($proposals->items() as $p)$p->journal_id=$posted->get($p->id);
|
||||
$eventPager=$this->q('events')->when($focus||$salesInvoice||$costOrder||$industrialLot,fn($q)=>$q->where(function($events)use($focus,$salesInvoice,$costOrder,$industrialLot){
|
||||
$events->where(fn($e)=>$e->where('kind','journal.prepared')->whereIn('entity_id',$this->q('proposals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement']))->select('id')))
|
||||
->orWhere(fn($e)=>$e->where('kind','journal.reviewed')->whereIn('entity_id',$this->q('reviews')->whereIn('proposal_id',$this->q('proposals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement']))->select('id'))->select('id')))
|
||||
->orWhere(fn($e)=>$e->where('kind','journal.posted')->whereIn('entity_id',$this->q('journals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement']))->select('id')));
|
||||
$events->where(fn($e)=>$e->where('kind','journal.prepared')->whereIn('entity_id',$this->q('proposals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement']))->select('id')))
|
||||
->orWhere(fn($e)=>$e->where('kind','journal.reviewed')->whereIn('entity_id',$this->q('reviews')->whereIn('proposal_id',$this->q('proposals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement']))->select('id'))->select('id')))
|
||||
->orWhere(fn($e)=>$e->where('kind','journal.posted')->whereIn('entity_id',$this->q('journals')->when($focus,fn($q)=>$q->where('obligation_id',$focus)->whereIn('snapshot->source->effect_type',self::SUPPLIER_EFFECTS))->when($salesInvoice,fn($q)=>$q->where('snapshot->source->invoice_id',$salesInvoice))->when($costOrder,fn($q)=>$q->where('snapshot->source->cost_order_id',$costOrder))->when($industrialLot,fn($q)=>$q->where('snapshot->source->industrial_lot_id',$industrialLot)->whereIn('snapshot->source->effect_type',['industry.cost.validated','industry.cost.reversal','industry.cost.replacement','assets.depreciation.reversal','assets.depreciation.replacement']))->select('id')));
|
||||
}))->orderByDesc('id')->paginate(25,['*'],'page',$page);
|
||||
return ['synthetic_only'=>true,'journals'=>$pager->items(),'lines'=>$lines->all(),'proposals'=>$proposals->items(),'reviews'=>$reviews->all(),'accounts'=>$withAccounts?$this->q('accounts')->orderBy('id')->get()->all():[],
|
||||
'events'=>$eventPager->items(),'totals'=>['debit_minor'=>(int)$sum->debit_minor,'credit_minor'=>(int)$sum->credit_minor,'net_minor'=>(int)$sum->debit_minor-(int)$sum->credit_minor],
|
||||
|
||||
Reference in New Issue
Block a user