environment('budget-testing'), 403, 'Controlo orçamental comum disponível apenas no ensaio isolado.'); $c = DB::connection(); $name = $c->getDatabaseName(); if ($c->getDriverName() !== 'pgsql' || ! preg_match('/^budget_core_sgf_[a-f0-9]{12}$/D', $name) || (string) $c->getConfig('host') !== '127.0.0.1' || ! empty($c->getConfig('url')) || $c->selectOne('select current_database() as db')->db !== $name) { throw new \LogicException('COMMON_BUDGET_QA_DATABASE_REFUSED'); } } public function for(User $user): BudgetControl { self::guard(); $selection = app(CompanyContext::class); abort_unless(auth()->id() === $user->id && $selection->userId() === $user->id && $selection->unitId() !== null, 403); $scope = $selection->identifiers() + ['unit_id' => $selection->unitId()]; $context = new BudgetContext($user->id, $scope, (int) now()->year, 'MZN'); return new BudgetControl(DB::connection(), $context, 'cb_', function (string $action, BudgetContext $ctx): void { self::guard(); $s = $ctx->scope; DB::table('companies')->where('tenant_id', $s['tenant_id'])->where('id', $s['company_id'])->lockForUpdate()->firstOrFail(); DB::table('users')->where('id', $ctx->actorId)->lockForUpdate()->firstOrFail(); abort_unless(auth()->id() === $ctx->actorId, 403); $current = app(CompanyContext::class); abort_unless($current->identifiers() === array_intersect_key($s, array_flip(['tenant_id', 'company_id'])) && $current->unitId() === $s['unit_id'], 409, 'O contexto mudou.'); $current->selectFor($ctx->actorId, $s['tenant_id'], $s['company_id'], $s['unit_id']); app(Permissions::class)->require($ctx->actorId, $s['tenant_id'], $s['company_id'], 'purchasing_budget.'.$action, $s['unit_id']); }, function (string $action, array $data, ?int $origin, BudgetContext $ctx): void { DB::table('cb_audit')->insert($ctx->scope + ['actor_id' => $ctx->actorId, 'action' => $action, 'origin_id' => $origin, 'payload' => json_encode($data, JSON_THROW_ON_ERROR), 'created_at' => now()]); }, function (int $author, BudgetContext $ctx): bool { $s = $ctx->scope; DB::table('users')->where('id', $author)->lockForUpdate()->firstOrFail(); return DB::table('company_memberships')->where(['user_id' => $author, 'tenant_id' => $s['tenant_id'], 'company_id' => $s['company_id']])->exists() && app(Permissions::class)->has($author, $s['tenant_id'], $s['company_id'], 'purchasing_budget.reinforcement.propose', $s['unit_id']); }, ); } /** A mapped procurement reference, never an industrial lot or a department inferred as a unit. */ public function record(User $user, int $origin, string $kind, int $amount = 0, ?int $subtotal = null): ?array { self::guard(); return DB::transaction(function () use ($user, $origin, $kind, $amount, $subtotal) { $engine = $this->for($user); $source = DB::table('cb_origins')->where($engine->context->scope)->where('id', $origin)->first(); abort_unless($source, 403, 'Origem de compras não mapeada neste contexto.'); return $engine->record($origin, (int) $source->cost_center_id, $source->required_kind, $kind, $amount, $subtotal); }); } public function create(User $user, array $data): int { self::guard(); $engine = $this->for($user); abort_unless(DB::table('cb_centres')->where($engine->context->scope)->where('id', $data['cost_center_id'])->exists(), 403, 'Centro não mapeado neste contexto.'); return $engine->create($data); } }