☰
ERPNext v12.2.0 深度解析:固定资产模块重构与会计引擎优化
2026/9/30 1:54:56 网站建设 项目流程
  • 后端
  • 企业应用

【免费下载链接】erpnext

Free and Open Source Enterprise Resource Planning (ERP)

项目地址:https://gitcode.com/GitHub_Trending/er/erpnext
点击查看免费下载

ERPNext v12.2.0 是本仓库(Free and Open Source Enterprise Resource Planning)在 v12 稳定周期内的一次关键功能迭代。本次版本重点围绕固定资产(Fixed Asset)生命周期管理展开五项实质性增强,同时对 Landed Cost Voucher 的过账逻辑、BOM 更新工具、永续盘存下的账实同步以及 Email Campaign 模板渲染进行了工程化改进。阅读本文后,你将完整掌握该版本对固定资产主数据、CWIP 资本化、多资产调拨、月度折旧等工作流的变更细节,并理解其背后的源码实现。

一、固定资产(Fixed Asset)模块的五项核心增强

v12.2.0 在固定资产领域的一次集中升级,覆盖了从资产类别配置、采购入库、资产主数据到资产调拨与折旧计提的完整链路。

1. "Enable CWIP" 选项从 Asset Settings 迁移至 Asset Category

变更内容:在早期版本中,资本性在建工程(Capital Work in Progress,CWIP)会计处理由"资产设置(Asset Settings)"中的全局开关控制。v12.2.0 将"Enable CWIP" 选项下移到资产类别(Asset Category)层级,使 CWIP 会计能力可以在不同资产类别间差异化启用。

源码印证:在 asset_category.py 中,AssetCategory文档类型新增了enable_cwip_accounting: DF.Check字段(第 25 行),并在校验阶段通过validate_cwip_accounts()(第 110-127 行)强制约束:一旦启用 CWIP 会计,每个公司行的账户表必须配置capital_work_in_progress_account,否则抛出"Missing Account"错误——同时也允许在 Company 主数据中设置默认 CWIP 账户作为兜底:

def validate_cwip_accounts(self): if self.enable_cwip_accounting: missing_cwip_accounts_for_company = [] for d in self.accounts: if not d.capital_work_in_progress_account and not frappe.get_cached_value( "Company", d.company_name, "capital_work_in_progress_account" ): missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name)) if missing_cwip_accounts_for_company: frappe.throw(msg, title=_("Missing Account"))

同时,asset.py 提供了统一的判断入口is_cwip_accounting_enabled(asset_category)(第 1165-1166 行),直接按资产类别读取enable_cwip_accounting;purchase_receipt.py 在第 277-283 行创建自动资产前调用该函数校验 CWIP 账户,避免"资产已创建"后才暴露缺账户的错误。

2. 从 Purchase Receipt Item 表中移除 Asset 链接

变更内容:采购收货(Purchase Receipt)的明细行(Item 表)中原有的"资产(Asset)"链接字段被移除。这意味着资产与采购单据的关联方式从"明细行级联"转向"批量自动创建 + 资产主数据自维护"。

源码印证:在 purchase_receipt.py 中,资产的生成改为update_assets()(第 479 行起),通过frappe.db.get_all("Asset", ...)按收货明细批量查询既有资产并更新其采购金额(purchase_amount = flt(valuation_rate) * asset.asset_quantity);mapper.py 仍保留is_fixed_asset、asset_location、asset_category等映射字段,用于从采购单(Purchase Order)映射到收货单,说明"资产类别"仍是收货流程的关键驱动字段。

3. Enhanced Asset master(资产主数据增强)

变更内容:资产主数据(Asset 单据)本身得到增强。结合后续月度折旧与多资产移动的支持,Asset主文档在 v12.2.0 中对折旧计划、财务账簿(Finance Book)配置的承载能力进一步提升。

