warehouse = $warehouse; $this->inboundDate = $inboundDate; $this->notes = $notes; } public function map($row): array { // 處理條碼或代號為字串 if (isset($row['商品條碼'])) { $row['商品條碼'] = (string) $row['商品條碼']; } if (isset($row['商品代號'])) { $row['商品代號'] = (string) $row['商品代號']; } if (isset($row['儲位/貨道'])) { $row['儲位/貨道'] = (string) $row['儲位/貨道']; } return $row; } public function model(array $row) { // 查找商品 $product = null; if (!empty($row['商品條碼'])) { $product = Product::where('barcode', $row['商品條碼'])->first(); } if (!$product && !empty($row['商品代號'])) { $product = Product::where('code', $row['商品代號'])->first(); } if (!$product) { return null; // 透過 Validation 攔截 } $quantity = (float) $row['數量']; $unitCost = isset($row['入庫單價']) ? (float) $row['入庫單價'] : ($product->cost_price ?? 0); $location = $row['儲位/貨道'] ?? null; // 批號邏輯:若 Excel 留空則使用 NO-BATCH $batchNumber = !empty($row['批號']) ? $row['批號'] : 'NO-BATCH'; $originCountry = $row['產地'] ?? 'TW'; $expiryDate = !empty($row['效期']) ? $row['效期'] : null; return DB::transaction(function () use ($product, $quantity, $unitCost, $location, $batchNumber, $originCountry, $expiryDate) { // 使用與 InventoryController 相同的 firstOrNew 邏輯 $inventory = $this->warehouse->inventories()->withTrashed()->firstOrNew( [ 'product_id' => $product->id, 'batch_number' => $batchNumber, 'location' => $location, // 加入儲位/貨道作為區分關鍵字 ], [ 'quantity' => 0, 'unit_cost' => $unitCost, 'total_value' => 0, 'arrival_date' => $this->inboundDate, 'expiry_date' => $expiryDate, 'origin_country' => $originCountry, ] ); if ($inventory->trashed()) { $inventory->restore(); } // 更新數量 $oldQty = $inventory->quantity; $inventory->quantity += $quantity; // 更新單價與總價值 $inventory->unit_cost = $unitCost; $inventory->total_value = $inventory->quantity * $unitCost; $inventory->save(); // 記錄交易歷史 $inventory->transactions()->create([ 'warehouse_id' => $this->warehouse->id, 'product_id' => $product->id, 'batch_number' => $inventory->batch_number, 'quantity' => $quantity, 'unit_cost' => $unitCost, 'type' => '手動入庫', 'reason' => 'Excel 匯入入庫', 'balance_before' => $oldQty, 'balance_after' => $inventory->quantity, 'actual_time' => $this->inboundDate, 'notes' => $this->notes, 'expiry_date' => $inventory->expiry_date, ]); return $inventory; }); } public function rules(): array { return [ '商品條碼' => ['nullable', 'string'], '商品代號' => ['nullable', 'string'], '數量' => [ 'required_with:商品條碼,商品代號', // 只有在有商品資訊時,數量才是必填 'numeric', 'min:0' // 允許數量為 0 ], '入庫單價' => ['nullable', 'numeric', 'min:0'], '儲位/貨道' => ['nullable', 'string', 'max:50'], '批號' => ['nullable', 'string'], '效期' => ['nullable', 'date'], '產地' => ['nullable', 'string', 'max:2'], ]; } }