id() === $user->id && $user->tenant_id !== null, 403); abort_unless(DB::table('tenants')->where('id', $user->tenant_id)->value('currency') === 'MZN', 422, 'Controlo comum desta entrega suporta apenas MZN; câmbio permanece pendente.'); $context = new BudgetContext($user->id, ['tenant_id' => (int) $user->tenant_id], (int) now()->year, 'MZN'); return new BudgetControl(DB::connection(), $context, '', function (string $action) use ($user): void { app(ConflictGuard::class)->lockCompany($user); abort_unless(DB::table('tenants')->where('id', $user->tenant_id)->value('currency') === 'MZN', 422, 'Moeda da organização incompatível com esta entrega.'); $roles = in_array($action, ['create', 'approve', 'reinforcement.propose', 'reinforcement.review', 'view'], true) ? [PurchasingRole::Admin, PurchasingRole::Finance] : ($action === 'consume' ? [PurchasingRole::Finance] : null); app(CurrentActor::class)->lock($user, $roles); }, function (string $action, array $data, ?int $origin) use ($user): void { // InvoiceWorkflow retains invoice.validated in the enclosing transaction. if ($action !== 'budget.consumed') { $request = $origin ? Requisition::query()->findOrFail($origin) : null; app(RequisitionWorkflow::class)->audit($user, $action, $request, null, $data); } }, fn (int $id): bool => User::query()->where('tenant_id', $user->tenant_id)->whereKey($id)->lockForUpdate()->first()?->hasFunctions([PurchasingRole::Admin, PurchasingRole::Finance]) ?? false, ); } /** * @template T * * @param callable(): T $command * @return T */ public function execute(callable $command): mixed { try { return $command(); } catch (BudgetFailure $e) { abort_if($e->status !== 422, $e->status, $e->getMessage()); throw ValidationException::withMessages([$e->field => $e->getMessage()]); } } }