This commit is contained in:
2026-11-03 11:51:44 +02:00
parent badaada043
commit e753f7a0a4
309 changed files with 14537 additions and 529 deletions
+60
View File
@@ -0,0 +1,60 @@
<?php
use App\Http\Controllers\QuotationController;
use App\Http\Controllers\SupplierController;
use App\Http\Requests\SaveSupplierRequest;
use App\Models\Quotation;
use App\Models\Requisition;
use App\Models\Supplier;
use App\Services\RequisitionWorkflow;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/** Shared driver of native product commands, only called by guarded local pilot scripts. */
function pilotMarketScenario($app, array $proposal, array $next, Closure $actor, Closure $check): array
{
$preparer = $actor('prepare');
$check($preparer->id === $next['preparer_id'], 'Preparador diferente da proposta.');
$purchase = Requisition::query()->findOrFail($next['purchase_id']);
$check($purchase->status === 'approved' && $purchase->number === $next['purchase_number'] && $purchase->items()->sole()->quantity === '2.000' && $purchase->total_minor === 2320, 'Compra diferente da proposta.');
$check(Quotation::query()->where('requisition_id', $purchase->id)->count() === 0, 'Já existem propostas: não duplicar.');
$existing = Supplier::query()->findOrFail($next['existing_eligible_supplier_id']);
$check($existing->eligible(), 'O fornecedor existente deixou de estar elegível.');
$protected = ['users', 'organization_access', 'organization_profiles', 'organization_memberships', 'purchase_orders', 'stock_movements', 'requisitions', 'requisition_items', 'budget_entries', 'purchase_awards'];
$fingerprint = fn () => collect($protected)->mapWithKeys(fn ($table) => [$table => hash('sha256', json_encode(DB::table($table)->orderBy('id')->get(), JSON_THROW_ON_ERROR))])->all();
$baseline = $fingerprint();
$suppliersBefore = DB::table('suppliers')->orderBy('id')->get()->toArray();
$supplierIds = ['existing' => $existing->id];
$workflow = app(RequisitionWorkflow::class);
foreach ($next['new_synthetic_suppliers'] as $index => $values) {
$preparer = $actor('prepare');
$form = SaveSupplierRequest::create($proposal['environment'].'/fornecedores', 'POST', [...$next['synthetic_supplier_fields'], ...$values]);
$form->setContainer($app)->setRedirector($app['redirect']);
$form->setUserResolver(fn () => $preparer);
$form->validateResolved();
app(SupplierController::class)->store($form, $workflow);
$supplier = Supplier::query()->where('nuit', $values['nuit'])->sole();
$reviewer = $actor('review');
$check($reviewer->id === $next['supplier_reviewer_id'] && $supplier->created_by !== $reviewer->id, 'Conferência sem independência.');
$review = Request::create($proposal['environment'].'/fornecedores/'.$supplier->id.'/validar', 'POST', ['version' => $supplier->version, 'status' => 'active',
'comment' => 'Activação exclusivamente sintética para o piloto local; não representa validação comercial real.']);
$review->setUserResolver(fn () => $reviewer);
app(SupplierController::class)->review($review, $supplier, $workflow);
$supplierIds[$index === 0 ? 'alternative_a' : 'alternative_b'] = $supplier->id;
}
$preparer = $actor('prepare');
foreach ($next['quotations'] as $quote) {
$input = Request::create($proposal['environment'].'/requisicoes/'.$purchase->id.'/cotacoes', 'POST', ['supplier_id' => $supplierIds[$quote['supplier']],
'reference' => $quote['reference'], 'delivery_days' => $quote['delivery_days'], 'valid_until' => $next['quotation_valid_until'], 'payment_terms' => $next['payment_terms'],
'no_conflict' => true, 'items' => [['requisition_item_id' => $purchase->items()->sole()->id, 'unit_price' => $quote['unit_price']]]]);
$input->setUserResolver(fn () => $preparer);
app(QuotationController::class)->store($input, $purchase, $workflow);
$saved = Quotation::query()->where('requisition_id', $purchase->id)->where('reference', $quote['reference'])->sole();
$check($saved->total_minor === $quote['expected_total_minor'], 'Totais diferentes da proposta sintética.');
}
$check($fingerprint() === $baseline && Quotation::query()->where('requisition_id', $purchase->id)->count() === 3, 'A consulta alterou dados fora do âmbito.');
$check(DB::table('suppliers')->whereNotIn('id', [$supplierIds['alternative_a'], $supplierIds['alternative_b']])->orderBy('id')->get()->toArray() == $suppliersBefore, 'Cadastro anterior alterado.');
$check(Quotation::query()->where('requisition_id', $purchase->id)->withCount('evaluations')->get()->sum('evaluations_count') === 0, 'Não avaliar automaticamente.');
return ['supplier_ids' => $supplierIds, 'quotation_ids' => Quotation::query()->where('requisition_id', $purchase->id)->orderBy('id')->pluck('id')->all(), 'protected_tables' => $protected];
}