第3章 应收管理(AR):从发票到回款的全链路实战

应收管理,说白了就是盯着客户把钱收回来。我做了这么多年ERP财务系统,发现很多开发同学容易把AR模块想简单了——不就是记个账嘛?其实不然。应收模块是财务系统里最考验业务理解深度的模块之一,因为它直接关系到企业的现金流。

今天我就带大家把AR模块的核心流程拆开揉碎了讲。嗯,咱们从业务流程开始。

3.1 应收模块的核心业务流程

先看一张我手绘的业务流转图(用文字描述):

销售订单 → 发货/出库 → 开票 → 应收确认 → 收款 → 核销 → 账龄分析

这个链条里,每个环节都可能出幺蛾子。我记得刚入行时,有个项目上线后客户投诉说应收余额对不上,查了一整天发现是开票环节和发货环节的时间差没处理好。

具体来说,业务流程包含这几个关键节点:

  • 销售出库:货物发出,库存减少,但此时还没产生应收
  • 开票确认:开具销售发票,正式确认应收账款
  • 收款登记:客户打款,银行到账
  • 核销处理:将收款与对应发票匹配,关闭应收
  • 账龄分析:定期检查应收逾期情况
注意: 我曾经在一个项目中遇到过业务部门要求「先开票后发货」的场景。这在ERP标准流程里是不允许的,但实际业务中确实存在。解决方案是引入「预开票」状态,等发货完成后再转为正式发票。

3.2 客户管理:不只是个通讯录

客户管理在AR模块里,绝不仅仅是存个公司名称和地址。我个人的习惯是把客户信息分为三层:

层级 字段示例 业务意义
基础信息 客户编码、名称、税号、地址 用于发票抬头、税务申报
信用信息 信用额度、账期、信用等级 控制应收风险
结算信息 银行账户、付款方式、对账周期 用于收款和对账

这里有个坑:很多系统把客户信息和财务信息混在一个表里。我建议分开设计,因为客户基础信息由销售部门维护,而信用和结算信息由财务部门维护。权限分离能避免很多麻烦。

实战技巧: 客户编码建议用「区域+类型+序号」的规则,比如「CN-01-0001」。这样在报表里一眼就能看出客户归属地。我之前有个项目没注意编码规则,后来做区域分析时不得不写一堆转换逻辑,后悔啊。

3.3 发票处理:税务合规是底线

发票处理是AR模块里最敏感的部分。为什么?因为发票直接关联到税务申报,错一张发票可能让企业多交或少交几万块的税。

发票处理的核心流程:

  1. 发票生成:根据销售订单或发货单生成发票草稿
  2. 发票校验:检查金额、税率、客户信息是否正确
  3. 发票过账:正式生成应收凭证,更新总账
  4. 发票打印/导出:生成纸质发票或电子发票

代码层面,发票过账的核心逻辑大概是这样的:

-- 发票过账时生成应收凭证
INSERT INTO ar_invoice (
    invoice_no, customer_id, invoice_date,
    total_amount, tax_amount, status
) VALUES (
    'INV-2024-001', 'CUST-001', '2024-01-15',
    11300.00, 1300.00, 'POSTED'
);

-- 同时更新总账
INSERT INTO gl_entries (
    account_code, debit_amount, credit_amount,
    reference_type, reference_id
) VALUES 
    ('1122', 11300.00, 0, 'AR_INVOICE', 'INV-2024-001'),  -- 应收账款增加
    ('6001', 0, 10000.00, 'AR_INVOICE', 'INV-2024-001'),  -- 销售收入
    ('2221', 0, 1300.00, 'AR_INVOICE', 'INV-2024-001');   -- 应交税费

你想想看,如果这里税率算错了,或者科目配错了,月底结账时对账对不上,那才叫一个头大。

核心原则: 发票一旦过账,原则上不允许修改。如果开错了,必须走「红冲」流程——生成一张负数发票冲销原发票,再重新开一张正确的。这是税务合规的要求,别想着直接UPDATE数据库。

3.4 收款与核销:钱和票的匹配艺术

收款和核销,是AR模块里最考验设计功力的部分。为什么?因为客户的付款方式五花八门:

  • 一笔付款对应一张发票(最简单的情况)
  • 一笔付款对应多张发票(客户合并付款)
  • 多笔付款对应一张发票(分期付款)
  • 预付款+尾款(先打一部分钱,货到后再付清)

我建议的核销设计思路是这样的:

-- 核销表结构(简化版)
CREATE TABLE ar_receipt_apply (
    apply_id INT PRIMARY KEY,
    receipt_id INT,          -- 收款单ID
    invoice_id INT,          -- 发票ID
    apply_amount DECIMAL,    -- 本次核销金额
    apply_date DATE,         -- 核销日期
    status VARCHAR(20)       -- 状态:DRAFT/POSTED
);

-- 核销时更新应收余额
UPDATE ar_invoice 
SET paid_amount = paid_amount + @apply_amount,
    status = CASE 
        WHEN paid_amount + @apply_amount >= total_amount 
        THEN 'CLOSED' 
        ELSE 'PARTIAL' 
    END
WHERE invoice_id = @invoice_id;

这里有个细节:核销时一定要记录「核销差额」。为什么?因为客户付款时可能会有抹零、折扣等情况。比如发票金额是1000元,客户只打了999.5元,那0.5元怎么处理?

我的做法是引入「核销差异科目」,允许小额差异自动计入财务费用。当然,这个差异阈值需要由财务总监设定,比如单笔不超过1元。

避坑指南: 我曾经遇到一个项目,核销时没有考虑币种差异。客户付的是美元,发票开的是人民币,汇率波动导致核销金额对不上。后来我们加了一个「汇率差异处理」步骤,在核销时自动计算汇兑损益。

3.5 应收报表与账龄分析

最后说说报表。应收模块的报表,财务最关心的是这两张:

报表名称 用途 关键字段
应收余额表 查看每个客户的当前欠款 客户、期初余额、本期新增、本期收款、期末余额
账龄分析表 分析应收逾期情况 客户、未到期、逾期1-30天、31-60天、60天以上

账龄分析的计算逻辑其实不复杂:

-- 计算账龄(按发票日期)
SELECT 
    customer_id,
    SUM(CASE 
        WHEN DATEDIFF(NOW(), invoice_date) <= 30 
        THEN unpaid_amount ELSE 0 
    END) AS '0-30天',
    SUM(CASE 
        WHEN DATEDIFF(NOW(), invoice_date) BETWEEN 31 AND 60 
        THEN unpaid_amount ELSE 0 
    END) AS '31-60天',
    SUM(CASE 
        WHEN DATEDIFF(NOW(), invoice_date) > 60 
        THEN unpaid_amount ELSE 0 
    END) AS '60天以上'
FROM ar_invoice
WHERE status IN ('OPEN', 'PARTIAL')
GROUP BY customer_id;

嗯,这里要注意:账龄的起点是发票日期还是到期日?不同企业要求不一样。我建议做成可配置的,让财务自己选。

个人经验: 账龄分析报表最好加上「催收建议」列。比如逾期30天以内的发邮件提醒,逾期60天以上的电话催收。我见过一个项目,财务每个月手动做催收,后来我们加了个自动催收功能,效率提升了好几倍。

好了,应收模块的核心内容就这些。说白了,AR模块就是管好「客户欠我多少钱,什么时候还,还了没有」这三件事。但要把这三件事做好,需要把业务流程、数据模型、税务合规都考虑进去。希望今天的分享对你有帮助。