【问题标题】:Schema Design for Invoices and Payments - better model than M:M relationship?发票和付款的模式设计 - 比 M:M 关系更好的模型?
【发布时间】:2012-01-31 19:36:29
【问题描述】:

在我现有的架构中,发票和项目之间存在 1:1 的关系。我正在修改它以允许一个项目有许多发票,但我还需要跟踪付款。

目前,单笔付款涉及一张(或多张)发票(实际上是一批发票)。理想情况下,可以跟踪每张发票的多次付款。为此,我认为发票和付款之间需要存在 M:M 关系。

那么,一旦存储了数据,如何确定一个项目还需要支付多少费用?如何确定单个客户的欠款金额?我想不出如何正确分组和求和。

到目前为止,我想出了几种方法,方法 1-3 在不同程度上基于下面代码中的表/值。我刚才想出了方法 4,目前看来是一个可行的选择,尽管它不允许您在所有情况下确定单个项目的欠款。

  1. 创建 3 个依赖于关系类型(#payinv 表中的 relType 字段)的不同查询,其中 2 个我知道如何创建。例如,relType = '1:M',我可以在 1 侧分组,并在多侧求和; relType = 'M:1' 反之亦然。但是当relType = 'M:M我不知所措。

  2. 通过在每个发票/付款中包含属性 ProjectID 和 BillToPartyID。我可以对单个项目的所有发票和付款进行汇总和比较,但是当付款涵盖多张发票时,似乎为该项目支付了太多费用。如果我追讨 BillToParty 的欠款,我无法确定他们还欠哪个项目的钱。

  3. 我可以尝试在联结表中存储发票和付款值。例如,当输入付款时,用户会将付款应用到发票上。系统将通过 3 个步骤确定在用户尝试创建的记录中输入的值。 #1 它将通过将已在联结表中的当前发票金额相加来计算剩余余额,然后从原始发票金额中减去该值。 #2 它将以类似的方式计算可用于支付的剩余资金。 #3 它将剩余余额与可用资金进行比较,并将付款和发票金额中的较小者输入到联结表中。我看到了三种 3 种可能性: a) 如果发票余额为 0,则付款和发票均关闭以供进一步使用。 b) 如果发票的余额少于付款,发票将关闭,付款仍可用于另一张发票。 c) 如果发票的余额大于付款,则付款关闭,发票保持打开状态。除了复杂性之外,我认为这个想法没有任何问题......

  4. 我可以将 BillToParty 分配给 Invoice Batch,Invoice Batch 将是 Invoices 和 Payments 的父级。如果客户少付,我们可以计算余额并生成新的“发票”(不是原始意义上的发票,可能不想存储这个???)。如果他们多付,我们必须开具信用证,因为我们无法将付款应用到另一批。

        CREATE TABLE #inv
            (
            invID int NOT NULL,
            invAMT int NULL
            )  ON [PRIMARY]
        GO
        ALTER TABLE #inv ADD CONSTRAINT
            PK_inv PRIMARY KEY CLUSTERED 
            (
            invID
            )
        CREATE TABLE #pay
            (
            payID varchar(50) NOT NULL,
            payAMT int NULL
            )  ON [PRIMARY]
        GO
        ALTER TABLE #pay ADD CONSTRAINT
            PK_pay PRIMARY KEY CLUSTERED 
            (
            payID
            ) 
        CREATE TABLE #payinv
            (
            payID varchar(50) NOT NULL,
            invID int NOT NULL,
            relType varchar(50) not null
            )  ON [PRIMARY]
        GO
        ALTER TABLE #payinv ADD CONSTRAINT
            PK_payinv PRIMARY KEY CLUSTERED 
            (
            payID,
            invID
            )
        INSERT INTO #inv (invID, invAMT)
        select 1,110
        union
        select 2,400
        union
        select 3,600
        union
        select 4,100000
        union
        select 5,10000
        union
        select 6,1000000;
    
        INSERT INTO #pay (payID,payAMT)
        select 'a',10
        union
        select 'b',100
        union
        select 'c',1000
        union
        select 'd',10000
        union
        select 'e',100000
        union
        select 'f',1000000;
    
        INSERT INTO #payinv(payID,invID,relType)
        select 'a',1,'1:M'
        union
        select 'b',1,'1:M'
        union
        select 'c',2,'M:1'
        union
        select 'c',3,'M:1'
        union
        select 'd',4,'M:M'
        union
        select 'e',4,'M:M'
        union
        select 'e',5,'M:M'
        union
        select 'f',6,'1:1';
    
        select #inv.invAMT, #inv.invID, #pay.payID, #pay.payAMT, #payinv.relType
        from #inv inner join #payinv on #inv.invID = #payinv.invID inner join #pay on #payinv.payID = #pay.payID
    

【问题讨论】:

  • 为什么您的会计系统不这样做?
  • 我们目前在与 QuickBooks 和异地兼职会计师关联的巨型电子表格上跟踪发票和付款。在创建发票的系统中跟踪付款似乎是合乎逻辑的步骤。能够在同一系统中报告所有统一数据将是一个加分项。

标签: sql sql-server-2008 relational-database database-schema


【解决方案1】:

您的项目、发票和付款表中需要您的 billToPartyID(或 customerID,我假设它们是相同的,或者它们之间存在 1->1 关系)作为 PK 的一部分。那么你的关系是:

“一个客户/billToParty 可以有很多项目,可以有很多发票。客户/billToParty 支付的款项可以应用于客户的许多发票,必须指定。”

那么应该更容易按 customerID 跟踪总计,包括已付款和未结发票金额。

【讨论】:

  • 很遗憾,我无法将客户/BillToParty 添加到项目的 PK 中,因为可以为单个项目向多方开具发票。此外,方法 2 描述了我预见到的该实现的一些问题。你觉得方法三怎么样?
猜你喜欢
  • 1970-01-01
  • 2016-05-10
  • 2017-12-25
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-24
相关资源
最近更新 更多