feat: 實作出貨單模組並暫時導向通用製作中頁面,同步優化盤點與調撥功能的活動日誌顯示
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 1m11s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped

This commit is contained in:
2026-02-05 09:33:36 +08:00
parent 4299e985e9
commit 04f3891275
23 changed files with 1410 additions and 30 deletions

View File

@@ -106,6 +106,16 @@ interface InventoryServiceInterface
*/
public function decreaseInventoryQuantity(int $inventoryId, float $quantity, ?string $reason = null, ?string $referenceType = null, $referenceId = null);
/**
* Find a specific inventory record by warehouse, product and batch.
*
* @param int $warehouseId
* @param int $productId
* @param string|null $batchNumber
* @return object|null
*/
public function findInventoryByBatch(int $warehouseId, int $productId, ?string $batchNumber);
/**
* Get statistics for the dashboard.
*

View File

@@ -127,7 +127,7 @@ class TransferOrderController extends Controller
'batch_number' => $item->batch_number,
'unit' => $item->product->baseUnit?->name,
'quantity' => (float) $item->quantity,
'max_quantity' => $stock ? (float) $stock->quantity : 0.0,
'max_quantity' => $item->snapshot_quantity ? (float) $item->snapshot_quantity : ($stock ? (float) $stock->quantity : 0.0),
'notes' => $item->notes,
];
}),
@@ -154,14 +154,22 @@ class TransferOrderController extends Controller
]);
// 1. 先更新資料
$itemsChanged = false;
if ($request->has('items')) {
$this->transferService->updateItems($order, $validated['items']);
$itemsChanged = $this->transferService->updateItems($order, $validated['items']);
}
$order->fill($request->only(['remarks']));
$remarksChanged = $order->remarks !== ($validated['remarks'] ?? null);
// [IMPORTANT] 使用 touch() 確保即便只有品項異動,也會因為 updated_at 變更而觸發自動日誌
$order->touch();
if ($itemsChanged || $remarksChanged) {
$order->remarks = $validated['remarks'] ?? null;
// [IMPORTANT] 使用 touch() 確保即便只有品項異動,也會因為 updated_at 變更而觸發自動日誌
$order->touch();
$message = '儲存成功';
} else {
$message = '資料未變更';
// 如果沒變更,就不執行 touch(),也不會產生 Activity Log
}
// 2. 判斷是否需要過帳
if ($request->input('action') === 'post') {
@@ -174,7 +182,7 @@ class TransferOrderController extends Controller
}
}
return redirect()->back()->with('success', '儲存成功');
return redirect()->back()->with('success', $message);
}
public function destroy(InventoryTransferOrder $order)

View File

@@ -15,6 +15,7 @@ class InventoryTransferItem extends Model
'product_id',
'batch_number',
'quantity',
'snapshot_quantity',
'notes',
];

View File

@@ -188,6 +188,14 @@ class InventoryService implements InventoryServiceInterface
});
}
public function findInventoryByBatch(int $warehouseId, int $productId, ?string $batchNumber)
{
return Inventory::where('warehouse_id', $warehouseId)
->where('product_id', $productId)
->where('batch_number', $batchNumber)
->first();
}
public function getDashboardStats(): array
{
// 庫存總表 join 安全庫存表,計算低庫存

View File

@@ -31,9 +31,9 @@ class TransferService
/**
* 更新調撥單明細 (支援精確 Diff 與自動日誌整合)
*/
public function updateItems(InventoryTransferOrder $order, array $itemsData): void
public function updateItems(InventoryTransferOrder $order, array $itemsData): bool
{
DB::transaction(function () use ($order, $itemsData) {
return DB::transaction(function () use ($order, $itemsData) {
// 1. 準備舊資料索引 (Key: product_id . '_' . batch_number)
$oldItemsMap = $order->items->mapWithKeys(function ($item) {
$key = $item->product_id . '_' . ($item->batch_number ?? '');
@@ -88,9 +88,13 @@ class TransferService
];
}
} else {
// 新增
$diff['added'][] = [
// 新增 (使用者需求:顯示為更新,從 0 -> X)
$diff['updated'][] = [
'product_name' => $item->product->name,
'old' => [
'quantity' => 0,
'notes' => null,
],
'new' => [
'quantity' => (float)$item->quantity,
'notes' => $item->notes,
@@ -113,10 +117,12 @@ class TransferService
}
// 4. 將 Diff 注入到 Model 的暫存屬性中
// 如果 Diff 有內容,才注入
if (!empty($diff['added']) || !empty($diff['removed']) || !empty($diff['updated'])) {
$hasChanged = !empty($diff['added']) || !empty($diff['removed']) || !empty($diff['updated']);
if ($hasChanged) {
$order->activityProperties['items_diff'] = $diff;
}
return $hasChanged;
});
}
@@ -149,6 +155,9 @@ class TransferService
$oldSourceQty = $sourceInventory->quantity;
$newSourceQty = $oldSourceQty - $item->quantity;
// 儲存庫存快照
$item->update(['snapshot_quantity' => $oldSourceQty]);
$sourceInventory->quantity = $newSourceQty;
// 更新總值 (假設成本不變)