Schema Design for Invoices and Payments - better model than M:M relationship?

可紊 提交于 2019-12-24 20:08:00

问题


There is a 1:1 relationship between invoices and projects in my existing schema. I am in the process of modifying it to allow for one project to have many invoices, but I also need to track payments.

Currently, a single payment relates to 1 (or more) invoices (really it's an Invoice Batch). Ideally, multiple payments per invoice can be tracked. To do that, I think there needs to be a M:M relationship between invoices and payments.

So, once the data is stored, how does one determine how much is left to pay on a single Project? How does one determine how much a single customer owes? I can't think of how to properly group and sum.

I have came up short with a couple methods so far, methods 1-3 are, to varying degrees, based on the tables/values in the code below. I came up with method 4 just now, and currently seems like a workable option, though it doesn't allow you to determine what's owed on a single project in all scenarios.

  1. Create 3 different queries that rely on the relationship type (relType field in the #payinv table), 2 of which I know how to make. E.g., where relType = '1:M', I can group on the 1 side, and sum the many side; and vice versa for relType = 'M:1'. But when relType = 'M:M I am at a loss.

  2. By including the attributes ProjectID and BillToPartyID on each Invoice/Payment. I can sum and compare all the invoices and payments for a single project, but when a payment covers multiple invoices, it will appear that too much has been paid for that one project. If I pull what the BillToParty owes, I can't pinpoint which project they still owe money on.

  3. I can try to store invoice and payment values in the junction table. E.g., when entering payments, the user would apply a payment to an invoice. The system would determine what values to enter in the record the user is trying to create in 3 steps. #1 It would calculate the remaining balance by summing the amounts of the current invoice already in the junction table, then subtracting that value from the original invoice amount. #2 It would calculate the remaining funds available for the payment in a similar way. #3 it would compare the remaining balance with the funds available and enter the lesser of both amounts for both payment and invoice into the junction table. I see three 3 possibilities: a) If the remaining balance of the invoice is 0, both the payment and invoice are closed for further use. b) If the remaining balance of the invoice is less than the payment, the invoice is closed and the payment remains available to be applied to a different invoice. c) If the remaining balance of the invoice is greater than the payment, the payment is closed and the invoice remains open. Other than complexity, I don't see problems with this idea...

  4. I can assign the BillToParty to an Invoice Batch, the Invoice Batch will be the parent of both Invoices and Payments. If a customer underpays, we can calculate the remaining balance and generate a new "invoice" (Not an invoice in the original sense, and probably don't want to store this???). If they overpay, we must issue a credit because we cannot apply the payment to another batch.

        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
    

回答1:


You need your billToPartyID (or customerID, I'm assuming they're the same thing, or there's a 1->1 relationship between them) in your project, invoice, and payment tables as part of the PK. Your relationships are then:

"a customer/billToParty can have many projects, which can have many invoices. A payment made by a customer/billToParty can apply to many of the customer's invoices, which must be specified."

Then it should be easier to track totals by customerID, both payments made and invoice amounts outstanding.



来源:https://stackoverflow.com/questions/9085741/schema-design-for-invoices-and-payments-better-model-than-mm-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!