validate([ 'sourceWarehouseId' => 'required|exists:warehouses,id', 'targetWarehouseId' => 'required|exists:warehouses,id|different:sourceWarehouseId', 'productId' => 'required|exists:products,id', 'quantity' => 'required|numeric|min:0.01', 'transferDate' => 'required|date', 'status' => 'required|in:待處理,處理中,已完成,已取消', // 目前僅支援立即完成或單純記錄 'notes' => 'nullable|string', 'batchNumber' => 'nullable|string', // 暫時接收,雖然 DB 可能沒存 ]); return DB::transaction(function () use ($validated) { // 1. 檢查來源倉庫庫存 (精確匹配產品與批號) $sourceInventory = Inventory::where('warehouse_id', $validated['sourceWarehouseId']) ->where('product_id', $validated['productId']) ->where('batch_number', $validated['batchNumber']) ->first(); if (!$sourceInventory || $sourceInventory->quantity < $validated['quantity']) { throw ValidationException::withMessages([ 'quantity' => ['來源倉庫指定批號庫存不足'], ]); } // 2. 獲取或建立目標倉庫庫存 (精確匹配產品與批號,並繼承效期與品質狀態) $targetInventory = Inventory::firstOrCreate( [ 'warehouse_id' => $validated['targetWarehouseId'], 'product_id' => $validated['productId'], 'batch_number' => $validated['batchNumber'], ], [ 'quantity' => 0, 'unit_cost' => $sourceInventory->unit_cost, // 繼承成本 'total_value' => 0, 'expiry_date' => $sourceInventory->expiry_date, 'quality_status' => $sourceInventory->quality_status, 'origin_country' => $sourceInventory->origin_country, ] ); $sourceWarehouse = Warehouse::find($validated['sourceWarehouseId']); $targetWarehouse = Warehouse::find($validated['targetWarehouseId']); // 3. 執行庫存轉移 (扣除來源) $oldSourceQty = $sourceInventory->quantity; $newSourceQty = $oldSourceQty - $validated['quantity']; // 設定活動紀錄原因 $sourceInventory->activityLogReason = "撥補出庫 至 {$targetWarehouse->name}"; $sourceInventory->quantity = $newSourceQty; $sourceInventory->total_value = $sourceInventory->quantity * $sourceInventory->unit_cost; // 更新總值 $sourceInventory->save(); // 記錄來源異動 $sourceInventory->transactions()->create([ 'type' => '撥補出庫', 'quantity' => -$validated['quantity'], 'unit_cost' => $sourceInventory->unit_cost, // 記錄 'balance_before' => $oldSourceQty, 'balance_after' => $newSourceQty, 'reason' => "撥補至 {$targetWarehouse->name}" . ($validated['notes'] ? " ({$validated['notes']})" : ""), 'actual_time' => $validated['transferDate'], 'user_id' => auth()->id(), ]); // 4. 執行庫存轉移 (增加目標) $oldTargetQty = $targetInventory->quantity; $newTargetQty = $oldTargetQty + $validated['quantity']; // 設定活動紀錄原因 $targetInventory->activityLogReason = "撥補入庫 來自 {$sourceWarehouse->name}"; // 確保目標庫存也有成本 (如果是繼承來的) if ($targetInventory->unit_cost == 0 && $sourceInventory->unit_cost > 0) { $targetInventory->unit_cost = $sourceInventory->unit_cost; } $targetInventory->quantity = $newTargetQty; $targetInventory->total_value = $targetInventory->quantity * $targetInventory->unit_cost; // 更新總值 $targetInventory->save(); // 記錄目標異動 $targetInventory->transactions()->create([ 'type' => '撥補入庫', 'quantity' => $validated['quantity'], 'unit_cost' => $targetInventory->unit_cost, // 記錄 'balance_before' => $oldTargetQty, 'balance_after' => $newTargetQty, 'reason' => "來自 {$sourceWarehouse->name} 的撥補" . ($validated['notes'] ? " ({$validated['notes']})" : ""), 'actual_time' => $validated['transferDate'], 'user_id' => auth()->id(), ]); // TODO: 未來若有獨立的 TransferOrder 模型,可在此建立紀錄 return redirect()->back()->with('success', '撥補單已建立且庫存已轉移'); }); } /** * 獲取特定倉庫的庫存列表 (API) */ public function getWarehouseInventories(Warehouse $warehouse) { $inventories = $warehouse->inventories() ->with(['product.baseUnit', 'product.category']) ->where('quantity', '>', 0) // 只回傳有庫存的 ->get() ->map(function ($inv) { return [ 'product_id' => (string) $inv->product_id, 'product_name' => $inv->product->name, 'batch_number' => $inv->batch_number, 'quantity' => (float) $inv->quantity, 'unit_cost' => (float) $inv->unit_cost, // 新增 'total_value' => (float) $inv->total_value, // 新增 'unit_name' => $inv->product->baseUnit?->name ?? '個', 'expiry_date' => $inv->expiry_date ? $inv->expiry_date->format('Y-m-d') : null, ]; }); return response()->json($inventories); } }