- 后端
- 企业应用
【免费下载链接】erpnext
Free and Open Source Enterprise Resource Planning (ERP)
Bank Clearance Detail 是 ERPNext 会计模块(Accounts)中Bank Clearance(银行清算)工具的核心子表 DocType,以表格字段的形式承载每一笔待清算交易的明细行。本文以该子表的字段定义与父表控制器的真实实现为骨架,讲解它的数据结构、数据来源(Journal Entry / Payment Entry / Purchase Invoice / POS 收款四类来源)、Clearance Date 的批量回写流程与校验规则,帮助读者掌握 ERPNext 银行清算工具背后的数据模型与调用链,并能在实际对账场景中正确使用"Get Payment Entries / Update Clearance Date"操作。
一、Bank Clearance Detail 是什么:父表工具的行明细
在 ERPNext 中,Bank Clearance 是一个 Single(issingle: 1)类型的工具文档,用于"更新银行交易的实际到账/清账日期"(父表 README 原文:Tool to update realization dates for banking transactions)。而 Bank Clearance Detail 正是它的行明细——子表 README 的一行描述直接点明了它的定位:
Detail of transaction for parent Bank Clearance.
在父表 bank_clearance.json 中,payment_entries字段被定义为:
{ "allow_bulk_edit": 1, "fieldname": "payment_entries", "fieldtype": "Table", "label": "Payment Entries", "options": "Bank Clearance Detail" }即父表的payment_entries(支付条目)表格字段,其options直接指向Bank Clearance Detail,二者构成一对多的主从关系。子表自身在 bank_clearance_detail.json 中被标记为"istable": 1(Table DocType,不独立创建单据,只作为行数据存在)、"editable_grid": 1(网格内可编辑)、"quick_entry": 1与"row_format": "Dynamic"。
二、字段结构全景:九个字段逐一解析
子表的field_order与fields定义了 9 个字段(共 2 列布局),是整篇文章的数据核心。完整定义见 bank_clearance_detail.json,字段明细如下:
| 字段名 (fieldname) | 标签 (label) | 字段类型 (fieldtype) | 关键属性 | 说明 |
|---|---|---|---|---|
payment_document | Payment Document | Link → DocType | — | 记录来源单据类型(如 Journal Entry、Payment Entry、Purchase Invoice、Sales Invoice) |
payment_entry | Payment Entry | Dynamic Link →payment_document | in_list_view: 1,网格内 2 列 | 动态链接字段,根据payment_document指向具体单据名称;旧字段名为voucher_id |
against_account | Against Account | Data | read_only: 1,in_list_view: 1,宽 15 | 对方科目/往来方(供应商、客户等),由系统填充,不可手改 |
amount | Amount | Data | read_only: 1,in_list_view: 1 | 金额,注意类型是 Data(格式化字符串,见下文"借/贷方向"说明);旧字段名为debit |
posting_date | Posting Date | Date | read_only: 1 | 过账日期 |
cheque_number | Cheque Number | Data | read_only: 1,in_list_view: 1 | 支票/参考号,取自各来源单据的参考号字段 |
cheque_date | Cheque Date | Date | read_only: 1,in_list_view: 1 | 支票日期 |
clearance_date | Clearance Date | Date | in_list_view: 1,唯一可编辑字段 | 清账/实际到账日期,用户在此输入新日期后批量回写来源单据 |
几点值得注意的设计细节:
- Dynamic Link 组合:
payment_entry使用Dynamic Link,其options: "payment_document"表示链接目标类型由同行的payment_document字段动态决定——这是 ERPNext 中"多类型单据引用"的标准做法,避免为每类单据单独建链接字段。类型注释(bank_clearance_detail.py 中的DF.DynamicLink)与之一致。 - amount 是 Data 而非 Currency:金额字段被定义为
Data且read_only,因为父控制器在填充时会把数值格式化为带借/贷方向的展示字符串(详见第三节),因此这里不需要货币数值语义。 - 旧字段名痕迹:多个字段保留
oldfieldname(voucher_id、against_account、debit、posting_date、cheque_number、cheque_date、clearance_date),表明该子表自 2013 年("creation": "2013-02-22")以来历经字段演进,oldfieldname用于历史数据迁移兼容。 - 权限与只读策略:父表为
read_only: 1、hide_toolbar: 1,仅授予Accounts User角色(create/read/write/share);子表本身permissions为空(Table 类型不单独配置权限,随父文档权限走),所有明细字段默认只读,仅clearance_date开放编辑,确保用户只能修改清算日期而不能篡改交易事实。
三、数据从哪来:get_payment_entries 与 hooks 调用链
子表行数据由父表 bank_clearance.py 中的白名单方法get_payment_entries(L42–L90)批量填充。其核心逻辑:
@frappe.whitelist() def get_payment_entries(self): if not (self.from_date and self.to_date): frappe.throw(_("From Date and To Date are Mandatory")) if not self.account: frappe.throw(_("Account is mandatory to get payment entries")) entries = [] precision = cint(frappe.db.get_default("currency_precision")) or 2 for method_name in frappe.get_hooks("get_payment_entries_for_bank_clearance"): entries += ( frappe.get_attr(method_name)( self.from_date, self.to_date, self.account, self.bank_account, self.include_reconciled_entries, self.include_pos_transactions, ) or [] ) entries = sorted(entries, key=lambda k: getdate(k["posting_date"])) self.set("payment_entries", []) ... for d in entries: row = self.append("payment_entries", {}) amount = flt(d.get("debit", 0)) - flt(d.get("credit", 0)) ... formatted_amount = fmt_money(abs(amount), precision, d.account_currency) d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr")) d.posting_date = getdate(d.posting_date) d.pop("credit"); d.pop("debit"); d.pop("account_currency") row.update(d)要点:
- Hook 驱动:方法通过
frappe.get_hooks("get_payment_entries_for_bank_clearance")遍历所有注册的取数函数,支持多应用扩展。当前仓库在 hooks.py 注册了默认实现:get_payment_entries_for_bank_clearance = ( "erpnext.accounts.doctype.bank_clearance.bank_clearance.get_payment_entries_for_bank_clearance" ) - 四类来源单据(
bank_clearance.pyL183–L391 的get_payment_entries_for_bank_clearance):- Journal Entry(日记账分录,L188–L229):按
account与name分组聚合,取cheque_no/cheque_date、借贷合计、against_account、clearance_date,过滤docstatus == 1且is_opening == "No"; - Payment Entry(付款单,L231–L303):覆盖
paid_from == account或paid_to == account两种情况,通过Case表达式将付款额、税费、收款额折算为 debit/credit,并取reference_no/reference_date作为支票号/支票日期; - Purchase Invoice(已付款)(L305–L341):
is_paid == 1且cash_bank_account == account的采购发票,取bill_no作为支票号; - POS 收款(Sales Invoice Payment)(L343–L382):仅当勾选
include_pos_transactions时查询,取reference_no与收款金额。
- Journal Entry(日记账分录,L188–L229):按
- 过滤已清算条目:默认(不勾选
include_reconciled_entries)只取clearance_date为空(含 MySQL 的'0000-00-00'/ Postgres 的 NULL)的未清算交易;查询语句中对数据库方言做了兼容处理。 - 金额方向:
debit - credit为正则追加 "Dr"(借),为负则追加 "Cr"(贷),金额以fmt_money按账户币种格式化后存入 Data 类型的amount字段——这也是字段定义中 amount 用 Data 的原因。 - 排序:结果按
posting_date升序排列后逐行append("payment_entries", ...)写入子表。
四、Clearance Date 更新流程与校验规则
用户填写各行clearance_date后,点击"Update Clearance Date",调用父控制器update_clearance_date(bank_clearance.py L92–L180)。流程分三步:
1. 行级校验(L99–L132)
def validate_entry(d): is_valid = True if not d.payment_document: invalid_document.append(str(d.idx)) is_valid = False if d.clearance_date and d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): invalid_cheque_date.append(str(d.idx)) is_valid = False return is_valid规则有两类:① 每行必须存在payment_document(来源类型),否则报错"Payment document required for row(s)";② 清算日期不得早于支票日期(clearance_date < cheque_date视为非法),报错"Clearance date must be after cheque date for row(s)"。非法行会以msgprint汇总列出行号(idx)并中止本次更新。
2. 收集待更新行(L111–L116):通过校验且(clearance_date有值或勾选了include_reconciled_entries)的行才进入entries_to_update;若没有任何待更新行,提示"Clearance Date not mentioned"。
3. 逐行回写来源单据(L138–L180):
- Sales Invoice 特例:若
payment_document == "Sales Invoice",更新的是 POS 收款子表Sales Invoice Payment中匹配parent == payment_entry、account == self.account、amount > 0记录的clearance_date(使用frappe.db.set_value),并在销售发票上追加评论; - 其他单据:通过
frappe.get_lazy_doc(d.payment_document, d.payment_entry)取得来源单据,调用payment_entry.db_set("clearance_date", d.clearance_date)回写(注释明确说明"using db_set to trigger notification",即借 db_set 触发更新通知),同时记录评论:Clearance date changed from {old} to {new} via Bank Clearance Tool
更新完成后重新调用self.get_payment_entries()刷新子表并提示"Clearance Date updated"。注意方法开头执行self.check_permission("write"),与父表仅授Accounts User写权限的配置呼应。
五、客户端交互与使用步骤
前端脚本 bank_clearance.js 定义了完整交互:
- 表单加载(onload):自动带入公司默认银行账户(
default_bank_account)到account字段,from_date/to_date默认设为当月的第一天与最后一天; - 账户筛选(set_query):
account只能选择account_type为 Bank/Cash 且非分组(is_group: 0)的科目;bank_account只能选择is_company_account: 1的银行账户; - 按钮逻辑(refresh):表单
disable_save()(工具型 Single 文档不保存),始终显示主按钮"Get Payment Entries";当payment_entries有数据时追加"Update Clearance Date"主按钮; - 调用方式:两个按钮均通过
frappe.call({ method, doc: frm.doc })调用上文的白名单方法,返回后frm.refresh()刷新表格。
典型使用流程:
- 在"会计 → 银行清算(Bank Clearance)"工具中选择银行账户(默认自动带出),确认起始/截止日期;
- 点击Get Payment Entries,系统按第三节的四类来源把未清算交易逐行填充进 Bank Clearance Detail 子表;
- 需要看到历史已清算条目时勾选Include Reconciled Entries,需要纳入 POS 收款时勾选Include POS Transactions;
- 逐行核对并填写实际到账的Clearance Date(不得早于支票日期);
- 点击Update Clearance Date,系统校验后批量回写来源单据(日记账、付款单、采购发票或 Sales Invoice Payment)的
clearance_date,并留下审计评论。
六、设计要点小结
- 主从结构清晰:Bank Clearance(Single 工具文档)→
payment_entries(Table 字段)→ Bank Clearance Detail(istable子表),是 ERPNext "工具型 Single + 明细子表"的典型范式; - 只读快照 + 单点编辑:明细行除
clearance_date外全部read_only,保证来源交易信息不被篡改,用户只需维护"清算日期"这一个状态; - 动态链接解耦:
payment_document(Link→DocType)+payment_entry(Dynamic Link)组合,让一个子表承载多种单据类型而无需扩表; - Hook 扩展点:取数逻辑挂在
get_payment_entries_for_bank_clearancehook 上,其他应用可追加自己的数据来源; - 校验与审计闭环:清算日期 ≥ 支票日期、必填来源类型等行级校验,加上"changed from X to Y via Bank Clearance Tool"评论,为对账操作保留完整可追溯记录。
相关文件索引:子表定义 | 子表控制器 | 父表控制器 | 父表定义 | 客户端脚本 | Hook 注册。
- 后端
- 企业应用
【免费下载链接】erpnext
Free and Open Source Enterprise Resource Planning (ERP)
相关推荐
MCP Inspector工具列表动态更新机制深度解析
MCP Inspector工具列表动态更新机制深度解析 还在为MCP服务器工具列表的实时同步问题烦恼吗?本文将深入剖析MCP Inspector如何实现工具列表
开发工具MCP Clients调试器Gramophone项目架构分析:从应用层到底层模块的完整设计
Gramophone项目架构分析:从应用层到底层模块的完整设计 Gramophone是一款基于Android Media3和Material Design库构建
rippled 账本处理机制深入解析:生命周期、账本流、数据结构与账本清理器
rippled 账本处理机制深入解析:生命周期、账本流、数据结构与账本清理器 导读 本文以 rippled(XRP Ledger 服务器实现)的 Ledger
区块链
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考