人财事物信息化 - bank_transaction.py

该目录下包含多个Python文件,以下是各文件的核心函数功能和业务逻辑总结:

1. bank_transaction.py

功能定位

银行交易主文档的后端逻辑,处理交易数据的验证、提交、匹配等核心流程。

关键函数

  • validate
  • 验证交易日期、金额、账户等必填字段。
  • 检查交易类型(如收入/支出)与金额符号的一致性(如支出金额应为负数)。
  • 关联银行账户,验证账户是否启用及币种匹配。
  • on_submit
  • 提交时触发自动对账逻辑,调用auto_match_party模块匹配客户/供应商(见auto_match_party.py)。
  • 生成会计凭证(如日记账分录),更新账户余额。
  • on_cancel
  • 取消交易时回滚对账记录和会计凭证,恢复账户余额。
  • get_bank_balance
  • 查询指定银行账户的实时余额,用于核对交易数据。

2. auto_match_party.py

功能定位

自动匹配银行交易对应的业务方(客户/供应商),减少人工录入。

关键函数

  • match_party_by_name
  • 根据交易对手方名称模糊匹配客户或供应商主数据,支持拼音首字母匹配(如“华为”匹配“HW”)。
  • match_party_by_account_number
  • 通过交易对手方银行账号匹配关联方的银行信息,需预先在客户/供应商档案中维护银行账号。
  • get_possible_matches
  • 综合名称、账号、交易金额等多维度生成匹配候选列表,按匹配度排序返回。
  • apply_auto_match_rules
  • 执行预设的自动匹配规则(如固定金额对应特定客户),自动填充交易关联方。

3. bank_transaction_upload.py

功能定位

处理银行对账单批量导入,解析文件并生成系统内的银行交易记录。

关键函数

  • parse_csv_upload
  • 解析CSV格式对账单,支持不同银行的列映射配置(如日期、金额、对手方名称的列名适配)。
  • validate_uploaded_data
  • 校验导入数据的格式(如日期格式、金额数值)、唯一性(避免重复导入)。
  • create_bank_transactions
  • 根据解析后的数据批量创建Bank Transaction文档,支持事务性提交(失败时回滚)。
  • handle_duplicate_transactions
  • 通过交易流水号或哈希值检测重复记录,提示用户确认是否覆盖。

4. test_auto_match_party.py & test_bank_transaction.py

功能定位

单元测试文件,验证银行交易相关功能的正确性。

关键逻辑

  • 测试用例覆盖
  • test_auto_match_party:验证不同场景下的自动匹配逻辑(如名称匹配、账号匹配、无匹配时的空值处理)。
  • test_bank_transaction:测试交易创建、提交、取消的状态流转,以及金额验证、账户余额更新等核心流程。
  • 数据模拟
  • 使用frappe.set_userfrappe.get_doc模拟用户操作和数据创建,确保测试隔离性。
  • 断言验证
  • 检查匹配结果是否符合预期、交易提交后状态是否正确、余额变动是否与交易金额一致等。

业务逻辑总结

  1. 数据流转
  • 银行对账单通过bank_transaction_upload.py导入系统,解析为Bank Transaction文档。
  • bank_transaction.py处理交易的增删改查,提交时触发auto_match_party.py匹配业务方,并生成财务凭证。
  1. 自动化目标
  • 通过自动匹配(auto_match_party)减少人工录入错误,提升对账效率。
  • 批量导入(bank_transaction_upload)支持大规模数据处理,适配不同银行的对账单格式。
  1. 财务集成
  • 交易数据与会计模块联动,提交交易时自动更新账户余额,并可生成日记账分录,确保财务数据一致性。

客户化功能增强(待验证)

以下是针对bank_transaction 模块新增功能的设计方案,涉及代码修改和逻辑扩展:

一、功能①:上传银行流水后自动创建 Payment Entry

  1. 修改 bank_transaction_upload.py 批量创建逻辑
# 在 parse_csv_upload 或 create_bank_transactions 中添加自动创建 Payment Entry 的逻辑
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry

