Files
SGF/app/Modules/Workshop/ComposedWorkshopStockBook.php
T
2026-11-11 09:00:17 +02:00

42 lines
4.0 KiB
PHP

<?php
namespace App\Modules\Workshop;
use App\Shared\Context\CompanyContext;
use App\Shared\Installation\CompositionRuntime;
use App\Modules\Purchasing\ReceiptRuntime;
use Illuminate\Support\Facades\{DB,Auth,Schema};
/** Keep workshop observations in the host and physical commands in their original book. */
final class ComposedWorkshopStockBook
{
private static ?object $native=null;
private static ?int $nativeActor=null;
public static function active():bool{return self::$native!==null;}
public static function connection(){return self::$native??DB::connection();}
public static function table(string $name){return self::connection()->table($name);}
public static function hasTable(string $name):bool{return self::connection()->getSchemaBuilder()->hasTable($name);}
public static function actor(int $hostActor):int{return self::$nativeActor??$hostActor;}
public function run(\Closure $work):mixed
{
CompositionRuntime::assert();abort_unless(config('workshop_stock.composed_enabled')===true,404);
$ctx=app(CompanyContext::class);$scope=$ctx->identifiers()+['unit_id'=>$ctx->unitId()];$host=DB::connection();$user=\App\Models\User::findOrFail($ctx->userId());$physical=CompositionRuntime::physical($host);
abort_unless(request()->session()->get('company_context.module_id')==='workshop',409,'Seleccione Oficinas.');
$file='C:/laragon/data/sgf-tempo/run/modular-regression-2576427d1939/private/receipt-composition-'.$physical['composition'].'/workshop-stock-binding.json';$pin=config('workshop_stock.composed_sha256');
abort_unless(is_string($pin)&&preg_match('/^[a-f0-9]{64}$/D',$pin)&&is_file($file)&&!is_link($file)&&hash_equals($pin,hash_file('sha256',$file)),503,'Ligação física não conferida.');
$p=json_decode(file_get_contents($file),true,64,JSON_THROW_ON_ERROR);abort_unless(($p['scope']??null)===$scope&&($p['composition']??null)===$physical['composition']&&($p['source']['schema']??null)==='public',403);
foreach(['resources','eligible_order_ids','eligible_deliveries'] as $key)abort_unless(is_array($p['source'][$key]??null)&&count($p['source'][$key])<=1000,503);
return $host->transaction(function()use($scope,$host,$user,$p,$work){
$host->statement("SET LOCAL lock_timeout='5s'");$host->table('companies')->where(['tenant_id'=>$scope['tenant_id'],'id'=>$scope['company_id']])->lockForUpdate()->firstOrFail();
return app(ReceiptRuntime::class)->run('stock.view',function($actor,$binding)use($scope,$host,$user,$p,$work){
$native=DB::connection();$marker=$native->table('gl_sgf_composition')->where('tenant_id',$scope['tenant_id'])->first();abort_unless($marker&&$marker->host_database===$host->getDatabaseName()&&(int)$marker->company_id===$scope['company_id']&&(int)$marker->unit_id===$scope['unit_id'],403);
foreach($p['source']['resources'] as $resource)abort_if(array_diff($resource['warehouse_ids'],$binding['warehouse_ids']??[]),403);
// Physical authors are native warehouse identities, never coincident host IDs.
if(request()->isMethod('POST')&&str_ends_with(request()->path(),'/confirm'))abort_unless($actor->hasFunctions(['warehouse']),403);
$default=DB::getDefaultConnection();$guard=Auth::getDefaultDriver();$environment=app()->environment();$old=config('workshop_stock.synthetic_sources');
self::$native=$native;self::$nativeActor=(int)$actor->id;DB::setDefaultConnection($host->getName());Schema::clearResolvedInstance('db.schema');Auth::shouldUse('web');Auth::guard('web')->setUser($user);app()->instance('env','budget-testing');config(['workshop_stock.synthetic_sources.'.implode(':',$scope)=>$p['source']]);
try{return $work();}finally{self::$native=null;self::$nativeActor=null;config(['workshop_stock.synthetic_sources'=>$old]);DB::setDefaultConnection($default);Schema::clearResolvedInstance('db.schema');Auth::shouldUse($guard);Auth::guard($guard)->setUser($actor);app()->instance('env',$environment);}
});
});
}
}