Home > OS >  Creating Ageing report (>0), (0-30), (31-60)
Creating Ageing report (>0), (0-30), (31-60)

Time:11-09

I am trying to write an Ageing Report on SQL Server which shows the total amount of overdue invoices (later on I will have to deduct Credit Notes) that fall in the different columns depending on how many days the have been overdued. I.e (>0), (0-30), (31-60), (61-90), etc.

This is the part of the query I have written so far mostly looking at old post in this forum but it's giving me a lot of duplicates even for Accounts where there is not due balance.

Any idea what I am doing wrong?

SELECT O.cardcode      AS [Account],
       O.cardname      AS [Name],
       O.u_creditlimit AS [Credit Limit],
       O.u_onhold      AS [On Hold],
       O.balance,
       Isnull(CASE
                WHEN Datediff(day, INV.docduedate, Getdate()) >= 0 AND Datediff(day, INV.docduedate, Getdate()) < 30 
                THEN (
                       SELECT Sum(doctotal)
                       FROM   oinv
                       WHERE cardcode = INV.cardcode)
              END, 0)  AS [0 to 30 Days],
       Isnull(CASE
                WHEN Datediff(day, INV.docduedate, Getdate()) >= 31 AND Datediff(day, INV.docduedate, Getdate()) < 60 
                THEN (
                      SELECT Sum(doctotal)
                      FROM   oinv
                      WHERE cardcode = INV.cardcode)
              END, 0)  AS [31 to 60 Days],
       Isnull(CASE
                WHEN Datediff(day, INV.docduedate, Getdate()) >= 61 AND Datediff(day, INV.docduedate, Getdate()) < 90 
                THEN (
                      SELECT Sum(doctotal)
                      FROM   oinv
                      WHERE cardcode = INV.cardcode)
              END, 0)  AS [61 to 90 Days],
       Isnull(CASE
                WHEN Datediff(day, INV.docduedate, Getdate()) >= 91 AND Datediff(day, INV.docduedate, Getdate()) < 120 
                THEN (
                      SELECT Sum(doctotal)
                      FROM   oinv
                      WHERE  cardcode = INV.cardcode)
              END, 0)  AS [91 to 120 Days],
       Isnull(CASE
                WHEN Datediff(day, INV.docduedate, Getdate()) >= 121 
                THEN(
                      SELECT Sum(doctotal)
                      FROM oinv
                      WHERE cardcode = INV.cardcode)
              END, 0)  AS [121  Days]
FROM   ocrd O
       INNER JOIN oinv INV
               ON O.cardcode = INV.cardcode
WHERE  territory = 3
       AND INV.docstatus = 'O' 

Thank you very much.

CodePudding user response:

You can clean this up a bit

First. use a CROSS APPLY to calculate the Days-Past-Due once, and then a conditional aggregation for the final results

Example (Untested)

  Select O.cardcode
        ,O.cardname  
        ,[Credit Limit]   = max(O.u_creditlimit)
        ,[On Hold]        = max(O.u_onhold)
        ,[0 to 30 Days]   = sum( case when DPD between  0 and  30 then doctotal else 0 end)
        ,[31 to 60 Days]  = sum( case when DPD between 31 and  60 then doctotal else 0 end)
        ,[61 to 90 Days]  = sum( case when DPD between 61 and  90 then doctotal else 0 end)
        ,[91 to 120 Days] = sum( case when DPD between 91 and 120 then doctotal else 0 end)
        ,[121  Days ]     = sum( case when DPD >=121              then doctotal else 0 end)
   From  ocrd O
   Join  oinv INV on O.cardcode = INV.cardcode
   Cross Apply (values ( Datediff(day, INV.docduedate, Getdate()) ) ) P(DPD)
   Where territory = 3
     and INV.docstatus = 'O' 
     and DPD >= 0
  Group By O.cardcode
          ,O.cardname  

CodePudding user response:

when asking questions is a great idea to provide demo data, and a better one to provide it as an object we can easily re recreate:

DECLARE @table TABLE (RecordID INT IDENTITY, CardCode INT, CardName NVARCHAR(100), u_CreditLimit DECIMAL(10,2), u_onhold DECIMAL(10,2), balance DECIMAL(10,2))
INSERT INTO @table (CardCode, CardName, u_CreditLimit, u_onhold, balance) VALUES (1, 'John Smith', 10000, 0, 200),
(1, 'John Smith', 10000, 0, 400),
(1, 'John Smith', 10000, 0, 200)

This would allow someone to just run the TSQL to create and populate the object.

Now using that object we could write something like

SELECT RecordID, CardCode, CardName, U_CreditLimit, U_OnHold, Balance, COALESCE(LAG(Balance,1) OVER (PARTITION BY CardCode ORDER BY RecordID) - Balance,Balance) AS RunningTotal
  FROM @table

This psudeo code, and may need some tweaking to get exactly what you're looking for.

A quick word on LAG and its pal LEAD. You specify a column and an offset in rows. LAG looks backwards, LEAD forwards. They both use over clauses just like any other windowed function.

  • Related