Consolida recepcoes, stock, equipamentos e identidade SGC

This commit is contained in:
2026-10-28 13:54:53 +02:00
parent eb1c8b8074
commit 75ffdb395c
76 changed files with 6933 additions and 152 deletions
+29
View File
@@ -3,17 +3,25 @@
use App\Enums\PurchasingRole;
use App\Models\AuditLog;
use App\Models\CostCenter;
use App\Models\CriticalityLevel;
use App\Models\Department;
use App\Models\Equipment;
use App\Models\EquipmentSparePart;
use App\Models\PurchaseAward;
use App\Models\PurchaseOrder;
use App\Models\Quotation;
use App\Models\Requisition;
use App\Models\SparePart;
use App\Models\StockItem;
use App\Models\StockMovement;
use App\Models\Supplier;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Warehouse;
use App\Services\RequisitionWorkflow;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Testing\AssertableInertia as Assert;
function awardPrices(object $test, int $firstPrice, int $taxRate = 0): void
@@ -373,3 +381,24 @@ test('delivery dates that expire before emission are not silently replaced', fun
$this->actingAs($this->buyer)->post('/adjudicacoes/'.$award->id.'/emitir')->assertSessionHasErrors('delivery_date');
expect(PurchaseOrder::query()->count())->toBe(0);
});
test('spare purchase flows through award order receipt and issue with equipment trace', function () {
$level = CriticalityLevel::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $this->admin->id, 'code' => 'PART-TEST', 'name' => 'Normal de teste', 'criteria' => 'Critério explícito da fixture', 'is_critical' => false, 'created_at' => now()]);
$asset = Equipment::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $this->buyer->id, 'department_id' => $this->department->id, 'criticality_level_id' => $level->id, 'code' => 'EQ-FLOW', 'description' => 'Máquina da fixture', 'area' => 'Produção', 'status' => 'active']);
$stock = StockItem::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $this->buyer->id, 'code' => 'PART-FLOW', 'name' => 'Peça da fixture', 'unit' => 'un']);
$part = SparePart::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $this->buyer->id, 'stock_item_id' => $stock->id, 'criticality_level_id' => $level->id, 'minimum_milli' => 500]);
EquipmentSparePart::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $this->buyer->id, 'equipment_id' => $asset->id, 'spare_part_id' => $part->id, 'created_at' => now()]);
$this->item->update(['purchase_type' => 'spare_part', 'spare_part_id' => $part->id, 'equipment_id' => $asset->id]);
$award = selectPurchaseAward($this);
$order = issuePurchaseOrder($this, $award);
expect($order->items()->firstOrFail()->spare_part_id)->toBe($part->id)->and($order->snapshot['equipment_context'][0]['equipment']['description'])->toBe('Máquina da fixture');
$asset->update(['description' => 'Nome actualizado']);
expect($order->refresh()->snapshot['equipment_context'][0]['equipment']['description'])->toBe('Máquina da fixture');
$receiver = User::factory()->create(['tenant_id' => $this->tenant->id, 'department_id' => $this->department->id, 'purchasing_role' => PurchasingRole::Warehouse]);
$this->actingAs($receiver);
$warehouse = Warehouse::query()->create(['tenant_id' => $this->tenant->id, 'created_by' => $receiver->id, 'code' => 'FLOW', 'name' => 'Armazém de teste']);
$receipt = ['idempotency_key' => (string) Str::uuid(), 'delivery_note' => 'GR-FLOW', 'received_on' => today()->format('Y-m-d'), 'warehouse_id' => $warehouse->id, 'items' => [['purchase_order_item_id' => $order->items()->firstOrFail()->id, 'received_quantity' => '1.000', 'accepted_quantity' => '1.000', 'quality_status' => 'compliant', 'stock_item_id' => $stock->id]]];
$this->postJson('/ordens/'.$order->id.'/recepcoes', $receipt)->assertCreated();
$this->postJson('/stock/operacoes', ['kind' => 'issue', 'reference' => 'SAI-FLOW', 'idempotency_key' => (string) Str::uuid(), 'warehouse_id' => $warehouse->id, 'stock_item_id' => $stock->id, 'responsible_id' => $receiver->id, 'destination' => 'Produção', 'reason' => 'Montagem no equipamento', 'quantity' => '0.500', 'equipment_id' => $asset->id])->assertCreated();
expect((int) StockMovement::query()->where('stock_item_id', $stock->id)->sum('quantity_milli'))->toBe(500)->and(StockMovement::query()->where('equipment_id', $asset->id)->count())->toBe(2);
});