def create_bank_transactions(uploaded_data, company):
    transactions = []
    for data in uploaded_data:
        # 创建 Bank Transaction 文档
        bt = frappe.get_doc({
            "doctype": "Bank Transaction",
            "transaction_date": data["date"],
            "amount": data["amount"],
            "party_type": data["party_type"],  # 需从流水解析或自动匹配
            "party": data["party"],
            "bank_account": data["bank_account"],
            "reference_number": data["reference_number"],
            "is_auto_created": True  # 标记为自动创建
        }).insert()

        # 自动创建 Payment Entry(需确保匹配到 party 和账户)
        if bt.party and bt.bank_account:
            pe = get_payment_entry(
                dt=bt.party_type,
                dn=bt.party,
                bank_account=bt.bank_account,
                amount=bt.amount,
                payment_type="Receive" if bt.amount > 0 else "Pay",
                reference_date=bt.transaction_date,
                remarks="Auto-created from bank transaction: {0}".format(bt.name)
            )
            pe.save(ignore_permissions=True)
            bt.payment_entry = pe.name
            bt.save()
    frappe.db.commit()
  1. 关键配置项
  • Bank Transaction 文档中添加字段 auto_create_payment_entry(勾选框),控制是否自动创建。
  • 依赖 auto_match_party 模块提前匹配 party_typeparty,否则跳过自动创建。

二、功能②:解析 OFD 回单并关联 Payment Entry

  1. 新增 OFD 解析工具集成
# 在 bank_transaction.py 中添加 OFD 解析逻辑(需安装 ofdpy 库)
import ofdpy
from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry

@frappe.whitelist()
def attach_and_parse_ofd(payment_entry_name, file_url):
    # 下载 OFD 文件(需处理文件存储路径)
    ofd_content = requests.get(file_url).content

    # 解析 OFD 文本内容(示例,需根据实际版式调整)
    parser = ofdpy.OFDParser(ofd_content)
    text = parser.extract_text()

    # 从文本中提取关键信息(如交易金额、日期、流水号)
    amount = parse_amount_from_text(text)
    transaction_date = parse_date_from_text(text)
    reference_number = parse_ref_from_text(text)

    # 关联到 Payment Entry
    pe = frappe.get_doc("Payment Entry", payment_entry_name)
    pe.append("attachments", {
        "file_url": file_url,
        "is_primary": 1,
        "remarks": "Parsed OFD content: {0}".format(text[:100])
    })

    # 校验回单与 Payment Entry 一致性
    if not (pe.amount == amount and pe.posting_date == transaction_date):
        frappe.throw("回单与付款记录金额或日期不一致")

    pe.save()
    return {"status": "success", "parsed_data": {"amount": amount, "date": transaction_date}}
  1. 文档结构调整
  • Payment Entry 中添加 ofd_attachment 字段(链接到文件)和 parsed_ofd_data 字段(存储解析后的JSON)。
  • 通过 frappe.call 在前端上传OFD文件时触发后端解析函数。

三、功能③:月末对账单完整性与余额校验

  1. 添加月末校验脚本(可定时任务执行)
# 在 bank_transaction.py 中添加校验函数
from frappe.utils import get_last_day_of_month

