Files
compras/app/Services/CommonBudget.php
T

59 lines
2.6 KiB
PHP

<?php
namespace App\Services;
use App\Enums\PurchasingRole;
use App\Models\Requisition;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Tempo\Finance\Budget\BudgetContext;
use Tempo\Finance\Budget\BudgetControl;
use Tempo\Finance\Budget\BudgetFailure;
/** Local host adapter: auth, tenant lock and audit; no budget arithmetic. */
final class CommonBudget
{
public function for(User $user): BudgetControl
{
abort_unless(auth()->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()->whereIn('purchasing_role', [PurchasingRole::Admin->value, PurchasingRole::Finance->value])->exists(),
);
}
/**
* @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()]);
}
}
}