From b4e068471be6d16f7b8dae7a9a8b614063f22776 Mon Sep 17 00:00:00 2001 From: tony-claw Date: Mon, 18 May 2026 18:06:24 +0800 Subject: [PATCH] Add ERPNext Accounting Structure Analysis --- ERPNext_Accounting_Structure.md | 704 ++++++++++++++++++++++++++++++++ 1 file changed, 704 insertions(+) create mode 100644 ERPNext_Accounting_Structure.md diff --git a/ERPNext_Accounting_Structure.md b/ERPNext_Accounting_Structure.md new file mode 100644 index 0000000..60bbc57 --- /dev/null +++ b/ERPNext_Accounting_Structure.md @@ -0,0 +1,704 @@ +# ERPNext 會計數據結構分析 + +> **日期**: 2026-04-18 +> **版本**: ERPNext v17 (develop branch) +> **程式碼位置**: `/home/tony/erpnext-analysis/erpnext` + +--- + +## 目錄 + +1. [環境現狀](#環境現狀) +2. [核心會計數據模型](#核心會計數據模型) +3. [GL Entry 總帳分錄結構](#gl-entry-總帳分錄結構) +4. [主要 Doctype 欄位詳解](#主要-doctype-欄位詳解) +5. [會計數據導入的 4 種方式](#會計數據導入的-4-種方式) +6. [基礎資料依賴](#基礎資料依賴) +7. [自訂 API 開發架構](#自訂-api-開發架構) +8. [關鍵程式邏輯](#關鍵程式邏輯) +9. [下一步建議](#下一步建議) + +--- + +## 環境現狀 + +| 項目 | 狀態 | +|------|------| +| **ERPNext 版本** | v17 (develop branch) | +| **程式碼位置** | `/home/tony/erpnext-analysis/erpnext`、`/home/tony/erpnext/erpnext` | +| **Frappe Core** | ❌ 未找到 | +| **Bench 環境** | ❌ 無 `sites/`、無 `bench` 目錄 | +| **執行中服務** | ❌ 無 ERPNext/Frappe 實例運行 | +| **可用資源** | PostgreSQL (nexus_postgres:5432)、Qdrant (6333-6334)、Neo4j (7474/7687) | +| **API 端口** | 8084 (本地 LLM)、8082 (Python 服務) | + +> **結論**: 目前只能做**靜態結構分析**,要實際開發 API 需要部署 Frappe bench 環境。 + +--- + +## 核心會計數據模型 + +ERPNext 的會計數據透過 **Doctype(文件類型)**組織,核心流程是: + +``` +交易文件 (Invoice/Payment) → General Ledger (GL Entry) → 總帳/分類帳 +``` + +### 層級關係圖 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 主交易文件 │ +├─────────────────────────────────────────────────────────────────┤ +│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │ +│ │ Sales Invoice │ │ Purchase Invoice │ │ Payment Entry │ │ +│ │ (銷售發票) │ │ (採購發票) │ │ (付款/收款) │ │ +│ └────────┬─────────┘ └────────┬─────────┘ └───────┬───────┘ │ +│ │ │ │ │ +│ ┌────────┴─────────────────────┴─────────────────────┴───────┐ │ +│ │ controller.py (accounts/doctype/...) │ │ +│ │ 自動生成 GL Entry + Payment Ledger Entry │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ 底層分錄 │ +├─────────────────────────────────────────────────────────────────┤ +│ ┌──────────────────┐ ┌──────────────────────────┐ │ +│ │ GL Entry │ │ Payment Ledger Entry │ │ +│ │ (總帳分錄) │ │ (付款分類帳) │ │ +│ │ • 不可手動編輯 │ │ • 不可手動編輯 │ │ +│ │ • 借貸平衡 │ │ • AR/AP 追蹤 │ │ +│ └──────────────────┘ └──────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 主要 Doctype 總覽 + +| 層級 | Doctype | 說明 | 關聯 | +|------|---------|------|------| +| **主文件** | `sales_invoice` | 銷售發票 | → GL Entry | +| **主文件** | `purchase_invoice` | 採購發票 | → GL Entry | +| **主文件** | `payment_entry` | 付款/收款記錄 | → GL Entry + Payment Ledger | +| **主文件** | `journal_entry` | 日記帳(手動過帳) | → GL Entry | +| **主文件** | `pos_invoice` | POS 發票 | → GL Entry | +| **子表** | `sales_invoice_item` | 發票明細(商品/收入帳戶) | | +| **子表** | `purchase_invoice_item` | 採購明細(費用帳戶) | | +| **子表** | `journal_entry_account` | 日記帳分录行 | | +| **底層** | `gl_entry` | 總帳分錄(最終會計記錄) | 不可手動編輯 | +| **底層** | `payment_ledger_entry` | 付款分類帳(AR/AP 追蹤) | 不可手動編輯 | + +--- + +## GL Entry(總帳分錄)結構 + +> ⚠️ **GL Entry 是 ERPNext 會計數據的終極歸宿**,所有交易文件最終都會轉成 GL Entry 記錄。 + +### 完整欄位列表 + +| 欄位 | 類型 | 可空 | 說明 | +|------|------|------|------| +| `posting_date` | Date | ✅ | 入帳日期 | +| `transaction_date` | Date | ✅ | 交易日期 | +| `account` | Link → Account | ✅ | 會計科目 | +| `party_type` | Link → DocType | ✅ | 往來對象類型(Customer/Supplier/Employee 等) | +| `party` | Dynamic Link | ✅ | 往來對象(動態引用 party_type 指向的 Doctype) | +| `cost_center` | Link → Cost Center | ✅ | 成本中心 | +| `debit` | Currency | ✅ | 借方金額(公司幣) | +| `credit` | Currency | ✅ | 貸方金額(公司幣) | +| `account_currency` | Link → Currency | ✅ | 科目幣別 | +| `debit_in_account_currency` | Currency | ✅ | 科目幣別借方 | +| `credit_in_account_currency` | Currency | ✅ | 科目幣別貸方 | +| `against` | Text | ✅ | 對應科目(簡述) | +| `against_voucher_type` | Link → DocType | ✅ | 對應憑證類型 | +| `against_voucher` | Dynamic Link | ✅ | 對應憑證編號 | +| `voucher_type` | Link → DocType | ✅ | 來源文件類型 | +| `voucher_no` | Dynamic Link | ✅ | 來源文件編號 | +| `voucher_detail_no` | Data | ✅ | 來源明細編號(子表行號) | +| `project` | Link → Project | ✅ | 專案 | +| `remarks` | Text | ✅ | 備註 | +| `is_opening` | Select | ✅ | 是否為期初分錄 | +| `is_advance` | Select | ✅ | 是否為預付款 | +| `fiscal_year` | Link → Fiscal Year | ✅ | 會計年度 | +| `company` | Link → Company | ✅ | 公司 | +| `finance_book` | Link → Finance Book | ✅ | 財務簿 | +| `to_rename` | Check | ✅ | 待重新編號 | +| `due_date` | Date | ✅ | 到期日 | +| `is_cancelled` | Check | ✅ | 是否已取消 | +| `transaction_currency` | Link → Currency | ✅ | 交易幣別 | +| `transaction_exchange_rate` | Float | ✅ | 交易匯率 | +| `debit_in_transaction_currency` | Currency | ✅ | 交易幣別借方 | +| `credit_in_transaction_currency` | Currency | ✅ | 交易幣別貸方 | +| `voucher_subtype` | Small Text | ✅ | 憑證子類型 | +| `debit_in_reporting_currency` | Currency | ✅ | 報表幣別借方 | +| `credit_in_reporting_currency` | Currency | ✅ | 報表幣別貸方 | +| `reporting_currency_exchange_rate` | Float | ✅ | 報表幣別匯率 | + +### 分錄結構要求 + +``` +每筆分录: debit = credit(借貸必須平衡) +每筆交易: 所有 GL Entry 的 debit 總和 = credit 總和 +``` + +--- + +## 主要 Doctype 欄位詳解 + +### Sales Invoice(銷售發票) + +#### Header(主文件)核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `naming_series` | Select | ✅ | 編號系列 | +| `customer` | Link → Customer | ✅ | 客戶 | +| `posting_date` | Date | ✅ | 入帳日期 | +| `posting_time` | Time | ✅ | 入帳時間 | +| `company` | Link → Company | ✅ | 公司 | +| `tax_category` | Link → Tax Category | ✅ | 稅務類別 | +| `taxes_and_charges` | Link → Sales Taxes and Charges Template | ✅ | 稅金模板 | +| `is_return` | Check | ✅ | 是否為退貨 | +| `return_against` | Data | ✅ | 退回原始發票 | +| `is_consolidated` | Check | ✅ | 是否為合併發票 | +| `is_discounted` | Check | ✅ | 是否為折讓發票 | +| `base_grand_total` | Currency | ✅ | 稅前總額(公司幣) | +| `base_rounding_adjustment` | Currency | ✅ | 四捨五入調整(公司幣) | +| `round_total` | Check | ✅ | 是否四捨五入 | +| `base_rounded_total` | Currency | ✅ | 四捨五入後總額(公司幣) | +| `grand_total` | Currency | ✅ | 總額 | +| `rounded_total` | Currency | ✅ | 四捨五入後總額 | +| `total_taxes_and_charges` | Currency | ✅ | 總稅金 | +| `total_qty` | Float | ✅ | 總數量 | +| `status` | Select | ✅ | 狀態(Draft/Submitted/Cancelled/Paid) | +| `is_pos` | Check | ✅ | 是否為 POS 發票 | +| `is_return` | Check | ✅ | 是否為退貨 | +| `update_stock` | Check | ✅ | 是否更新庫存 | +| `update_billed_amount_in_delivery_note` | Check | ✅ | 是否更新送貨單已計費金額 | +| `update_billed_amount_in_sales_order` | Check | ✅ | 是否更新訂單已計費金額 | +| `write_off_amount` | Currency | ✅ | 核銷金額 | +| `is_opening` | Select | ✅ | 是否為期初 | +| `amended_from` | Link → Sales Invoice | ✅ | 來源修改 | + +#### Sales Invoice Item(明細子表)核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `item_code` | Link → Item | ✅ | 商品 | +| `item_name` | Data | ✅ | 商品名稱 | +| `uom` | Link → UOM | ✅ | 計量單位 | +| `conversion_factor` | Float | ✅ | 單位換算係數 | +| `qty` | Float | ✅ | 數量 | +| `stock_qty` | Float | ✅ | 庫存單位數量 | +| `rate` | Currency | ✅ | 單價 | +| `amount` | Currency | ✅ | 金額 | +| `base_rate` | Currency | ✅ | 公司幣單價 | +| `base_amount` | Currency | ✅ | 公司幣金額 | +| `income_account` | Link → Account | ✅ | 收入科目 | +| `expense_account` | Link → Account | ❌ | 費用科目 | +| `cost_center` | Link → Cost Center | ✅ | 成本中心 | +| `item_tax_template` | Link → Item Tax Template | ✅ | 商品稅金模板 | +| `warehouse` | Link → Warehouse | ✅ | 倉庫 | +| `delivery_note` | Link → Delivery Note | ❌ | 送貨單 | +| `sales_order` | Link → Sales Order | ❌ | 銷售訂單 | +| `project` | Link → Project | ❌ | 專案 | +| `is_fixed_asset` | Check | ❌ | 是否為固定資產 | +| `asset` | Link → Asset | ❌ | 資產編號 | +| `enable_deferred_revenue` | Check | ❌ | 是否啟用遞延收入 | +| `service_start_date` | Date | ❌ | 服務開始日期 | +| `service_end_date` | Date | ❌ | 服務結束日期 | + +--- + +### Purchase Invoice(採購發票) + +#### Header 核心欄位(與 Sales Invoice 類似) + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `supplier` | Link → Supplier | ✅ | 供應商 | +| `posting_date` | Date | ✅ | 入帳日期 | +| `company` | Link → Company | ✅ | 公司 | +| `taxes_and_charges` | Link → Purchase Taxes and Charges Template | ✅ | 稅金模板 | +| `is_return` | Check | ✅ | 是否為退貨 | +| `return_against` | Data | ✅ | 退回原始發票 | +| `grand_total` | Currency | ✅ | 總額 | +| `base_grand_total` | Currency | ✅ | 公司幣總額 | +| `update_stock` | Check | ✅ | 是否更新庫存 | +| `is_paid` | Check | ✅ | 是否為即期付款 | +| `status` | Select | ✅ | 狀態 | + +#### Purchase Invoice Item 核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `item_code` | Link → Item | ✅ | 商品 | +| `qty` | Float | ✅ | 接收數量 | +| `rejected_qty` | Float | ❌ | 拒收數量 | +| `stock_qty` | Float | ✅ | 庫存單位數量 | +| `rate` | Currency | ✅ | 單價 | +| `amount` | Currency | ✅ | 金額 | +| `expense_account` | Link → Account | ✅ | 費用科目 | +| `cost_center` | Link → Cost Center | ✅ | 成本中心 | +| `warehouse` | Link → Warehouse | ❌ | 接收倉庫 | +| `rejected_warehouse` | Link → Warehouse | ❌ | 拒收倉庫 | +| `item_tax_template` | Link → Item Tax Template | ✅ | 商品稅金模板 | +| `purchase_order` | Link → Purchase Order | ❌ | 採購訂單 | +| `purchase_receipt` | Link → Purchase Receipt | ❌ | 採購收據 | +| `project` | Link → Project | ❌ | 專案 | +| `is_fixed_asset` | Check | ❌ | 是否為固定資產 | +| `asset_location` | Link → Asset Location | ❌ | 資產位置 | + +--- + +### Payment Entry(付款/收款) + +#### Header 核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `payment_type` | Select | ✅ | 付款類型(Receive/Pay/Internal Transfer) | +| `posting_date` | Date | ✅ | 入帳日期 | +| `company` | Link → Company | ✅ | 公司 | +| `party_type` | Link → DocType | ✅ | 對象類型(Customer/Supplier) | +| `party_name` | Data | ✅ | 對象名稱 | +| `paid_from` | Link → Account | ✅ | 付款科目(借方) | +| `paid_from_account_currency` | Link → Currency | ✅ | 付款科目幣別 | +| `paid_to` | Link → Account | ✅ | 收款科目(貸方) | +| `paid_to_account_currency` | Link → Currency | ✅ | 收款科目幣別 | +| `paid_amount` | Currency | ✅ | 支付金額 | +| `base_paid_amount` | Currency | ✅ | 公司幣支付金額 | +| `received_amount` | Currency | ✅ | 收到金額 | +| `base_received_amount` | Currency | ✅ | 公司幣收到金額 | +| `source_exchange_rate` | Float | ✅ | 來源匯率 | +| `target_exchange_rate` | Float | ✅ | 目標匯率 | +| `reference_no` | Data | ❌ | 支票/參考編號 | +| `reference_date` | Date | ❌ | 支票日期 | +| `clearance_date` | Date | ❌ | 清償日期 | +| `mode_of_payment` | Link → Mode of Payment | ❌ | 付款方式 | +| `bank_account` | Link → Company Bank Account | ❌ | 公司銀行帳戶 | +| `party_bank_account` | Link → Party Bank Account | ❌ | 對方銀行帳戶 | +| `status` | Select | ✅ | 狀態 | +| `amended_from` | Link → Payment Entry | ❌ | 來源修改 | + +#### Payment Entry Reference(明細子表) + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `reference_doctype` | Link → DocType | ✅ | 引用文件類型 | +| `reference_name` | Data | ✅ | 引用文件編號 | +| `total_amount` | Currency | ✅ | 總金額 | +| `outstanding_amount` | Currency | ✅ | 未清金額 | +| `allocated_amount` | Currency | ✅ | 已分配金額 | +| `payment_term` | Data | ❌ | 付款條件 | + +--- + +### Journal Entry(日記帳) + +#### Header 核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `voucher_type` | Select | ✅ | 分录類型 | +| `naming_series` | Select | ✅ | 編號系列 | +| `posting_date` | Date | ✅ | 入帳日期 | +| `company` | Link → Company | ✅ | 公司 | +| `cheque_no` | Data | ❌ | 支票號碼 | +| `cheque_date` | Date | ❌ | 支票日期 | +| `total_debit` | Currency | ✅ | 總借方 | +| `total_credit` | Currency | ✅ | 總貸方 | +| `difference` | Currency | ✅ | 差額 | +| `user_remark` | Small Text | ❌ | 使用者備註 | +| `multi_currency` | Check | ❌ | 是否多幣別 | +| `clearance_date` | Date | ❌ | 清償日期 | +| `is_opening` | Select | ❌ | 是否為期初 | +| `amended_from` | Link → Journal Entry | ❌ | 來源修改 | + +#### Journal Entry Account(分录子表)核心欄位 + +| 欄位 | 類型 | 必填 | 說明 | +|------|------|------|------| +| `account` | Link → Account | ✅ | 會計科目 | +| `account_currency` | Link → Currency | ✅ | 科目幣別 | +| `exchange_rate` | Float | ✅ | 匯率 | +| `debit_in_account_currency` | Currency | ❌ | 科目幣別借方 | +| `credit_in_account_currency` | Currency | ❌ | 科目幣別貸方 | +| `debit` | Currency | ❌ | 公司幣借方 | +| `credit` | Currency | ❌ | 公司幣貸方 | +| `cost_center` | Link → Cost Center | ❌ | 成本中心 | +| `is_advance` | Select | ❌ | 是否為預付款 | +| `party_type` | Link → DocType | ❌ | 對象類型 | +| `party` | Dynamic Link | ❌ | 對象 | +| `project` | Link → Project | ❌ | 專案 | +| `reference_type` | Link → DocType | ❌ | 參考文件類型 | +| `reference_name` | Data | ❌ | 參考文件編號 | + +--- + +## Payment Ledger Entry(付款分類帳) + +> Payment Ledger Entry 追蹤 AR/AP 的餘額變化,由 ERPNext 自動生成。 + +| 欄位 | 類型 | 說明 | +|------|------|------| +| `posting_date` | Date | 入帳日期 | +| `account_type` | Select | 科目類型 | +| `account` | Link → Account | 會計科目 | +| `party_type` | Link → DocType | 對象類型 | +| `party` | Dynamic Link | 對象 | +| `voucher_type` | Link → DocType | 來源文件類型 | +| `voucher_no` | Dynamic Link | 來源文件編號 | +| `against_voucher_type` | Link → DocType | 對應憑證類型 | +| `against_voucher_no` | Dynamic Link | 對應憑證編號 | +| `amount` | Currency | 金額 | +| `account_currency` | Link → Currency | 科目幣別 | +| `amount_in_account_currency` | Currency | 科目幣別金額 | +| `delinked` | Check | 是否已解綁 | +| `company` | Link → Company | 公司 | +| `cost_center` | Link → Cost Center | 成本中心 | +| `project` | Link → Project | 專案 | +| `due_date` | Date | 到期日 | +| `finance_book` | Link → Finance Book | 財務簿 | +| `remarks` | Text | 備註 | +| `voucher_detail_no` | Data | 明細編號 | + +--- + +## 會計數據導入的 4 種方式 + +### 方式 A: API 創建交易文件(推薦 ⭐) + +直接創建 `sales_invoice`、`purchase_invoice`、`payment_entry` 或 `journal_entry`。 +ERPNext 的 `controller` 會自動生成 GL Entry。 + +**優點**: +- 自動化程度高 +- 觸發完整業務邏輯(庫存、AR/AP、稅務) +- 可追溯來源文件 + +**缺點**: +- 需要遵循 ERPNext 的表單結構 +- 外幣交易需要處理匯率 + +**API 示例**: +```bash +# 創建 Sales Invoice +curl -X POST \ + -H "Authorization: token :" \ + -H "Content-Type: application/json" \ + -d '{ + "customer": "Customer Name", + "posting_date": "2026-04-18", + "company": "Company Name", + "items": [ + { + "item_code": "Item Code", + "qty": 1, + "rate": 100, + "income_account": "Revenue - Company", + "cost_center": "Main - Company" + } + ], + "taxes": [...] + }' \ + https:///api/resource/Sales%20Invoice + +# 提交(Submit) +curl -X POST \ + -H "Authorization: token :" \ + https:///api/method/frappe.desk.form.submit/submit?doclist=%5B%22INV-00001%22%5D +``` + +--- + +### 方式 B: API 直接創建 Journal Entry(最靈活) + +手動建立日記帳分录,適合批次導入。 + +**優點**: +- 完全控制借貸分录 +- 適合複雜的會計調整 +- 不受業務流程限制 + +**缺點**: +- 不會自動更新 AR/AP +- 不會觸發庫存變動 +- 需要手動處理 Payment Ledger Entry + +**API 示例**: +```bash +curl -X POST \ + -H "Authorization: token :" \ + -H "Content-Type: application/json" \ + -d '{ + "company": "Company Name", + "posting_date": "2026-04-18", + "accounts": [ + { + "account": "Revenue - Company", + "credit_in_account_currency": 1000, + "debit_in_account_currency": 0, + "cost_center": "Main - Company" + }, + { + "account": "Cash - Company", + "debit_in_account_currency": 1000, + "credit_in_account_currency": 0, + "cost_center": "Main - Company" + } + ] + }' \ + https:///api/resource/Journal%20Entry +``` + +--- + +### 方式 C: Data Import(內建工具) + +ERPNext 內建 Data Import 功能,支援 CSV/JSON 批量導入。 + +**優點**: +- 無需寫程式碼 +- 支援映射模板 +- 適合歷史數據導入 + +**缺點**: +- 無法自訂業務邏輯 +- 外幣交易處理較複雜 +- 錯誤處理較弱 + +--- + +### 方式 D: 自訂 API Endpoint + +在 hooks.py 註冊自訂 API,處理特定業務邏輯。 + +**優點**: +- 完全控制業務邏輯 +- 可處理複雜轉換 +- 可與外部系統整合 + +**缺點**: +- 需要維護自訂程式碼 +- 更新 ERPNext 版本時可能衝突 + +--- + +## 基礎資料依賴 + +在導入會計數據前,以下 Doctype 必須先存在: + +| 基礎資料 | Doctype | 用途 | +|---------|---------|------| +| 公司 | `company` | 所有分录的歸屬 | +| 會計科目 | `account` | 借貸方科目 | +| 成本中心 | `cost_center` | 成本歸屬 | +| 客戶 | `customer` | 往來對象 | +| 供應商 | `supplier` | 往來對象 | +| 商品 | `item` | 商品/服務 | +| 會計年度 | `fiscal_year` | 年度設定 | +| 幣別 | `currency` | 外幣交易 | +| 銀行帳戶 | `bank_account` | 付款來源 | +| 倉庫 | `warehouse` | 庫存管理 | +| 計量單位 | `uom` | 商品單位 | +| 稅金模板 | `taxes_and_charges_template` | 稅務計算 | +| 專案 | `project` | 專案管理 | +| 財務簿 | `finance_book` | 財務簿設定 | + +### 科目表結構(Chart of Accounts) + +ERPNext 的科目表是**階層式結構**,類似樹狀: + +``` +資產 (Asset) +├── 流動資產 (Current Asset) +│ ├── 現金 (Cash) +│ │ ├── 現金 - 公司 (Cash - Company) +│ │ └── 銀行存款 - 公司 (Bank - Company) +│ └── 應收帳款 (Accounts Receivable) +│ └── 應收帳款 - 公司 (Debtors - Company) +└── 固定資產 (Fixed Asset) + └── 設備 - 公司 (Equipment - Company) + +負債 (Liability) +├── 流動負債 (Current Liability) +│ └── 應付帳款 (Accounts Payable) +│ └── 應付帳款 - 公司 (Creditors - Company) +└── 長期負債 (Long Term Liability) + +權益 (Equity) +└── 業主權益 (Capital) + └── 資本 - 公司 (Capital - Company) + +收入 (Income) +└── 營業收入 (Operating Income) + └── 銷售收入 - 公司 (Sales - Company) + +支出 (Expense) +└── 營業支出 (Operating Expense) + └── 商品成本 - 公司 (Cost of Goods Sold - Company) +``` + +--- + +## 自訂 API 開發架構 + +### 目錄結構 + +``` +/home/tony/erpnext-analysis/ +├── erpnext/ +│ └── accounts/ +│ ├── api/ +│ │ ├── __init__.py +│ │ ├── v1/ +│ │ │ ├── __init__.py +│ │ │ ├── imports.py # 導入 API +│ │ │ ├── reconciliation.py # 對帳 API +│ │ │ └── reports.py # 報表 API +│ │ └── utils.py # 公用函數 +│ ├── doctype/ +│ │ ├── gl_entry/ +│ │ ├── sales_invoice/ +│ │ └── purchase_invoice/ +│ ├── general_ledger.py # 核心 GL 邏輯 +│ └── utils.py # 公用函數 +└── ERPNext_Accounting_Structure.md # 本文件 +``` + +### hooks.py 註冊自訂 API + +```python +# erpnext/hooks.py 中加入 + +get_api_versions = lambda: [ + { + "version": "v1", + "path": "/api/method/accounts.api.v1.*" + } +] +``` + +### 自訂 API 模組範例 + +```python +# accounts/api/v1/imports.py +import frappe +from frappe import _ +from frappe.utils import nowdate + +@frappe.whitelist() +def import_sales_invoice(data): + """ + 批量導入銷售發票 + :param data: JSON 格式的發票數據 + """ + invoices = frappe.parse_json(data) + results = [] + + for inv_data in invoices: + try: + invoice = frappe.get_doc({ + "doctype": "Sales Invoice", + "customer": inv_data.get("customer"), + "posting_date": inv_data.get("posting_date") or nowdate(), + "company": inv_data.get("company"), + "items": [ + { + "item_code": item.get("item_code"), + "qty": item.get("qty", 1), + "rate": item.get("rate"), + "income_account": item.get("income_account"), + "cost_center": item.get("cost_center") + } + for item in inv_data.get("items", []) + ] + }) + invoice.insert() + invoice.submit() # 提交(生成 GL Entry) + results.append({ + "name": invoice.name, + "status": "success" + }) + except Exception as e: + results.append({ + "customer": inv_data.get("customer"), + "status": "error", + "message": str(e) + }) + + return {"results": results} +``` + +--- + +## 關鍵程式邏輯 + +### general_ledger.py 核心函數 + +| 函數 | 行號 | 說明 | +|------|------|------| +| `make_gl_entries()` | L28 | 創建 GL Entry | +| `process_gl_map()` | L189 | 處理分錄映射 | +| `save_entries()` | L407 | 保存分錄 | +| `make_entry()` | L425 | 創建單筆分錄 | +| `process_debit_credit_difference()` | L472 | 處理借貸差額 | +| `make_round_off_gle()` | L550 | 創建四捨五入分錄 | +| `make_reverse_gl_entries()` | L682 | 創建反向分錄(取消) | +| `check_freezing_date()` | L794 | 檢查會計凍結日期 | +| `validate_against_pcv()` | L818 | 驗證與期間關閉憑證 | + +### accounts/utils.py 核心函數 + +| 函數 | 行號 | 說明 | +|------|------|------| +| `get_fiscal_year()` | L63 | 獲取會計年度 | +| `get_balance_on()` | L202 | 獲取科目餘額 | +| `get_account_name()` | L1321 | 獲取科目名稱 | +| `get_company_default()` | L1158 | 獲取公司預設值 | +| `reconcile_against_document()` | L509 | 文件對帳 | +| `update_gl_entries_after()` | L1652 | 更新之後的分錄 | +| `get_voucherwise_gl_entries()` | L1796 | 獲取憑證相關分錄 | +| `get_stock_accounts()` | L1853 | 獲取庫存科目 | +| `create_payment_ledger_entry()` | L2135 | 創建付款分類帳 | +| `update_voucher_outstanding()` | L2158 | 更新憑證未清金額 | +| `run_ledger_health_checks()` | L2608 | 執行分類帳健康檢查 | + +--- + +## 下一步建議 + +### 目前狀態 + +你目前**沒有運行中的 ERPNext 實例**,要開發 API 需要: + +1. **部署環境**: 建立 Frappe bench(需要 Python 3.10+, PostgreSQL, Redis) +2. **安裝 ERPNext**: `bench get-app erpnext` +3. **建立公司與會計科目**: 設定 Chart of Accounts +4. **開發 API**: 在自訂 app 或 ERPNext 目錄下寫自訂 API + +### 可選方向 + +- **(A)** 繼續靜態分析其他 Doctype 結構(如 Payment Entry 的 reference 機制) +- **(B)** 規劃 ERPNext bench 部署的步驟和 Docker 設定 +- **(C)** 分析特定場景的導入流程(例如:批量導入發票 → 自動生成 GL Entry) + +### 推薦做法 + +對於**導入會計數據**的需求,建議優先考慮: + +1. **少量數據** → 使用方式 A(API 創建交易文件) +2. **大量歷史數據** → 使用方式 C(Data Import) +3. **特殊業務邏輯** → 使用方式 D(自訂 API Endpoint) +4. **複雜調整分录** → 使用方式 B(Journal Entry) + +--- + +## 參考資源 + +- ERPNext 官方文件: https://docs.erpnext.com/ +- GitHub 原始碼: https://github.com/frappe/erpnext +- Frappe 框架文件: https://frappeframework.com/docs + +--- + +> **Generated by Nexus** | 2026-04-18