PAYATHON 2026

AX 2012 中 ledgerJournalTrans 的付款与发票关系

支付小周

明确结论

LedgerJournalTrans 中的付款行和发票行之间没有可靠的主外键关系,不能只通过 Invoice 字段直接关联。

在 AX 2012 中:

  • 过账前,付款日记账中选中的待结发票通常通过 SpecTrans 保存标记结算关系。
  • 过账后,需要通过 VendTrans 和 VendSettlement 查询实际的结算关系。
  • LedgerJournalTrans.Invoice 是业务编号,不是唯一键。即使付款日记账和发票日记账中的值相同,也不能将它作为唯一关联条件。

为什么不能直接关联 LedgerJournalTrans

LedgerJournalTrans 保存日记账行,过账后生成的供应商交易则保存在 VendTrans 中。一笔付款可以结算多张发票,一张发票也可能分多次支付,因此付款和发票之间本来就是多对多关系。

直接使用以下条件进行关联会有风险:

LedgerJournalTrans.Invoice == LedgerJournalTrans.Invoice

主要原因有:

  • 不同供应商可能使用相同的发票号。
  • 同一张发票可能分期支付。
  • 一笔付款可能同时结算多张发票。
  • 付款行的 Invoice 字段不一定有值。
  • 付款可能通过付款建议、手工标记或后续结算完成。
  • 结算可能被冲销,也可能重新结算。

因此,Invoice、Voucher、AccountNum 和 TransDate 等字段只能帮助定位记录,不能代替结算表中保存的正式关系。

查询已过账的付款与发票

已过账交易的主要数据关系如下:

LedgerJournalTrans
        ↓ 过账
VendTrans
        ↓ 结算
VendSettlement
        ↓
另一个 VendTrans

VendSettlement 中常用的关联字段包括:

  • TransRecId:被结算供应商交易的 VendTrans.RecId
  • OffsetRecid:参与结算的另一笔 VendTrans.RecId
  • TransCompany:TransRecId 所属公司
  • OffsetCompany:OffsetRecid 所属公司
  • SettleAmountCur:结算金额
  • SettlementVoucher:结算凭证

下面的 X++ 示例根据一笔 VendTrans 查询与其发生结算的其他供应商交易:

static void FindRelatedVendTransactions(Args _args)
{
    VendTrans       sourceTrans;
    VendTrans       relatedTrans;
    VendSettlement  settlement;
    RecId           sourceRecId = 5637144576; // 替换为实际 VendTrans.RecId

    select firstOnly sourceTrans
        where sourceTrans.RecId == sourceRecId;

    if (!sourceTrans)
    {
        info("未找到指定的 VendTrans。");
        return;
    }

    // sourceTrans 位于 VendSettlement.TransRecId 一侧
    while select settlement
        where settlement.TransRecId == sourceTrans.RecId
           && settlement.TransCompany == sourceTrans.DataAreaId
    {
        relatedTrans.clear();

        changecompany(settlement.OffsetCompany)
        {
            select firstOnly relatedTrans
                where relatedTrans.RecId == settlement.OffsetRecid;
        }

        if (relatedTrans)
        {
            info(strFmt(
                "源交易=%1,关联交易=%2,发票=%3,结算金额=%4,结算凭证=%5",
                sourceTrans.Voucher,
                relatedTrans.Voucher,
                relatedTrans.Invoice,
                settlement.SettleAmountCur,
                settlement.SettlementVoucher));
        }
    }

    // sourceTrans 也可能位于 VendSettlement.OffsetRecid 一侧
    while select settlement
        where settlement.OffsetRecid == sourceTrans.RecId
           && settlement.OffsetCompany == sourceTrans.DataAreaId
    {
        relatedTrans.clear();

        changecompany(settlement.TransCompany)
        {
            select firstOnly relatedTrans
                where relatedTrans.RecId == settlement.TransRecId;
        }

        if (relatedTrans)
        {
            info(strFmt(
                "源交易=%1,关联交易=%2,发票=%3,结算金额=%4,结算凭证=%5",
                sourceTrans.Voucher,
                relatedTrans.Voucher,
                relatedTrans.Invoice,
                settlement.SettleAmountCur,
                settlement.SettlementVoucher));
        }
    }
}

