32 lines
5.9 KiB
PHP
32 lines
5.9 KiB
PHP
<?php
|
|
namespace App\Modules\Purchasing;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Tempo\Purchasing\Embedded\Models\{PurchaseAward,PurchaseOrder,Requisition,Quotation};
|
|
use Tempo\Purchasing\Embedded\Services\{PurchaseWorkflow,ProposalComparison,RequisitionWorkflow};
|
|
|
|
/** Issuance from an already approved award; no supplier selection or approval by this controller. */
|
|
final class OrderController
|
|
{
|
|
private function guard():void{abort_unless(config('receipt_qa.orders_enabled')===true,404);}
|
|
private function viewActor(object $actor):void{abort_unless($actor->hasFunctions(['admin','procurement','finance','department_head']),403);}
|
|
private function award(int $id,array $binding,object $actor):array{
|
|
$this->viewActor($actor);
|
|
$award=PurchaseAward::where('id',$id)->where(fn($q)=>$q->whereIn('id',$binding['award_ids']??[])->orWhereIn('requisition_id',RequisitionAccess::ids($binding)))->first();abort_unless($award,403);
|
|
$request=Requisition::visibleTo($actor)->findOrFail($award->requisition_id);
|
|
return [$award,$request];
|
|
}
|
|
private function basis(PurchaseAward $award,Requisition $request):array{
|
|
$quote=Quotation::findOrFail($award->quotation_id)->load(['items','supplier']);
|
|
$basis=['award'=>$award->load('approvals')->toArray(),'requisition'=>$request->load(['items','steps'])->toArray(),'quote'=>$quote->toArray(),'comparison'=>app(ProposalComparison::class)->inspect($request)];
|
|
return ['revision'=>hash('sha256',json_encode($basis,JSON_THROW_ON_ERROR)),'basis'=>$basis];
|
|
}
|
|
private function output(PurchaseOrder $order,bool $replayed):array{return ['order'=>$order->load('items')->toArray(),'replayed'=>$replayed,'receipt_url'=>'/purchasing/receipts','stock_created'=>false,'invoice_created'=>false,'payment_created'=>false];}
|
|
public function screen(Request $r,ReceiptRuntime $runtime){$this->guard();$runtime->run('purchasing_orders.view',fn($actor)=>$this->viewActor($actor));return response()->view('order-workspace',['context'=>$r->session()->get('company_context.token'),'csrf'=>csrf_token()]);}
|
|
public function index(Request $r,ReceiptRuntime $runtime){$this->guard();$d=$r->validate(['page'=>'sometimes|integer|min:1|max:10000']);abort_if(array_diff(array_keys($r->query()),array_keys($d)),422);return $runtime->run('purchasing_orders.view',function($actor,$binding)use($d){$this->viewActor($actor);return response()->json(PurchaseAward::where(fn($q)=>$q->whereIn('id',$binding['award_ids']??[])->orWhereIn('requisition_id',RequisitionAccess::ids($binding)))->whereIn('requisition_id',Requisition::visibleTo($actor)->select('id'))->orderBy('id')->paginate(25,['id','requisition_id','status','total_minor'],'page',(int)($d['page']??1)));});}
|
|
public function show(int $id,ReceiptRuntime $runtime){$this->guard();$ctx=app(\App\Shared\Context\CompanyContext::class);$scope=$ctx->identifiers();$can=app(\App\Modules\Identity\Services\Permissions::class)->has($ctx->userId(),$scope['tenant_id'],$scope['company_id'],'purchasing_orders.issue',$ctx->unitId());return $runtime->run('purchasing_orders.view',function($actor,$binding)use($id,$can){[$award,$request]=$this->award($id,$binding,$actor);$basis=$this->basis($award,$request);$order=PurchaseOrder::where('requisition_id',$request->id)->first();return response()->json($basis+['can_issue'=>$can&&app(PurchaseWorkflow::class)->canIssue($actor,$award,$request),'existing'=>$order?$this->output($order,true):null]);});}
|
|
public function preview(int $id,Request $r,ReceiptRuntime $runtime){$this->guard();$d=$r->validate(['revision'=>'required|string|size:64']);abort_if(array_diff(array_keys($r->all()),['revision']),422);return $runtime->run('purchasing_orders.issue',function($actor,$binding,$fingerprint)use($id,$d){[$award,$request]=$this->award($id,$binding,$actor);abort_unless(app(PurchaseWorkflow::class)->canIssue($actor,$award,$request),403);$basis=$this->basis($award,$request);abort_unless(hash_equals($basis['revision'],$d['revision']),409,'A base aprovada mudou. Confira novamente.');$cut=['award'=>$id,'actor'=>$actor->id,'binding'=>$fingerprint,'revision'=>$basis['revision'],'observed'=>time()];return response()->json(['preview'=>Crypt::encryptString(json_encode($cut,JSON_THROW_ON_ERROR)),'impact'=>['supplier'=>$basis['basis']['quote']['supplier']['name'],'total_minor'=>$award->total_minor,'currency'=>$basis['basis']['quote']['currency'],'delivery_date'=>$basis['basis']['award']['delivery_date'],'delivery_location'=>$award->delivery_location,'items'=>$basis['basis']['quote']['items'],'budget'=>'O workflow original revalida e regista o compromisso, se configurado.','stock'=>false,'invoice'=>false,'payment'=>false]]);});}
|
|
public function issue(int $id,Request $r,ReceiptRuntime $runtime){$this->guard();$d=$r->validate(['preview'=>'required|string|max:32768']);abort_if(array_diff(array_keys($r->all()),['preview']),422);try{$cut=json_decode(Crypt::decryptString($d['preview']),true,64,JSON_THROW_ON_ERROR);}catch(\Throwable){abort(409,'Prévia inválida.');}return $runtime->run('purchasing_orders.issue',function($actor,$binding,$fingerprint,$origin)use($id,$cut){[$award,$request]=$this->award($id,$binding,$actor);abort_unless(($cut['award']??null)===$id&&($cut['actor']??null)===$actor->id&&($cut['binding']??null)===$fingerprint&&is_int($cut['observed']??null)&&$cut['observed']<=time()&&$cut['observed']>=time()-600,409,'O contexto ou a prévia mudou.');$existing=PurchaseOrder::where('requisition_id',$request->id)->first();abort_unless($existing||hash_equals($this->basis($award,$request)['revision'],$cut['revision']),409,'A base mudou desde a conferência.');$order=app(PurchaseWorkflow::class)->issue($actor,$award);if(!$existing)app(RequisitionWorkflow::class)->audit($actor,'sgf.order.issued',$request,null,$origin+['order_id'=>$order->id,'award_id'=>$award->id,'binding_revision'=>$fingerprint]);return response()->json($this->output($order,(bool)$existing),$existing?200:201);});}
|
|
}
|