Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
thenearfuture
Starting Member
35 Posts |
Posted - 2008-01-10 : 22:27:11
|
Well... this one's long (it's getting to be a habit with me), but I figure it's better to be thorough than to bring an entire website of otherwise kind and helpful SQL experts to a slow and tortuous boil in a series of 10 short and uninformative posts :). Introduction At my job, I have been working on our new invoicing module. We are in the financial services industry (we offer various aspects of accounting) and one of the things we do is to handle incoming payments for our clients. We charge our clients a commission fee on a percentage of the payments that we process. That fee is based on a fee agreement that depends on several factors. I've been making an invoicing module that automatically creates an invoice on the basis of these fee agreements so that we can bill our commission fee. I've been making fairly good progress (first time designing something from scratch... lots of nail-biting), but I've hit upon an issue that's been causing me some trouble, so I thought I'd ask here for some perspectives. Before I get to the problem, I thought I'd describe exactly what I'm doing. I have simplified a lot of the details, as they involve date ranges and other complicating factors that don't really have anything to do with my problem. The basic setupAs I said before, the fee that we charge is based on a commission fee agreement (or invoice agreement) that depends on the following things:1.) The process id (int_CTLProcessId)Our files are organized into case numbers. Every case goes through certain processes as it progresses through the system. The commission fee that we charge can depend on the process the case was in when the payment was made. However, it is not necessary for a commission fee to be based on a process. 2.) The claim part id (chr_CSEClaimPartId)Each payment is organized into so-called "claim parts" - what part of the payment goes toward the original principal, what part goes to interest, etc. The commission fee agreement is always based on these claim parts, as we only charge for certain claim parts in certain types of agreements.What is an Invoice Agreement?So, an invoice agreement would typically have an invoice agreement id, a process id (not always necessary), a claim part id, and a percentage showing how much of that particular entry we take as a fee (in reality, the invoice agreement is more complicated, involving to/from dates and some other things, but that doesn't really matter here). Here is a simplified example:---------------------------------------------------------------------------| InvoiceAgreement | ProcessId | ClaimPartId | FeePercent |---------------------------------------------------------------------------| 11 | NULL | COLSAL | 30% |---------------------------------------------------------------------------| 11 | NULL | CRDINV | 40% |---------------------------------------------------------------------------| 13 | 1 | COLSAL | 50% |---------------------------------------------------------------------------| 13 | 1 | CRDINV | 20% |--------------------------------------------------------------------------- Additional data you will see in the code For our invoicing requirements, we also need to be able to see two things in the invoice item details:1.) The account entry type (chr_CSEAccountEntryTypeId)We organize payments into two broad categories - payments sent directly to us (PAYCOL%), and payments that were sent to our client (PAYCRD%) and then forwarded to us. Payments can be positive or negative. If a payment is negative, this means that the payment was rejected for some reason (PAYCRDREJ/PAYCOLREJ, for example). 1.) The voucher ID (int_LEDVoucherId)The voucher ID applies only to payments that come to us (PAYCOL%). It does not exist for PAYCRD% payments.One last thing Payments are initially recorded into our Account Entry tables. The first table is the actual Account Entry table showing the entire amount that was paid. The second table is the Actor Account Entry table that specifies the payments into claim parts and has a foreign key linked to the AccountEntryId. You can see how these tables are related in my sample script.Finally: the problemIn the CombinedInfo table, I have summarized all the information necessary to produce an invoice. These are all the details related to different actor account entries that need to be invoiced (in reality, this is the result of a join involving quite a few tables).Here, you can see that AccountEntryId # 12 has two ActorAccountEntries that have to be invoiced (#16 and #17). The invoice agreement that applies to this situation is #13. That agreement says that payments in Process 1 that have claim part "COLSAL" have to billed at 50%, while claim part "CRDINV" requires 20%. You can also see the account entry type id and the voucher id. I want to combine all of this into our two Invoice tables: Invoice and InvoiceItem. The InvoiceItem table groups the CombinedInfo table information. The Invoice table just adds up the money amount in the InvoiceItem table for that particular invoice.What I need to do is find a way to add an InvoiceItem foreign key to the Actor Account Entry table, so that each Actor Account Entry that has been invoiced gets a link to its entry in the InvoiceItem table. Basically, it would need to be something like an @@Identity command for multiple entries. Is there a way to do this? Perhaps the entire procedure needs to be changed around? I would be grateful for any suggestions.CODE:/********** Create sample data showing all the information necessary to create an invoice.This table is actually a combination of data from several different tables */DECLARE @CombinedInfo TABLE (int_CSEAcctEntryId INT, int_CSEActorAcctEntryId INT, int_INVInvoiceAgreementId INT, int_CTLProcessId INT, chr_CSEClaimPartId CHAR(10), chr_CSEAccountEntryTypeId CHAR(10), int_LEDVoucherId INT, mny_Amount MONEY, mny_Percent MONEY)INSERT INTO @CombinedInfo ( [int_CSEAcctEntryId], [int_CSEActorAcctEntryId], [int_INVInvoiceAgreementId], [int_CTLProcessId], [chr_CSEClaimPartId], [chr_CSEAccountEntryTypeId], [int_LEDVoucherId], [mny_Amount], [mny_Percent])SELECT 12, 16, 13, 1, 'COLSAL', 'PAYCOL', 5, 200, 0.5 UNION ALL SELECT 12, 17, 13, 1, 'CRDINV', 'PAYCOL', 5, 115, 0.2 UNION ALLSELECT 16, 24, 13, 1, 'CRDINV', 'PAYCOL', 5, 100, 0.2 UNION ALLSELECT 19, 35, 13, 1, 'COLSAL', 'PAYCRD', NULL, 58, 0.5 UNION ALL SELECT 23, 41, 11, NULL, 'COLSAL', 'PAYCOLREJ', 7, -100, 0.3 UNION ALLSELECT 28, 52, 11, NULL, 'CRDINV', 'PAYCRD', NULL, 150, 0.4SELECT * FROM @CombinedInfo/********** Create the sample Account Entry tables to serve as a reference*/DECLARE @AccountEntry TABLE (int_CSEAccountEntryId INT, dtm_EntryCreated DATETIME, chr_CSEAccountEntryTypeId CHAR(10), mny_Amount MONEY)INSERT INTO @AccountEntry ( [int_CSEaccountEntryId], [chr_CSEAccountEntryTypeId], [mny_Amount])SELECT 12, 'PAYCOL', 315 UNION ALLSELECT 16, 'PAYCOL', 100 UNION ALLSELECT 19, 'PAYCRD', 58 UNION ALLSELECT 23, 'PAYCOLREJ', -100 UNION ALLSELECT 28, 'PAYCRD', 150 DECLARE @ActorAcctEntry TABLE (int_CSEActorAcctEntryId INT, int_CSEAccountEntryId INT, chr_CSEClaimPartId CHAR(10), mny_Amount MONEY, int_INVInvoiceItemId INT)INSERT INTO @ActorAcctEntry ( [int_CSEActorAcctEntryId], int_CSEAccountEntryId, chr_CSEClaimPartId, [mny_Amount], [int_INVInvoiceItemId])SELECT 16, 12, 'COLSAL', 200, NULL UNION ALLSELECT 17, 12, 'CRDINV', 115, NULL UNION ALLSELECT 24, 16, 'CRDINV', 100, NULL UNION ALL SELECT 35, 19, 'COLSAL', 58, NULL UNION ALLSELECT 41, 23, 'COLSAL', -100, NULL UNION ALLSELECT 52, 28, 'CRDINV', 150, NULL /********** Create the Invoice table, insert a placeholder value of zero for the money amount, and return the InvoiceId for the next step */DECLARE @INVInvoice TABLE (int_INVInvoiceId INT IDENTITY(1,1) NOT NULL, mny_Amt MONEY NOT NULL)INSERT INTO @INVInvoice ( [mny_Amt]) VALUES (0)DECLARE @InvoiceId INT SET @InvoiceId = SCOPE_IDENTITY() /********** Create the InvoiceItem table and populate it with data from the CombinedInfo table above */DECLARE @INVInvoiceItem TABLE (int_INVInvoiceItemId INT IDENTITY(1,1) NOT NULL, int_INVInvoiceId INT, int_INVInvoiceAgreementId INT, int_CTLProcessId INT, chr_CSEClaimPartId CHAR(10), chr_CSEAccountEntryTypeId CHAR(10), int_LEDVoucherId INT, mny_AmountTotal MONEY, mny_Percent MONEY, mny_AmountShare MONEY) INSERT INTO @INVInvoiceItem ( [int_INVInvoiceId], [int_INVInvoiceAgreementId], [int_CTLProcessId], [chr_CSEClaimPartId], [chr_CSEAccountEntryTypeId], [int_LEDVoucherId], [mny_AmountTotal], [mny_Percent], [mny_AmountShare]) SELECT @InvoiceId, int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, SUM(mny_amount), mny_Percent, SUM(mny_amount) * mny_PercentFROM @CombinedInfoGROUP BY int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, mny_PercentORDER BY int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, mny_PercentSELECT * FROM @INVInvoiceItem/********** Sum up the commission share from the InvoiceItem table and update the Invoice table with that money amount */DECLARE @InvoiceItemShareSum MONEYSELECT @InvoiceItemShareSum = SUM([mny_AmountShare])FROM @INVInvoiceItem WHERE [int_INVInvoiceId] = @InvoiceIdUPDATE @INVInvoiceSET [mny_Amt] = @InvoiceItemShareSum WHERE [int_INVInvoiceId] = @InvoiceIdSELECT *FROM @INVInvoice/********** Show the expected result: Put InvoiceItem foreign key intoActorAcctEntry table */DECLARE @ActorAcctEntry_with_InvoiceItem TABLE (int_CSEActorAcctEntryId INT, int_CSEAccountEntryId INT, chr_CSEClaimPartId CHAR(10), mny_Amount MONEY, int_INVInvoiceItemId INT)INSERT INTO @ActorAcctEntry_with_InvoiceItem ( [int_CSEActorAcctEntryId], int_CSEAccountEntryId, chr_CSEClaimPartId, [mny_Amount], [int_INVInvoiceItemId])SELECT 16, 12, 'COLSAL', 200, 3 UNION ALLSELECT 17, 12, 'CRDINV', 115, 5 UNION ALLSELECT 24, 16, 'CRDINV', 100, 5 UNION ALL SELECT 35, 19, 'COLSAL', 58, 4 UNION ALLSELECT 41, 23, 'COLSAL', -100, 1 UNION ALLSELECT 52, 28, 'CRDINV', 150, 2SELECT *FROM @ActorAcctEntry_with_InvoiceItem |
|
|
visakh16
Very Important crosS Applying yaK Herder
52326 Posts |
Posted - 2008-01-10 : 23:59:37
|
quote: Originally posted by thenearfuture Well... this one's long (it's getting to be a habit with me), but I figure it's better to be thorough than to bring an entire website of otherwise kind and helpful SQL experts to a slow and tortuous boil in a series of 10 short and uninformative posts :). Introduction At my job, I have been working on our new invoicing module. We are in the financial services industry (we offer various aspects of accounting) and one of the things we do is to handle incoming payments for our clients. We charge our clients a commission fee on a percentage of the payments that we process. That fee is based on a fee agreement that depends on several factors. I've been making an invoicing module that automatically creates an invoice on the basis of these fee agreements so that we can bill our commission fee. I've been making fairly good progress (first time designing something from scratch... lots of nail-biting), but I've hit upon an issue that's been causing me some trouble, so I thought I'd ask here for some perspectives. Before I get to the problem, I thought I'd describe exactly what I'm doing. I have simplified a lot of the details, as they involve date ranges and other complicating factors that don't really have anything to do with my problem. The basic setupAs I said before, the fee that we charge is based on a commission fee agreement (or invoice agreement) that depends on the following things:1.) The process id (int_CTLProcessId)Our files are organized into case numbers. Every case goes through certain processes as it progresses through the system. The commission fee that we charge can depend on the process the case was in when the payment was made. However, it is not necessary for a commission fee to be based on a process. 2.) The claim part id (chr_CSEClaimPartId)Each payment is organized into so-called "claim parts" - what part of the payment goes toward the original principal, what part goes to interest, etc. The commission fee agreement is always based on these claim parts, as we only charge for certain claim parts in certain types of agreements.What is an Invoice Agreement?So, an invoice agreement would typically have an invoice agreement id, a process id (not always necessary), a claim part id, and a percentage showing how much of that particular entry we take as a fee (in reality, the invoice agreement is more complicated, involving to/from dates and some other things, but that doesn't really matter here). Here is a simplified example:---------------------------------------------------------------------------| InvoiceAgreement | ProcessId | ClaimPartId | FeePercent |---------------------------------------------------------------------------| 11 | NULL | COLSAL | 30% |---------------------------------------------------------------------------| 11 | NULL | CRDINV | 40% |---------------------------------------------------------------------------| 13 | 1 | COLSAL | 50% |---------------------------------------------------------------------------| 13 | 1 | CRDINV | 20% |--------------------------------------------------------------------------- Additional data you will see in the code For our invoicing requirements, we also need to be able to see two things in the invoice item details:1.) The account entry type (chr_CSEAccountEntryTypeId)We organize payments into two broad categories - payments sent directly to us (PAYCOL%), and payments that were sent to our client (PAYCRD%) and then forwarded to us. Payments can be positive or negative. If a payment is negative, this means that the payment was rejected for some reason (PAYCRDREJ/PAYCOLREJ, for example). 1.) The voucher ID (int_LEDVoucherId)The voucher ID applies only to payments that come to us (PAYCOL%). It does not exist for PAYCRD% payments.One last thing Payments are initially recorded into our Account Entry tables. The first table is the actual Account Entry table showing the entire amount that was paid. The second table is the Actor Account Entry table that specifies the payments into claim parts and has a foreign key linked to the AccountEntryId. You can see how these tables are related in my sample script.Finally: the problemIn the CombinedInfo table, I have summarized all the information necessary to produce an invoice. These are all the details related to different actor account entries that need to be invoiced (in reality, this is the result of a join involving quite a few tables).Here, you can see that AccountEntryId # 12 has two ActorAccountEntries that have to be invoiced (#16 and #17). The invoice agreement that applies to this situation is #13. That agreement says that payments in Process 1 that have claim part "COLSAL" have to billed at 50%, while claim part "CRDINV" requires 20%. You can also see the account entry type id and the voucher id. I want to combine all of this into our two Invoice tables: Invoice and InvoiceItem. The InvoiceItem table groups the CombinedInfo table information. The Invoice table just adds up the money amount in the InvoiceItem table for that particular invoice.What I need to do is find a way to add an InvoiceItem foreign key to the Actor Account Entry table, so that each Actor Account Entry that has been invoiced gets a link to its entry in the InvoiceItem table. Basically, it would need to be something like an @@Identity command for multiple entries. Is there a way to do this? Perhaps the entire procedure needs to be changed around? I would be grateful for any suggestions.CODE:/********** Create sample data showing all the information necessary to create an invoice.This table is actually a combination of data from several different tables */DECLARE @CombinedInfo TABLE (int_CSEAcctEntryId INT, int_CSEActorAcctEntryId INT, int_INVInvoiceAgreementId INT, int_CTLProcessId INT, chr_CSEClaimPartId CHAR(10), chr_CSEAccountEntryTypeId CHAR(10), int_LEDVoucherId INT, mny_Amount MONEY, mny_Percent MONEY)INSERT INTO @CombinedInfo ( [int_CSEAcctEntryId], [int_CSEActorAcctEntryId], [int_INVInvoiceAgreementId], [int_CTLProcessId], [chr_CSEClaimPartId], [chr_CSEAccountEntryTypeId], [int_LEDVoucherId], [mny_Amount], [mny_Percent])SELECT 12, 16, 13, 1, 'COLSAL', 'PAYCOL', 5, 200, 0.5 UNION ALL SELECT 12, 17, 13, 1, 'CRDINV', 'PAYCOL', 5, 115, 0.2 UNION ALLSELECT 16, 24, 13, 1, 'CRDINV', 'PAYCOL', 5, 100, 0.2 UNION ALLSELECT 19, 35, 13, 1, 'COLSAL', 'PAYCRD', NULL, 58, 0.5 UNION ALL SELECT 23, 41, 11, NULL, 'COLSAL', 'PAYCOLREJ', 7, -100, 0.3 UNION ALLSELECT 28, 52, 11, NULL, 'CRDINV', 'PAYCRD', NULL, 150, 0.4SELECT * FROM @CombinedInfo/********** Create the sample Account Entry tables to serve as a reference*/DECLARE @AccountEntry TABLE (int_CSEAccountEntryId INT, dtm_EntryCreated DATETIME, chr_CSEAccountEntryTypeId CHAR(10), mny_Amount MONEY)INSERT INTO @AccountEntry ( [int_CSEaccountEntryId], [chr_CSEAccountEntryTypeId], [mny_Amount])SELECT 12, 'PAYCOL', 315 UNION ALLSELECT 16, 'PAYCOL', 100 UNION ALLSELECT 19, 'PAYCRD', 58 UNION ALLSELECT 23, 'PAYCOLREJ', -100 UNION ALLSELECT 28, 'PAYCRD', 150 DECLARE @ActorAcctEntry TABLE (int_CSEActorAcctEntryId INT, int_CSEAccountEntryId INT, chr_CSEClaimPartId CHAR(10), mny_Amount MONEY, int_INVInvoiceItemId INT)INSERT INTO @ActorAcctEntry ( [int_CSEActorAcctEntryId], int_CSEAccountEntryId, chr_CSEClaimPartId, [mny_Amount], [int_INVInvoiceItemId])SELECT 16, 12, 'COLSAL', 200, NULL UNION ALLSELECT 17, 12, 'CRDINV', 115, NULL UNION ALLSELECT 24, 16, 'CRDINV', 100, NULL UNION ALL SELECT 35, 19, 'COLSAL', 58, NULL UNION ALLSELECT 41, 23, 'COLSAL', -100, NULL UNION ALLSELECT 52, 28, 'CRDINV', 150, NULL /********** Create the Invoice table, insert a placeholder value of zero for the money amount, and return the InvoiceId for the next step */DECLARE @INVInvoice TABLE (int_INVInvoiceId INT IDENTITY(1,1) NOT NULL, mny_Amt MONEY NOT NULL)INSERT INTO @INVInvoice ( [mny_Amt]) VALUES (0)DECLARE @InvoiceId INT SET @InvoiceId = SCOPE_IDENTITY() /********** Create the InvoiceItem table and populate it with data from the CombinedInfo table above */DECLARE @INVInvoiceItem TABLE (int_INVInvoiceItemId INT IDENTITY(1,1) NOT NULL, int_INVInvoiceId INT, int_INVInvoiceAgreementId INT, int_CTLProcessId INT, chr_CSEClaimPartId CHAR(10), chr_CSEAccountEntryTypeId CHAR(10), int_LEDVoucherId INT, mny_AmountTotal MONEY, mny_Percent MONEY, mny_AmountShare MONEY) INSERT INTO @INVInvoiceItem ( [int_INVInvoiceId], [int_INVInvoiceAgreementId], [int_CTLProcessId], [chr_CSEClaimPartId], [chr_CSEAccountEntryTypeId], [int_LEDVoucherId], [mny_AmountTotal], [mny_Percent], [mny_AmountShare]) SELECT @InvoiceId, int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, SUM(mny_amount), mny_Percent, SUM(mny_amount) * mny_PercentFROM @CombinedInfoGROUP BY int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, mny_PercentORDER BY int_INVInvoiceAgreementId, int_CTLProcessId, chr_CSEClaimPartId, chr_CSEAccountEntryTypeId, int_LEDVoucherId, mny_PercentSELECT * FROM @INVInvoiceItem/********** Sum up the commission share from the InvoiceItem table and update the Invoice table with that money amount */DECLARE @InvoiceItemShareSum MONEYSELECT @InvoiceItemShareSum = SUM([mny_AmountShare])FROM @INVInvoiceItem WHERE [int_INVInvoiceId] = @InvoiceIdUPDATE @INVInvoiceSET [mny_Amt] = @InvoiceItemShareSum WHERE [int_INVInvoiceId] = @InvoiceIdSELECT *FROM @INVInvoice/********** Show the expected result: Put InvoiceItem foreign key intoActorAcctEntry table */DECLARE @ActorAcctEntry_with_InvoiceItem TABLE (int_CSEActorAcctEntryId INT, int_CSEAccountEntryId INT, chr_CSEClaimPartId CHAR(10), mny_Amount MONEY, int_INVInvoiceItemId INT)INSERT INTO @ActorAcctEntry_with_InvoiceItem ( [int_CSEActorAcctEntryId], int_CSEAccountEntryId, chr_CSEClaimPartId, [mny_Amount], [int_INVInvoiceItemId])SELECT 16, 12, 'COLSAL', 200, 3 UNION ALLSELECT 17, 12, 'CRDINV', 115, 5 UNION ALLSELECT 24, 16, 'CRDINV', 100, 5 UNION ALL SELECT 35, 19, 'COLSAL', 58, 4 UNION ALLSELECT 41, 23, 'COLSAL', -100, 1 UNION ALLSELECT 52, 28, 'CRDINV', 150, 2SELECT *FROM @ActorAcctEntry_with_InvoiceItem
Try this:-UPDATE aeSET ae.int_INVInvoiceItemId=inv.int_INVInvoiceItemIdFROM @ActorAcctEntry aeINNER JOIN @CombinedInfo cfON cf.int_CSEAcctEntryId=ae.int_CSEAccountEntryIdAND cf.int_CSEActorAcctEntryId=ae.int_CSEActorAcctEntryIdINNER JOIN @INVInvoiceItem invON cf.chr_CSEClaimPartId=inv.chr_CSEClaimPartIdAND ISNULL(cf.int_LEDVoucherId,0)=ISNULL(inv.int_LEDVoucherId,0)Select * from @ActorAcctEntry output:-int_CSEActorAcctEntryId int_CSEAccountEntryId chr_CSEClaimPartId mny_Amount int_INVInvoiceItemId----------------------- --------------------- ------------------ --------------------- --------------------16 12 COLSAL 200.00 317 12 CRDINV 115.00 524 16 CRDINV 100.00 535 19 COLSAL 58.00 441 23 COLSAL -100.00 152 28 CRDINV 150.00 2 |
 |
|
|
thenearfuture
Starting Member
35 Posts |
Posted - 2008-01-11 : 15:50:19
|
| Thanks, visakh. I had something like that in mind, actually, but wasn't sure if there were any other ways of doing it or if I was missing something. It's great to have confirmation from an expert.One question, though: Are you quoting the entire post as part of the war against rampant deletion? |
 |
|
|
|
|
|
|
|