feat: 修正庫存與撥補單邏輯並整合文件
1. 修復倉庫統計數據加總與樣式。 2. 修正可用庫存計算邏輯(排除不可銷售倉庫)。 3. 撥補單商品列表加入批號與效期顯示。 4. 修正撥補單儲存邏輯以支援精確批號轉移。 5. 整合 FEATURES.md 至 README.md。
This commit is contained in:
@@ -5,11 +5,16 @@ namespace App\Modules\Inventory\Controllers;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Inertia\Inertia;
|
||||
use App\Modules\Inventory\Models\Warehouse;
|
||||
use App\Modules\Inventory\Models\Product;
|
||||
use App\Modules\Inventory\Models\Inventory;
|
||||
use App\Modules\Inventory\Models\WarehouseProductSafetyStock;
|
||||
|
||||
class InventoryController extends Controller
|
||||
{
|
||||
public function index(\Illuminate\Http\Request $request, \App\Modules\Inventory\Models\Warehouse $warehouse)
|
||||
public function index(Request $request, Warehouse $warehouse)
|
||||
{
|
||||
$warehouse->load([
|
||||
'inventories.product.category',
|
||||
@@ -17,7 +22,7 @@ class InventoryController extends Controller
|
||||
'inventories.lastIncomingTransaction',
|
||||
'inventories.lastOutgoingTransaction'
|
||||
]);
|
||||
$allProducts = \App\Modules\Inventory\Models\Product::with('category')->get();
|
||||
$allProducts = Product::with('category')->get();
|
||||
|
||||
// 1. 準備 availableProducts
|
||||
$availableProducts = $allProducts->map(function ($product) {
|
||||
@@ -98,7 +103,7 @@ class InventoryController extends Controller
|
||||
];
|
||||
});
|
||||
|
||||
return \Inertia\Inertia::render('Warehouse/Inventory', [
|
||||
return Inertia::render('Warehouse/Inventory', [
|
||||
'warehouse' => $warehouse,
|
||||
'inventories' => $inventories,
|
||||
'safetyStockSettings' => $safetyStockSettings,
|
||||
@@ -106,10 +111,10 @@ class InventoryController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(\App\Modules\Inventory\Models\Warehouse $warehouse)
|
||||
public function create(Warehouse $warehouse)
|
||||
{
|
||||
// 取得所有商品供前端選單使用
|
||||
$products = \App\Modules\Inventory\Models\Product::with(['baseUnit', 'largeUnit'])
|
||||
$products = Product::with(['baseUnit', 'largeUnit'])
|
||||
->select('id', 'name', 'code', 'base_unit_id', 'large_unit_id', 'conversion_rate')
|
||||
->get()
|
||||
->map(function ($product) {
|
||||
@@ -123,13 +128,13 @@ class InventoryController extends Controller
|
||||
];
|
||||
});
|
||||
|
||||
return \Inertia\Inertia::render('Warehouse/AddInventory', [
|
||||
return Inertia::render('Warehouse/AddInventory', [
|
||||
'warehouse' => $warehouse,
|
||||
'products' => $products,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(\Illuminate\Http\Request $request, \App\Modules\Inventory\Models\Warehouse $warehouse)
|
||||
public function store(Request $request, Warehouse $warehouse)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'inboundDate' => 'required|date',
|
||||
@@ -144,22 +149,22 @@ class InventoryController extends Controller
|
||||
'items.*.expiryDate' => 'nullable|date',
|
||||
]);
|
||||
|
||||
return \Illuminate\Support\Facades\DB::transaction(function () use ($validated, $warehouse) {
|
||||
return DB::transaction(function () use ($validated, $warehouse) {
|
||||
foreach ($validated['items'] as $item) {
|
||||
$inventory = null;
|
||||
|
||||
if ($item['batchMode'] === 'existing') {
|
||||
// 模式 A:選擇現有批號 (包含已刪除的也要能找回來累加)
|
||||
$inventory = \App\Modules\Inventory\Models\Inventory::withTrashed()->findOrFail($item['inventoryId']);
|
||||
$inventory = Inventory::withTrashed()->findOrFail($item['inventoryId']);
|
||||
if ($inventory->trashed()) {
|
||||
$inventory->restore();
|
||||
}
|
||||
} else {
|
||||
// 模式 B:建立新批號
|
||||
$originCountry = $item['originCountry'] ?? 'TW';
|
||||
$product = \App\Modules\Inventory\Models\Product::find($item['productId']);
|
||||
$product = Product::find($item['productId']);
|
||||
|
||||
$batchNumber = \App\Modules\Inventory\Models\Inventory::generateBatchNumber(
|
||||
$batchNumber = Inventory::generateBatchNumber(
|
||||
$product->code ?? 'UNK',
|
||||
$originCountry,
|
||||
$validated['inboundDate']
|
||||
@@ -210,12 +215,12 @@ class InventoryController extends Controller
|
||||
/**
|
||||
* API: 取得商品在特定倉庫的所有批號,並回傳當前日期/產地下的一個流水號
|
||||
*/
|
||||
public function getBatches(\App\Modules\Inventory\Models\Warehouse $warehouse, $productId, \Illuminate\Http\Request $request)
|
||||
public function getBatches(Warehouse $warehouse, $productId, Request $request)
|
||||
{
|
||||
$originCountry = $request->query('originCountry', 'TW');
|
||||
$arrivalDate = $request->query('arrivalDate', now()->format('Y-m-d'));
|
||||
|
||||
$batches = \App\Modules\Inventory\Models\Inventory::where('warehouse_id', $warehouse->id)
|
||||
$batches = Inventory::where('warehouse_id', $warehouse->id)
|
||||
->where('product_id', $productId)
|
||||
->get()
|
||||
->map(function ($inventory) {
|
||||
@@ -229,10 +234,10 @@ class InventoryController extends Controller
|
||||
});
|
||||
|
||||
// 計算下一個流水號
|
||||
$product = \App\Modules\Inventory\Models\Product::find($productId);
|
||||
$product = Product::find($productId);
|
||||
$nextSequence = '01';
|
||||
if ($product) {
|
||||
$batchNumber = \App\Modules\Inventory\Models\Inventory::generateBatchNumber(
|
||||
$batchNumber = Inventory::generateBatchNumber(
|
||||
$product->code ?? 'UNK',
|
||||
$originCountry,
|
||||
$arrivalDate
|
||||
@@ -246,7 +251,7 @@ class InventoryController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(\Illuminate\Http\Request $request, \App\Modules\Inventory\Models\Warehouse $warehouse, $inventoryId)
|
||||
public function edit(Request $request, Warehouse $warehouse, $inventoryId)
|
||||
{
|
||||
// 取得庫存紀錄,包含商品資訊與異動紀錄 (含經手人)
|
||||
// 這裡如果是 Mock 的 ID (mock-inv-1),需要特殊處理
|
||||
@@ -254,7 +259,7 @@ class InventoryController extends Controller
|
||||
return redirect()->back()->with('error', '無法編輯範例資料');
|
||||
}
|
||||
|
||||
$inventory = \App\Modules\Inventory\Models\Inventory::with(['product', 'transactions' => function($query) {
|
||||
$inventory = Inventory::with(['product', 'transactions' => function($query) {
|
||||
$query->orderBy('actual_time', 'desc')->orderBy('id', 'desc');
|
||||
}, 'transactions.user'])->findOrFail($inventoryId);
|
||||
|
||||
@@ -284,20 +289,20 @@ class InventoryController extends Controller
|
||||
];
|
||||
});
|
||||
|
||||
return \Inertia\Inertia::render('Warehouse/EditInventory', [
|
||||
return Inertia::render('Warehouse/EditInventory', [
|
||||
'warehouse' => $warehouse,
|
||||
'inventory' => $inventoryData,
|
||||
'transactions' => $transactions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(\Illuminate\Http\Request $request, \App\Modules\Inventory\Models\Warehouse $warehouse, $inventoryId)
|
||||
public function update(Request $request, Warehouse $warehouse, $inventoryId)
|
||||
{
|
||||
// 若是 product ID (舊邏輯),先轉為 inventory
|
||||
// 但新路由我們傳的是 inventory ID
|
||||
// 為了相容,我們先判斷 $inventoryId 是 inventory ID
|
||||
|
||||
$inventory = \App\Modules\Inventory\Models\Inventory::find($inventoryId);
|
||||
$inventory = Inventory::find($inventoryId);
|
||||
|
||||
// 如果找不到 (可能是舊路由傳 product ID)
|
||||
if (!$inventory) {
|
||||
@@ -322,7 +327,7 @@ class InventoryController extends Controller
|
||||
'lastOutboundDate' => 'nullable|date',
|
||||
]);
|
||||
|
||||
return \Illuminate\Support\Facades\DB::transaction(function () use ($validated, $inventory) {
|
||||
return DB::transaction(function () use ($validated, $inventory) {
|
||||
$currentQty = (float) $inventory->quantity;
|
||||
$newQty = (float) $validated['quantity'];
|
||||
|
||||
@@ -395,9 +400,9 @@ class InventoryController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
public function destroy(\App\Modules\Inventory\Models\Warehouse $warehouse, $inventoryId)
|
||||
public function destroy(Warehouse $warehouse, $inventoryId)
|
||||
{
|
||||
$inventory = \App\Modules\Inventory\Models\Inventory::findOrFail($inventoryId);
|
||||
$inventory = Inventory::findOrFail($inventoryId);
|
||||
|
||||
// 庫存 > 0 不允許刪除 (哪怕是軟刪除)
|
||||
if ($inventory->quantity > 0) {
|
||||
@@ -430,7 +435,7 @@ class InventoryController extends Controller
|
||||
|
||||
if ($productId) {
|
||||
// 商品層級查詢
|
||||
$inventories = \App\Modules\Inventory\Models\Inventory::where('warehouse_id', $warehouse->id)
|
||||
$inventories = Inventory::where('warehouse_id', $warehouse->id)
|
||||
->where('product_id', $productId)
|
||||
->with(['product', 'transactions' => function($query) {
|
||||
$query->orderBy('actual_time', 'desc')->orderBy('id', 'desc');
|
||||
@@ -491,7 +496,7 @@ class InventoryController extends Controller
|
||||
];
|
||||
})->values();
|
||||
|
||||
return \Inertia\Inertia::render('Warehouse/InventoryHistory', [
|
||||
return Inertia::render('Warehouse/InventoryHistory', [
|
||||
'warehouse' => $warehouse,
|
||||
'inventory' => [
|
||||
'id' => 'product-' . $productId,
|
||||
@@ -505,7 +510,7 @@ class InventoryController extends Controller
|
||||
|
||||
if ($inventoryId) {
|
||||
// 單一批號查詢
|
||||
$inventory = \App\Modules\Inventory\Models\Inventory::with(['product', 'transactions' => function($query) {
|
||||
$inventory = Inventory::with(['product', 'transactions' => function($query) {
|
||||
$query->orderBy('actual_time', 'desc')->orderBy('id', 'desc');
|
||||
}, 'transactions.user'])->findOrFail($inventoryId);
|
||||
|
||||
@@ -521,7 +526,7 @@ class InventoryController extends Controller
|
||||
];
|
||||
});
|
||||
|
||||
return \Inertia\Inertia::render('Warehouse/InventoryHistory', [
|
||||
return Inertia::render('Warehouse/InventoryHistory', [
|
||||
'warehouse' => $warehouse,
|
||||
'inventory' => [
|
||||
'id' => (string) $inventory->id,
|
||||
|
||||
Reference in New Issue
Block a user