inventoryService = $inventoryService; $this->procurementService = $procurementService; } /** * Store a new Goods Receipt and process inventory. * * @param array $data * @return GoodsReceipt * @throws \Exception */ public function store(array $data) { return DB::transaction(function () use ($data) { // 1. Generate Code $data['code'] = $this->generateCode($data['received_date']); $data['user_id'] = auth()->id(); $data['status'] = 'completed'; // Direct completion for now // 2. Create Header $goodsReceipt = GoodsReceipt::create($data); // 3. Process Items foreach ($data['items'] as $itemData) { // Create GR Item $grItem = new GoodsReceiptItem([ 'product_id' => $itemData['product_id'], 'purchase_order_item_id' => $itemData['purchase_order_item_id'] ?? null, 'quantity_received' => $itemData['quantity_received'], 'unit_price' => $itemData['unit_price'], 'total_amount' => $itemData['quantity_received'] * $itemData['unit_price'], 'batch_number' => $itemData['batch_number'] ?? null, 'expiry_date' => $itemData['expiry_date'] ?? null, ]); $goodsReceipt->items()->save($grItem); // 4. Update Inventory $reason = match($goodsReceipt->type) { 'standard' => '採購進貨', 'miscellaneous' => '雜項入庫', 'other' => '其他入庫', default => '進貨入庫', }; $this->inventoryService->createInventoryRecord([ 'warehouse_id' => $goodsReceipt->warehouse_id, 'product_id' => $grItem->product_id, 'quantity' => $grItem->quantity_received, 'unit_cost' => $grItem->unit_price, 'batch_number' => $grItem->batch_number, 'expiry_date' => $grItem->expiry_date, 'reason' => $reason, 'reference_type' => GoodsReceipt::class, 'reference_id' => $goodsReceipt->id, 'source_purchase_order_id' => $goodsReceipt->purchase_order_id, 'arrival_date' => $goodsReceipt->received_date, ]); // 5. Update PO if linked and type is standard if ($goodsReceipt->type === 'standard' && $goodsReceipt->purchase_order_id && $grItem->purchase_order_item_id) { $this->procurementService->updateReceivedQuantity( $grItem->purchase_order_item_id, $grItem->quantity_received ); } } return $goodsReceipt; }); } private function generateCode(string $date) { // Format: GR-YYYYMMDD-NN $prefix = 'GR-' . date('Ymd', strtotime($date)) . '-'; $last = GoodsReceipt::where('code', 'like', $prefix . '%') ->orderBy('id', 'desc') ->lockForUpdate() ->first(); if ($last) { $seq = intval(substr($last->code, -2)) + 1; } else { $seq = 1; } return $prefix . str_pad($seq, 2, '0', STR_PAD_LEFT); } }