Inventory Query Help for linking Purchase order to transactions

  • Hello Experts,

    I'm currently attempting to create an inventory report using data from inventory transactions. I have an inventory table that resembles the one below, and I'm working on generating a report that identifies which claim transactions are associated with purchase orders. It's important to note that in some cases, a single claim transaction quantity can be split and associated with two different purchase orders due to the nature of our pack size ordering process. I've made an initial query attempt, but I'm struggling to properly split these transactions. Any assistance or guidance would be greatly appreciated.  I have attached sample data and expected result.

    Here is the query I am trying.

    CREATE TABLE tempInventory (

    Date DATE,

    Sponsor VARCHAR(255),

    Pharmacy VARCHAR(255),

    Drug VARCHAR(255),

    TransactionType VARCHAR(255),

    TransactionValue VARCHAR(255),

    Quantity INT,

    QuantityOnHand INT

    );

    -- Insert the sample data

    INSERT INTO tempInventory (Date, Sponsor, Pharmacy, Drug, TransactionType, TransactionValue, Quantity, QuantityOnHand)

    VALUES

    ('2023-01-01', 'CO', 'P1', 'D1', 'Claim', 'C1', -30, -30),

    ('2023-01-02', 'CO', 'P1', 'D1', 'Claim', 'C2', -60, -90),

    ('2023-01-03', 'CO', 'P1', 'D1', 'Claim', 'C3', -30, -120),

    ('2023-01-04', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O1', 100, -20),

    ('2023-01-05', 'CO', 'P1', 'D1', 'Claim', 'C4', -30, -50),

    ('2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C5', -60, -110),

    ('2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C6', -120, -230),

    ('2023-01-07', 'CO', 'P1', 'D1', 'Claim', 'C7', -30, -260),

    ('2023-01-08', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O2', 200, -60),

    ('2023-01-09', 'CO', 'P1', 'D1', 'Claim', 'C8', -40, -100),

    ('2023-01-11', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O3', 100, 0);

    select * from dba..tempInventory

     

     

     

    -- Create a temporary table to store the report

    CREATE TABLE #Report (

    TransactionValue1 NVARCHAR(255),

    TransactionValue2 NVARCHAR(255),

    LinkedQuantity INT

    );

    -- Declare variables for cursor

    DECLARE @ClaimTransactionValue NVARCHAR(255);

    DECLARE @ClaimQuantity INT;

    -- Create a cursor for claim transactions

    DECLARE claimCursor CURSOR FOR

    SELECT TransactionValue, Quantity

    from dba..tempInventory

    WHERE TransactionType = 'Claim';

    -- Open the cursor

    OPEN claimCursor;

    -- Fetch the first row from the cursor

    FETCH NEXT FROM claimCursor INTO @ClaimTransactionValue, @ClaimQuantity;

    -- Declare variables for purchase order

    DECLARE @PurchaseOrderTransactionValue NVARCHAR(255);

    DECLARE @PurchaseOrderQuantity INT;

    -- Iterate through the cursor

    WHILE @@FETCH_STATUS = 0

    BEGIN

    -- Create a cursor for purchase order transactions following the claim

    DECLARE purchaseOrderCursor CURSOR FOR

    SELECT TransactionValue, Quantity

    from dba..tempInventory

    WHERE TransactionType = 'PurchaseOrder'

    AND Pharmacy = (SELECT Pharmacy from dba..tempInventory WHERE TransactionValue = @ClaimTransactionValue)

    AND Drug = (SELECT Drug from dba..tempInventory WHERE TransactionValue = @ClaimTransactionValue)

    AND Date > (SELECT Date from dba..tempInventory WHERE TransactionValue = @ClaimTransactionValue);

    -- Open the purchase order cursor

    OPEN purchaseOrderCursor;

    -- Fetch and insert rows into the report table

    FETCH NEXT FROM purchaseOrderCursor INTO @PurchaseOrderTransactionValue, @PurchaseOrderQuantity;

    WHILE @@FETCH_STATUS = 0

    BEGIN

    -- Determine the claim quantity to link with this purchase order

    DECLARE @LinkedQuantity INT;

    IF @ClaimQuantity > @PurchaseOrderQuantity

    SET @LinkedQuantity = -@PurchaseOrderQuantity;

    ELSE

    SET @LinkedQuantity = -@ClaimQuantity;

    INSERT INTO #Report (TransactionValue1, TransactionValue2, LinkedQuantity)

    VALUES (@PurchaseOrderTransactionValue, @ClaimTransactionValue, @LinkedQuantity);

    -- Update the claim quantity and purchase order quantity

    SET @ClaimQuantity = @ClaimQuantity + @LinkedQuantity;

    SET @PurchaseOrderQuantity = @PurchaseOrderQuantity + @LinkedQuantity;

    -- Fetch the next purchase order from the purchase order cursor

    FETCH NEXT FROM purchaseOrderCursor INTO @PurchaseOrderTransactionValue, @PurchaseOrderQuantity;

    END;

    -- Close and deallocate the purchase order cursor

    CLOSE purchaseOrderCursor;

    DEALLOCATE purchaseOrderCursor;

    -- Fetch the next claim from the claim cursor

    FETCH NEXT FROM claimCursor INTO @ClaimTransactionValue, @ClaimQuantity;

    END;

    -- Close and deallocate the claim cursor

    CLOSE claimCursor;

    DEALLOCATE claimCursor;

    -- Select the final report

    SELECT * FROM #Report order by TransactionValue1,TransactionValue2;

    -- Clean up the temporary table

    DROP TABLE #Report;

     

    Attachments:
    You must be logged in to view attached files.
  • This gives your expected results.  I partitioned and joined on the Sponsor, Pharmacy, and Drug.  You may need to change this.

    WITH RunningTotals AS
    (
    SELECT i.[Date]
    , i.Sponsor
    , i.Pharmacy
    , i.Drug
    , i.TransactionType
    , i.TransactionValue
    , i.Quantity
    , i.QuantityOnHand
    , COALESCE(SUM(i.Quantity) OVER(PARTITION BY i.Sponsor, i.Pharmacy, i.Drug, i.TransactionType ORDER BY i.[Date] ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS PrevRunningTotal
    , SUM(i.Quantity) OVER(PARTITION BY i.Sponsor, i.Pharmacy, i.Drug, i.TransactionType ORDER BY i.[Date] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS CurRunningTotal
    FROM #tempInventory AS i
    ), Claims AS
    (
    SELECT rt.Date
    , rt.Sponsor
    , rt.Pharmacy
    , rt.Drug
    , rt.TransactionType
    , rt.TransactionValue
    , rt.Quantity
    , rt.QuantityOnHand
    , -rt.PrevRunningTotal AS PrevRunningTotal
    , -rt.CurRunningTotal AS CurRunningTotal
    FROM RunningTotals AS rt
    WHERE rt.TransactionType = 'Claim'
    ), PurchaseOrders AS
    (
    SELECT rt.Date
    , rt.Sponsor
    , rt.Pharmacy
    , rt.Drug
    , rt.TransactionType
    , rt.TransactionValue
    , rt.Quantity
    , rt.QuantityOnHand
    , rt.PrevRunningTotal
    , rt.CurRunningTotal
    FROM RunningTotals AS rt
    WHERE rt.TransactionType = 'PurchaseOrder'
    )
    SELECT po.TransactionValue
    , c.TransactionValue
    , CASE WHEN c.CurRunningTotal > po.CurRunningTotal
    THEN c.PrevRunningTotal - po.CurRunningTotal
    WHEN c.PrevRunningTotal < po.PrevRunningTotal
    THEN po.PrevRunningTotal - c.CurRunningTotal
    ELSE c.Quantity
    END AS LinkedQuantity
    FROM Claims AS c
    INNER JOIN PurchaseOrders AS po
    ON c.Sponsor = po.Sponsor
    AND c.Pharmacy = po.Pharmacy
    AND c.Drug = po.Drug
    AND c.PrevRunningTotal < po.CurRunningTotal
    AND po.PrevRunningTotal < c.CurRunningTotal

    Drew

    NOTE:  It is a good idea to use the {;}Code button when posting code.

    NOTE 2: People are loathe to download potentially infected files posted by random strangers.  It's better to post scripts to create and populate a table with your expected results.

    EDIT: Changed the FULL OUTER JOIN to an INNER JOIN.  I used the FULL OUTER JOIN in my testing, and forgot to change it back.

    • This reply was modified 1 year, 2 months ago by  drew.allen.

    J. Drew Allen
    Business Intelligence Analyst
    Philadelphia, PA

  • Thank you so much Drew.  I will try with few different scenarios.

     

    Thanks

  • Hello Expert,

    The query performed perfectly, but I had to account for specific cases where it didn't work as expected. Some transactions have reversals. For instance, consider the scenario where the C0 claim transaction is executed and then reversed before an order is placed. Hence, it shouldn't be considered in the linking result. However, the C3 claim gets reversed at a later stage, allowing the original C3 transaction to link with the O1 and O2 orders. Yet, the reversed C3 claim should be ignored for linking purposes, and part of C8 should also be disregarded due to the impact of C3 on inventory. I'd really appreciate your help in handling this particular situation.  I am adding the insert script and expected result below

    INSERT INTO tempInventory (Date, Sponsor, Pharmacy, Drug, TransactionType, TransactionValue, Quantity, QuantityOnHand)

    VALUES

    ('2023-01-01', 'CO', 'P1', 'D1', 'Claim', 'C0', -60, -60),

    ('2023-01-01', 'CO', 'P1', 'D1', 'Claim', 'C0', 60, 0), --Reversed

    ('2023-01-01', 'CO', 'P1', 'D1', 'Claim', 'C1', -30, -30),

    ('2023-01-02', 'CO', 'P1', 'D1', 'Claim', 'C2', -60, -90),

    ('2023-01-03', 'CO', 'P1', 'D1', 'Claim', 'C3', -30, -120),

    ('2023-01-04', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O1', 100, -20),

    ('2023-01-05', 'CO', 'P1', 'D1', 'Claim', 'C4', -30, -50),

    ('2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C5', -60, -110),

    ('2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C6', -120, -230),

    ('2023-01-07', 'CO', 'P1', 'D1', 'Claim', 'C7', -30, -260),

    ('2023-01-08', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O2', 200, -60),

    ('2023-01-09', 'CO', 'P1', 'D1', 'Claim', 'C8', -40, -100),

    ('2023-01-09', 'CO', 'P1', 'D1', 'Claim', 'C3', 30, -70), --Reversed

    ('2023-01-09', 'CO', 'P1', 'D1', 'Claim', 'C9', -30, -100),

    ('2023-01-11', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O3', 100, 0);

    TransactionValue1 TransactionValue2 LinkedQuantity

    O1                               C1                                    -30

    O1                              C2                                     -60

    O1                              C3                                     -10

    O2                              C3                                     -20

    O2                             C4                                      -30

    O2                             C5                                      -60

    O2                             C6                                      -90

    O3                             C6                                       -30

    O3                             C7                                       -30

    O3                             C8                                      -10

    O3                             C9                                      -30

     

  • SQL Experts,  Any help is appreciated!

  • Can you elaborate a bit. Reversed=same quantity?

    What is the relation of

    O1                              C3                                     -10

    I only see

    ('2023-01-03', 'CO', 'P1', 'D1', 'Claim', 'C3', -30, -120) before

    ('2023-01-04', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O1', 100, -20),

  • Yes, When a transaction is reversed, it involves returning the same quantity (as a negative value) back to the inventory.

    Explanation for

    O1           C3               -10

    This result shows that even though the original transaction under transaction C3 had a quantity of 30, only a portion (-10) of that quantity participated in Purchase Order O1. The remaining quantity of the original transaction might be linked or associated with a different purchase order, such as O2.

    Note: Drew's earlier response is accurate when handling scenarios without any reversed transactions. However, it's providing incorrect linking between purchase orders and transactions in cases involving reversed transactions.

    Really Appreciate looking into this...

     

  • Any help is greatly appreciated!

  • Is there a primary key on Inventory table? It would help understanding what is going on.

    Zidar's Theorem: The best code is no code at all...

  • Hi Zidar, The order data and claim transaction data originate from separate tables and are structured as a dataset for this purpose. If having a primary key column would assist in the query, we can incorporate that. Additionally, the combination of sponsor, pharmacy, drug, and transaction value always remains unique. I'm including a new script with the primary key in case it proves helpful. Thank you for looking into this.

    CREATE TABLE tempInventory (

    ID int,

    Date DATE,

    Sponsor VARCHAR(255),

    Pharmacy VARCHAR(255),

    Drug VARCHAR(255),

    TransactionType VARCHAR(255),

    TransactionValue VARCHAR(255),

    Quantity INT,

    QuantityOnHand INT

    );

    -- Insert the sample data

    INSERT INTO tempInventory (ID,Date, Sponsor, Pharmacy, Drug, TransactionType, TransactionValue, Quantity, QuantityOnHand)

    VALUES

    (1,'2023-01-01', 'CO', 'P1', 'D1', 'Claim', 'C1', -30, -30),

    (2, '2023-01-02', 'CO', 'P1', 'D1', 'Claim', 'C2', -60, -90),

    (3, '2023-01-03', 'CO', 'P1', 'D1', 'Claim', 'C3', -30, -120),

    (4, '2023-01-04', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O1', 100, -20),

    (5,'2023-01-05', 'CO', 'P1', 'D1', 'Claim', 'C4', -30, -50),

    (6,'2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C5', -60, -110),

    (7,'2023-01-06', 'CO', 'P1', 'D1', 'Claim', 'C6', -120, -230),

    (8,'2023-01-07', 'CO', 'P1', 'D1', 'Claim', 'C7', -30, -260),

    (9,'2023-01-08', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O2', 200, -60),

    (10,'2023-01-09', 'CO', 'P1', 'D1', 'Claim', 'C8', -40, -100),

    (11,'2023-01-11', 'CO', 'P1', 'D1', 'PurchaseOrder', 'O3', 100, 0);

  • I apologize for the delay, was out of town for a few days.

    Thank you for refreshing the script and the explanation.

    I noticed that when listed in proper order, a picture emerges. It is safe to assume that each row is a single transaction. transactions come in some natural order - at different dates, but not always. There are two types of transaction - Claims and PurchaseOrders. Claims have negative values, they decrease Quantity on hand. Purchase Orders have positive values and they   increase Quantity on hand.

     

    I believe you want to associate PurchaseOrder with claims before it. Somethng like this does the trick.

    WITH PurchaseOrders AS
    (-- Extract PO transactions
    SELECT Sponsor, Pharmacy, Drug, TransactionValue
    , TransactionType, Quantity
    , [Date]
    FROM tempInventory
    WHERE TransactionType = 'PurchaseOrder'
    )
    , PO_dateRanges AS
    (-- Determine date ranges covered by POs
    SELECT Sponsor, Pharmacy, Drug, TransactionValue
    , TransactionType, Quantity
    , [Date]
    , PrevDate = LAG([Date],1,null) OVER (ORDER BY [Date])
    FROM PurchaseOrders
    )
    SELECT I.Sponsor, I.Pharmacy, I.Drug, I.TransactionValue
    , I.TransactionType, I.Quantity, QuantityOnHand
    , I.[Date] AS TransDate
    , PO.TransactionValue AS PurchaseOrder
    , PO.Date AS PurchOrderDate
    FROM tempInventory As I
    JOIN PO_dateRanges AS PO ON 1=1
    WHERE 1=1
    AND I.Date<=PO.DAte AND (I.Date>PO.PrevDate OR PO.PrevDate IS NULL)
    ORDER BY I.[Date] ,sponsor, pharmacy, drug, transactionvalue, QuantityOnHand
    ;

    which returns this result:

    Sponsor Pharmacy Drug  TransactionValue TransactionType         Quantity QuantityOnHand TransDate  PurchaseOrder PurchOrderDate
    ------- -------- ----- ---------------- -------------------- ----------- -------------- ---------- ------------- --------------
    CO P1 D1 C1 Claim -30 -30 2023-01-01 O1 2023-01-04
    CO P1 D1 C2 Claim -60 -90 2023-01-02 O1 2023-01-04
    CO P1 D1 C3 Claim -30 -120 2023-01-03 O1 2023-01-04
    CO P1 D1 O1 PurchaseOrder 100 -20 2023-01-04 O1 2023-01-04
    CO P1 D1 C4 Claim -30 -50 2023-01-05 O2 2023-01-08
    CO P1 D1 C5 Claim -60 -110 2023-01-06 O2 2023-01-08
    CO P1 D1 C6 Claim -120 -230 2023-01-06 O2 2023-01-08
    CO P1 D1 C7 Claim -30 -260 2023-01-07 O2 2023-01-08
    CO P1 D1 O2 PurchaseOrder 200 -60 2023-01-08 O2 2023-01-08
    CO P1 D1 C8 Claim -40 -100 2023-01-09 O3 2023-01-11
    CO P1 D1 O3 PurchaseOrder 100 0 2023-01-11 O3 2023-01-11

    (11 rows affected)


    Completion time: 2023-12-04T12:39:41.0673208-05:00

    It is quite possible that a simpler query could be written.

    🙂

    Zidar's Theorem: The best code is no code at all...

Viewing 11 posts - 1 through 10 (of 10 total)

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