goodsReceiptService = $goodsReceiptService; } /** * 根據進貨單建立應付帳款 * * @param int $goodsReceiptId * @param int $userId 執行操作的使用者 ID * @return AccountPayable * @throws \Exception */ public function createFromGoodsReceipt(int $goodsReceiptId, int $userId): AccountPayable { // 透過 Contract 取得 Inventory 模組的資料,避免直接依賴 Model $receiptData = $this->goodsReceiptService->getGoodsReceiptData($goodsReceiptId); if (!$receiptData) { throw new \Exception("找不到對應的進貨單資料 (ID: {$goodsReceiptId})"); } // 檢查是否已經建立過(密等性) $existingAp = AccountPayable::where('source_document_type', 'goods_receipt') ->where('source_document_id', $goodsReceiptId) ->first(); if ($existingAp) { return $existingAp; } return DB::transaction(function () use ($receiptData, $userId) { $ap = AccountPayable::create([ 'vendor_id' => $receiptData['vendor_id'], 'source_document_type' => 'goods_receipt', 'source_document_id' => $receiptData['id'], 'document_number' => $this->generateApNumber(), 'total_amount' => collect($receiptData['items'] ?? [])->sum('total_amount'), 'tax_amount' => 0, // 假設後續會實作稅額計算,目前預設為 0 'status' => AccountPayable::STATUS_PENDING, // 設定應付日期,預設為進貨後天數 (由系統設定決定,預設 30 天) 'due_date' => now()->addDays(\App\Modules\Core\Models\SystemSetting::getVal('finance.ap_payment_days', 30))->toDateString(), 'created_by' => $userId, 'remarks' => "由進貨單 {$receiptData['code']} 自動生成", ]); return $ap; }); } /** * 產生應付帳款單號 */ protected function generateApNumber(): string { $prefix = 'AP-' . date('Ymd') . '-'; $lastPrefix = "{$prefix}%"; $latest = AccountPayable::where('document_number', 'like', $lastPrefix) ->orderBy('document_number', 'desc') ->lockForUpdate() ->first(); if (!$latest) { return $prefix . '01'; } $parts = explode('-', $latest->document_number); $lastNumber = intval(end($parts)); $newNumber = str_pad((string)($lastNumber + 1), 2, '0', STR_PAD_LEFT); return $prefix . $newNumber; } }