$warehouseId, 'count_doc_id' => $countDocId, 'status' => 'draft', 'reason' => $reason, 'remarks' => $remarks, 'created_by' => $userId, ]); } /** * 從盤點單建立盤調單 */ public function createFromCountDoc(InventoryCountDoc $countDoc, int $userId): InventoryAdjustDoc { return DB::transaction(function () use ($countDoc, $userId) { // 1. 建立盤調單頭 $adjDoc = $this->createDoc( $countDoc->warehouse_id, "盤點調整: " . $countDoc->doc_no, "由盤點單 {$countDoc->doc_no} 自動生成", $userId, $countDoc->id ); // 2. 抓取有差異的明細 (diff_qty != 0) foreach ($countDoc->items as $item) { if (abs($item->diff_qty) < 0.0001) continue; $adjDoc->items()->create([ 'product_id' => $item->product_id, 'batch_number' => $item->batch_number, 'qty_before' => $item->system_qty, 'adjust_qty' => $item->diff_qty, 'notes' => "盤點差異: " . $item->diff_qty, ]); } return $adjDoc; }); } /** * 更新調整單內容 (Items) * 此處採用 "全量更新" 方式處理 items (先刪後加),簡單可靠 */ public function updateItems(InventoryAdjustDoc $doc, array $itemsData): void { DB::transaction(function () use ($doc, $itemsData) { $doc->items()->delete(); foreach ($itemsData as $data) { // 取得當前庫存作為 qty_before 參考 (僅參考,實際扣減以過帳當下為準) $inventory = Inventory::where('warehouse_id', $doc->warehouse_id) ->where('product_id', $data['product_id']) ->where('batch_number', $data['batch_number'] ?? null) ->first(); $qtyBefore = $inventory ? $inventory->quantity : 0; $doc->items()->create([ 'product_id' => $data['product_id'], 'batch_number' => $data['batch_number'] ?? null, 'qty_before' => $qtyBefore, 'adjust_qty' => $data['adjust_qty'], 'notes' => $data['notes'] ?? null, ]); } }); } /** * 過帳 (Post) - 生效庫存異動 */ public function post(InventoryAdjustDoc $doc, int $userId): void { DB::transaction(function () use ($doc, $userId) { foreach ($doc->items as $item) { if ($item->adjust_qty == 0) continue; // 找尋或建立 Inventory // 若是減少庫存,必須確保 Inventory 存在 (且理論上不能變負? 視策略而定,這裡假設允許變負或由 InventoryService 控管) // 若是增加庫存,若不存在需建立 $inventory = Inventory::firstOrNew([ 'warehouse_id' => $doc->warehouse_id, 'product_id' => $item->product_id, 'batch_number' => $item->batch_number, ]); // 如果是新建立的 object (id 為空),需要初始化 default if (!$inventory->exists) { // 繼承 Product 成本或預設 0 (簡化處理) $inventory->unit_cost = $item->product->cost ?? 0; $inventory->quantity = 0; } $oldQty = $inventory->quantity; $newQty = $oldQty + $item->adjust_qty; $inventory->quantity = $newQty; // 用最新的數量 * 單位成本 (簡化成本計算,不採用移動加權) $inventory->total_value = $newQty * $inventory->unit_cost; $inventory->save(); // 建立 Transaction $inventory->transactions()->create([ 'type' => '庫存調整', 'quantity' => $item->adjust_qty, 'unit_cost' => $inventory->unit_cost, 'balance_before' => $oldQty, 'balance_after' => $newQty, 'reason' => "調整單 {$doc->doc_no}: " . ($doc->reason ?? '手動調整'), 'actual_time' => now(), 'user_id' => $userId, ]); } $doc->update([ 'status' => 'posted', 'posted_at' => now(), 'posted_by' => $userId, ]); }); } /** * 作廢 (Void) */ public function void(InventoryAdjustDoc $doc, int $userId): void { if ($doc->status !== 'draft') { throw new \Exception('只能作廢草稿狀態的單據'); } $doc->update([ 'status' => 'voided', 'updated_by' => $userId ]); } }