2025-12-30 15:03:19 +08:00
< ? php
2026-01-26 10:37:47 +08:00
namespace App\Modules\Inventory\Controllers ;
use App\Http\Controllers\Controller ;
2025-12-30 15:03:19 +08:00
use Illuminate\Http\Request ;
2026-01-26 10:37:47 +08:00
use App\Modules\Inventory\Models\Warehouse ;
2025-12-30 15:03:19 +08:00
use Inertia\Inertia ;
class WarehouseController extends Controller
{
public function index ( Request $request )
{
$query = Warehouse :: query ();
if ( $request -> has ( 'search' )) {
$search = $request -> input ( 'search' );
$query -> where ( function ( $q ) use ( $search ) {
$q -> where ( 'name' , 'like' , " % { $search } % " )
-> orWhere ( 'code' , 'like' , " % { $search } % " );
});
}
2026-01-26 14:59:24 +08:00
$warehouses = $query -> withSum ( 'inventories as book_stock' , 'quantity' ) // 帳面庫存 = 所有庫存總和
-> withSum ([ 'inventories as available_stock' => function ( $query ) {
2026-01-27 10:23:49 +08:00
// 可用庫存 = 庫存 > 0 且 品質正常 且 (未過期 或 無效期) 且 倉庫類型不為瑕疵倉
2026-01-26 14:59:24 +08:00
$query -> where ( 'quantity' , '>' , 0 )
-> where ( 'quality_status' , 'normal' )
2026-01-27 10:23:49 +08:00
-> whereHas ( 'warehouse' , function ( $q ) {
$q -> where ( 'type' , '!=' , \App\Enums\WarehouseType :: QUARANTINE );
})
2026-01-26 14:59:24 +08:00
-> where ( function ( $q ) {
$q -> whereNull ( 'expiry_date' )
-> orWhere ( 'expiry_date' , '>=' , now ());
});
}], 'quantity' )
2026-02-02 11:03:09 +08:00
-> addSelect ([ 'low_stock_count' => function ( $query ) {
$query -> selectRaw ( 'count(*)' )
-> from ( 'warehouse_product_safety_stocks as ss' )
-> whereColumn ( 'ss.warehouse_id' , 'warehouses.id' )
-> whereRaw ( '(SELECT COALESCE(SUM(quantity), 0) FROM inventories WHERE warehouse_id = ss.warehouse_id AND product_id = ss.product_id) < ss.safety_stock' );
}])
2025-12-30 15:03:19 +08:00
-> orderBy ( 'created_at' , 'desc' )
-> paginate ( 10 )
-> withQueryString ();
2026-01-27 10:23:49 +08:00
// 移除原本對 is_sellable 的手動修正邏輯,現在由 type 自動過濾
2026-01-26 14:59:24 +08:00
// 計算全域總計 (不分頁)
$totals = [
'available_stock' => \App\Modules\Inventory\Models\Inventory :: where ( 'quantity' , '>' , 0 )
-> where ( 'quality_status' , 'normal' )
-> whereHas ( 'warehouse' , function ( $q ) {
2026-01-27 10:23:49 +08:00
$q -> where ( 'type' , '!=' , \App\Enums\WarehouseType :: QUARANTINE );
2026-01-26 14:59:24 +08:00
})
-> where ( function ( $q ) {
$q -> whereNull ( 'expiry_date' )
-> orWhere ( 'expiry_date' , '>=' , now ());
}) -> sum ( 'quantity' ),
'book_stock' => \App\Modules\Inventory\Models\Inventory :: sum ( 'quantity' ),
];
2025-12-30 15:03:19 +08:00
return Inertia :: render ( 'Warehouse/Index' , [
'warehouses' => $warehouses ,
2026-01-26 14:59:24 +08:00
'totals' => $totals ,
2025-12-30 15:03:19 +08:00
'filters' => $request -> only ([ 'search' ]),
]);
}
public function store ( Request $request )
{
$validated = $request -> validate ([
'name' => 'required|string|max:50' ,
'address' => 'nullable|string|max:255' ,
'description' => 'nullable|string' ,
2026-01-26 17:27:34 +08:00
'type' => 'required|string' ,
'license_plate' => 'nullable|string|max:20' ,
'driver_name' => 'nullable|string|max:50' ,
2025-12-30 15:03:19 +08:00
]);
2026-01-26 14:59:24 +08:00
// 自動產生代碼
2025-12-30 15:03:19 +08:00
$prefix = 'WH' ;
$lastWarehouse = Warehouse :: latest ( 'id' ) -> first ();
$nextId = $lastWarehouse ? $lastWarehouse -> id + 1 : 1 ;
$code = $prefix . str_pad ( $nextId , 3 , '0' , STR_PAD_LEFT );
$validated [ 'code' ] = $code ;
Warehouse :: create ( $validated );
return redirect () -> back () -> with ( 'success' , '倉庫已建立' );
}
public function update ( Request $request , Warehouse $warehouse )
{
$validated = $request -> validate ([
'name' => 'required|string|max:50' ,
'address' => 'nullable|string|max:255' ,
'description' => 'nullable|string' ,
2026-01-26 17:27:34 +08:00
'type' => 'required|string' ,
'license_plate' => 'nullable|string|max:20' ,
'driver_name' => 'nullable|string|max:50' ,
2025-12-30 15:03:19 +08:00
]);
$warehouse -> update ( $validated );
return redirect () -> back () -> with ( 'success' , '倉庫資訊已更新' );
}
public function destroy ( Warehouse $warehouse )
{
2026-01-08 16:32:10 +08:00
// 檢查是否有相關聯的採購單
if ( $warehouse -> purchaseOrders () -> exists ()) {
return redirect () -> back () -> with ( 'error' , '無法刪除:該倉庫有相關聯的採購單,請先處理採購單。' );
}
2025-12-30 15:03:19 +08:00
2026-01-08 16:32:10 +08:00
\Illuminate\Support\Facades\DB :: transaction ( function () use ( $warehouse ) {
// 刪除庫存異動紀錄 (透過庫存關聯)
foreach ( $warehouse -> inventories as $inventory ) {
// 刪除該庫存的所有異動紀錄
$inventory -> transactions () -> delete ();
}
// 刪除庫存項目
$warehouse -> inventories () -> delete ();
// 刪除倉庫
$warehouse -> delete ();
});
return redirect () -> back () -> with ( 'success' , '倉庫及其庫存與紀錄已刪除' );
2025-12-30 15:03:19 +08:00
}
}