From c13f418517bb5af6e643b60acca49dcc41884edc Mon Sep 17 00:00:00 2001 From: Ajjj Date: Wed, 5 Aug 2026 16:23:57 +0800 Subject: [PATCH] feat: implement inventory management controllers, dispatch service logic, and frontend inventory tracking dashboard --- .../resources/js/Pages/Bids/Show.tsx | 13 ++++++- .../Http/Controllers/MaterialController.php | 34 ++++++++++++++----- .../Http/Controllers/WarehouseController.php | 12 ++++++- .../resources/js/Pages/Inventory/Index.tsx | 8 ++--- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/Modules/BiddingManagement/resources/js/Pages/Bids/Show.tsx b/Modules/BiddingManagement/resources/js/Pages/Bids/Show.tsx index 4c23082..79ef9c6 100644 --- a/Modules/BiddingManagement/resources/js/Pages/Bids/Show.tsx +++ b/Modules/BiddingManagement/resources/js/Pages/Bids/Show.tsx @@ -23,6 +23,7 @@ import { } from 'lucide-react'; import { PageProps } from '@/types'; import { FormEvent, useMemo, useState } from 'react'; +import { ConfirmModal } from '@/Components/ConfirmModal'; interface Criteria { id: number; name: string; weight: string; description?: string } interface Score { id: number; bid_criteria_id: number; score: string; notes?: string; evaluator: { name: string } } @@ -87,6 +88,7 @@ export default function Show({ package: pkg, contractors }: Props) { const [inviteDialog, setInviteDialog] = useState(false); const [awardDialog, setAwardDialog] = useState(false); const [scoringSubmission, setScoringSubmission] = useState(null); + const [cancelDialog, setCancelDialog] = useState(false); const inviteForm = useForm({ contractor_ids: [] as string[] }); const awardForm = useForm({ submission_id: '', notes: '' }); @@ -234,7 +236,7 @@ export default function Show({ package: pkg, contractors }: Props) { )} {canCancel && ( - )} @@ -667,6 +669,15 @@ export default function Show({ package: pkg, contractors }: Props) { )} + router.post(route('bids.cancel', pkg.ulid))} + /> diff --git a/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php b/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php index b4437c8..f0e6f24 100644 --- a/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php +++ b/Modules/MaterialLogistics/app/Http/Controllers/MaterialController.php @@ -26,7 +26,9 @@ class MaterialController extends Controller // --- Inventory Dashboard --- public function inventory(Request $request) { - $warehouses = Warehouse::active()->select('id', 'ulid', 'name', 'code', 'type') + $warehouses = Warehouse::active() + ->whereIn('id', $this->visibleWarehouseIdsQuery()) + ->select('id', 'ulid', 'name', 'code', 'type') ->with(['stock' => function ($query) { $query->where('quantity', '>', 0); }]) @@ -62,7 +64,8 @@ class MaterialController extends Controller }); // Warehouse stock - $warehouseStockQuery = WarehouseStock::with([ + $warehouseStockQuery = WarehouseStock::whereIn('warehouse_id', $this->visibleWarehouseIdsQuery()) + ->with([ 'warehouse:id,ulid,name,code', 'material' => function ($query) { $query->select('id', 'ulid', 'name', 'sku', 'unit', 'category', 'type') @@ -79,7 +82,8 @@ class MaterialController extends Controller ->withQueryString(); // Project site stock - $siteStockQuery = ProjectInventory::with([ + $siteStockQuery = ProjectInventory::whereIn('project_id', $this->visibleProjectIdsQuery()) + ->with([ 'project:id,ulid,name,code', 'material' => function ($query) { $query->select('id', 'ulid', 'name', 'sku', 'unit', 'category', 'type') @@ -96,11 +100,11 @@ class MaterialController extends Controller // Summary stats $summary = [ - 'total_warehouse' => WarehouseStock::sum('quantity'), + 'total_warehouse' => WarehouseStock::whereIn('warehouse_id', $this->visibleWarehouseIdsQuery())->sum('quantity'), - 'total_onsite' => ProjectInventory::sum('on_hand_qty'), - 'total_reserved' => ProjectInventory::sum('allocated_qty'), - 'total_consumed' => ProjectInventory::sum('consumed_qty'), + 'total_onsite' => ProjectInventory::whereIn('project_id', $this->visibleProjectIdsQuery())->sum('on_hand_qty'), + 'total_reserved' => ProjectInventory::whereIn('project_id', $this->visibleProjectIdsQuery())->sum('allocated_qty'), + 'total_consumed' => ProjectInventory::whereIn('project_id', $this->visibleProjectIdsQuery())->sum('consumed_qty'), ]; return Inertia::render('MaterialLogistics::Inventory/Index', [ @@ -116,7 +120,9 @@ class MaterialController extends Controller // --- Inventory Operations Form Page --- public function operations(Request $request, string $type) { - $warehouses = Warehouse::active()->select('id', 'ulid', 'name', 'code')->get(); + $warehouses = Warehouse::active() + ->whereIn('id', $this->visibleWarehouseIdsQuery()) + ->select('id', 'ulid', 'name', 'code')->get(); $projects = Project::select('id', 'ulid', 'name', 'code') ->with(['personnel' => function ($query) { @@ -377,4 +383,16 @@ class MaterialController extends Controller return back()->with('success', 'Stock adjusted.'); } + + private function visibleProjectIdsQuery() + { + return Project::query()->select('projects.id'); + } + + private function visibleWarehouseIdsQuery() + { + return Warehouse::query() + ->whereIn('project_id', $this->visibleProjectIdsQuery()) + ->select('warehouses.id'); + } } diff --git a/Modules/MaterialLogistics/app/Http/Controllers/WarehouseController.php b/Modules/MaterialLogistics/app/Http/Controllers/WarehouseController.php index ba2c059..4f8ec0d 100644 --- a/Modules/MaterialLogistics/app/Http/Controllers/WarehouseController.php +++ b/Modules/MaterialLogistics/app/Http/Controllers/WarehouseController.php @@ -7,12 +7,13 @@ use Illuminate\Http\Request; use Inertia\Inertia; use Modules\MaterialLogistics\Models\InventoryMovement; use Modules\MaterialLogistics\Models\Warehouse; +use Modules\ProjectManagement\Models\Project; class WarehouseController extends Controller { public function index(Request $request) { - $query = Warehouse::query(); + $query = Warehouse::whereIn('id', $this->visibleWarehouseIdsQuery()); if ($search = $request->search) { $query->where(function ($q) use ($search) { @@ -40,6 +41,8 @@ class WarehouseController extends Controller public function show(Request $request, Warehouse $warehouse) { + abort_unless($this->visibleWarehouseIdsQuery()->where('warehouses.id', $warehouse->id)->exists(), 403); + // Paginated stock with search & category filter $stockQuery = $warehouse->stock()->with([ 'material:id,ulid,name,sku,unit,category,type', @@ -94,6 +97,13 @@ class WarehouseController extends Controller ]); } + private function visibleWarehouseIdsQuery() + { + return Warehouse::query() + ->whereIn('project_id', Project::query()->select('projects.id')) + ->select('warehouses.id'); + } + public function store(Request $request) { $validated = $request->validate([ diff --git a/Modules/MaterialLogistics/resources/js/Pages/Inventory/Index.tsx b/Modules/MaterialLogistics/resources/js/Pages/Inventory/Index.tsx index 6581634..c429479 100644 --- a/Modules/MaterialLogistics/resources/js/Pages/Inventory/Index.tsx +++ b/Modules/MaterialLogistics/resources/js/Pages/Inventory/Index.tsx @@ -128,7 +128,7 @@ export default function Index({ warehouses, projects, summary }: Props) { {/* Location Grids */}
{activeTab === 'warehouse' && ( -
+
{warehouses.length === 0 ? (
@@ -138,7 +138,7 @@ export default function Index({ warehouses, projects, summary }: Props) { ) : ( warehouses.map((wh) => ( - +
@@ -173,7 +173,7 @@ export default function Index({ warehouses, projects, summary }: Props) { )} {activeTab === 'site' && ( -
+
{projects.length === 0 ? (
@@ -183,7 +183,7 @@ export default function Index({ warehouses, projects, summary }: Props) { ) : ( projects.map((proj) => ( - +