Aging Payments query help

  • Could somebody(s) please help with this query? I've been asked to do the following:

    Create a chart of invoices that are more than 45 days old and unpaid, by month for 12 months, including MTD for the current month. For the current month, I want invoices that are older than 45 days (and unpaid) from GETDATE(). For previous months, I want invoices that are older than 45 days (and unpaid) at the last day of each month (it might have been paid in a future month).

    So, assuming today is Oct 6, I need unpaid invoices that were created before Aug 22 for this month, invoices that were created before Aug 16 and are unpaid as of the end of Sept for Sept, invoices that were created before July 17 and are unpaid as of the end of August for August, etc.

    My initial thought is that I can do the current month (somehow), and union that with the historical months (somehow). But that's where I get stuck.

    Hopefully this can be done in AW? I don't have access to that database at this company. If not, let me know and I'll create some sample data.

    Cheers,

    Tom

  • It would help if you could post the DDL for the table(s), sample data for the table(s), expected results based on the sample data, and what you have done so far to solve your problem.

    Please read the first article I reference below in my signature block regarding asking for help. It will show you how you should post the requested information. Doing this will get you the best help possible, plus you will get tested code in return.

  • Tom would some4thign like this bee what you are looking for?

    select

    InvoiceNumber,

    InvoiceDate,

    Month(InvoiceDate) AS Mn

    from sometable

    where InvoiceDate < dateadd(dd,-45,getdate)

    Group By

    InvoiceNumber,

    InvoiceDate

    Lowell


    --help us help you! If you post a question, make sure you include a CREATE TABLE... statement and INSERT INTO... statement into that table to give the volunteers here representative data. with your description of the problem, we can provide a tested, verifiable solution to your question! asking the question the right way gets you a tested answer the fastest way possible!

  • Lynn,

    That's why I was hoping this might be doable in the AdventureWorks database, but here's something:

    declare @Invoices table (

    InvoiceNumber int IDENTITY(1,1) NOT NULL,

    InvoiceDate date NOT NULL,

    PaidDate date NULL,

    IsPaid bit NOT NULL

    )

    INSERT @Invoices (InvoiceDate, PaidDate, IsPaid)

    VALUES ('2011-06-14', '2011-07-20', 1), -- < 45 days

    ('2011-06-17', '2011-08-02', 1), -- > 45 days in July

    ('2011-06-18', '2011-09-01', 1), -- > 45 days in July, Aug

    ('2011-07-10', '2011-09-01', 1), -- > 45 days in Aug

    ('2011-07-30', NULL, 0), -- > 45 days in Sept and Oct MTD

    ('2011-07-30', '2011-08-15', 1), -- < 45 days

    ('2011-08-01', NULL, 0), -- > 45 days in Sept and Oct MTD

    ('2011-08-21',NULL, 0), -- > 45 days in Oct MTD

    ('2011-08-30', NULL, 0) -- < 45 days

    Lowell (10/6/2011)


    Tom would some4thign like this bee what you are looking for?

    select

    InvoiceNumber,

    InvoiceDate,

    Month(InvoiceDate) AS Mn

    from sometable

    where InvoiceDate < dateadd(dd,-45,getdate)

    Group By

    InvoiceNumber,

    InvoiceDate

    Lowell, that doesn't take into account invoices that are old for more than one month.

    This company is a startup in stealth mode, and the actual query has nothing to do with invoicing. But this is the idea behind what I need to do. If you need to add anything to the table to make this more efficient/easier, feel free. I might have to push back though. The database and a whole lot of .Net code already exists, so I'm not sure how much I can change in that regard.

  • I created invoices for my last company where we were a net 30 and some people had paid a %age POS, some had balances at 30, 60, 90, and 120+ days... Gimme a couple of minutes and I'll have something based on the given sample data.

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • Can you please give me a sample of what you want your aging report to look like? In terms of relevant data points for your purpose.

    EDIT: Also, are you just wanting a count or is there supposed to be $ amounts?

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • jared-709193 (10/6/2011)


    Can you please give me a sample of what you want your aging report to look like? In terms of relevant data points for your purpose.

    Thanks,

    Jared

    I'm guessing I need Month (Year and Month so it will be in correct order when we cross over a year boundary?) and number of invoices > 45 days old, for current month and past 11 months. Just count of old items. So it should be:

    July 2

    Aug 2

    Sep 2

    Oct 3

    It's going to go into a line graph.

  • Here's where I am at... I will keep going as I love this kind of stuff! 🙂

    SELECT InvoiceNumber, YEAR(InvoiceDate) AS InvoiceYear, MONTH(InvoiceDate) AS InvoiceMonth, InvoiceDate, PaidDate,

    DATEADD(D,45, InvoiceDate) Date45Days, IsPaid,

    CASE IsPaid WHEN 1 THEN 0 ELSE DATEDIFF(D,InvoiceDate, GETDATE()) END AS TotalDaysLate

    FROM #invoices

    WHERE PaidDate IS NULL OR PaidDate > DATEADD(D,46, InvoiceDate)

    I add lots of extra columns at first for ease of debugging and to see my calculations straight out.

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • Try this for now. I would leave the data in a raw form, and then group on things in the report itself if you are making a SSRS report. Otherwise, I think this will help you to start grouping and counting. You may need a temp table to store the total for the current month and then union that 1 row with the rest.

    SELECT InvoiceNumber,

    YEAR(InvoiceDate) AS InvoiceYear,

    MONTH(InvoiceDate) AS InvoiceMonth,

    YEAR(PaidDate) AS PaidYear,

    MONTH(PaidDate) AS PaidMonth,

    YEAR(DATEADD(D,45, InvoiceDate)) AS Year45,

    MONTH(DATEADD(D,45, InvoiceDate)) AS Month45,

    DATEADD(D,45, InvoiceDate) Date45Days,

    CASE

    WHEN PaidDate IS NULL THEN DATEDIFF(D,InvoiceDate, GETDATE())

    WHEN PaidDate > InvoiceDate THEN DATEDIFF(D,InvoiceDate, PaidDate)

    WHEN PaidDate < InvoiceDate THEN 0

    ELSE -1

    END AS TotalDaysLate,

    CASE IsPaid

    WHEN 1 THEN 0

    ELSE DATEDIFF(D,InvoiceDate, GETDATE())

    END AS TotalDaysLateToday

    FROM #invoices

    WHERE PaidDate IS NULL

    OR (PaidDate > DATEADD(D,45, InvoiceDate))

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • Thanks Jared. Looks promising. But:

    Is the only way to get what I want to do something like:

    SELECT Count(*), Year, Month

    from ...

    group by year, month

    where year and month = current year and month

    UNION

    SELECT Count(*), Year, Month

    from ...

    group by year, month

    where year and month = previous year and month

    UNION

    SELECT Count(*), Year, Month

    from ...

    group by year, month

    where year and month = year and month - 2

    UNION

    ...

    for the 6 or 12 months that I want? Is there a better way?

  • Well... I think the best way may be to create a table based function that looks at the raw data and use CROSS APPLY to bring your counts.

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • jared-709193 (10/7/2011)


    Well... I think the best way may be to create a table based function that looks at the raw data and use CROSS APPLY to bring your counts.

    Thanks,

    Jared

    I'm dense this morning. CROSS APPLY the raw data with what?

  • Look at this article http://www.sqlteam.com/article/using-cross-apply-in-sql-server-2005

    Let me know if this helps 🙂

    Thanks,

    Jared

    Jared
    CE - Microsoft

  • different approach....may be give you some ideas

    pls note ...date range vailidity needs scrutiny

    comments in code

    USE [tempdb] --- a safe place

    GO

    --== condtionally drop table for retesting in SSMS

    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tbl_invoices]') AND type in (N'U'))

    DROP TABLE [dbo].[tbl_invoices]

    GO

    CREATE TABLE [dbo].[tbl_invoices](

    [InvoiceNumber] [int] IDENTITY(1,1) NOT NULL,

    [InvoiceDate] [date] NOT NULL,

    [PaidDate] [date] NULL,

    [IsPaid] [bit] NOT NULL

    ) ON [PRIMARY]

    GO

    SET IDENTITY_INSERT [dbo].[tbl_invoices] ON;

    INSERT INTO [dbo].[tbl_invoices]([InvoiceNumber], [InvoiceDate], [PaidDate], [IsPaid])

    SELECT 1, '20110614 00:00:00.000', '20110720 00:00:00.000', 1 UNION ALL

    SELECT 2, '20110617 00:00:00.000', '20110802 00:00:00.000', 1 UNION ALL

    SELECT 3, '20110618 00:00:00.000', '20110901 00:00:00.000', 1 UNION ALL

    SELECT 4, '20110710 00:00:00.000', '20110901 00:00:00.000', 1 UNION ALL

    SELECT 5, '20110730 00:00:00.000', NULL, 0 UNION ALL

    SELECT 6, '20110730 00:00:00.000', '20110815 00:00:00.000', 1 UNION ALL

    SELECT 7, '20110801 00:00:00.000', NULL, 0 UNION ALL

    SELECT 8, '20110821 00:00:00.000', NULL, 0 UNION ALL

    SELECT 9, '20110830 00:00:00.000', NULL, 0

    SET IDENTITY_INSERT [dbo].[tbl_invoices] OFF;

    --- the following add cols and update could be added into the results select statement

    --- but for clarity of thought I have added them as a separate process

    --== add a couple of new columns

    ALTER TABLE dbo.tbl_invoices ADD

    PayDueDate datetime NULL,

    PayInTerms varchar (1) NULL

    GO

    --== update columns

    UPDATE tbl_invoices

    SET PayDueDate = dateadd(d,45,InvoiceDate),

    PayInTerms = CASE when PaidDate < dateadd(d,45,InvoiceDate) then 'Y' ELSE 'N' END

    ---results

    DECLARE @TD DATETIME; --- not necessary, used for testing, replace with GETDATE() in code below

    SET @TD = Getdate(); --- see above

    --- I have not had time to check the validity of the dates...but hopefully you will get the gist of what this is about.

    --- ND...dependent on your data volume , pls check execution plan and create required indexes.

    SELECT SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD), 0) - 1 )

    AND PayDueDate < ( Getdate() )

    THEN 1

    ELSE 0

    END) AS CURMTH,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 1, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD), 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS1,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 2, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD) - 1, 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS2,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 3, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD) - 2, 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS3

    FROM tbl_invoices

    WHERE PayInTerms = 'N'

    ________________________________________________________________
    you can lead a user to data....but you cannot make them think
    and remember....every day is a school day

  • J Livingston SQL (10/9/2011)


    different approach....may be give you some ideas

    pls note ...date range vailidity needs scrutiny

    comments in code

    USE [tempdb] --- a safe place

    GO

    --== condtionally drop table for retesting in SSMS

    IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tbl_invoices]') AND type in (N'U'))

    DROP TABLE [dbo].[tbl_invoices]

    GO

    CREATE TABLE [dbo].[tbl_invoices](

    [InvoiceNumber] [int] IDENTITY(1,1) NOT NULL,

    [InvoiceDate] [date] NOT NULL,

    [PaidDate] [date] NULL,

    [IsPaid] [bit] NOT NULL

    ) ON [PRIMARY]

    GO

    SET IDENTITY_INSERT [dbo].[tbl_invoices] ON;

    INSERT INTO [dbo].[tbl_invoices]([InvoiceNumber], [InvoiceDate], [PaidDate], [IsPaid])

    SELECT 1, '20110614 00:00:00.000', '20110720 00:00:00.000', 1 UNION ALL

    SELECT 2, '20110617 00:00:00.000', '20110802 00:00:00.000', 1 UNION ALL

    SELECT 3, '20110618 00:00:00.000', '20110901 00:00:00.000', 1 UNION ALL

    SELECT 4, '20110710 00:00:00.000', '20110901 00:00:00.000', 1 UNION ALL

    SELECT 5, '20110730 00:00:00.000', NULL, 0 UNION ALL

    SELECT 6, '20110730 00:00:00.000', '20110815 00:00:00.000', 1 UNION ALL

    SELECT 7, '20110801 00:00:00.000', NULL, 0 UNION ALL

    SELECT 8, '20110821 00:00:00.000', NULL, 0 UNION ALL

    SELECT 9, '20110830 00:00:00.000', NULL, 0

    SET IDENTITY_INSERT [dbo].[tbl_invoices] OFF;

    --- the following add cols and update could be added into the results select statement

    --- but for clarity of thought I have added them as a separate process

    --== add a couple of new columns

    ALTER TABLE dbo.tbl_invoices ADD

    PayDueDate datetime NULL,

    PayInTerms varchar (1) NULL

    GO

    --== update columns

    UPDATE tbl_invoices

    SET PayDueDate = dateadd(d,45,InvoiceDate),

    PayInTerms = CASE when PaidDate < dateadd(d,45,InvoiceDate) then 'Y' ELSE 'N' END

    ---results

    DECLARE @TD DATETIME; --- not necessary, used for testing, replace with GETDATE() in code below

    SET @TD = Getdate(); --- see above

    --- I have not had time to check the validity of the dates...but hopefully you will get the gist of what this is about.

    --- ND...dependent on your data volume , pls check execution plan and create required indexes.

    SELECT SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD), 0) - 1 )

    AND PayDueDate < ( Getdate() )

    THEN 1

    ELSE 0

    END) AS CURMTH,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 1, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD), 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS1,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 2, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD) - 1, 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS2,

    SUM(CASE

    WHEN PayDueDate > ( Dateadd(MM, Datediff(MM, 0, @TD) - 3, 0) - 1 )

    AND PayDueDate < ( Dateadd(MM, Datediff(MM, 0, @TD) - 2, 0) )

    THEN 1

    ELSE 0

    END) AS MTHMINUS3

    FROM tbl_invoices

    WHERE PayInTerms = 'N'

    I'm glad I read this whole thread before I started on a possible solution, JLS. Your CROSS TAB solution is pretty much where I was going to head on this one. Nicely done.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

Viewing 15 posts - 1 through 15 (of 23 total)

You must be logged in to reply to this topic. Login to reply