源码印证:在 asset.py 中,资产的折旧计划基于Asset Finance Book表中的frequency_of_depreciation(折旧频率,第 76 行)与total_number_of_depreciations等字段展开计算(第 1019、1046 行),并校验frequency_of_depreciation必须大于 0(第 672 行);asset_category.py 的get_item_details()白名单接口(第 1111-1136 行)会从资产类别财务账簿读取折旧方法、折旧频率、残值率、每日按比例计提(daily_prorata_based)、轮班制(shift_based)等参数,回填到资产主数据,形成"类别模板 → 资产实例"的参数继承链。

4. Asset Movement 支持多资产同时移动

变更内容:Asset Movement(资产移动)从"一次移动单个资产"升级为通过子表 Asset Movement Item 支持批量移动多个资产,可在同一张调拨单中登记多条资产的 Issue(发放)、Receipt(接收)、Transfer(转移)、Transfer and Issue(转移并发放)。

源码印证:在 asset_movement.py 中,文档类型通过assets: DF.Table[AssetMovementItem]子表承载多行资产(第 25 行),提交/取消时遍历子表逐行处理:

  • validate()(第 33-37 行)对每行执行资产状态校验(Draft/Scrapped/Sold 资产不可转移)、公司归属校验、地点/员工校验与交易日期不能早于该资产上一次移动日期的时序校验;
  • on_submit()/on_cancel()(第 116-120 行)调用set_latest_location_and_custodian_in_asset(),对子表每一行查询该资产最新的移动记录(按transaction_date、name倒序取首条),回写Asset的location与custodian(第 122-159 行),并记录资产活动日志。

前端 asset_movement.js 中的frappe.ui.form.on("Asset Movement Item", ...)处理器用于管理子表行的交互。配套测试 test_asset_movement.py 覆盖了多资产移动与取消后回滚到上一位置/保管人的场景。

5. 引入月度折旧(Monthly Depreciation)

变更内容:折旧频率体系正式支持月度折旧。此前折旧频率通常以年/月整数表达,v12.2.0 明确将月度作为标准折旧周期纳入资产类别与财务账簿的配置选项。

源码印证:frequency_of_depreciation字段贯穿折旧计算全链路:

  • 在 asset_category.py 的validate_finance_books()(第 36-42 行)中,要求"Total Number of Depreciations"与"Frequency of Depreciation"必须大于 0;
  • 在 asset.py 中,折旧计划按frequency_of_depreciation推进剩余折旧期数与计提金额(第 1019、1046 行);
  • 月度折旧配合daily_prorata_based(按日比例计提)可精确处理资产在月中投入使用时的首月折旧分摊。

二、Landed Cost Voucher:GL 分录直接过账至各 Charges 账户

变更内容:分摊到岸成本凭证(Landed Cost Voucher,LCV)的 GL 分录,现在直接针对每个费用(Charges)账户分别过账,而不是合并/汇总到一个兜底费用账户。这一改动使采购成本分摊的科目归集更精细、审计更直观。

源码印证:在 landed_cost_voucher.py 中,核心函数get_item_account_wise_lcv_entries()(第 702-779 行)将 LCV 的税费行按(expense_account, dimension values)分组,为每个科目生成独立的 GL 分录条目:

for account in landed_cost_voucher_doc.taxes: dimensions = get_row_dimensions(account, item, dimension_fields) group_key = (account.expense_account, tuple(dimensions.get(field) for field in dimension_fields)) item_row = charges.setdefault(group_key, frappe._dict( expense_account=account.expense_account, amount=0.0, base_amount=0.0, dimensions=dimensions)) item_row.amount += account.amount * item.get(based_on_field) / total_item_cost

函数注释明确说明:即使同一张凭证中有多行税费指向同一科目、但带不同会计维度值,也会保持为独立分录而非静默合并。费用分摊比例依据distribute_charges_based_on(按金额/数量/重量/手动分配)计算(第 735-738 行),并支持外币税率行(exchange_rate折算)。相关过账入口位于第 454-456 行的make_gl_entries(via_landed_cost_voucher=True)与make_gl_entries(),测试覆盖见 test_landed_cost_voucher.py(含多币种 Shipping Charges 分摊用例,第 680-704 行)。

