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
@@ -0,0 +1,84 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('warehouses', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->string('code', 30);
$table->string('name', 200);
$table->timestamps();
$table->unique(['tenant_id', 'code']);
});
Schema::create('stock_items', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->string('code', 30);
$table->string('name', 200);
$table->string('unit', 30);
$table->timestamps();
$table->unique(['tenant_id', 'code']);
});
Schema::create('goods_receipts', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('purchase_order_id')->constrained()->restrictOnDelete();
$table->foreignId('received_by')->constrained('users')->restrictOnDelete();
$table->foreignId('warehouse_id')->nullable()->constrained()->restrictOnDelete();
$table->string('number')->nullable()->unique();
$table->uuid('idempotency_key');
$table->string('payload_hash', 64);
$table->string('delivery_note', 100);
$table->date('received_on');
$table->string('status');
$table->json('snapshot');
$table->timestamp('confirmed_at');
$table->unique(['purchase_order_id', 'idempotency_key']);
$table->unique(['purchase_order_id', 'delivery_note']);
});
Schema::create('goods_receipt_items', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('goods_receipt_id')->constrained()->restrictOnDelete();
$table->foreignId('purchase_order_item_id')->constrained()->restrictOnDelete();
$table->foreignId('stock_item_id')->nullable()->constrained()->restrictOnDelete();
$table->unsignedBigInteger('received_milli');
$table->unsignedBigInteger('accepted_milli');
$table->unsignedBigInteger('rejected_milli');
$table->string('quality_status');
$table->text('notes')->nullable();
$table->json('snapshot');
$table->unique(['goods_receipt_id', 'purchase_order_item_id']);
});
Schema::create('stock_movements', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('goods_receipt_item_id')->unique()->constrained()->restrictOnDelete();
$table->foreignId('stock_item_id')->constrained()->restrictOnDelete();
$table->foreignId('warehouse_id')->constrained()->restrictOnDelete();
$table->foreignId('moved_by')->constrained('users')->restrictOnDelete();
$table->string('movement_type')->default('receipt_in');
$table->unsignedBigInteger('quantity_milli');
$table->json('snapshot');
$table->timestamp('moved_at');
$table->index(['tenant_id', 'stock_item_id', 'warehouse_id']);
});
}
public function down(): void
{
Schema::dropIfExists('stock_movements');
Schema::dropIfExists('goods_receipt_items');
Schema::dropIfExists('goods_receipts');
Schema::dropIfExists('stock_items');
Schema::dropIfExists('warehouses');
}
};
@@ -0,0 +1,47 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('stock_operations', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->foreignId('responsible_id')->constrained('users')->restrictOnDelete();
$table->foreignId('warehouse_id')->constrained()->restrictOnDelete();
$table->foreignId('destination_warehouse_id')->nullable()->constrained('warehouses')->restrictOnDelete();
$table->foreignId('stock_item_id')->constrained()->restrictOnDelete();
$table->foreignId('count_id')->nullable()->unique()->constrained('stock_operations')->restrictOnDelete();
$table->string('kind');
$table->string('reference', 100);
$table->uuid('idempotency_key');
$table->string('payload_hash', 64);
$table->string('destination', 200)->nullable();
$table->text('reason');
$table->bigInteger('quantity_milli');
$table->bigInteger('theoretical_milli')->nullable();
$table->unsignedBigInteger('ledger_version')->nullable();
$table->json('snapshot');
$table->timestamp('recorded_at');
$table->unique(['tenant_id', 'reference']);
$table->unique(['tenant_id', 'idempotency_key']);
});
Schema::table('stock_movements', function (Blueprint $table) {
$table->unsignedBigInteger('goods_receipt_item_id')->nullable()->change();
$table->bigInteger('quantity_milli')->change();
$table->foreignId('stock_operation_id')->nullable()->constrained()->restrictOnDelete();
$table->unique(['stock_operation_id', 'warehouse_id']);
});
}
public function down(): void
{
// Signed ledger entries must never be coerced back to unsigned quantities.
throw new LogicException('Migração de histórico irreversível; preservar movimentos e usar migrações aditivas.');
}
};
@@ -0,0 +1,85 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('criticality_levels', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->string('code', 30);
$table->string('name', 150);
$table->text('criteria');
$table->boolean('is_critical');
$table->timestamp('created_at');
$table->unique(['tenant_id', 'code']);
});
Schema::create('equipment', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->foreignId('department_id')->constrained()->restrictOnDelete();
$table->foreignId('stock_item_id')->nullable()->constrained()->restrictOnDelete();
$table->foreignId('criticality_level_id')->constrained()->restrictOnDelete();
$table->string('code', 30);
$table->string('description', 255);
$table->string('area', 200);
$table->string('serial_number', 100)->nullable();
$table->string('status', 30);
$table->unsignedInteger('version')->default(1);
$table->timestamps();
$table->unique(['tenant_id', 'code']);
});
Schema::create('spare_parts', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->foreignId('stock_item_id')->unique()->constrained()->restrictOnDelete();
$table->foreignId('criticality_level_id')->constrained()->restrictOnDelete();
$table->unsignedBigInteger('minimum_milli');
$table->unsignedInteger('version')->default(1);
$table->timestamps();
});
Schema::create('equipment_spare_parts', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('equipment_id')->constrained('equipment')->restrictOnDelete();
$table->foreignId('spare_part_id')->constrained()->restrictOnDelete();
$table->foreignId('created_by')->constrained('users')->restrictOnDelete();
$table->timestamp('created_at');
$table->unique(['equipment_id', 'spare_part_id']);
});
Schema::create('technical_reviews', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->restrictOnDelete();
$table->foreignId('requisition_id')->constrained()->restrictOnDelete();
$table->foreignId('reviewed_by')->constrained('users')->restrictOnDelete();
$table->string('basis_hash', 64);
$table->string('decision', 30);
$table->text('reason');
$table->json('snapshot');
$table->timestamp('reviewed_at');
$table->unique(['requisition_id', 'basis_hash']);
});
foreach (['stock_operations', 'stock_movements', 'requisition_items', 'purchase_order_items'] as $name) {
Schema::table($name, function (Blueprint $table) {
$table->foreignId('equipment_id')->nullable()->constrained('equipment')->restrictOnDelete();
$table->foreignId('spare_part_id')->nullable()->constrained()->restrictOnDelete();
});
}
Schema::table('requisition_items', function (Blueprint $table) {
$table->string('production_area', 200)->nullable();
$table->timestamp('downtime_started_at')->nullable();
});
}
public function down(): void
{
throw new LogicException('Preservar cadastro, associações e decisões técnicas; usar migrações aditivas.');
}
};