完全指南:字段配置、账户映射与 POS 集成原理)
后端企业应用【免费下载链接】erpnextFree and Open Source Enterprise Resource Planning (ERP)项目地址https://gitcode.com/GitHub_Trending/er/erpnext点击查看免费下载Mode of Payment付款方式是 ERPNext 中用于统管客户/供应商以何种方式付款的基础主数据MasterCash、Credit Card、Bank Transfer、Check 等常见付款方式都以它为单位进行维护。本文以 erpnext/accounts/doctype/mode_of_payment/README.md 为骨架结合仓库内该 DocType 的字段定义、校验逻辑、测试用例及 POS、付款单、日记账等下游集成代码完整讲解付款方式的配置方法、默认账户映射机制与底层校验原理帮助你正确搭建一套可复用的收款/付款主数据体系。一、什么是 Mode of Payment一份付款方式主数据在 ERPNext 的会计模块Accounts中付款方式不是一个散落在各单据里的下拉选项而是一张独立的主数据表。官方 README 的一句话定义是Master for modes of payment.付款方式主数据并给出了四个典型示例Cash现金Credit Card信用卡Bank Transfer银行转账Check支票从源码角度看它是一个标准的 Frappe DocType定义于 mode_of_payment.jsondoctype: DocType、document_type: Setup属于设置Setup类文档而不是会提交Submit的业务单据autoname: field:mode_of_payment文档名称直接取自Mode of Payment字段本身因此名称天然唯一icon: wallet、quick_entry: 1支持快速新建入口index_web_pages_for_search: 1、show_name_in_global_search: 1、translated_doctype: 1支持全局搜索、网页索引与翻译。可以这样理解它的定位付款方式是付款手段的字典表它本身不做账但为后续所有涉及收付款的单据POS 发票、付款单、日记账、银行对账等提供用哪种方式收/付以及这笔钱进哪个账户的标准答案。二、字段详解Mode of Payment 的四个核心字段根据 mode_of_payment.json 的field_ordermode_of_payment→enabled→type→accounts主文档共四个字段字段名字段类型必填/唯一取值与说明mode_of_paymentData单行文本reqd: 1、unique: 1付款方式名称如 Cash同时作为文档自动命名来源全局唯一typeSelect下拉否取值固定为Cash、Bank、General、Phone四类用于对付款方式进行归类如现金类、银行类、通用类、手机支付类enabledCheck勾选否默认1是否启用置为未勾选即停用该付款方式accountsTable子表否指向子表 Mode of Payment Account用于按公司配置默认入账账户其中type字段在 JSON 中以options: Cash\nBank\nGeneral\nPhone定义in_standard_filter: 1表明它可直接作为列表页的标准筛选条件。字段mode_of_payment同时设置了in_list_view: 1保证列表视图直接展示名称。Python 侧的类型定义见 mode_of_payment.pyaccounts: DF.Table[ModeofPaymentAccount] enabled: DF.Check mode_of_payment: DF.Data type: DF.Literal[Cash, Bank, General, Phone]三、账户映射Mode of Payment Account 子表与默认账户付款方式真正的记账落点在子表Mode of Payment Account。该子表定义于 mode_of_payment_account.json仅含两个字段字段类型说明companyLink → Company该映射所属公司default_accountLink → Account该公司下的默认账户现金/银行/应收类子表字段描述明确写道Default account will be automatically updated in POS Invoice when this mode is selected.选择该付款方式时默认账户会自动更新到 POS 发票中。这正是整个映射机制的核心用途一个付款方式可以在不同公司下对应不同账户。为保证default_account选得合法前端 mode_of_payment.js 为子表账户设置了查询过滤器frm.set_query(default_account, accounts, function (doc, cdt, cdn) { let d locals[cdt][cdn]; return { filters: [ [Account, account_type, in, [Bank, Cash, Receivable]], [Account, is_group, , 0], [Account, company, , d.company], ], }; });即只能从银行Bank、现金Cash、应收Receivable三类非分组账户中挑选且账户所属公司必须与子表行中的公司一致从源头避免选了个别的公司账户这类低级错误。账户映射的消费方get_bank_cash_account下游单据拿到mode_of_payment后如何反查默认账户核心实现在 erpnext/accounts/doctype/sales_invoice/services/pos.pydef get_bank_cash_account(mode_of_payment: str, company: str) - dict: account frappe.db.get_value( Mode of Payment Account, {parent: mode_of_payment, company: company}, default_account, ) if not account: frappe.throw( _(Please set default Cash or Bank account in Mode of Payment {0}).format( get_link_to_form(Mode of Payment, mode_of_payment) ), title_(Missing Account), ) return {account: account}逻辑要点以(mode_of_payment, company)为键精确查子表default_account查不到即抛错Please set default Cash or Bank account in Mode of Payment ...强制用户把映射补齐防止单据带着空账户过账。同样的查询在 journal_entry.py 中也被复用日记账通过get_bank_cash_account(mode_of_payment, company)获取账户后写入分录。四、保存时的三层校验逻辑付款方式保存validate时mode_of_payment.py 依次执行三个校验def validate(self): self.validate_accounts() self.validate_repeating_companies() self.validate_pos_mode_of_payment()1. 账户与公司一致性校验validate_accounts逐行检查子表default_account所属公司是否与行内company一致mode_of_payment.pyif frappe.get_cached_value(Account, entry.default_account, company) ! entry.company: frappe.throw( _(Account {0} does not match with Company {1} in Mode of Account: {2}).format(...) )即使前端过滤器被绕过后端保存时仍会二次校验防止脏数据入库。2. 公司重复校验validate_repeating_companies同一付款方式下同一公司只允许出现一次mode_of_payment.pyif len(accounts_list) ! len(set(accounts_list)): frappe.throw(_(Same Company is entered more than once))3. POS 引用保护validate_pos_mode_of_payment当勾掉enabled停用某付款方式时会先检查它是否仍被 POS Profile 引用mode_of_payment.pypos_profiles frappe.get_all( Sales Invoice Payment, filters{parenttype: POS Profile, mode_of_payment: self.name}, pluckparent, ) if pos_profiles: frappe.throw(message, title_(Not Allowed))若仍被引用则拒绝停用并提示POS Profile {0} contains Mode of Payment {1}...Please remove them to disable this mode.测试用例对校验逻辑的佐证test_mode_of_payment.py 通过ERPNextTestSuite覆盖了上述行为test_valid_mode_of_payment_saves为_Test Company配置 Cash 账户后可正常保存test_account_of_wrong_company_throws把另一公司的账户挂进来会抛出frappe.ValidationErrortest_repeating_company_throws同一公司出现两行会触发校验异常test_disabling_unreferenced_mode_succeeds未被引用的付款方式可正常停用。值得留意的是test_disabling_mode_referenced_by_pos_profile_is_not_blocked测试注释明确指出一个疑似缺陷validate_pos_mode_of_payment查询的是parenttype POS Profile的Sales Invoice Payment行但 POS Profile 的付款方式实际存放在POS Payment Method子表中过滤器永远不会命中导致引用保护实际失效——测试刻意锁定了这一当前行为以便将来修复该守卫时能被测试捕获。如果你在实施时依赖POS 引用中的付款方式不可停用这一保护请先核实该缺陷在目标版本中的修复状态。五、与业务单据的集成POS、付款单与日记账5.1 POS 发票 / 销售发票付款方式与 POS 的联系最为紧密。销售发票通过POSService把付款方式映射成支付行sales_invoice.pyset_account_for_mode_of_payment将支付行中mode_of_payment对应的get_bank_cash_account(...)账户写入payment.account见 services/pos.pyupdate_multi_mode_option按 POS Profile 中的支付方式POS Payment Method行重建发票的payments子表逐行填充default、mode_of_payment、account、type若某个付款方式未配置默认账户会报错 Please set default Cash or Bank account in Mode of Payment ...services/pos.pyclear_unallocated_mode_of_payments清除金额为 0 的支付行避免空行干扰过账services/pos.py。5.2 付款单Payment Entry付款单上直接带有mode_of_payment链接字段见 payment_entry.json。前端在切换付款方式时调用erpnext.accounts.pos.get_payment_mode_account自动带出账户payment_entry.js后端创建付款单时也会把来源单据的mode_of_payment透传过去payment_entry.py。5.3 银行对账与收银结算银行对账工具Bank Reconciliation Tool创建付款单时支持直接指定mode_of_paymentbank_reconciliation_tool.py银行交易按付款方式归类收银结算Cashier Closing其payments子表同样引用付款方式cashier_closing_payments.py测试中也以{mode_of_payment: Cash, amount: ...}形式构造数据。六、权限模型与角色可见性mode_of_payment.json 定义了清晰的分层权限角色权限Accounts Manager全部create / read / write / share / print / report / emailAccounts User只读 reportHR User / HR Manager / Maintenance Manager / Maintenance User / Purchase Manager / Purchase User / Sales Manager / Sales User仅select可用于下拉引用不能编辑也就是说付款方式主数据默认只有会计管理员能增删改其余业务角色仅能引用这符合主数据集中维护、全局引用的管理惯例。七、编程式维护set_default_account_for_mode_of_payment在集成或测试场景下可通过测试模块导出的辅助函数以代码方式维护默认账户test_mode_of_payment.pydef set_default_account_for_mode_of_payment(mode_of_payment, company, account): mode_of_payment.reload() if frappe.db.exists( Mode of Payment Account, {parent: mode_of_payment.mode_of_payment, company: company} ): frappe.db.set_value( Mode of Payment Account, {parent: mode_of_payment.mode_of_payment, company: company}, default_account, account, ) return mode_of_payment.append(accounts, {company: company, default_account: account}) mode_of_payment.save()该函数遵循已存在则更新、不存在则追加并保存的幂等语义被 bank_clearance/test_bank_clearance.py 与 bank_transaction/test_bank_transaction.py 等测试直接复用可作为二次开发时维护付款方式映射的参考范本。八、实操建议与配置清单基于以上源码事实配置一套可用的付款方式体系建议按以下步骤进行创建主数据新建 Mode of Payment命名建议保持简短唯一Cash、Bank Transfer、Check、UPI 等autoname会直接取字段值作为文档名归类按type选择 Cash / Bank / General / Phone便于列表筛选与报表归类按公司映射账户在 Accounts 子表逐公司添加default_account限 Bank / Cash / Receivable 类非分组账户且公司必须匹配启用状态默认enabled 1停用前先确认没有 POS Profile / 收银结算仍在引用并留意上文提到的 POS 引用保护可能存在的已知缺陷权限控制仅授予 Accounts Manager 写权限其他业务角色保持只读/引用权限。完成以上配置后POS 发票、付款单、日记账、银行对账工具即可统一通过mode_of_payment自动落账到正确的现金/银行账户实现付款方式一处维护、全链路复用。赞分享后端企业应用【免费下载链接】erpnextFree and Open Source Enterprise Resource Planning (ERP)项目地址https://gitcode.com/GitHub_Trending/er/erpnext点击查看免费下载相关推荐RegClient完全指南一站式掌握Docker与OCI Registry客户端开发RegClient完全指南一站式掌握Docker与OCI Registry客户端开发 RegClient是一个用Go语言开发的Docker和OCI Regis云原生镜像仓库开发工具CLINocoBase 主数据源接入 PostgreSQL安装配置、版本要求与字段类型映射完全指南NocoBase 主数据源接入 PostgreSQL安装配置、版本要求与字段类型映射完全指南 导读 本文讲解如何将 PostgreSQL 配置为 NocoBa低代码后端前端人工智能AI 应用工作流自动化Keystone 6 Timestamp 字段完全指南配置选项、GraphQL 行为与数据库映射Keystone 6 Timestamp 字段完全指南配置选项、GraphQL 行为与数据库映射 导读 timestamp 是 Keystone 6 内置的标后端上一篇为什么 Shiori 能免费自托管还这么强大Go 语言书签管理器的终极入门指南下一篇Cycle.js PWA缓存清理实现管理响应式应用存储创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考