Files
compras/app/Services/ReplenishmentRun.php
T
2026-11-02 20:27:11 +02:00

55 lines
2.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\User;
use Illuminate\Support\Facades\DB;
/** A narrow CLI execution scope, never a session, account or general authority bypass. */
final class ReplenishmentRun
{
private ?int $tenant = null;
private ?int $work = null;
private ?int $responsible = null;
public function tenantId(): ?int
{
return $this->tenant;
}
public function lockResponsible(User $user): User
{
abort_unless($this->tenant !== null && $user->tenant_id === $this->tenant && DB::transactionLevel() > 0 && ! auth()->check(), 403);
// A stock writer may have updated the signal while this worker waited for the company lock.
$responsible = DB::table('replenishment_work')->where('tenant_id', $this->tenant)->where('id', $this->work)->value('responsible_id');
abort_unless($user->id === $responsible, 403);
$this->responsible = $responsible;
return User::query()->where('tenant_id', $this->tenant)->whereKey($user->id)->lockForUpdate()->firstOrFail();
}
/** @return array<string,mixed> */
public function evidence(): array
{
return ['mode' => 'scheduled_replenishment', 'work_id' => $this->work, 'tenant_id' => $this->tenant, 'responsible_user_id' => $this->responsible, 'attribution' => 'responsible_for_trigger_or_rule; not_an_interactive_action'];
}
public function execute(int $id, callable $callback): mixed
{
abort_unless(config('work_context.composition') === 'sgc' && app()->runningInConsole() && ! auth()->check() && $this->tenant === null && (! request()->hasSession()), 403);
$work = DB::table('replenishment_work')->where('id', $id)->firstOrFail();
$this->tenant = $work->tenant_id;
$this->work = $work->id;
$this->responsible = $work->responsible_id;
try {
return $callback();
} finally {
$this->tenant = null;
$this->work = null;
$this->responsible = null;
}
}
}