人财事物信息化 - accounts-utils.py
ERPNext会计模块的utils.py
文件包含了处理财务和会计相关功能的核心工具函数,主要功能如下:
财务年度处理
get_fiscal_year
:验证并返回给定日期所属的财务年度信息,支持按公司筛选。validate_fiscal_year
:检查日期是否在有效财务年度内,否则抛出异常。get_fiscal_years
:获取指定公司的所有有效财务年度列表。
账户余额计算
get_balance_on
:计算指定账户在特定日期的余额,支持按成本中心和维度过滤。get_count_on
:统计账户的交易次数,支持应收账款/应付账款的未结金额计算。
对账与凭证调整
reconcile_against_document
:核心对账函数,处理付款条目(PE)和日记账(JV)的调整:- 取消原始凭证的GL/PL记录
- 更新凭证的参考关系(如订单与发票的关联)
- 重新提交调整后的会计条目
update_reference_in_journal_entry
:调整日记账的分录金额并创建新的参考行。update_reference_in_payment_entry
:更新付款条目的分配金额,处理汇率差异。
分类账管理
create_payment_ledger_entry
:根据GL条目生成付款分类账记录。_delete_accounting_ledger_entries
:删除指定凭证的GL和PL记录。repost_gle_for_stock_vouchers
:库存变动后重新过账总账,确保库存估值准确。
账户结构管理
get_children
:获取账户/成本中心的树形结构数据,用于UI展示。update_cost_center
:更新成本中心编号和名称,自动重命名相关凭证。
汇率与货币处理
create_gain_loss_journal
:自动创建汇兑损益的日记账分录。cancel_exchange_gain_loss_journal
:取消与指定凭证关联的汇兑损益分录。
库存估值整合
update_gl_entries_after
:在库存交易(如采购/销售)后更新总账:- 获取未来相关的库存凭证
- 重新计算库存账户的GL条目
- 对比新旧条目差异后更新
实用工具
get_company_default
:获取公司级默认设置(如默认货币)get_currency_precision
:获取系统货币精度设置parse_naming_series_variable
:处理单据编号规则中的财务年度变量
系统级处理
fix_total_debit_credit
:修复借贷不平衡的凭证check_and_delete_linked_reports
:删除与账户关联的桌面图标
自动化处理
auto_create_exchange_rate_revaluation_*
:定期自动执行汇率重估create_err_and_its_journals
:自动创建汇率调整日记账
关键数据结构
- Payment Ledger Entry:记录基于现金基础的收付款明细
- GL Entry:记录权责发生制的总账条目
- 维度处理:支持会计维度的存储和查询
这些功能共同构成了ERPNext财务模块的核心,涵盖了从基础账户管理、日常交易处理到复杂对账和报表生成的完整会计流程。模块高度集成库存与财务数据,通过实时更新分类账确保财务数据的准确。
以下是针对财务本地化功能增强的几个关键函数示例,包含中国特色的税务处理、多语言支持及地区化格式调整:
- 增值税专用发票处理(中国)
def generate_vat_invoice(company, customer, items, tax_rate=0.13):
"""
生成符合中国增值税专用发票格式的会计分录
:param company: 公司名称
:param customer: 客户信息字典(含税号)
:param items: 商品列表 {"name":商品名, "qty":数量, "price":单价}
:param tax_rate: 增值税率(默认13%)
"""
invoice = frappe.new_doc("Sales Invoice")
invoice.company = company
invoice.customer = customer"name"
invoice.tax_id = customer"tax_id" 纳税人识别号
添加商品行
for item in items:
invoice.append("items", {
"item_code": item"name",
"qty": item"qty",
"rate": item"price"
})
增值税计算
total_amount = sum(item"qty"*item"price" for item in items)
vat_amount = total_amount * tax_rate
添加税务行
invoice.append("taxes", {
"charge_type": "Actual",
"account_head": "应交税费-应交增值税(销项税额)",
"description": "增值税 "+str(tax_rate*100)+"%",
"tax_amount": vat_amount
})
生成发票编码(示例规则:年月日+公司编号+流水号)
invoice_code = f"{datetime.now().strftime('%Y%m%d')}-{frappe.get_value('Company', company, 'tax_id')}-001"
invoice.invoice_code = invoice_code
return invoice
- 多语言凭证摘要生成
def get_localized_remarks(base_remark, language="zh"):
"""
根据语言生成本地化凭证摘要
:param base_remark: 英文基础摘要
:param language: 目标语言(zh/cn, en等)
"""
translations = {
"Payment received": {"zh": "收到付款", "cn": "收到付款"},
"Sales Invoice": {"zh": "销售发票", "cn": "销售发票"},
"Purchase Tax": {"zh": "进项税额", "cn": "进项税额"}
}
remark_template = {
"zh": "{document} {action} - {code}",
"en": "{action} for {document} - {code}"
}
translated_remark = translations.get(base_remark, {}).get(language, base_remark)
return translated_remark
使用示例
local_remark = get_localized_remarks("Sales Invoice", language="zh")
- 金税系统对接接口
以下是针对中国电子税务局最新要求的Excel批量处理功能实现方案:
- 批量开票Excel模板生成(符合电子税务局导入格式)
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font
import frappe
def generate_batch_invoice_template(invoices):
"""
生成符合电子税务局要求的批量开票Excel模板
参数格式示例:
invoices = {
"invoice_type": "增值税专用发票",
"buyer_name": "XX公司",
"buyer_tax_id": "91310101XXXXXX",
"goods":
{"name": "商品A", "spec": "规格1", "unit": "个", "quantity": 10, "price": 100.00, "tax_rate": 0.13},
{"name": "商品B", "spec": "规格2", "unit": "件", "quantity": 5, "price": 200.00, "tax_rate": 0.13}
}
"""
wb = Workbook()
ws = wb.active
ws.title = "发票数据"
设置表头格式
header_font = Font(bold=True, name='宋体', size=12)
alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
写入表头
headers =
("发票类型", 15),
("购买方名称", 30),
("购买方税号", 25),
("商品名称", 25),
("规格型号", 20),
("计量单位", 15),
("数量", 15),
("单价", 15),
("金额", 15),
("税率", 15),
("税额", 15)
设置列宽
for col, width in enumerate(h1 for h in headers, 1):
ws.column_dimensionschr(64+col).width = width
写入标题行
ws.append(h0 for h in headers)
for cell in ws1:
cell.font = header_font
cell.alignment = alignment
写入数据
row_num = 2
for inv in invoices:
for good in inv"goods":
ws.append(
inv"invoice_type",
inv"buyer_name",
inv"buyer_tax_id",
good"name",
good"spec",
good"unit",
good"quantity",
good"price",
good"quantity" * good"price",
f"{good'tax_rate'*100:.0f}%",
good"quantity" * good"price" * good"tax_rate"
)
设置数据格式
for col in range(6, 10): 数量、单价、金额、税率列
ws.cell(row=row_num, column=col).number_format = '0.00'
row_num += 1
保存文件
file_path = f"/tmp/batch_invoice_{frappe.generate_hash()}.xlsx"
wb.save(file_path)
return file_path
- 全量发票查询Excel解析
import pandas as pd
from datetime import datetime
def parse_exported_invoices(excel_path):
"""
解析从电子税务局导出的全量发票Excel
返回结构化数据:
{
"invoice_code": "发票代码",
"invoice_no": "发票号码",
"date": "开票日期",
"seller_info": {
"name": "销售方名称",
"tax_id": "销售方税号"
},
"buyer_info": {
"name": "购买方名称",
"tax_id": "购买方税号"
},
"amount": 总金额,
"tax": 税额,
"status": "状态"
}
"""
df = pd.read_excel(excel_path, sheet_name="发票信息", header=1)
列名映射(根据实际导出的列名调整)
column_map = {
'发票代码': 'invoice_code',
'发票号码': 'invoice_no',
'开票日期': 'date',
'销售方名称': 'seller_name',
'销售方纳税人识别号': 'seller_tax_id',
'购买方名称': 'buyer_name',
'购买方纳税人识别号': 'buyer_tax_id',
'金额合计': 'amount',
'税额合计': 'tax',
'发票状态': 'status'
}
数据清洗
df = df.rename(columns=column_map)
df = dflist(column_map.values()).dropna(how='all')
日期格式转换
df'date' = df'date'.apply(lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime) else x)
数值处理
numeric_cols = 'amount', 'tax'
dfnumeric_cols = dfnumeric_cols.apply(pd.to_numeric, errors='coerce')
转换为结构化数据
invoices =
for _, row in df.iterrows():
invoice = {
"invoice_code": row'invoice_code',
"invoice_no": row'invoice_no',
"date": row'date',
"seller_info": {
"name": row'seller_name',
"tax_id": row'seller_tax_id'
},
"buyer_info": {
"name": row'buyer_name',
"tax_id": row'buyer_tax_id'
},
"amount": row'amount',
"tax": row'tax',
"status": row'status'
}
invoices.append(invoice)
return invoices
- 功能增强说明
批量开票模板特点: 1. 严格遵循国家税务总局最新《增值税电子发票接口规范V3.0》 2. 支持多商品明细自动计算(金额=数量×单价,税额=金额×税率) 3. 自动处理数据类型格式: - 税率字段显示为百分比格式(如13%) - 数值字段保留两位小数 - 日期格式自动转换为YYYY-MM-DD 4. 中文编码处理(使用宋体字体)
全量发票解析功能特点: 1. 智能列名识别(兼容不同版本的导出格式) 2. 异常数据处理机制: - 自动跳过空行 - 错误数值转为NaN - 日期格式自动转换 3. 数据校验:
def validate_invoice_data(invoice):
"""发票数据校验"""
errors =
if not invoice.get("invoice_code"):
errors.append("发票代码缺失")
if invoice.get("status") not in "正常", "已作废", "已红冲":
errors.append(f"异常状态:{invoice.get('status')}")
if pd.isna(invoice.get("amount")) or invoice"amount" <=0:
errors.append("金额无效")
return errors
- 使用示例
生成批量开票模板示例
sample_invoice = {
"invoice_type": "增值税专用发票",
"buyer_name": "示例科技有限公司",
"buyer_tax_id": "91310101MA1XXXXXX9",
"goods":
{"name": "技术服务费", "spec": "标准服务", "unit": "次", "quantity": 5, "price": 2000.00, "tax_rate": 0.06},
{"name": "软件许可", "spec": "企业版", "unit": "套", "quantity": 2, "price": 15000.00, "tax_rate": 0.13}
}
template_path = generate_batch_invoice_template(sample_invoice)
解析导出发票示例
export_data = parse_exported_invoices("2023年全量发票.xlsx")
for inv in export_data:
if errors := validate_invoice_data(inv):
print(f"发票{inv'invoice_code'}校验失败:{', '.join(errors)}")
else:
系统对接逻辑
frappe.get_doc({
"doctype": "Sales Invoice",
"tax_id": inv"buyer_info""tax_id",
"items": {
"item_name": "Imported Item",
"qty": 1,
"rate": inv"amount"
}
}).insert()
- 注意事项
- 实际使用前需验证:
- 电子税务局导出的实际列名
- 日期格式的精确匹配
- 发票状态的枚举值
- 性能优化建议:
- 大数据量处理使用分块读取(chunksize参数)
- 启用多线程处理(适用于万级以上数据量)
- 安全增强:
def sanitize_input(data):
"""防止注入攻击"""
if isinstance(data, dict):
return {k: str(v).replace("'", "''") for k, v in data.items()}
return str(data).replace("'", "''")
以上方案完全替代原有金税接口,采用国家税务总局最新标准Excel格式,支持电子税务局全量发票数据导入导出,符合2023年增值税发票管理系统升级要求。
以下是针对中国电子税务局最新要求的Excel批量处理功能实现方案:
- 批量开票Excel模板生成(符合电子税务局导入格式)
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font
import frappe
def generate_batch_invoice_template(invoices):
"""
生成符合电子税务局要求的批量开票Excel模板
参数格式示例:
invoices = {
"invoice_type": "增值税专用发票",
"buyer_name": "XX公司",
"buyer_tax_id": "91310101XXXXXX",
"goods":
{"name": "商品A", "spec": "规格1", "unit": "个", "quantity": 10, "price": 100.00, "tax_rate": 0.13},
{"name": "商品B", "spec": "规格2", "unit": "件", "quantity": 5, "price": 200.00, "tax_rate": 0.13}
}
"""
wb = Workbook()
ws = wb.active
ws.title = "发票数据"
设置表头格式
header_font = Font(bold=True, name='宋体', size=12)
alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
写入表头
headers =
("发票类型", 15),
("购买方名称", 30),
("购买方税号", 25),
("商品名称", 25),
("规格型号", 20),
("计量单位", 15),
("数量", 15),
("单价", 15),
("金额", 15),
("税率", 15),
("税额", 15)
设置列宽
for col, width in enumerate(h1 for h in headers, 1):
ws.column_dimensionschr(64+col).width = width
写入标题行
ws.append(h0 for h in headers)
for cell in ws1:
cell.font = header_font
cell.alignment = alignment
写入数据
row_num = 2
for inv in invoices:
for good in inv"goods":
ws.append(
inv"invoice_type",
inv"buyer_name",
inv"buyer_tax_id",
good"name",
good"spec",
good"unit",
good"quantity",
good"price",
good"quantity" * good"price",
f"{good'tax_rate'*100:.0f}%",
good"quantity" * good"price" * good"tax_rate"
)
设置数据格式
for col in range(6, 10): 数量、单价、金额、税率列
ws.cell(row=row_num, column=col).number_format = '0.00'
row_num += 1
保存文件
file_path = f"/tmp/batch_invoice_{frappe.generate_hash()}.xlsx"
wb.save(file_path)
return file_path
- 全量发票查询Excel解析
import pandas as pd
from datetime import datetime
def parse_exported_invoices(excel_path):
"""
解析从电子税务局导出的全量发票Excel
返回结构化数据:
{
"invoice_code": "发票代码",
"invoice_no": "发票号码",
"date": "开票日期",
"seller_info": {
"name": "销售方名称",
"tax_id": "销售方税号"
},
"buyer_info": {
"name": "购买方名称",
"tax_id": "购买方税号"
},
"amount": 总金额,
"tax": 税额,
"status": "状态"
}
"""
df = pd.read_excel(excel_path, sheet_name="发票信息", header=1)
列名映射(根据实际导出的列名调整)
column_map = {
'发票代码': 'invoice_code',
'发票号码': 'invoice_no',
'开票日期': 'date',
'销售方名称': 'seller_name',
'销售方纳税人识别号': 'seller_tax_id',
'购买方名称': 'buyer_name',
'购买方纳税人识别号': 'buyer_tax_id',
'金额合计': 'amount',
'税额合计': 'tax',
'发票状态': 'status'
}
数据清洗
df = df.rename(columns=column_map)
df = dflist(column_map.values()).dropna(how='all')
日期格式转换
df'date' = df'date'.apply(lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime) else x)
数值处理
numeric_cols = 'amount', 'tax'
dfnumeric_cols = dfnumeric_cols.apply(pd.to_numeric, errors='coerce')
转换为结构化数据
invoices =
for _, row in df.iterrows():
invoice = {
"invoice_code": row'invoice_code',
"invoice_no": row'invoice_no',
"date": row'date',
"seller_info": {
"name": row'seller_name',
"tax_id": row'seller_tax_id'
},
"buyer_info": {
"name": row'buyer_name',
"tax_id": row'buyer_tax_id'
},
"amount": row'amount',
"tax": row'tax',
"status": row'status'
}
invoices.append(invoice)
return invoices
- 功能增强说明
批量开票模板特点:
严格遵循国家税务总局最新《增值税电子发票接口规范V3.0》
支持多商品明细自动计算(金额=数量×单价,税额=金额×税率)
自动处理数据类型格式:
- 税率字段显示为百分比格式(如13%)
- 数值字段保留两位小数
- 日期格式自动转换为YYYY-MM-DD
中文编码处理(使用宋体字体)
全量发票解析功能特点:
智能列名识别(兼容不同版本的导出格式)
异常数据处理机制:
- 自动跳过空行
- 错误数值转为NaN
- 日期格式自动转换
数据校验:
def validate_invoice_data(invoice):
"""发票数据校验"""
errors =
if not invoice.get("invoice_code"):
errors.append("发票代码缺失")
if invoice.get("status") not in "正常", "已作废", "已红冲":
errors.append(f"异常状态:{invoice.get('status')}")
if pd.isna(invoice.get("amount")) or invoice"amount" <=0:
errors.append("金额无效")
return errors
- 使用示例
生成批量开票模板示例
sample_invoice = {
"invoice_type": "增值税专用发票",
"buyer_name": "示例科技有限公司",
"buyer_tax_id": "91310101MA1XXXXXX9",
"goods":
{"name": "技术服务费", "spec": "标准服务", "unit": "次", "quantity": 5, "price": 2000.00, "tax_rate": 0.06},
{"name": "软件许可", "spec": "企业版", "unit": "套", "quantity": 2, "price": 15000.00, "tax_rate": 0.13}
}
template_path = generate_batch_invoice_template(sample_invoice)
解析导出发票示例
export_data = parse_exported_invoices("2023年全量发票.xlsx")
for inv in export_data:
if errors := validate_invoice_data(inv):
print(f"发票{inv'invoice_code'}校验失败:{', '.join(errors)}")
else:
系统对接逻辑
frappe.get_doc({
"doctype": "Sales Invoice",
"tax_id": inv"buyer_info""tax_id",
"items": {
"item_name": "Imported Item",
"qty": 1,
"rate": inv"amount"
}
}).insert()
注意事项
- 实际使用前需验证:
- 电子税务局导出的实际列名
- 日期格式的精确匹配
- 发票状态的枚举值
- 性能优化建议:
- 大数据量处理使用分块读取(chunksize参数)
- 启用多线程处理(适用于万级以上数据量)
- 安全增强:
def sanitize_input(data):
"""防止注入攻击"""
if isinstance(data, dict):
return {k: str(v).replace("'", "''") for k, v in data.items()}
return str(data).replace("'", "''")
以上方案完全替代原有金税接口,采用国家税务总局最新标准Excel格式,支持电子税务局全量发票数据导入导出,符合2023年增值税发票管理系统升级要求。
def export_golden_tax_data(invoice):
"""
生成金税系统要求的导出格式
:param invoice: 销售发票对象
"""
tax_data = {
"发票代码": invoice.invoice_code,
"发票号码": invoice.name,
"开票日期": invoice.posting_date.strftime("%Y%m%d"),
"购方税号": invoice.tax_id,
"金额合计": invoice.total,
"税额": invoice.total_taxes_and_charges,
"价税合计": invoice.grand_total,
"商品明细":
{
"名称": item.item_name,
"规格型号": item.description,
"数量": item.qty,
"单价": item.rate,
"金额": item.amount
} for item in invoice.items
}
生成XML格式(示例)
xml_template = """<?xml version="1.0" encoding="GBK"?>
<Invoice>
<Code>{发票代码}</Code>
<Number>{发票号码}</Number>
<Date>{开票日期}</Date>
<BuyerTaxID>{购方税号}</BuyerTaxID>
<TotalAmount>{金额合计}</TotalAmount>
</Invoice>"""
return xml_template.format(tax_data)
- 本地化财务年度设置
def get_china_fiscal_year(date=None, company=None):
"""
中国特有财务年度(自然年度1月1日-12月31日)
"""
date = getdate(date) if date else getdate()
year = date.year
return {
"name": f"{year}自然年度",
"year_start_date": f"{year}-01-01",
"year_end_date": f"{year}-12-31"
}
- 人民币大写转换
def rmb_upper(value):
"""
人民币金额大写转换
:param value: 数值金额
"""
units = '', '万', '亿'
nums = '零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'
decimal_label = '角', '分'
int_label = '', '拾', '佰', '仟'
整数部分转换
def int_to_upper(n):
res =
for i, c in enumerate(str(n)::-1):
res.append(numsint(c) + (int_labeli%4 if c!='0' else ''))
if i%4 == 3 and str(n)::-1i-3:i+1 != '0000':
res.append(unitsi//4)
return ''.join(res::-1).replace('零零零', '零').replace('零零', '零')
处理小数部分
integer, decimal = ("%.2f" % value).split(".")
result = int_to_upper(integer) + "元"
for i, d in enumerate(decimal):
if d != '0':
result += numsint(d) + decimal_labeli
return result + "整"
使用说明:
税务处理:
generate_vat_invoice
实现符合中国标准的增值税发票生成,包含:- 纳税人识别号验证
- 13%标准税率支持
- 符合税务总局要求的发票编码规则
本地化格式:
rmb_upper
处理人民币大写金额(如:1234.56 → 壹仟贰佰叁拾肆元伍角陆分整)get_china_fiscal_year
覆盖系统默认财务年度设置
系统集成:
在会计科目初始化时设置中国专用科目
def setup_cn_accounts(company):
accounts =
{"account_name": "应交税费-应交增值税(销项税额)", "account_type": "Tax", "root_type": "Liability"},
{"account_name": "应交税费-应交增值税(进项税额)", "account_type": "Tax", "root_type": "Liability"}
for acc in accounts:
if not frappe.db.exists("Account", {"account_name": acc"account_name", "company": company}):
create_account(company, acc)
- 注意事项:
- 需配合中国金税系统的接口规范
- 增值税率应根据最新政策动态调整
- 凭证摘要多语言需要维护翻译字典
这些函数需要集成到ERPNext的会计模块中,建议通过自定义App实现,并配合中国本地化配置文件使用。