如何将发票付款信息导入 Sage 500/Mas 500
结论
不要用 UPDATE 直接修改发票余额或关闭状态。在 Sage 500/MAS 500 中,结清发票通常不是改一个字段,而是走完“录入收款 → 将收款核销到发票 → 过账批次”的完整业务流程。
如果必须通过 SQL 自动化,应围绕 Accounts Receivable(AR)中的 Cash Receipts、Payment Application 和 Batch Posting 逻辑来实现。具体的 stored procedure 名称会因 Sage 500 版本、补丁和定制内容而异,不能只凭名称猜测并直接调用。建议按以下顺序选择实现方式:
- 优先使用当前版本提供的业务对象或集成接口。
- 其次使用 Data Import Manager 导入收款批次。
- 如果采用 SQL,应跟踪 Sage 500 客户端执行的标准收款操作,确认它实际调用的 stored procedures、参数和执行顺序。
为什么不能直接更新发票
一笔应收付款通常会同时影响:
- 收款批次及批次控制金额
- 客户账户余额
- 发票未结余额
- 收款与发票之间的核销关系
- 折扣、手续费或汇兑差额
- AR 明细和历史记录
- 总账分录及过账状态
- 审计字段、期间和会计日期
- 支票号、信用卡交易号等外部引用
即使把发票余额直接改为 0 后,界面暂时显示为已结清,也可能造成客户余额、AR Aging、总账和核销历史不一致。这类问题往往要到月末对账或重新生成账龄报表时才会暴露。
推荐的集成流程
1. 为付款建立唯一标识
支付引擎应为每笔付款生成唯一的外部交易号,例如:
PAY-20260829-00018452
将该交易号写入 Sage 500 中可查询的 reference、check number、comment 或其他合适字段。每次重新提交前,先检查该标识是否已经存在,避免网络重试产生重复收款。
建议在数据库中另外维护一张集成控制表:
CREATE TABLE IntegrationPaymentLog
(
ExternalPaymentId varchar(100) NOT NULL PRIMARY KEY,
CustomerKey int NOT NULL,
InvoiceNumber varchar(50) NOT NULL,
PaymentAmount decimal(19,4) NOT NULL,
Status varchar(20) NOT NULL,
SageBatchNumber varchar(50) NULL,
SageReceiptNumber varchar(50) NULL,
LastError nvarchar(2000) NULL,
CreatedAt datetime2 NOT NULL DEFAULT SYSUTCDATETIME(),
UpdatedAt datetime2 NOT NULL DEFAULT SYSUTCDATETIME()
);
这张表只属于集成层,不能替代 Sage 500 自身的会计记录。
2. 验证付款条件
写入前至少需要检查:
- 公司、客户和发票是否匹配
- 发票当前是否仍未结清
- 付款金额是否大于零
- 付款金额是否超过当前可核销余额
- 发票币种与付款币种是否一致
- 会计期间是否开放
- 付款日期和过账日期是否合法
- 外部交易号是否已经导入
- 是否允许部分付款、批量支付或一笔付款核销多张发票
不要使用支付页面查询时保存的旧余额。正式核销前必须重新读取当前余额,因为客户付款期间,其他用户可能已经录入贷项、调整或另一笔收款。
3. 创建 AR 收款批次
按照 Sage 500 的标准流程创建 Cash Receipts batch,并设置:
- Batch number
- Company
- Accounting date
- Deposit date
- Payment type
- Batch control total
- Receipt count
批次控制总额应与本次导入的付款金额合计一致。测试阶段建议先创建未过账批次,交由财务人员复核。
4. 创建收款记录
每笔付款至少需要保存:
- 客户标识
- 付款金额
- 付款日期
- 付款方式
- 支票号或外部交易号
- 银行或现金账户
- 收款批次
- 必要的描述和审计信息
不要把信用卡授权信息、网关 token 或银行账户敏感信息写入普通备注字段。通常只需保留支付网关交易号和必要的非敏感引用。
5. 将收款核销到发票
创建收款后,还要建立收款与目标发票之间的 application 记录。事务结构可以参考以下伪代码:
BEGIN TRANSACTION;
-- 1. 锁定并重新读取目标发票
-- 2. 检查 ExternalPaymentId 是否已经处理
-- 3. 创建或取得未过账的 Cash Receipts batch
-- 4. 创建 receipt
-- 5. 将 receipt application 到指定 invoice
-- 6. 调用 Sage 500 的校验或更新过程
-- 7. 写入 IntegrationPaymentLog
COMMIT TRANSACTION;
这只是事务结构示例,不能直接作为写入 Sage 500 的脚本使用。表名、主键生成方式和过程参数必须以实际安装版本为准。
部分付款后,发票应继续保持未结状态,只减少剩余余额。全额付款后,应由核销和过账逻辑判断是否关闭发票,集成程序不应强制设置关闭标志。
6. 校验并过账
收款和核销关系创建成功后,再执行批次校验和过账。建议将“导入”与“过账”分为两个阶段:
支付成功
↓
建立未过账收款批次
↓
核对控制金额和错误信息
↓
由自动任务或授权用户过账
如果客户、期间、账户或金额校验失败,这种方式允许直接修正批次,不会留下只完成部分更新的会计数据。
如何定位正确的 stored procedures
不要根据网上其他版本的过程名编写生产集成。应在测试公司中手工完成一次操作,同时记录 Sage 500 客户端实际执行的数据库调用。
需要重点跟踪以下业务动作:
- 新建 AR Cash Receipts batch
- 添加客户收款
- 输入支票号或付款引用
- 选择并核销指定发票
- 保存批次
- 校验批次
- 过账批次
可以使用 SQL Server Extended Events,旧环境也可能使用 SQL Server Profiler。过滤条件可包括:
- Sage 500 应用使用的数据库
- 客户端登录名
- 客户端主机名
RPC:CompletedSQL:BatchCompleted
可以先通过数据库元数据搜索可能相关的过程,但结果只能作为调查线索:
SELECT
s.name AS SchemaName,
p.name AS ProcedureName
FROM sys.procedures AS p
JOIN sys.schemas AS s
ON s.schema_id = p.schema_id
WHERE p.name LIKE '%Receipt%'
OR p.name LIKE '%Cash%'
OR p.name LIKE '%Apply%'
OR p.name LIKE '%Batch%'
OR p.name LIKE '%Post%'
ORDER BY s.name, p.name;
还可以搜索过程定义中引用 AR 收款或核销对象的代码:
SELECT
s.name AS SchemaName,
o.name AS ObjectName
FROM sys.sql_modules AS m
JOIN sys.objects AS o
ON o.object_id = m.object_id
JOIN sys.schemas AS s
ON s.schema_id = o.schema_id
WHERE m.definition LIKE '%Cash Receipt%'
OR m.definition LIKE '%Application%'
ORDER BY s.name, o.name;
这些查询无法判断哪个过程可以安全调用,还需要确认:
- 哪些过程是公开集成入口,哪些只是内部子过程
- 主键、批次号和单据号如何生成
- 是否要求特定 session context
- 是否依赖临时表
- 调用顺序和事务边界
- 输出参数及错误返回方式
- 过程是否负责总账分录
- 升级后接口是否保持兼容
如果某个过程只供 Sage 500 客户端内部调用,直接依赖它可能不属于受支持的集成方式,也可能在升级或安装补丁后失效。
SQL 自动化的建议结构
可以让 SQL 负责暂存、幂等控制和调度,再由 Sage 500 支持的导入机制创建会计记录:
Payment Engine
→ Staging Table
→ Validation Job
→ Sage 500 Business Interface / Data Import Manager
→ Unposted Cash Receipts Batch
→ Review and Posting
暂存表可以这样设计:
CREATE TABLE StagingARPayment
(
StagingId bigint IDENTITY(1,1) PRIMARY KEY,
ExternalId varchar(100) NOT NULL UNIQUE,
CustomerNumber varchar(50) NOT NULL,
InvoiceNumber varchar(50) NOT NULL,
PaymentDate date NOT NULL,
PaymentAmount decimal(19,4) NOT NULL,
PaymentMethod varchar(20) NOT NULL,
PaymentReference varchar(100) NOT NULL,
ProcessingStatus varchar(20) NOT NULL DEFAULT 'Pending',
ErrorMessage nvarchar(2000) NULL
);
导入任务从 Pending 状态的记录开始处理。成功后将状态改为 Imported,失败时改为 Error 并保留原因。状态更新必须关联 Sage 500 返回的批次号或收款号,不能仅凭“SQL 没有报错”判断导入成功。
必须覆盖的异常场景
上线前至少需要测试:
- 全额支付一张发票
- 部分支付
- 一笔付款核销多张发票
- 多笔付款核销同一张发票
- 重复提交同一外部交易号
- 付款期间发票余额发生变化
- 付款金额超过余额
- 发票已关闭或已冲销
- 客户存在贷项
- 折扣或短付
- 跨币种付款
- 已关闭会计期间
- 无效银行或总账账户
- 导入成功但过账失败
- 过账过程中连接中断
- 付款退款、撤销和拒付
支付网关的“授权成功”和 Sage 500 的“收款已过账”是两个不同的状态,不能直接等同。系统应分别记录并支持对账。
实施建议
如果无法确认某组 stored procedures 是当前版本认可的集成入口,使用 Data Import Manager 或 Sage 500 对应版本提供的业务接口更可靠。SQL 仍可承担大部分自动化工作,包括暂存、验证、去重、批次生成、状态跟踪和错误重试。
无论采用哪种方式,都应先在测试公司和数据库备份上验证,并交叉核对以下结果:
- 发票剩余余额
- 客户余额
- AR Aging
- Cash Receipts register
- 批次控制总额
- 总账过账金额
- 外部支付平台结算金额
只有这些结果全部一致,才能确认付款导入和发票关闭流程已经完整成功。
备注:内容仅供参考。