在实际环境中,还需要判断 relatedTrans 属于发票、付款、贷项通知单还是其他供应商交易。不能只根据金额的正负值判断,因为冲销、退款和贷项通知单同样会影响金额符号。

从 LedgerJournalTrans 定位 VendTrans

如果只有已过账的 LedgerJournalTrans,通常要先根据公司、供应商账户、凭证号和过账日期查找对应的 VendTrans:

static VendTrans findVendTransFromJournalLine(
    LedgerJournalTrans _journalLine)
{
    VendTrans vendTrans;

    select firstOnly vendTrans
        where vendTrans.AccountNum == _journalLine.AccountNum
           && vendTrans.Voucher    == _journalLine.Voucher
           && vendTrans.TransDate  == _journalLine.TransDate;

    return vendTrans;
}

这段代码仅用于说明定位方式,不能保证在所有配置下都能得到唯一记录。生产代码还需要结合当前环境中可用的字段缩小查询范围,例如:

  • DataAreaId
  • AccountNum
  • Voucher
  • TransDate
  • Invoice
  • 金额及币种
  • 日记账批次和过账凭证相关信息

如果同一凭证中包含多条供应商交易,必须确认查询结果是否唯一,不能直接依赖 firstOnly。

查询尚未过账的标记关系

付款日记账尚未过账时,系统中还没有最终的 VendSettlement 记录。用户在“结算未结交易”窗口中选中的发票,通常记录在 SpecTrans 中。

SpecTrans 保存临时或待处理的标记关系。查询时需要使用当前结算上下文中的以下字段:

  • SpecCompany
  • SpecTableId
  • SpecRecId
  • RefCompany
  • RefTableId
  • RefRecId

其中,RefRecId 通常指向被标记的开放供应商交易记录。SpecTableId 和 SpecRecId 的具体取值取决于付款日记账的结算调用路径,也可能受到 AX 2012 定制的影响,因此不建议自行拼接查询条件。更稳妥的方式是沿用标准结算窗体或 SpecTransManager 使用的上下文来查询标记记录。

需要注意,SpecTrans 表示的是“准备结算”或“已经标记”,不是最终结算结果。付款取消过账、取消标记或重新选择发票后,这些关系都可能发生变化。

实际处理建议

如果要制作“发票对应哪些付款”的报表,可以按以下顺序处理:

  1. 从发票对应的 VendTrans 开始查询。
  2. 通过 VendSettlement.TransRecId 和 VendSettlement.OffsetRecid 双向查找相关交易。
  3. 读取另一侧对应的 VendTrans。
  4. 根据交易类型、发票号、凭证、金额和币种判断交易的业务性质。
  5. 使用 SettleAmountCur 展示本次结算金额,不要直接把付款总额作为发票的支付金额。
  6. 按照报表口径处理结算冲销、部分结算和跨公司结算。

如果要查询付款日记账当前选中了哪些发票,应查询 SpecTrans,而不是 VendSettlement。

注意事项

  • VendTrans.Invoice 和 LedgerJournalTrans.Invoice 都不是全局唯一值。
  • 同一笔付款可能对应多条 VendSettlement。
  • 同一张发票也可能对应多条付款结算记录。
  • 发生部分付款时,应使用结算金额,不能只比较原始交易金额。
  • 查询时要考虑 TransCompany 和 OffsetCompany,启用了集中付款或跨公司结算时尤其如此。
  • 是否显示已冲销的结算取决于报表口径,需要结合结算状态和冲销记录处理。
  • AX 2012 的不同累计更新版本以及客户定制可能增加字段或改变结算流程。字段关系应以当前环境中的 AOT 表关系和标准结算代码为准。

备注:内容仅供参考。