人财事物信息化 - accounts-utils.py

ERPNext会计模块的utils.py文件包含了处理财务和会计相关功能的核心工具函数,主要功能如下:

  1. 财务年度处理

    • get_fiscal_year:验证并返回给定日期所属的财务年度信息,支持按公司筛选。
    • validate_fiscal_year:检查日期是否在有效财务年度内,否则抛出异常。
    • get_fiscal_years:获取指定公司的所有有效财务年度列表。
  2. 账户余额计算

    • get_balance_on:计算指定账户在特定日期的余额,支持按成本中心和维度过滤。
    • get_count_on:统计账户的交易次数,支持应收账款/应付账款的未结金额计算。
  3. 对账与凭证调整

    • reconcile_against_document:核心对账函数,处理付款条目(PE)和日记账(JV)的调整:
    • 取消原始凭证的GL/PL记录
    • 更新凭证的参考关系(如订单与发票的关联)
    • 重新提交调整后的会计条目
    • update_reference_in_journal_entry:调整日记账的分录金额并创建新的参考行。
    • update_reference_in_payment_entry:更新付款条目的分配金额,处理汇率差异。
  4. 分类账管理

    • create_payment_ledger_entry:根据GL条目生成付款分类账记录。
    • _delete_accounting_ledger_entries:删除指定凭证的GL和PL记录。
    • repost_gle_for_stock_vouchers:库存变动后重新过账总账,确保库存估值准确。
  5. 账户结构管理

    • get_children:获取账户/成本中心的树形结构数据,用于UI展示。
    • update_cost_center:更新成本中心编号和名称,自动重命名相关凭证。
  6. 汇率与货币处理

    • create_gain_loss_journal:自动创建汇兑损益的日记账分录。
    • cancel_exchange_gain_loss_journal:取消与指定凭证关联的汇兑损益分录。
  7. 库存估值整合

    • update_gl_entries_after:在库存交易(如采购/销售)后更新总账:
    • 获取未来相关的库存凭证
    • 重新计算库存账户的GL条目
    • 对比新旧条目差异后更新
  8. 实用工具

    • get_company_default:获取公司级默认设置(如默认货币)
    • get_currency_precision:获取系统货币精度设置
    • parse_naming_series_variable:处理单据编号规则中的财务年度变量
  9. 系统级处理

    • fix_total_debit_credit:修复借贷不平衡的凭证
    • check_and_delete_linked_reports:删除与账户关联的桌面图标
  10. 自动化处理

    • auto_create_exchange_rate_revaluation_*:定期自动执行汇率重估
    • create_err_and_its_journals:自动创建汇率调整日记账

    关键数据结构

    • Payment Ledger Entry:记录基于现金基础的收付款明细
    • GL Entry:记录权责发生制的总账条目
    • 维度处理:支持会计维度的存储和查询

这些功能共同构成了ERPNext财务模块的核心,涵盖了从基础账户管理、日常交易处理到复杂对账和报表生成的完整会计流程。模块高度集成库存与财务数据,通过实时更新分类账确保财务数据的准确。

以下是针对财务本地化功能增强的几个关键函数示例,包含中国特色的税务处理、多语言支持及地区化格式调整:


  1. 增值税专用发票处理(中国)
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

  1. 多语言凭证摘要生成
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")

  1. 金税系统对接接口

以下是针对中国电子税务局最新要求的Excel批量处理功能实现方案:

  1. 批量开票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
  1. 全量发票查询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. 功能增强说明

批量开票模板特点: 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
  1. 使用示例
 生成批量开票模板示例
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()
  1. 注意事项
    1. 实际使用前需验证:
    • 电子税务局导出的实际列名
    • 日期格式的精确匹配
    • 发票状态的枚举值
    1. 性能优化建议:
    • 大数据量处理使用分块读取(chunksize参数)
    • 启用多线程处理(适用于万级以上数据量)
    1. 安全增强:
   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批量处理功能实现方案:

  1. 批量开票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
  1. 全量发票查询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. 功能增强说明

批量开票模板特点:

  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
  1. 使用示例
 生成批量开票模板示例
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()
  1. 注意事项

    1. 实际使用前需验证:
    • 电子税务局导出的实际列名
    • 日期格式的精确匹配
    • 发票状态的枚举值
    1. 性能优化建议:
    • 大数据量处理使用分块读取(chunksize参数)
    • 启用多线程处理(适用于万级以上数据量)
    1. 安全增强:
   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)
  1. 本地化财务年度设置
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"
    }
  1. 人民币大写转换
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 + "整"

使用说明:

  1. 税务处理:generate_vat_invoice 实现符合中国标准的增值税发票生成,包含:

    • 纳税人识别号验证
    • 13%标准税率支持
    • 符合税务总局要求的发票编码规则
  2. 本地化格式:

    • rmb_upper 处理人民币大写金额(如:1234.56 → 壹仟贰佰叁拾肆元伍角陆分整)
    • get_china_fiscal_year 覆盖系统默认财务年度设置
  3. 系统集成:

    在会计科目初始化时设置中国专用科目
   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)
  1. 注意事项:
    • 需配合中国金税系统的接口规范
    • 增值税率应根据最新政策动态调整
    • 凭证摘要多语言需要维护翻译字典

这些函数需要集成到ERPNext的会计模块中,建议通过自定义App实现,并配合中国本地化配置文件使用。

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