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-02-03 17:24:34 +08:00
$perPage = $request -> input ( 'per_page' , 10 );
if ( ! in_array ( $perPage , [ 10 , 20 , 50 , 100 ])) {
$perPage = 10 ;
}
2026-01-26 14:59:24 +08:00
$warehouses = $query -> withSum ( 'inventories as book_stock' , 'quantity' ) // 帳面庫存 = 所有庫存總和
2026-02-05 13:18:22 +08:00
-> withSum ( 'inventories as book_amount' , 'total_value' ) // 帳面金額
2026-01-26 14:59:24 +08:00
-> withSum ([ 'inventories as available_stock' => function ( $query ) {
2026-02-05 13:18:22 +08:00
// 可用庫存條件
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-05 13:18:22 +08:00
-> withSum ([ 'inventories as available_amount' => function ( $query ) {
// 可用金額條件 (與可用庫存一致)
$query -> where ( 'quantity' , '>' , 0 )
-> where ( 'quality_status' , 'normal' )
-> whereHas ( 'warehouse' , function ( $q ) {
$q -> where ( 'type' , '!=' , \App\Enums\WarehouseType :: QUARANTINE );
})
-> where ( function ( $q ) {
$q -> whereNull ( 'expiry_date' )
-> orWhere ( 'expiry_date' , '>=' , now ());
});
}], 'total_value' )
2026-02-05 15:50:14 +08:00
-> withSum ([ 'inventories as abnormal_amount' => function ( $query ) {
$query -> where ( 'quantity' , '>' , 0 )
-> where ( function ( $q ) {
$q -> where ( 'quality_status' , '!=' , 'normal' )
-> orWhere ( function ( $sq ) {
$sq -> whereNotNull ( 'expiry_date' )
-> where ( 'expiry_date' , '<' , now ());
})
-> orWhereHas ( 'warehouse' , function ( $wq ) {
$wq -> where ( 'type' , \App\Enums\WarehouseType :: QUARANTINE );
});
});
}], 'total_value' )
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' )
2026-02-03 17:24:34 +08:00
-> paginate ( $perPage )
2025-12-30 15:03:19 +08:00
-> withQueryString ();
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' ),
2026-02-05 13:18:22 +08:00
'available_amount' => \App\Modules\Inventory\Models\Inventory :: where ( 'quantity' , '>' , 0 )
-> where ( 'quality_status' , 'normal' )
-> whereHas ( 'warehouse' , function ( $q ) {
$q -> where ( 'type' , '!=' , \App\Enums\WarehouseType :: QUARANTINE );
})
-> where ( function ( $q ) {
$q -> whereNull ( 'expiry_date' )
-> orWhere ( 'expiry_date' , '>=' , now ());
}) -> sum ( 'total_value' ),
2026-02-05 15:50:14 +08:00
'abnormal_amount' => \App\Modules\Inventory\Models\Inventory :: where ( 'quantity' , '>' , 0 )
-> where ( function ( $q ) {
$q -> where ( 'quality_status' , '!=' , 'normal' )
-> orWhere ( function ( $sq ) {
$sq -> whereNotNull ( 'expiry_date' )
-> where ( 'expiry_date' , '<' , now ());
})
-> orWhereHas ( 'warehouse' , function ( $wq ) {
$wq -> where ( 'type' , \App\Enums\WarehouseType :: QUARANTINE );
});
}) -> sum ( 'total_value' ),
2026-01-26 14:59:24 +08:00
'book_stock' => \App\Modules\Inventory\Models\Inventory :: sum ( 'quantity' ),
2026-02-05 13:18:22 +08:00
'book_amount' => \App\Modules\Inventory\Models\Inventory :: sum ( 'total_value' ),
2026-01-26 14:59:24 +08:00
];
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 ,
2026-02-03 17:24:34 +08:00
'filters' => $request -> only ([ 'search' , 'per_page' ]),
2025-12-30 15:03:19 +08:00
]);
}
public function store ( Request $request )
{
$validated = $request -> validate ([
2026-02-06 16:36:14 +08:00
'code' => 'required|string|max:20|unique:warehouses,code' ,
2025-12-30 15:03:19 +08:00
'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 :: create ( $validated );
return redirect () -> back () -> with ( 'success' , '倉庫已建立' );
}
public function update ( Request $request , Warehouse $warehouse )
{
$validated = $request -> validate ([
2026-02-06 16:36:14 +08:00
'code' => 'required|string|max:20|unique:warehouses,code,' . $warehouse -> id ,
2025-12-30 15:03:19 +08:00
'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
}
}