commit melhorias sgf
This commit is contained in:
@@ -13,7 +13,7 @@ final class SalesLedgerSource
|
||||
{
|
||||
private function scope(User $u):array
|
||||
{
|
||||
abort_unless(config('sales_ledger.synthetic_enabled')===true,403);ServiceReceivables::guard();foreach(['si_invoices','sr_records','sr_effects','gl_sales_capability'] as $table)abort_unless(DB::getSchemaBuilder()->hasTable($table),503,'Composição comercial contabilística não instalada.');$ctx=app(CompanyContext::class);
|
||||
abort_unless(config('sales_ledger.synthetic_enabled')===true,403);ServiceReceivables::guard();foreach(['si_invoices','sr_records','sr_effects','gl_sales_capability'] as $table)abort_unless(str_starts_with($table,'gl_')?SourceBook::has($table):DB::getSchemaBuilder()->hasTable($table),503,'Composição comercial contabilística não instalada.');$ctx=app(CompanyContext::class);
|
||||
abort_unless(auth()->id()===$u->id&&$ctx->userId()===$u->id&&$ctx->unitId(),403);$scope=$ctx->identifiers()+['unit_id'=>$ctx->unitId()];
|
||||
foreach(['service_invoices.view','service_receivables.view'] as $p)app(Permissions::class)->require($u->id,$scope['tenant_id'],$scope['company_id'],$p,$scope['unit_id']);return $scope;
|
||||
}
|
||||
@@ -37,27 +37,38 @@ final class SalesLedgerSource
|
||||
public function source(User $u,string $kind,int $id):array
|
||||
{
|
||||
$scope=$this->scope($u);if($kind==='sales.invoice')return $this->invoice($u,$scope,$id);
|
||||
$expected=match($kind){'receivable.allocated'=>'allocate','receivable.reversed'=>'reverse_allocation',default=>throw new \DomainException('Efeito comercial não suportado.')};
|
||||
$expected=match($kind){'receivable.allocated'=>'allocate','receivable.reversed'=>'reverse_allocation','receivable.reduced'=>'reduction','receivable.reduction_reversed'=>'reverse_reduction',default=>throw new \DomainException('Efeito comercial não suportado.')};
|
||||
$e=$this->effectQuery($scope)->where('e.id',$id)->where('e.kind',$expected)->first()??throw new \DomainException('Afectação/reversão confirmada fora do âmbito.');
|
||||
$ob=DB::table('sr_records')->where($scope)->where('id',$e->obligation_id)->where('kind','obligation')->first()??throw new \DomainException('Obrigação a receber indisponível.');
|
||||
$base=$this->invoice($u,$scope,(int)$ob->invoice_id);
|
||||
if($kind==='receivable.reduction_reversed'){
|
||||
$target=$this->effectQuery($scope)->where(['e.id'=>$e->target_effect_id,'e.kind'=>'reduction','e.obligation_id'=>$ob->id])->first();$r=DB::table('sr_records')->where($scope)->where(['id'=>$e->record_id,'kind'=>'correction'])->first();$cancel=DB::table('sd_cancellations')->where($scope)->where(['effect_id'=>$e->id,'state'=>'cancelled'])->first();
|
||||
if(!$target||!$r||!$cancel||$r->actor_id===$e->actor_id||$target->amount!==$e->amount||!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','receivable.reduced')->where('snapshot->source->source_id',(int)$target->id)->exists())throw new \DomainException('Reversão sem crédito original contabilizado e anulação distinta.');
|
||||
return array_replace($base,['effect_type'=>$kind,'source_id'=>$id,'amount_minor'=>$this->minor($e->amount),'tax_minor'=>0,'recognition_revision'=>$this->hash(['invoice'=>$base['recognition_revision'],'target'=>(array)$target,'effect'=>(array)$e,'record'=>(array)$r,'cancellation'=>(array)$cancel]),'financial_effect'=>(array)$e]);
|
||||
}
|
||||
if($kind==='receivable.reduced'){
|
||||
$r=DB::table('sr_records')->where($scope)->where(['id'=>$e->record_id,'kind'=>'correction','invoice_id'=>$ob->invoice_id,'obligation_id'=>$ob->id])->first();
|
||||
$doc=DB::getSchemaBuilder()->hasTable('sd_outputs')?DB::table('sd_outputs')->where($scope)->where(['effect_id'=>$e->id,'invoice_id'=>$ob->invoice_id,'kind'=>'credit_note'])->first():null;
|
||||
if(!$r||!$doc||$r->actor_id===$e->actor_id||$r->amount!==$e->amount||!hash_equals($doc->pdf_sha256,hash('sha256',base64_decode($doc->pdf_base64,true)))||!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->where('snapshot->source->source_id',(int)$ob->invoice_id)->exists())throw new \DomainException('Crédito sem documento, decisão distinta ou factura original contabilizada.');
|
||||
return array_replace($base,['effect_type'=>$kind,'source_id'=>$id,'amount_minor'=>$this->minor($e->amount),'tax_minor'=>0,'invoice_tax_minor'=>$base['tax_minor'],'recognition_revision'=>$this->hash(['invoice'=>$base['recognition_revision'],'effect'=>(array)$e,'record'=>(array)$r,'credit_document'=>(array)$doc]),'financial_effect'=>(array)$e,'credit_document_id'=>$doc->id,'operational_nominal_reduction'=>true]);
|
||||
}
|
||||
$receipt=DB::table('sr_records')->where($scope)->where('id',$e->receipt_id)->where(['kind'=>'receipt','obligation_id'=>$ob->id,'invoice_id'=>$ob->invoice_id])->first();
|
||||
$confirmation=$receipt?$this->effectQuery($scope)->where('e.record_id',$receipt->id)->where('e.kind','receipt_confirm')->first():null;
|
||||
if(!$receipt||!$confirmation||$receipt->actor_id===$confirmation->actor_id)throw new \DomainException('Recebimento não confirmado independentemente.');
|
||||
if(!DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->where('snapshot->source->source_id',(int)$ob->invoice_id)->exists())throw new \DomainException('Contabilize primeiro a factura original.');
|
||||
if(!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->where('snapshot->source->source_id',(int)$ob->invoice_id)->exists())throw new \DomainException('Contabilize primeiro a factura original.');
|
||||
if($kind==='receivable.reversed'){
|
||||
$target=$this->effectQuery($scope)->where('e.id',$e->target_effect_id)->where('e.kind','allocate')->where('e.obligation_id',$ob->id)->first();
|
||||
if(!$target||$target->amount!==$e->amount||!DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','receivable.allocated')->where('snapshot->source->source_id',(int)$target->id)->exists())throw new \DomainException('Reversão exige a afectação original contabilizada integralmente.');
|
||||
if(!$target||$target->amount!==$e->amount||!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','receivable.allocated')->where('snapshot->source->source_id',(int)$target->id)->exists())throw new \DomainException('Reversão exige a afectação original contabilizada integralmente.');
|
||||
}
|
||||
return array_replace($base,['effect_type'=>$kind,'source_id'=>$id,'amount_minor'=>$this->minor($e->amount),'tax_minor'=>0,'invoice_tax_minor'=>$base['tax_minor'],'recognition_revision'=>$this->hash(['invoice'=>$base['recognition_revision'],'effect'=>(array)$e,'receipt'=>(array)$receipt,'confirmation'=>(array)$confirmation]),'financial_effect'=>(array)$e,'receipt_confirmation_id'=>$confirmation->id]);
|
||||
}
|
||||
public function period(User $u,array $period):array
|
||||
{
|
||||
$scope=$this->scope($u);$h=hash_init('sha256');$facts=0;$pending=0;
|
||||
foreach(DB::table('si_invoices')->where($scope)->orderBy('id')->cursor() as $i){$facts++;hash_update($h,$i->snapshot);hash_update($h,$i->pdf_sha256);if(!DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->where('snapshot->source->source_id',(int)$i->id)->exists())$pending++;}
|
||||
foreach(DB::table('si_invoices')->where($scope)->orderBy('id')->cursor() as $i){$facts++;hash_update($h,$i->snapshot);hash_update($h,$i->pdf_sha256);if(!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type','sales.invoice')->where('snapshot->source->source_id',(int)$i->id)->exists())$pending++;}
|
||||
foreach(DB::table('sr_records')->where($scope)->orderBy('id')->cursor() as $r){hash_update($h,Payables::canonical((array)$r));if(!$this->effectQuery($scope)->where('e.record_id',$r->id)->where('e.kind','<>','allocate')->exists())$pending++;}
|
||||
foreach($this->effectQuery($scope)->orderBy('e.id')->cursor() as $e){$facts++;hash_update($h,Payables::canonical((array)$e));
|
||||
if(in_array($e->kind,['allocate','reverse_allocation'],true)){if(!DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type',$e->kind==='allocate'?'receivable.allocated':'receivable.reversed')->where('snapshot->source->source_id',(int)$e->id)->exists())$pending++;}
|
||||
if(in_array($e->kind,['allocate','reverse_allocation','reduction','reverse_reduction'],true)){if(!SourceBook::table('gl_journals')->where($scope)->where('snapshot->source->effect_type',match($e->kind){'allocate'=>'receivable.allocated','reduction'=>'receivable.reduced','reverse_reduction'=>'receivable.reduction_reversed',default=>'receivable.reversed'})->where('snapshot->source->source_id',(int)$e->id)->exists())$pending++;}
|
||||
elseif(!in_array($e->kind,['open','receipt_confirm','refused'],true))$pending++; // Unsupported financial changes are visible blockers, never ignored.
|
||||
}
|
||||
return ['facts'=>$facts,'pending'=>$pending,'hash'=>hash_final($h)];
|
||||
|
||||
Reference in New Issue
Block a user