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.

 All Forums
 SQL Server 2005 Forums
 Transact-SQL (2005)
 Something like @@Identity for multiple rows?

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 setup

As 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 problem

In 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 ALL
SELECT 16, 24, 13, 1, 'CRDINV', 'PAYCOL', 5, 100, 0.2 UNION ALL
SELECT 19, 35, 13, 1, 'COLSAL', 'PAYCRD', NULL, 58, 0.5 UNION ALL
SELECT 23, 41, 11, NULL, 'COLSAL', 'PAYCOLREJ', 7, -100, 0.3 UNION ALL
SELECT 28, 52, 11, NULL, 'CRDINV', 'PAYCRD', NULL, 150, 0.4

SELECT *
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 ALL
SELECT 16, 'PAYCOL', 100 UNION ALL
SELECT 19, 'PAYCRD', 58 UNION ALL
SELECT 23, 'PAYCOLREJ', -100 UNION ALL
SELECT 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 ALL
SELECT 17, 12, 'CRDINV', 115, NULL UNION ALL
SELECT 24, 16, 'CRDINV', 100, NULL UNION ALL
SELECT 35, 19, 'COLSAL', 58, NULL UNION ALL
SELECT 41, 23, 'COLSAL', -100, NULL UNION ALL
SELECT 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_Percent
FROM @CombinedInfo
GROUP BY
int_INVInvoiceAgreementId,
int_CTLProcessId,
chr_CSEClaimPartId,
chr_CSEAccountEntryTypeId,
int_LEDVoucherId,
mny_Percent
ORDER BY
int_INVInvoiceAgreementId,
int_CTLProcessId,
chr_CSEClaimPartId,
chr_CSEAccountEntryTypeId,
int_LEDVoucherId,
mny_Percent

SELECT *
FROM @INVInvoiceItem

/********** Sum up the commission share from the InvoiceItem table and
update the Invoice table with that money amount */

DECLARE @InvoiceItemShareSum MONEY

SELECT @InvoiceItemShareSum = SUM([mny_AmountShare])
FROM @INVInvoiceItem
WHERE [int_INVInvoiceId] = @InvoiceId

UPDATE @INVInvoice
SET [mny_Amt] = @InvoiceItemShareSum
WHERE [int_INVInvoiceId] = @InvoiceId

SELECT *
FROM @INVInvoice

/********** Show the expected result: Put InvoiceItem foreign key into
ActorAcctEntry 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 ALL
SELECT 17, 12, 'CRDINV', 115, 5 UNION ALL
SELECT 24, 16, 'CRDINV', 100, 5 UNION ALL
SELECT 35, 19, 'COLSAL', 58, 4 UNION ALL
SELECT 41, 23, 'COLSAL', -100, 1 UNION ALL
SELECT 52, 28, 'CRDINV', 150, 2

SELECT *
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 setup

As 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 problem

In 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 ALL
SELECT 16, 24, 13, 1, 'CRDINV', 'PAYCOL', 5, 100, 0.2 UNION ALL
SELECT 19, 35, 13, 1, 'COLSAL', 'PAYCRD', NULL, 58, 0.5 UNION ALL
SELECT 23, 41, 11, NULL, 'COLSAL', 'PAYCOLREJ', 7, -100, 0.3 UNION ALL
SELECT 28, 52, 11, NULL, 'CRDINV', 'PAYCRD', NULL, 150, 0.4

SELECT *
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 ALL
SELECT 16, 'PAYCOL', 100 UNION ALL
SELECT 19, 'PAYCRD', 58 UNION ALL
SELECT 23, 'PAYCOLREJ', -100 UNION ALL
SELECT 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 ALL
SELECT 17, 12, 'CRDINV', 115, NULL UNION ALL
SELECT 24, 16, 'CRDINV', 100, NULL UNION ALL
SELECT 35, 19, 'COLSAL', 58, NULL UNION ALL
SELECT 41, 23, 'COLSAL', -100, NULL UNION ALL
SELECT 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_Percent
FROM @CombinedInfo
GROUP BY
int_INVInvoiceAgreementId,
int_CTLProcessId,
chr_CSEClaimPartId,
chr_CSEAccountEntryTypeId,
int_LEDVoucherId,
mny_Percent
ORDER BY
int_INVInvoiceAgreementId,
int_CTLProcessId,
chr_CSEClaimPartId,
chr_CSEAccountEntryTypeId,
int_LEDVoucherId,
mny_Percent

SELECT *
FROM @INVInvoiceItem

/********** Sum up the commission share from the InvoiceItem table and
update the Invoice table with that money amount */

DECLARE @InvoiceItemShareSum MONEY

SELECT @InvoiceItemShareSum = SUM([mny_AmountShare])
FROM @INVInvoiceItem
WHERE [int_INVInvoiceId] = @InvoiceId

UPDATE @INVInvoice
SET [mny_Amt] = @InvoiceItemShareSum
WHERE [int_INVInvoiceId] = @InvoiceId

SELECT *
FROM @INVInvoice

/********** Show the expected result: Put InvoiceItem foreign key into
ActorAcctEntry 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 ALL
SELECT 17, 12, 'CRDINV', 115, 5 UNION ALL
SELECT 24, 16, 'CRDINV', 100, 5 UNION ALL
SELECT 35, 19, 'COLSAL', 58, 4 UNION ALL
SELECT 41, 23, 'COLSAL', -100, 1 UNION ALL
SELECT 52, 28, 'CRDINV', 150, 2

SELECT *
FROM @ActorAcctEntry_with_InvoiceItem





















Try this:-
UPDATE ae
SET ae.int_INVInvoiceItemId=inv.int_INVInvoiceItemId
FROM @ActorAcctEntry ae
INNER JOIN @CombinedInfo cf
ON cf.int_CSEAcctEntryId=ae.int_CSEAccountEntryId
AND cf.int_CSEActorAcctEntryId=ae.int_CSEActorAcctEntryId
INNER JOIN @INVInvoiceItem inv
ON cf.chr_CSEClaimPartId=inv.chr_CSEClaimPartId
AND 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 3
17 12 CRDINV 115.00 5
24 16 CRDINV 100.00 5
35 19 COLSAL 58.00 4
41 23 COLSAL -100.00 1
52 28 CRDINV 150.00 2
Go to Top of Page

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?
Go to Top of Page
   

- Advertisement -