Files
star-erp/app/Modules/Inventory/Controllers/WarehouseController.php

197 lines
8.4 KiB
PHP
Raw Normal View History

2025-12-30 15:03:19 +08:00
<?php
namespace App\Modules\Inventory\Controllers;
use App\Http\Controllers\Controller;
2025-12-30 15:03:19 +08:00
use Illuminate\Http\Request;
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}%");
});
}
$perPage = $request->input('per_page', 10);
if (!in_array($perPage, [10, 20, 50, 100])) {
$perPage = 10;
}
$warehouses = $query->withSum('inventories as book_stock', 'quantity') // 帳面庫存 = 所有庫存總和
->withSum('inventories as book_amount', 'total_value') // 帳面金額
->withSum(['inventories as available_stock' => 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());
});
}], 'quantity')
->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')
->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')
->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($perPage)
2025-12-30 15:03:19 +08:00
->withQueryString();
// 計算全域總計 (不分頁)
$totals = [
'available_stock' => \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('quantity'),
'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'),
'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'),
'book_stock' => \App\Modules\Inventory\Models\Inventory::sum('quantity'),
'book_amount' => \App\Modules\Inventory\Models\Inventory::sum('total_value'),
];
// 取得在途倉列表供前端選擇「預設在途倉」
$transitWarehouses = Warehouse::where('type', \App\Enums\WarehouseType::TRANSIT)
->select('id', 'name', 'license_plate', 'driver_name')
->orderBy('name')
->get()
->map(fn ($w) => [
'id' => (string) $w->id,
'name' => $w->name,
'license_plate' => $w->license_plate,
'driver_name' => $w->driver_name,
]);
2025-12-30 15:03:19 +08:00
return Inertia::render('Warehouse/Index', [
'warehouses' => $warehouses,
'totals' => $totals,
'transitWarehouses' => $transitWarehouses,
'filters' => $request->only(['search', 'per_page']),
2025-12-30 15:03:19 +08:00
]);
}
public function store(Request $request)
{
$validated = $request->validate([
'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',
'type' => 'required|string',
'license_plate' => 'nullable|string|max:20',
'driver_name' => 'nullable|string|max:50',
'default_transit_warehouse_id' => 'nullable|exists:warehouses,id',
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([
'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',
'type' => 'required|string',
'license_plate' => 'nullable|string|max:20',
'driver_name' => 'nullable|string|max:50',
'default_transit_warehouse_id' => 'nullable|exists:warehouses,id',
2025-12-30 15:03:19 +08:00
]);
$warehouse->update($validated);
return redirect()->back()->with('success', '倉庫資訊已更新');
}
public function destroy(Warehouse $warehouse)
{
// 檢查是否有相關聯的採購單 (跨模組檢查,不使用模型關聯以符合解耦規範)
$hasPurchaseOrders = \App\Modules\Procurement\Models\PurchaseOrder::where('warehouse_id', $warehouse->id)->exists();
if ($hasPurchaseOrders) {
2026-01-08 16:32:10 +08:00
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
}
}