def validate_monthly_reconciliation(company):
    last_day = get_last_day_of_month()
    bank_accounts = frappe.get_all("Bank Account", filters={"company": company}, pluck="name")

    for account in bank_accounts:
        # 1. 校验流水完整性:系统流水 vs 对账单流水
        system_transactions = frappe.get_all(
            "Bank Transaction",
            filters={
                "bank_account": account,
                "transaction_date": ("between", ["2025-01-01", last_day]),  # 动态日期
                "is_verified": 0  # 未验证的流水
            },
            pluck="reference_number"
        )

        # 模拟获取对账单流水(需对接银行API或导入对账单数据)
        statement_transactions = get_statement_transactions(account, last_day)  # 需实现

        missing_transactions = set(statement_transactions) - set(system_transactions)
        if missing_transactions:
            frappe.log_error(
                f"银行账户 {account} 缺失流水: {missing_transactions}",
                "Monthly Reconciliation Error"
            )

        # 2. 余额一致性校验:系统余额 vs 对账单余额
        system_balance = frappe.get_value("Bank Account", account, "balance")
        statement_balance = get_statement_balance(account, last_day)  # 需实现

        if not flt(system_balance, 2) == flt(statement_balance, 2):
            frappe.throw(
                f"银行账户 {account} 余额不一致:系统 {system_balance} vs 对账单 {statement_balance}",
                title="余额校验失败"
            )

    frappe.msgprint("月末对账完成,所有账户校验通过")
  1. 执行方式
  • 通过 frappe.publish_realtime 在月末触发校验,或配置定时任务(如Cron Job)调用 validate_monthly_reconciliation
  • 在校验报告中生成差异列表,供财务人员手动核查。

四、配套扩展与优化

  1. 权限控制
  • 为自动创建 Payment Entry 功能添加权限开关(如 allow_auto_payment_creation 角色权限)。
  • 限制OFD解析功能仅财务部门用户可用。
  1. 错误处理
  • 在自动创建 Payment Entry 失败时记录日志(如 frappe.log_error),并保留 Bank Transaction 状态为“未关联”。
  • OFD解析失败时提示用户手动上传PDF/图片附件。
  1. 性能优化
  • 批量创建 Payment Entry 时使用 frappe.db.sql 批量插入,减少单条事务开销。
  • 对账单校验时采用分页查询,避免全量数据加载。

五、前端交互调整(示例)

// 在 bank_transaction_list.js 中添加上传按钮和提示
frappe.ui.form.on("Bank Transaction", {
    after_save: function(frm) {
        if (frm.doc.is_auto_created && !frm.doc.payment_entry) {
            frappe.confirm("检测到新流水,是否自动创建付款记录?", function() {
                frappe.call({
                    method: "erpnext.accounts.doctype.bank_transaction.bank_transaction.create_payment_entry",
                    args: { "transaction_id": frm.doc.name },
                    callback: function(r) {
                        if (r.message) frappe.msgprint("付款记录已创建:" + r.message);
                    }
                });
            });
        }
    }
});

通过以上修改,系统将支持银行流水自动生成付款记录、OFD回单解析关联及月末对账校验,提升财务自动化水平。实际开发需根据银行接口规范和文件版式调整解析逻辑,并进行充分测试。

关联文件mapping和payments

以下是对 bank_transaction_mapping.pybank_transaction_payments.py 两个文件与 Bank Transaction 关联的详细解读:

一、bank_transaction_mapping.py 解析

文件定位

定义 Bank Transaction Mapping 文档类型,用于配置银行流水字段与系统字段的映射关系,解决不同银行对账单格式差异问题。

核心字段

