人财事物信息化 - 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_user
和frappe.get_doc
模拟用户操作和数据创建,确保测试隔离性。 - 断言验证
- 检查匹配结果是否符合预期、交易提交后状态是否正确、余额变动是否与交易金额一致等。
业务逻辑总结
- 数据流转
- 银行对账单通过
bank_transaction_upload.py
导入系统,解析为Bank Transaction
文档。 bank_transaction.py
处理交易的增删改查,提交时触发auto_match_party.py
匹配业务方,并生成财务凭证。
- 自动化目标
- 通过自动匹配(
auto_match_party
)减少人工录入错误,提升对账效率。 - 批量导入(
bank_transaction_upload
)支持大规模数据处理,适配不同银行的对账单格式。
- 财务集成
- 交易数据与会计模块联动,提交交易时自动更新账户余额,并可生成日记账分录,确保财务数据一致性。
客户化功能增强(待验证)
以下是针对bank_transaction
模块新增功能的设计方案,涉及代码修改和逻辑扩展:
一、功能①:上传银行流水后自动创建 Payment Entry
- 修改
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()
- 关键配置项
- 在
Bank Transaction
文档中添加字段auto_create_payment_entry
(勾选框),控制是否自动创建。 - 依赖
auto_match_party
模块提前匹配party_type
和party
,否则跳过自动创建。
二、功能②:解析 OFD 回单并关联 Payment Entry
- 新增 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}}
- 文档结构调整
- 在
Payment Entry
中添加ofd_attachment
字段(链接到文件)和parsed_ofd_data
字段(存储解析后的JSON)。 - 通过
frappe.call
在前端上传OFD文件时触发后端解析函数。
三、功能③:月末对账单完整性与余额校验
- 添加月末校验脚本(可定时任务执行)
# 在 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("月末对账完成,所有账户校验通过")
- 执行方式
- 通过
frappe.publish_realtime
在月末触发校验,或配置定时任务(如Cron Job)调用validate_monthly_reconciliation
。 - 在校验报告中生成差异列表,供财务人员手动核查。
四、配套扩展与优化
- 权限控制
- 为自动创建 Payment Entry 功能添加权限开关(如
allow_auto_payment_creation
角色权限)。 - 限制OFD解析功能仅财务部门用户可用。
- 错误处理
- 在自动创建 Payment Entry 失败时记录日志(如
frappe.log_error
),并保留Bank Transaction
状态为“未关联”。 - OFD解析失败时提示用户手动上传PDF/图片附件。
- 性能优化
- 批量创建 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.py
和 bank_transaction_payments.py
两个文件与 Bank Transaction
关联的详细解读:
一、bank_transaction_mapping.py
解析
文件定位
定义 Bank Transaction Mapping
文档类型,用于配置银行流水字段与系统字段的映射关系,解决不同银行对账单格式差异问题。
核心字段
字段名 | 类型 | 说明 |
---|---|---|
bank_transaction_field |
Literal |
银行对账单中的原始字段(如“交易日期”“金额”“对手方名称”),需枚举配置 |
file_field |
Data |
系统中对应的字段名(如 transaction_date 、amount 、party_name ) |
parent /parentfield /parenttype |
隐式关联字段 | 关联到父文档(如 Bank Transaction Upload ),用于批量映射配置 |
与 Bank Transaction
的关联
- 数据导入适配
- 当通过
bank_transaction_upload.py
导入银行流水时,系统根据Bank Transaction Mapping
中配置的映射关系,将对账单字段(如CSV中的列名)匹配到Bank Transaction
的文档字段。 - 示例:若银行流水的“交易日期”列名为
TxnDate
,可在映射中配置bank_transaction_field="TxnDate"`` →
filefield="transactiondate"`,确保数据正确写入系统。
- 批量配置管理
- 映射规则可针对不同银行模板单独配置(如“工商银行模板”“建设银行模板”),存储于
Bank Transaction Mapping
文档中,避免硬编码字段映射逻辑,提升扩展性。
二、bank_transaction_payments.py
解析
文件定位
定义 Bank Transaction Payments
文档类型,作为 Bank Transaction
与 Payment 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
的关联
- 交易-付款匹配
- 当
Bank Transaction
与Payment Entry
手动或自动匹配时,系统生成Bank Transaction Payments
记录,记录匹配的金额、凭证及状态。 - 示例:银行流水显示收到客户付款1000元,手动关联到系统中对应的
Payment Entry
后,在Bank Transaction
的子表中生成一条Bank Transaction Payments
记录,标记已核销金额。
- 部分核销支持
- 支持同一笔银行交易匹配多笔付款记录(如分阶段付款),通过
allocated_amount
累计核销总额,直至与交易金额一致。
- 对账状态跟踪
- 通过
clearance_date
和payment_entry
字段,可查询交易是否已结清、对应的付款凭证状态,辅助财务人员进行账龄分析和异常排查。
三、业务场景联动
场景1:银行流水导入与字段映射
用户上传某银行CSV对账单,系统读取文件头(如列名
TransactionDate
,Amount
,Payee
)。根据预先配置的
Bank Transaction Mapping
(如TransactionDate
→transaction_date
,Amount
→amount
,Payee
→party_name
),将数据映射到Bank Transaction
字段,生成交易记录。
场景2:交易自动匹配付款记录
Bank Transaction
提交后,系统通过auto_match_party.py
匹配到客户/供应商,触发自动创建Payment Entry
。创建完成后,在
Bank Transaction
的payments
子表(即Bank Transaction Payments
)中生成记录,关联Payment Entry
并标记allocated_amount
。
场景3:手动核销与对账
财务人员发现某笔银行交易未自动匹配,手动选择
Payment Entry
进行关联。系统在
Bank Transaction Payments
中记录匹配关系,更新交易状态为“已核销”,并同步更新Payment Entry
的对账状态。
四、代码层面关联
- 数据结构关联
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}
- 功能依赖
Bank Transaction Mapping
是批量导入功能的基础,解决“不同银行字段命名差异”的核心问题。Bank Transaction Payments
是交易与财务凭证关联的桥梁,支持自动核销、部分付款等复杂业务场景。
五、总结
文件 | 核心作用 | 与 Bank Transaction 的关键交互点 |
---|---|---|
bank_transaction_mapping |
字段映射配置,解决数据导入格式差异 | 导入时动态匹配字段,避免硬编码 |
bank_transaction_payments |
交易-付款关联中间表,管理核销记录 | 子表存储匹配记录,更新交易状态和财务凭证关联关系 |
这两个文件通过配置化字段映射和结构化关联记录,增强了 Bank Transaction
模块的灵活性和扩展性,使其能够适配不同银行的数据格式,并实现与财务模块的无缝对接。