chore: update document approval workflow and bug fixes
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?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
|
||||
{
|
||||
// Master material catalog
|
||||
Schema::create('materials', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('sku')->unique()->nullable();
|
||||
$table->string('category')->nullable(); // cement, steel, lumber, aggregate, etc.
|
||||
$table->string('unit'); // pcs, kg, cubic_meter, bag, etc.
|
||||
$table->decimal('unit_cost', 12, 2)->default(0);
|
||||
$table->text('description')->nullable();
|
||||
$table->string('status')->default('active'); // active, discontinued
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Per-project inventory tracking
|
||||
Schema::create('project_inventories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->decimal('on_hand_qty', 12, 2)->default(0);
|
||||
$table->decimal('allocated_qty', 12, 2)->default(0);
|
||||
$table->decimal('consumed_qty', 12, 2)->default(0);
|
||||
$table->decimal('unit_cost', 12, 2)->default(0); // cost at time of booking
|
||||
$table->timestamps();
|
||||
$table->unique(['project_id', 'material_id']);
|
||||
});
|
||||
|
||||
// Material requirements per task
|
||||
Schema::create('material_requirements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('task_id')->nullable()->constrained('tasks')->nullOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->decimal('required_qty', 12, 2);
|
||||
$table->decimal('unit_cost_at_booking', 12, 2)->default(0);
|
||||
$table->string('status')->default('pending'); // pending, allocated, fulfilled
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Material deployment tracking (request → dispatch → deliver → consume)
|
||||
Schema::create('material_deployments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('requested_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->decimal('quantity', 12, 2);
|
||||
$table->string('status')->default('requested'); // requested, pending_dispatch, in_transit, delivered, consumed, cancelled
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamp('dispatched_at')->nullable();
|
||||
$table->timestamp('delivered_at')->nullable();
|
||||
$table->timestamp('consumed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('material_deployments');
|
||||
Schema::dropIfExists('material_requirements');
|
||||
Schema::dropIfExists('project_inventories');
|
||||
Schema::dropIfExists('materials');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
<?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('material_groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->string('name');
|
||||
$table->text('description')->nullable();
|
||||
$table->string('status')->default('active'); // active, archived
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('material_group_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('material_group_id')->constrained('material_groups')->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['material_group_id', 'material_id']);
|
||||
});
|
||||
|
||||
Schema::create('project_material_groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_group_id')->constrained('material_groups')->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['project_id', 'material_group_id']);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('project_material_groups');
|
||||
Schema::dropIfExists('material_group_items');
|
||||
Schema::dropIfExists('material_groups');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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('material_requisitions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('document_number')->unique();
|
||||
$table->string('status')->default('draft'); // draft, submitted, approved, rejected
|
||||
$table->foreignId('requested_by')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('approved_at')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('material_requisition_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('material_requisition_id')->constrained('material_requisitions')->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->decimal('quantity', 12, 2);
|
||||
$table->decimal('unit_cost', 12, 2)->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('purchase_orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->foreignId('project_id')->constrained()->cascadeOnDelete();
|
||||
$table->string('document_number')->unique();
|
||||
$table->string('supplier')->nullable();
|
||||
$table->string('status')->default('draft'); // draft, submitted, approved, rejected
|
||||
$table->foreignId('requested_by')->constrained('users')->cascadeOnDelete();
|
||||
$table->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->timestamp('approved_at')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('purchase_order_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('purchase_order_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_requisition_item_id')->nullable()
|
||||
->constrained('material_requisition_items')->nullOnDelete();
|
||||
$table->decimal('quantity', 12, 2);
|
||||
$table->decimal('unit_cost', 12, 2)->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Pivot: 1:many MR → PO relationship
|
||||
Schema::create('purchase_order_requisitions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('purchase_order_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_requisition_id')->constrained('material_requisitions')->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
$table->unique(['purchase_order_id', 'material_requisition_id'], 'po_mr_unique');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('purchase_order_requisitions');
|
||||
Schema::dropIfExists('purchase_order_items');
|
||||
Schema::dropIfExists('purchase_orders');
|
||||
Schema::dropIfExists('material_requisition_items');
|
||||
Schema::dropIfExists('material_requisitions');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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
|
||||
{
|
||||
// Warehouse locations (main, yard, staging, etc.)
|
||||
Schema::create('warehouses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->string('name');
|
||||
$table->string('code')->unique();
|
||||
$table->text('address')->nullable();
|
||||
$table->string('type')->default('main'); // main, yard, staging
|
||||
$table->string('status')->default('active'); // active, inactive
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// Per-material stock per warehouse
|
||||
Schema::create('warehouse_stock', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->decimal('quantity', 14, 2)->default(0);
|
||||
$table->decimal('reserved_qty', 14, 2)->default(0);
|
||||
$table->decimal('unit_cost', 12, 2)->default(0);
|
||||
$table->timestamps();
|
||||
$table->unique(['warehouse_id', 'material_id']);
|
||||
});
|
||||
|
||||
// Full movement ledger — every qty change is a row
|
||||
Schema::create('inventory_movements', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('warehouse_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('project_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->foreignId('task_id')->nullable()->constrained()->nullOnDelete();
|
||||
$table->string('movement_type'); // purchase_receipt, dispatch, site_receipt, reserve, allocate, consume, return, adjustment
|
||||
$table->string('direction'); // in, out
|
||||
$table->decimal('quantity', 14, 2);
|
||||
$table->decimal('balance_after', 14, 2)->default(0);
|
||||
$table->string('from_location_type')->nullable(); // warehouse, project, task, external
|
||||
$table->unsignedBigInteger('from_location_id')->nullable();
|
||||
$table->string('to_location_type')->nullable();
|
||||
$table->unsignedBigInteger('to_location_id')->nullable();
|
||||
$table->nullableMorphs('reference'); // links to PO, MR, etc.
|
||||
$table->foreignId('performed_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
|
||||
$table->index(['material_id', 'movement_type']);
|
||||
$table->index(['warehouse_id', 'created_at']);
|
||||
$table->index(['project_id', 'created_at']);
|
||||
});
|
||||
|
||||
// PO payment tracking
|
||||
Schema::table('purchase_orders', function (Blueprint $table) {
|
||||
$table->string('payment_status')->default('unpaid')->after('status'); // unpaid, partial, paid
|
||||
$table->timestamp('paid_at')->nullable()->after('approved_at');
|
||||
$table->string('receipt_path')->nullable()->after('notes');
|
||||
$table->string('receipt_original_name')->nullable()->after('receipt_path');
|
||||
});
|
||||
|
||||
// Task execution area
|
||||
if (!Schema::hasColumn('tasks', 'area')) {
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
$table->string('area')->nullable()->after('description');
|
||||
});
|
||||
}
|
||||
|
||||
// Project inventory: floated qty tracking
|
||||
if (!Schema::hasColumn('project_inventories', 'floated_qty')) {
|
||||
Schema::table('project_inventories', function (Blueprint $table) {
|
||||
$table->decimal('floated_qty', 12, 2)->default(0)->after('on_hand_qty');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('project_inventories', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('project_inventories', 'floated_qty')) {
|
||||
$table->dropColumn('floated_qty');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('tasks', 'area')) {
|
||||
$table->dropColumn('area');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('purchase_orders', function (Blueprint $table) {
|
||||
$table->dropColumn(['payment_status', 'paid_at', 'receipt_path', 'receipt_original_name']);
|
||||
});
|
||||
|
||||
Schema::dropIfExists('inventory_movements');
|
||||
Schema::dropIfExists('warehouse_stock');
|
||||
Schema::dropIfExists('warehouses');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('inventory_movements', function (Blueprint $table) {
|
||||
$table->foreignId('dispatched_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('received_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('returned_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
$table->foreignId('adjusted_by')->nullable()->constrained('users')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('inventory_movements', function (Blueprint $table) {
|
||||
$table->dropForeign(['dispatched_by']);
|
||||
$table->dropForeign(['received_by']);
|
||||
$table->dropForeign(['returned_by']);
|
||||
$table->dropForeign(['adjusted_by']);
|
||||
|
||||
$table->dropColumn([
|
||||
'dispatched_by',
|
||||
'received_by',
|
||||
'returned_by',
|
||||
'adjusted_by',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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::table('material_requisitions', function (Blueprint $table) {
|
||||
$table->dropForeign(['project_id']);
|
||||
$table->dropColumn('project_id');
|
||||
});
|
||||
|
||||
Schema::table('purchase_orders', function (Blueprint $table) {
|
||||
$table->dropForeign(['project_id']);
|
||||
$table->dropColumn('project_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('material_requisitions', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->nullable()->constrained()->cascadeOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('purchase_orders', function (Blueprint $table) {
|
||||
$table->foreignId('project_id')->nullable()->constrained()->cascadeOnDelete();
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?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('inventory_batches', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('ulid', 26)->unique();
|
||||
$table->foreignId('warehouse_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('material_requisition_id')->nullable()->constrained('material_requisitions')->nullOnDelete();
|
||||
$table->foreignId('purchase_order_id')->nullable()->constrained('purchase_orders')->nullOnDelete();
|
||||
$table->decimal('quantity', 12, 2)->default(0);
|
||||
$table->decimal('unit_cost', 12, 2)->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('inventory_batches');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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::table('inventory_movements', function (Blueprint $table) {
|
||||
$table->foreignId('inventory_batch_id')->nullable()->constrained('inventory_batches')->nullOnDelete();
|
||||
});
|
||||
|
||||
Schema::table('material_deployments', function (Blueprint $table) {
|
||||
$table->foreignId('material_requisition_id')->nullable()->constrained('material_requisitions')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('inventory_movements', function (Blueprint $table) {
|
||||
$table->dropForeign(['inventory_batch_id']);
|
||||
$table->dropColumn('inventory_batch_id');
|
||||
});
|
||||
|
||||
Schema::table('material_deployments', function (Blueprint $table) {
|
||||
$table->dropForeign(['material_requisition_id']);
|
||||
$table->dropColumn('material_requisition_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
<?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::table('warehouses', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('warehouses', 'project_id')) {
|
||||
$table->foreignId('project_id')->nullable()->after('id')->constrained()->nullOnDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('warehouses', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('warehouses', 'project_id')) {
|
||||
$table->dropForeign(['project_id']);
|
||||
$table->dropColumn('project_id');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
<?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('material_transfers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('transfer_number')->unique();
|
||||
$table->foreignId('from_project_id')->constrained('projects');
|
||||
$table->foreignId('to_project_id')->constrained('projects');
|
||||
$table->string('status')->default('draft');
|
||||
|
||||
$table->foreignId('requested_by')->constrained('users');
|
||||
$table->foreignId('approved_by')->nullable()->constrained('users');
|
||||
$table->foreignId('dispatched_by')->nullable()->constrained('users');
|
||||
$table->foreignId('received_by')->nullable()->constrained('users');
|
||||
|
||||
$table->text('notes')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('material_transfer_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('material_transfer_id')->constrained('material_transfers')->cascadeOnDelete();
|
||||
$table->foreignId('material_id')->constrained('materials');
|
||||
|
||||
$table->decimal('requested_quantity', 10, 2);
|
||||
$table->decimal('dispatched_quantity', 10, 2)->default(0);
|
||||
$table->decimal('received_quantity', 10, 2)->default(0);
|
||||
$table->decimal('damaged_quantity', 10, 2)->default(0);
|
||||
$table->decimal('lost_quantity', 10, 2)->default(0);
|
||||
|
||||
$table->text('notes')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('material_transfer_items');
|
||||
Schema::dropIfExists('material_transfers');
|
||||
}
|
||||
};
|
||||
0
Modules/MaterialLogistics/database/seeders/.gitkeep
Normal file
0
Modules/MaterialLogistics/database/seeders/.gitkeep
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MaterialLogistics\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\MasterData\Models\Material;
|
||||
|
||||
class ConstructionMaterialsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$materials = [
|
||||
// Concrete & Cement
|
||||
['name' => 'Portland Cement Type I', 'category' => 'Concrete & Cement', 'unit' => 'Bag (40kg)', 'unit_cost' => 8.50, 'description' => 'General purpose portland cement.'] ,
|
||||
['name' => 'Portland Cement Type III', 'category' => 'Concrete & Cement', 'unit' => 'Bag (40kg)', 'unit_cost' => 10.00, 'description' => 'High early strength portland cement.'],
|
||||
['name' => 'Ready Mix Concrete (20 MPa)', 'category' => 'Concrete & Cement', 'unit' => 'Cubic Meter', 'unit_cost' => 110.00, 'description' => 'Standard strength ready mix concrete.'],
|
||||
['name' => 'Ready Mix Concrete (30 MPa)', 'category' => 'Concrete & Cement', 'unit' => 'Cubic Meter', 'unit_cost' => 130.00, 'description' => 'High strength ready mix concrete.'],
|
||||
['name' => 'Concrete Blocks (Hollow) 8x8x16', 'category' => 'Concrete & Cement', 'unit' => 'Piece', 'unit_cost' => 1.50, 'description' => 'Standard 8-inch hollow concrete block.'],
|
||||
|
||||
// Aggregates
|
||||
['name' => 'River Sand', 'category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 20.00, 'description' => 'Washed river sand for mixing.'],
|
||||
['name' => 'Crushed Stone (Gravel) 3/4"', 'category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 25.00, 'description' => '3/4 inch curshed limestone base.'],
|
||||
['name' => 'Pea Gravel', 'category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 30.00, 'description' => 'Smooth round pea gravel.'],
|
||||
['name' => 'Base Gravel (Class 5)', 'category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 18.00, 'description' => 'Class 5 aggregate base course material.'],
|
||||
|
||||
// Steel & Rebar
|
||||
['name' => 'Rebar #4 (1/2" x 20 ft)', 'category' => 'Steel & Metal', 'unit' => 'Piece', 'unit_cost' => 6.20, 'description' => 'Standard #4 reinforcing bar, 20 feet length.'],
|
||||
['name' => 'Rebar #5 (5/8" x 20 ft)', 'category' => 'Steel & Metal', 'unit' => 'Piece', 'unit_cost' => 9.50, 'description' => 'Standard #5 reinforcing bar.'],
|
||||
['name' => 'Wire Mesh (6x6 W1.4/W1.4) 150ft Roll', 'category' => 'Steel & Metal', 'unit' => 'Roll', 'unit_cost' => 125.00, 'description' => 'Welded wire mesh for concrete slabs.'],
|
||||
['name' => 'Structural Steel Beam (W8x10)', 'category' => 'Steel & Metal', 'unit' => 'Foot', 'unit_cost' => 15.00, 'description' => 'W8x10 wide flange steel beam.'],
|
||||
['name' => 'Tie Wire (Black Annealed) 16 Gauge', 'category' => 'Steel & Metal', 'unit' => 'Roll (3 lb)', 'unit_cost' => 8.00, 'description' => 'Tie wire for rebar.'],
|
||||
|
||||
// Timber & Wood
|
||||
['name' => '2x4 Framing Lumber (8 ft)', 'category' => 'Timber & Wood', 'unit' => 'Piece', 'unit_cost' => 3.50, 'description' => 'Standard SPF 2x4 framing stud.'],
|
||||
['name' => '2x6 Framing Lumber (10 ft)', 'category' => 'Timber & Wood', 'unit' => 'Piece', 'unit_cost' => 6.00, 'description' => 'Standard 2x6 timber.'],
|
||||
['name' => 'Plywood (CDX) 15/32" (4x8 ft)', 'category' => 'Timber & Wood', 'unit' => 'Sheet', 'unit_cost' => 22.00, 'description' => 'CDX structural plywood panel.'],
|
||||
['name' => 'OSB Sheathing 7/16" (4x8 ft)', 'category' => 'Timber & Wood', 'unit' => 'Sheet', 'unit_cost' => 14.50, 'description' => 'Oriented strand board.'],
|
||||
['name' => 'Treated Timber 4x4 (8 ft)', 'category' => 'Timber & Wood', 'unit' => 'Piece', 'unit_cost' => 11.20, 'description' => 'Pressure treated 4x4 post.'],
|
||||
|
||||
// Masonry & Bricks
|
||||
['name' => 'Red Clay Brick', 'category' => 'Masonry', 'unit' => 'Thousand', 'unit_cost' => 450.00, 'description' => 'Standard solid red clay brick.'],
|
||||
['name' => 'Mortar Mix (Type S)', 'category' => 'Masonry', 'unit' => 'Bag (80 lb)', 'unit_cost' => 9.00, 'description' => 'High strength masonry mortar.'],
|
||||
['name' => 'Grout Mix (Non-Shrink)', 'category' => 'Masonry', 'unit' => 'Bag (50 lb)', 'unit_cost' => 14.00, 'description' => 'Structural non-shrink grout.'],
|
||||
|
||||
// Roofing
|
||||
['name' => 'Asphalt Shingles (Architectural)', 'category' => 'Roofing', 'unit' => 'Bundle (33 sq ft)', 'unit_cost' => 35.00, 'description' => 'High quality architectural asphalt shingle.'],
|
||||
['name' => 'Roofing Felt (15 lb, 400 sq ft)', 'category' => 'Roofing', 'unit' => 'Roll', 'unit_cost' => 20.00, 'description' => 'Asphalt saturated roofing felt base.'],
|
||||
['name' => 'Corrugated Metal Roof Panel (12 ft)', 'category' => 'Roofing', 'unit' => 'Piece', 'unit_cost' => 28.00, 'description' => 'Galvanized iron corrugated sheet.'],
|
||||
['name' => 'Roofing Nails (1-1/4", 50 lb)', 'category' => 'Roofing', 'unit' => 'Box', 'unit_cost' => 45.00, 'description' => 'Galvanized roofing nails.'],
|
||||
|
||||
// Insulation & Drywall
|
||||
['name' => 'Fiberglass Insulation (R-13) 15" x 32\'', 'category' => 'Insulation', 'unit' => 'Roll', 'unit_cost' => 22.00, 'description' => 'Unfaced fiberglass insulation batt.'],
|
||||
['name' => 'Rigid Foam Insulation 1" (4x8 ft)', 'category' => 'Insulation', 'unit' => 'Sheet', 'unit_cost' => 16.50, 'description' => 'Extruded polystyrene (XPS) rigid foam.'],
|
||||
['name' => 'Drywall (Gypsum Board) 1/2" (4x8 ft)', 'category' => 'Drywall', 'unit' => 'Sheet', 'unit_cost' => 13.00, 'description' => 'Standard interior drywall.'],
|
||||
['name' => 'Joint Compound (All-Purpose) 4.5 Gal', 'category' => 'Drywall', 'unit' => 'Bucket', 'unit_cost' => 16.00, 'description' => 'Ready-mixed joint compound.'],
|
||||
['name' => 'Drywall Screws (1-1/4", 5 lb)', 'category' => 'Drywall', 'unit' => 'Box', 'unit_cost' => 18.00, 'description' => 'Coarse thread phosphate drywall screws.'],
|
||||
|
||||
// Plumbing & Piping
|
||||
['name' => 'PVC Pipe SCH 40 (2" x 10 ft)', 'category' => 'Plumbing', 'unit' => 'Piece', 'unit_cost' => 15.00, 'description' => 'Schedule 40 PVC pipe.'],
|
||||
['name' => 'PVC Pipe SCH 40 (4" x 10 ft)', 'category' => 'Plumbing', 'unit' => 'Piece', 'unit_cost' => 26.00, 'description' => 'Schedule 40 PVC pipe for drainage.'],
|
||||
['name' => 'Copper Pipe Type L (1/2" x 10 ft)', 'category' => 'Plumbing', 'unit' => 'Piece', 'unit_cost' => 24.00, 'description' => 'Standard soft/hard copper piping.'],
|
||||
['name' => 'PVC Cement & Primer Pack', 'category' => 'Plumbing', 'unit' => 'Pack', 'unit_cost' => 12.00, 'description' => 'Clear PVC cement and purple primer.'],
|
||||
|
||||
// Electrical
|
||||
['name' => 'NM-B Wire 12/2 (250 ft Roll)', 'category' => 'Electrical', 'unit' => 'Roll', 'unit_cost' => 85.00, 'description' => 'Romex 12/2 residential wire w/ ground.'],
|
||||
['name' => 'THHN Wire 10 AWG (500 ft Spool)', 'category' => 'Electrical', 'unit' => 'Spool', 'unit_cost' => 115.00, 'description' => 'Stranded copper building wire.'],
|
||||
['name' => 'Electrical Box (1-Gang, 18 cu in)', 'category' => 'Electrical', 'unit' => 'Piece', 'unit_cost' => 0.85, 'description' => 'Nail-on PVC electrical box.'],
|
||||
['name' => 'Conduit PVC SCH 40 (3/4" x 10 ft)', 'category' => 'Electrical', 'unit' => 'Piece', 'unit_cost' => 3.50, 'description' => 'Electrical PVC conduit.'],
|
||||
|
||||
// Paint & Finishes
|
||||
['name' => 'Latex Primer (Interior) 5 Gal', 'category' => 'Paint', 'unit' => 'Bucket', 'unit_cost' => 55.00, 'description' => 'White interior latex drywall primer.'],
|
||||
['name' => 'Interior Flat Paint (White) 5 Gal', 'category' => 'Paint', 'unit' => 'Bucket', 'unit_cost' => 70.00, 'description' => 'Professional grade flat white.'],
|
||||
['name' => 'Exterior Semi-Gloss Paint 5 Gal', 'category' => 'Paint', 'unit' => 'Bucket', 'unit_cost' => 95.00, 'description' => 'Durable exterior grade semi-gloss paint.'],
|
||||
|
||||
// Consumables & Tools
|
||||
['name' => 'Construction Adhesive (10 oz)', 'category' => 'Consumables', 'unit' => 'Tube', 'unit_cost' => 4.50, 'description' => 'Heavy duty polyurethane adhesive.'],
|
||||
['name' => 'Painter\'s Tape (1.88" x 60 yds)', 'category' => 'Consumables', 'unit' => 'Roll', 'unit_cost' => 6.00, 'description' => 'Blue masking tape.'],
|
||||
['name' => 'Work Gloves (Leather Palm)', 'category' => 'PPE', 'unit' => 'Pair', 'unit_cost' => 3.00, 'description' => 'Standard heavy-duty work gloves.'],
|
||||
['name' => 'Safety Glasses', 'category' => 'PPE', 'unit' => 'Piece', 'unit_cost' => 4.00, 'description' => 'Clear polycarbonate safety glasses.'],
|
||||
];
|
||||
|
||||
foreach ($materials as $materialData) {
|
||||
$baseSku = strtoupper(substr($materialData['category'], 0, 3)) . '-' . rand(1000, 9999);
|
||||
// Ensure unique sku
|
||||
while(Material::where('sku', $baseSku)->exists()) {
|
||||
$baseSku = strtoupper(substr($materialData['category'], 0, 3)) . '-' . rand(1000, 9999);
|
||||
}
|
||||
|
||||
Material::firstOrCreate(
|
||||
['name' => $materialData['name']],
|
||||
[
|
||||
'sku' => $baseSku,
|
||||
'category' => $materialData['category'],
|
||||
'unit' => $materialData['unit'],
|
||||
'unit_cost' => $materialData['unit_cost'],
|
||||
'description' => $materialData['description'],
|
||||
'status' => 'active'
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\MaterialLogistics\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MaterialLogisticsDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
ConstructionMaterialsSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user