字段名 类型 说明
bank_transaction_field Literal 银行对账单中的原始字段(如“交易日期”“金额”“对手方名称”),需枚举配置
file_field Data 系统中对应的字段名(如 transaction_dateamountparty_name
parent/parentfield/parenttype 隐式关联字段 关联到父文档(如 Bank Transaction Upload),用于批量映射配置

Bank Transaction 的关联

  1. 数据导入适配
  • 当通过 bank_transaction_upload.py 导入银行流水时,系统根据 Bank Transaction Mapping 中配置的映射关系,将对账单字段(如CSV中的列名)匹配到 Bank Transaction 的文档字段。
  • 示例:若银行流水的“交易日期”列名为 TxnDate,可在映射中配置 bank_transaction_field="TxnDate"`` →filefield="transactiondate"`,确保数据正确写入系统。
  1. 批量配置管理
  • 映射规则可针对不同银行模板单独配置(如“工商银行模板”“建设银行模板”),存储于 Bank Transaction Mapping 文档中,避免硬编码字段映射逻辑,提升扩展性。

二、bank_transaction_payments.py 解析

文件定位

定义 Bank Transaction Payments 文档类型,作为 Bank TransactionPayment Entry 之间的关联中间表,处理交易与付款记录的匹配和核销。

核心字段

字段名 类型 说明
allocated_amount Currency 本次匹配的金额,需与 Bank Transaction 金额一致或部分核销
clearance_date Date 付款到账日期,用于对账时效性管理
payment_document Link 关联的付款凭证类型(如“Payment Entry”“Journal Entry”)
payment_entry DynamicLink 动态关联具体的付款记录(如 Payment Entry 文档名)
parent/parentfield/parenttype 隐式关联字段 关联到父文档 Bank Transaction,作为子表存储匹配记录

Bank Transaction 的关联

  1. 交易-付款匹配
  • Bank TransactionPayment Entry 手动或自动匹配时,系统生成 Bank Transaction Payments 记录,记录匹配的金额、凭证及状态。
  • 示例:银行流水显示收到客户付款1000元,手动关联到系统中对应的 Payment Entry 后,在 Bank Transaction 的子表中生成一条 Bank Transaction Payments 记录,标记已核销金额。
  1. 部分核销支持
  • 支持同一笔银行交易匹配多笔付款记录(如分阶段付款),通过 allocated_amount 累计核销总额,直至与交易金额一致。
  1. 对账状态跟踪
  • 通过 clearance_datepayment_entry 字段,可查询交易是否已结清、对应的付款凭证状态,辅助财务人员进行账龄分析和异常排查。

三、业务场景联动

场景1:银行流水导入与字段映射

  1. 用户上传某银行CSV对账单,系统读取文件头(如列名 TransactionDate, Amount, Payee)。

  2. 根据预先配置的 Bank Transaction Mapping(如 TransactionDatetransaction_date, Amountamount, Payeeparty_name),将数据映射到 Bank Transaction 字段,生成交易记录。

场景2:交易自动匹配付款记录

  1. Bank Transaction 提交后,系统通过 auto_match_party.py 匹配到客户/供应商,触发自动创建 Payment Entry

  2. 创建完成后,在 Bank Transactionpayments 子表(即 Bank Transaction Payments)中生成记录,关联 Payment Entry 并标记 allocated_amount

场景3:手动核销与对账

  1. 财务人员发现某笔银行交易未自动匹配,手动选择 Payment Entry 进行关联。

  2. 系统在 Bank Transaction Payments 中记录匹配关系,更新交易状态为“已核销”,并同步更新 Payment Entry 的对账状态。

四、代码层面关联

  1. 数据结构关联
  • Bank Transaction 文档通过 extend 方法引用 Bank Transaction Payments 作为子表:
# bank_transaction.py 中可能的定义
class BankTransaction(Document):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.payments = self.get_table("bank_transaction_payments")  # 子表字段名与 doctype 对应
  • Bank Transaction Mapping 作为独立配置文档,通过 parenttype="Bank Transaction Upload" 等配置,在导入时被 bank_transaction_upload.py 调用:
# bank_transaction_upload.py 中可能的逻辑
mapping = frappe.get_all("Bank Transaction Mapping", filters={"parenttype": "Bank Transaction Upload"})
field_map = {d.bank_transaction_field: d.file_field for d in mapping}
  1. 功能依赖
  • Bank Transaction Mapping 是批量导入功能的基础,解决“不同银行字段命名差异”的核心问题。
  • Bank Transaction Payments 是交易与财务凭证关联的桥梁,支持自动核销、部分付款等复杂业务场景。

五、总结

文件 核心作用 Bank Transaction 的关键交互点
bank_transaction_mapping 字段映射配置,解决数据导入格式差异 导入时动态匹配字段,避免硬编码
bank_transaction_payments 交易-付款关联中间表,管理核销记录 子表存储匹配记录,更新交易状态和财务凭证关联关系

这两个文件通过配置化字段映射和结构化关联记录,增强了 Bank Transaction 模块的灵活性和扩展性,使其能够适配不同银行的数据格式,并实现与财务模块的无缝对接。

Discard
Save
Review Changes ← Back to Content
Message Status Space Raised By Last update on