三、BOM Update Tool 优化

变更内容:物料清单更新工具(BOM Update Tool)在 v12.2.0 中进行了优化,主要针对大批量 BOM 成本/工序更新时的性能与执行可靠性。

源码印证:该工具位于 bom_update_tool.py,通过异步的 bom_update_log 记录执行状态;bom_updation_utils.py 中提供日志标记(via BOM Update Tool)与错误日志封装(doc.log_error("BOM Update Tool Error"))。工具入口在 bom.py 中通过update_bom_creator_status()(第 503-514 行)联动 BOM 创建者状态。此外,manufacturing_settings.py 提供update_bom_costs_automatically开关,可让 BOM 成本在基础数据变更时自动联动更新(第 50 行调用)。测试用例见 test_bom_update_tool.py 与 test_bom.py。

四、永续盘存下强制同步 Stock 与 Account 余额

变更内容:在启用永续盘存(Perpetual Inventory,即库存账户与总账联动)的企业中,v12.2.0强制保证库存(Stock Ledger)余额与会计(Account)余额保持一致,防止因单据时序、重新过账等操作导致两个体系分叉。

源码印证:库存流水核心位于 stock_ledger_entry.py,其同步逻辑与 gl_entry.py 联动;配套测试 test_stock_ledger_entry.py 中专门构造了_Test Company with perpetual inventory(第 1462 行)来验证永续盘存场景下的过账行为。结合 repost_accounting_ledger 与 repost_payment_ledger 等重过账工具,该强制同步机制确保重算后的库存价值与总账分录始终收敛于同一结果。

五、Email Campaign 渲染邮件模板

变更内容:邮件营销(Email Campaign)在发送时改为先渲染 Email Template(支持 Jinja 模板语法)再发送,使得每封邮件可以基于收件人上下文(如 Lead、Contact、Email Group)动态生成个性化的主题与正文。

源码印证:在 email_campaign.py 中,发送流程首先读取 Email Template 并构建渲染上下文:

# Build context for template rendering if campaign_for != "Email Group": context = {"doc": frappe.get_doc(campaign_for, recipient)} else: context = {"doc": frappe.get_doc("Email Group", recipient)} # Render template subject = frappe.render_template(email_template.get("subject"), context, restrict_globals=True) content = frappe.render_template(email_template.response_, context, restrict_globals=True)

模板渲染使用frappe.render_template(基于 Jinja),并以restrict_globals=True限制全局函数暴露以增强安全性。渲染后的内容通过frappe.sendmail异步队列发送(queue_separately=True),同时写入 Communication 记录(communication_medium="Email"、sent_or_received="Sent")。取消订阅逻辑(unsubscribe_recipient)会按 Email Group 成员或 Lead/Contact 维度同步更新订阅状态。

版本信息与升级建议

v12.2.0 属于 ERPNext v12 生命周期内的补丁特性版本。从本次变更看,对已有部署影响较大的主要是两点:一是CWIP 开关的位置迁移——升级后需在资产类别层重新确认 CWIP 会计启用状态并补齐Capital Work in Progress账户;二是Purchase Receipt 明细行 Asset 链接移除——资产主数据与采购凭证的关联改由自动创建与主数据更新机制承载,涉及固定资产的采购流程建议在升级后做回归验证。

更多版本演进可参考本仓库 change_log 目录下的历史发布说明,固定资产相关源码集中在 assets/doctype,制造与库存侧的联动实现分别见 manufacturing/doctype/bom_update_tool 与 stock/doctype/purchase_receipt。

  • 后端
  • 企业应用

【免费下载链接】erpnext

Free and Open Source Enterprise Resource Planning (ERP)

项目地址:https://gitcode.com/GitHub_Trending/er/erpnext
点击查看免费下载

相关推荐

上一篇:防止调试数据丢失:gdbgui中的会话状态持久化方案
下一篇:Cloudflare Computer exec-wire协议解析:命令执行流式事件的线格式设计(完整指南)

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询