36 lines
7.1 KiB
PHP
36 lines
7.1 KiB
PHP
<?php
|
|
namespace App\Modules\Accounting;
|
|
use App\Models\User;
|
|
use App\Modules\Identity\Services\Permissions;
|
|
use App\Shared\Context\{CompanyContext,ModuleRecords};
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/** Bounded cross-source navigation. Recorded evidence never substitutes a current owner conference. */
|
|
final class LedgerComposition
|
|
{
|
|
private function requireTables(array $tables):void{foreach($tables as $t)abort_unless(DB::getSchemaBuilder()->hasTable($t),503,'Fonte composta indisponível; não interpretar como zero.');}
|
|
private function postings(array $scope,string $kind,string $key,array $ids):array
|
|
{
|
|
if(!$ids)return [];$rows=DB::table('gl_journals')->where($scope)->where('snapshot->source->effect_type',$kind)->whereIn('snapshot->source->'.$key,$ids)->select('id')->selectRaw("snapshot->'source'->>'$key' AS source_id")->get();$out=[];foreach($rows as $row)$out[(int)$row->source_id]=$row->id;return $out;
|
|
}
|
|
private function page($pager,array $rows):array{return ['allowed'=>true,'rows'=>$rows,'pagination'=>['page'=>$pager->currentPage(),'total'=>$pager->total(),'last_page'=>$pager->lastPage(),'per_page'=>25]];}
|
|
public function workspace(User $user,array $options=[]):array
|
|
{
|
|
abort_unless(config('general_ledger.composition_enabled')===true&&config('sales_ledger.synthetic_enabled')===true&&config('cost_ledger.synthetic_enabled')===true,403,'Composição disponível apenas no ensaio explicitamente preparado.');$own=DB::transactionLevel()===0;
|
|
return DB::transaction(function()use($user,$options,$own){
|
|
if($own)DB::statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY');$ledger=app(GeneralLedger::class);$book=$ledger->listing($user,['page'=>(int)($options['journals_page']??1),'period_key'=>$options['period_key']??'QA-2026-10'],false);$ctx=app(CompanyContext::class);$scope=$ctx->identifiers()+['unit_id'=>$ctx->unitId()];$permissions=app(Permissions::class);$can=fn($right)=>$permissions->has($user->id,$scope['tenant_id'],$scope['company_id'],$right,$scope['unit_id']);$panels=['sales'=>['allowed'=>false],'costs'=>['allowed'=>false],'suppliers'=>['allowed'=>false]];
|
|
if($can('service_invoices.view')&&$can('service_receivables.view')){
|
|
$this->requireTables(['si_invoices','sr_records','sr_effects','gl_sales_capability']);$p=\App\Modules\Services\QualifiedInvoiceSource::query()->where($scope)->orderBy('id')->paginate(25,['id','series_code','number','issued_at'],'invoices_page',(int)($options['invoices_page']??1));$ids=array_column($p->items(),'id');$posted=$this->postings($scope,'sales.invoice','invoice_id',$ids);$records=DB::table('sr_records')->where($scope)->whereIn('invoice_id',$ids)->where('kind','obligation')->get(['id','invoice_id'])->keyBy('invoice_id');$opened=DB::table('sr_effects')->whereIn('record_id',$records->pluck('id')->all())->where('kind','open')->pluck('record_id')->all();$rows=[];foreach($p->items() as $i){$ob=$records->get($i->id);$rows[]=['invoice_id'=>$i->id,'number'=>$i->series_code.'/'.$i->number,'issued_at'=>$i->issued_at,'confirmed_opening'=>$ob&&in_array($ob->id,$opened,true),'original_journal_id'=>$posted[$i->id]??null];}$panels['sales']=$this->page($p,$rows);
|
|
}
|
|
if($can('workshop.view')&&$can('direct_costs.view')){
|
|
$this->requireTables(['gl_cost_sources','wo_orders','dc_calculations','dc_decisions']);$p=DB::table('gl_cost_sources')->where($scope)->orderBy('order_id')->paginate(25,['order_id','period_key'],'costs_page',(int)($options['costs_page']??1));$ids=array_column($p->items(),'order_id');$calculations=DB::table('dc_calculations as c')->where(array_combine(array_map(fn($k)=>'c.'.$k,array_keys($scope)),array_values($scope)))->whereIn('c.order_id',$ids)->whereNotExists(fn($q)=>$q->selectRaw('1')->from('dc_calculations as n')->whereColumn('n.order_id','c.order_id')->whereColumn('n.version','>','c.version'))->get(['c.id','c.order_id','c.version'])->keyBy('order_id');$decisions=DB::table('dc_decisions')->whereIn('calculation_id',$calculations->pluck('id')->all())->get(['calculation_id','decision'])->keyBy('calculation_id');$posted=$this->postings($scope,'workshop.direct_cost.validated','cost_order_id',$ids);$results=DB::table('gl_correction_results')->where($scope)->whereIn('original_journal_id',array_values($posted))->get(['id','original_journal_id'])->keyBy('original_journal_id');$rows=[];foreach($p->items() as $m){$calc=$calculations->get($m->order_id);$j=$posted[$m->order_id]??null;$rows[]=['order_id'=>$m->order_id,'period_key'=>$m->period_key,'calculation_id'=>$calc?->id,'calculation_version'=>$calc?->version,'recorded_decision'=>$calc?$decisions->get($calc->id)?->decision:null,'original_journal_id'=>$j,'recorded_correction_result_id'=>$j?$results->get($j)?->id:null,'current_dependency_conference_required'=>true];}$panels['costs']=$this->page($p,$rows);
|
|
}
|
|
if($can('supplier_payables.view')){
|
|
$this->requireTables(['pa_obligations','pa_sources','pa_imported_sources']);$q=DB::table('pa_obligations as o')->join('pa_sources as s','s.id','=','o.source_id')->join('pa_imported_sources as r','r.id','=','s.local_source_id');foreach($scope as $k=>$v)$q->where('o.'.$k,$v)->where('s.'.$k,$v)->where('r.'.$k,$v);$p=$q->orderBy('o.id')->paginate(25,['o.id','o.principal_minor','o.currency','s.local_source_id','r.payload'],'suppliers_page',(int)($options['suppliers_page']??1));$posted=$this->postings($scope,'supplier_payable.recognised','obligation_id',array_column($p->items(),'id'));$rows=[];foreach($p->items() as $o)$rows[]=['obligation_id'=>$o->id,'source_id'=>$o->local_source_id,'document_number'=>json_decode($o->payload,true)['number']??'—','principal_minor'=>(int)$o->principal_minor,'currency'=>$o->currency,'original_journal_id'=>$posted[$o->id]??null];$panels['suppliers']=$this->page($p,$rows);
|
|
}
|
|
if(config('industrial_ledger.synthetic_enabled')===true)$panels['industrial']=$can('industrial_costs.view')?app(IndustrialLedgerSource::class)->cards($user,(int)($options['industrial_page']??1)):['allowed'=>false];
|
|
$key=$options['period_key']??'QA-2026-10';$period=DB::table('gl_periods')->where($scope)->where('period_key',$key)->orderByDesc('version')->first();$p=DB::table('gl_period_checks')->where($scope)->where('period_key',$key)->orderByDesc('id')->paginate(25,['*'],'checks_page',(int)($options['checks_page']??1));$ids=array_column($p->items(),'id');$reviews=DB::table('gl_period_reviews')->where($scope)->whereIn('check_id',$ids)->get()->keyBy('check_id');$closures=DB::table('gl_period_closures')->where($scope)->whereIn('check_id',$ids)->get()->keyBy('check_id');$checks=[];foreach($p->items() as $c)$checks[]=['capture'=>(array)$c,'review'=>$reviews->get($c->id),'closure'=>$closures->get($c->id)];$periodActions=[];foreach(['period_prepare','period_validate','period_close'] as $a)$periodActions[$a]=$ledger->can($user,$a);return ['synthetic_only'=>true,'scope'=>$scope,'panels'=>$panels,'book'=>$book,'period'=>$period,'checks'=>$this->page($p,$checks),'period_permissions'=>$periodActions,'all_current_source_views'=>!in_array(false,array_column($panels,'allowed'),true),'query'=>$options,'recorded_state_is_not_current_conference'=>true];
|
|
});
|
|
}
|
|
}
|