diff --git a/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php b/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php
index fa0e666..40ebe85 100644
--- a/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php
+++ b/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php
@@ -3,17 +3,18 @@
namespace Modules\MaterialLogistics\Http\Controllers;
use App\Http\Controllers\Controller;
+use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Modules\MasterData\Models\Material;
+use Modules\MaterialLogistics\Models\InventoryBatch;
use Modules\MaterialLogistics\Models\ProjectInventory;
use Modules\MaterialLogistics\Models\Warehouse;
use Modules\MaterialLogistics\Models\WarehouseStock;
use Modules\MaterialLogistics\Services\WarehouseService;
use Modules\ProjectManagement\Models\Project;
use Modules\ProjectManagement\Models\Task;
-use Modules\MaterialLogistics\Models\InventoryBatch;
class MaterialController extends Controller
{
@@ -64,7 +65,7 @@ class MaterialController extends Controller
'warehouse:id,ulid,name,code',
'material' => function ($query) {
$query->select('id', 'ulid', 'name', 'sku', 'unit', 'category', 'type')
- ->with('components.component:id,name,unit');
+ ->with('components.component:id,name,unit');
},
]);
@@ -81,7 +82,7 @@ class MaterialController extends Controller
'project:id,ulid,name,code',
'material' => function ($query) {
$query->select('id', 'ulid', 'name', 'sku', 'unit', 'category', 'type')
- ->with('components.component:id,name,unit');
+ ->with('components.component:id,name,unit');
},
]);
@@ -115,14 +116,34 @@ class MaterialController extends Controller
public function operations(Request $request, string $type)
{
$warehouses = Warehouse::active()->select('id', 'ulid', 'name', 'code')->get();
- $projects = Project::select('id', 'ulid', 'name', 'code')->latest()->get();
+
+ $projects = Project::select('id', 'ulid', 'name', 'code')
+ ->with(['personnel' => function ($query) {
+ $query->wherePivot('role', 'pm')->select('users.id', 'users.name');
+ }])
+ ->latest()
+ ->get()
+ ->map(function ($p) {
+ $pm = $p->personnel->first();
+
+ return [
+ 'id' => $p->id,
+ 'ulid' => $p->ulid,
+ 'name' => $p->name,
+ 'code' => $p->code,
+ 'pm' => $pm ? [
+ 'id' => $pm->id,
+ 'name' => $pm->name,
+ ] : null,
+ ];
+ });
// Materials with their warehouse/site stocks to enforce limits
$materials = Material::select('id', 'ulid', 'name', 'sku', 'unit', 'unit_cost')
->with(['warehouseStocks', 'projectInventories'])
->get();
-
- $projectManagers = \App\Models\User::role('project_manager')->select('id', 'name')->get();
+
+ $projectManagers = User::role('Project Manager')->select('id', 'name')->get();
return Inertia::render('MaterialLogistics::Inventory/OperationsForm', [
'warehouses' => $warehouses,
@@ -155,33 +176,32 @@ class MaterialController extends Controller
foreach ($validated['items'] as $item) {
$material = Material::where('ulid', $item['material_ulid'])->firstOrFail();
$batchId = null;
-
- if (!empty($item['inventory_batch_ulid'])) {
+
+ if (! empty($item['inventory_batch_ulid'])) {
$batchId = InventoryBatch::where('ulid', $item['inventory_batch_ulid'])->firstOrFail()->id;
}
-
+
$this->warehouseService->dispatchToProject(
- warehouse: $warehouse,
- project: $project,
+ warehouse: $warehouse,
+ project: $project,
material: $material,
quantity: $item['quantity'],
inventoryBatchId: $batchId,
- performedBy: auth()->id(),
- dispatchedBy: $validated['handler_id'],
+ performedBy: auth()->id(),
+ dispatchedBy: $validated['handler_id'],
notes: $validated['notes'] ?? null,
);
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
+
return back()->with('error', $e->getMessage());
}
return back()->with('success', "Materials dispatched to {$project->name}.");
}
-
-
// --- Reserve for Task ---
public function reserveForTask(Request $request)
{
diff --git a/Modules/MaterialLogistics/app/Http/Controllers/MaterialTransferController.php b/Modules/MaterialLogistics/app/Http/Controllers/MaterialTransferController.php
index 6a7984d..35058d6 100644
--- a/Modules/MaterialLogistics/app/Http/Controllers/MaterialTransferController.php
+++ b/Modules/MaterialLogistics/app/Http/Controllers/MaterialTransferController.php
@@ -179,7 +179,7 @@ class MaterialTransferController extends Controller
{
abort_if($materialTransfer->status !== MaterialTransferStatus::Draft, 403, 'Only draft transfers can be submitted.');
- $approverIds = \App\Models\User::role('project_manager')->pluck('id')->toArray();
+ $approverIds = \App\Models\User::role('Project Manager')->pluck('id')->toArray();
if (empty($approverIds)) {
$approverIds = [1]; // Fallback to admin/first user
}
diff --git a/Modules/MaterialLogistics/resources/js/Pages/Inventory/OperationsForm.tsx b/Modules/MaterialLogistics/resources/js/Pages/Inventory/OperationsForm.tsx
index a4a2c20..f3d957b 100644
--- a/Modules/MaterialLogistics/resources/js/Pages/Inventory/OperationsForm.tsx
+++ b/Modules/MaterialLogistics/resources/js/Pages/Inventory/OperationsForm.tsx
@@ -13,7 +13,7 @@ import {
ArrowDownToLine, ArrowRightLeft, ArrowUpFromLine, RotateCcw,
ChevronLeft, Loader2, Trash2, Search, PackageOpen
} from 'lucide-react';
-import { FormEvent, useMemo, useState } from 'react';
+import { FormEvent, useMemo, useState, useEffect } from 'react';
interface WarehouseStock { warehouse_id: number; quantity: string; }
interface ProjectInventory { project_id: number; on_hand_qty: string; allocated_qty: string; }
@@ -24,7 +24,13 @@ interface MaterialRef {
project_inventories: ProjectInventory[];
}
interface WarehouseRef { id: number; ulid: string; name: string; code: string; }
-interface ProjectRef { id: number; ulid: string; name: string; code: string; }
+interface ProjectRef {
+ id: number;
+ ulid: string;
+ name: string;
+ code: string;
+ pm?: { id: number; name: string } | null;
+}
interface UserRef { id: number; name: string; }
interface Props extends PageProps {
@@ -147,7 +153,51 @@ export default function OperationsForm({ warehouses, projects, materials, projec
const warehouseItems = useMemo(() => warehouses.map(w => ({ value: w.ulid, label: `${w.name} (${w.code})` })), [warehouses]);
const projectItems = useMemo(() => projects.map(p => ({ value: p.ulid, label: `${p.name} (${p.code})` })), [projects]);
- const personnelItems = useMemo(() => projectManagers.map(p => ({ value: String(p.id), label: p.name })), [projectManagers]);
+
+ const selectedProject = useMemo(() => {
+ const pUlid = defaultType === 'dispatch' || defaultType === 'return' ? data.project_ulid : (data.location_type === 'project' ? data.location_ulid : '');
+ return projects.find(p => p.ulid === pUlid);
+ }, [projects, data.project_ulid, data.location_ulid, data.location_type, defaultType]);
+
+ const mergedProjectManagers = useMemo(() => {
+ const managers = [...projectManagers];
+ if (selectedProject?.pm) {
+ const exists = managers.some(m => m.id === selectedProject.pm?.id);
+ if (!exists) {
+ managers.push(selectedProject.pm);
+ }
+ }
+ return managers;
+ }, [projectManagers, selectedProject]);
+
+ const personnelItems = useMemo(() => mergedProjectManagers.map(p => ({ value: String(p.id), label: p.name })), [mergedProjectManagers]);
+
+ const handleProjectSelection = (projectUlid: string | null, formField: 'project_ulid' | 'location_ulid') => {
+ if (!projectUlid) {
+ setData(prev => ({
+ ...prev,
+ [formField]: '',
+ }));
+ return;
+ }
+ const project = projects.find(p => p.ulid === projectUlid);
+ const pmId = project?.pm?.id ? String(project.pm.id) : '';
+ setData(prev => ({
+ ...prev,
+ [formField]: projectUlid,
+ handler_id: pmId || prev.handler_id
+ }));
+ };
+
+ useEffect(() => {
+ const pUlid = defaultType === 'dispatch' || defaultType === 'return' ? data.project_ulid : (data.location_type === 'project' ? data.location_ulid : '');
+ if (pUlid) {
+ const project = projects.find(p => p.ulid === pUlid);
+ if (project?.pm?.id && !data.handler_id) {
+ setData('handler_id', String(project.pm.id));
+ }
+ }
+ }, [projects, defaultType]);
// Filter materials for catalog
const filteredMaterials = useMemo(() => {
@@ -222,7 +272,7 @@ export default function OperationsForm({ warehouses, projects, materials, projec
-