USP_SALESORDER_DISTRIBUTEAPPLIEDDISCOUNTS

Parameters

Parameter Parameter Type Mode Description
@SALESORDERID uniqueidentifier IN
@CHANGEAGENTID uniqueidentifier IN
@CURRENTDATE datetime IN

Definition

Copy


CREATE procedure dbo.USP_SALESORDER_DISTRIBUTEAPPLIEDDISCOUNTS (
    @SALESORDERID uniqueidentifier,
    @CHANGEAGENTID uniqueidentifier = null,
    @CURRENTDATE datetime = null
)
as begin
    set nocount on;

    if (select ITEMDISCOUNTSCALCULATED from dbo.SALESORDER where ID = @SALESORDERID) = 1
        return;

    if @CHANGEAGENTID is null
        exec dbo.USP_CHANGEAGENT_GETORCREATECHANGEAGENT @CHANGEAGENTID output;

    if @CURRENTDATE is null
        set @CURRENTDATE = getdate();

    -- First, we need a set of ID's representing individual pieces of merchandise. They only exist so far as aggregates, but we need them individualized before we can calculate.

    declare @MERCHANDISEUNITS table (SALESORDERITEMID uniqueidentifier, MERCHANDISEUNITID uniqueidentifier, PRICE money, ROWNUM int);

    insert into @MERCHANDISEUNITS
    select
        SALESORDERITEM.ID,
        newid(),
        SALESORDERITEM.PRICE,
        NUMBERS.NUM
    from dbo.SALESORDERITEM
    inner join dbo.NUMBERS on NUMBERS.NUM < SALESORDERITEM.QUANTITY
    where SALESORDERITEM.SALESORDERID = @SALESORDERID
        and SALESORDERITEM.TYPECODE = 14;

    -- This table will store all results of discount applications; in the end, we add them up per item and store to the TICKET table and SALESORDERITEMMERCHANDISEUNIT table.

    declare @PRORATEDITEMAMOUNTS table (SALESORDERITEMID uniqueidentifier, ITEMID uniqueidentifier, ORIGINALAMOUNT money, DISCOUNTEDAMOUNT money, ISITEMLEVEL bit, TYPECODE tinyint);

    -- Step 1: Item-level discounts.

    -- If any item discount has not been applied to all individual items represented by the SALESORDERITEM, then we have to do more work to ensure we only distribute across a subset of the item records.

    if exists (
        select 1
        from dbo.SALESORDERITEMITEMDISCOUNT ITEMDISCOUNT
        inner join dbo.SALESORDERITEM on ITEMDISCOUNT.SALESORDERITEMID = SALESORDERITEM.ID
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
            and SALESORDERITEM.QUANTITY <> ITEMDISCOUNT.NUMBEROFDISCOUNTEDITEMS
    )
    begin
        -- Difficult case: some discount is not applied to all items.


        -- These CTE's match the tickets with which specific discount applies to them, enabling us to

        -- crank them through UFN_SPLITS_PRORATEAMOUNTS in a single non-iterative statement.

        with TICKETS_CTE as (
            select
                SALESORDERITEM.ID SALESORDERITEMID,
                TICKET.ID TICKETID,
                row_number() over (partition by SALESORDERITEM.ID order by TICKET.ID) ROWNUM
            from dbo.SALESORDERITEM
            inner join dbo.TICKET on TICKET.SALESORDERITEMTICKETID = SALESORDERITEM.ID
            where SALESORDERITEM.SALESORDERID = @SALESORDERID
                and SALESORDERITEM.TYPECODE = 0
        ),
        MERCHANDISE_CTE as (
            select
                SALESORDERITEM.ID SALESORDERITEMID,
                MERCHANDISE.MERCHANDISEUNITID,
                MERCHANDISE.ROWNUM
            from dbo.SALESORDERITEM
            inner join @MERCHANDISEUNITS MERCHANDISE on MERCHANDISE.SALESORDERITEMID = SALESORDERITEM.ID
        ),
        DISCOUNTS_CTE as (
            select
                ITEMDISCOUNT.SALESORDERITEMID SALESORDERITEMID,
                ITEMDISCOUNT.ID ITEMDISCOUNTID,
                ITEMDISCOUNT.AMOUNT TOTALAMOUNTDISCOUNTED,
                ITEMDISCOUNT.NUMBEROFDISCOUNTEDITEMS,
                row_number() over (partition by SALESORDERITEMID order by NUMBEROFDISCOUNTEDITEMS desc) ROWNUM
            from dbo.SALESORDERITEMITEMDISCOUNT ITEMDISCOUNT
            inner join dbo.NUMBERS on NUMBERS.NUM < ITEMDISCOUNT.NUMBEROFDISCOUNTEDITEMS
            where
                (select SALESORDERID from dbo.SALESORDERITEM where SALESORDERITEM.ID = ITEMDISCOUNT.SALESORDERITEMID) = @SALESORDERID
        )
        insert into @PRORATEDITEMAMOUNTS
        select SALESORDERITEM.ID, PRORATEDAMOUNTS.ID, SALESORDERITEM.PRICE, PRORATEDAMOUNTS.AMOUNT, 1, 0
        from dbo.SALESORDERITEM
        left join dbo.SALESORDERITEMITEMDISCOUNT ITEMDISCOUNT on ITEMDISCOUNT.SALESORDERITEMID = SALESORDERITEM.ID
        outer apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
            SALESORDERITEM.PRICE * ITEMDISCOUNT.NUMBEROFDISCOUNTEDITEMS,
            ITEMDISCOUNT.AMOUNT,
            2,
            (
                select TICKETS_CTE.TICKETID ID, SALESORDERITEM.PRICE AMOUNT
                from TICKETS_CTE
                left join DISCOUNTS_CTE on TICKETS_CTE.SALESORDERITEMID = DISCOUNTS_CTE.SALESORDERITEMID and TICKETS_CTE.ROWNUM = DISCOUNTS_CTE.ROWNUM
                where DISCOUNTS_CTE.ITEMDISCOUNTID = ITEMDISCOUNT.ID
                for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
            )
        ) PRORATEDAMOUNTS
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
            and SALESORDERITEM.TYPECODE = 0

        union all

        select SALESORDERITEM.ID, PRORATEDAMOUNTS.ID, SALESORDERITEM.PRICE, PRORATEDAMOUNTS.AMOUNT, 1, 14
        from dbo.SALESORDERITEM
        left join dbo.SALESORDERITEMITEMDISCOUNT ITEMDISCOUNT on ITEMDISCOUNT.SALESORDERITEMID = SALESORDERITEM.ID
        outer apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
            SALESORDERITEM.PRICE * ITEMDISCOUNT.NUMBEROFDISCOUNTEDITEMS,
            ITEMDISCOUNT.AMOUNT,
            2,
            (
                select MERCHANDISE_CTE.MERCHANDISEUNITID ID, SALESORDERITEM.PRICE AMOUNT
                from MERCHANDISE_CTE
                left join DISCOUNTS_CTE on MERCHANDISE_CTE.SALESORDERITEMID = DISCOUNTS_CTE.SALESORDERITEMID and MERCHANDISE_CTE.ROWNUM = DISCOUNTS_CTE.ROWNUM
                where DISCOUNTS_CTE.ITEMDISCOUNTID = ITEMDISCOUNT.ID
                for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
            )
        ) PRORATEDAMOUNTS
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
            and SALESORDERITEM.TYPECODE = 14;

    end
    else begin
        -- Easy case: there is at most one item discount per SALESORDERITEM, and it applies to all tickets represented by the item.


        -- Tickets

        insert into @PRORATEDITEMAMOUNTS
        select
            SALESORDERITEM.ID,
            PRORATEDAMOUNTS.ID,
            SALESORDERITEM.PRICE,
            PRORATEDAMOUNTS.AMOUNT,
            1,
            0
        from dbo.SALESORDERITEM
        inner join dbo.SALESORDERITEMITEMDISCOUNT on SALESORDERITEMITEMDISCOUNT.SALESORDERITEMID = SALESORDERITEM.ID
        outer apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
            SALESORDERITEM.QUANTITY * SALESORDERITEM.PRICE,
            SALESORDERITEMITEMDISCOUNT.AMOUNT,
            2,
            (
                select T.ID, T.PRICE as AMOUNT
                from dbo.TICKET T
                where T.SALESORDERITEMTICKETID = SALESORDERITEM.ID
                for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
            )
        ) PRORATEDAMOUNTS
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
            and SALESORDERITEM.TYPECODE = 0;

        -- Merchandise

        insert into @PRORATEDITEMAMOUNTS
        select
            SALESORDERITEM.ID,
            PRORATEDAMOUNTS.ID,
            SALESORDERITEM.PRICE,
            PRORATEDAMOUNTS.AMOUNT,
            1,
            14
        from dbo.SALESORDERITEM
        inner join dbo.SALESORDERITEMITEMDISCOUNT on SALESORDERITEMITEMDISCOUNT.SALESORDERITEMID = SALESORDERITEM.ID
        outer apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
            SALESORDERITEM.QUANTITY * SALESORDERITEM.PRICE,
            SALESORDERITEMITEMDISCOUNT.AMOUNT,
            2,
            (
                select MERCHANDISE.MERCHANDISEUNITID ID, MERCHANDISE.PRICE as AMOUNT
                from @MERCHANDISEUNITS MERCHANDISE
                where MERCHANDISE.SALESORDERITEMID = SALESORDERITEM.ID
                for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
            )
        ) PRORATEDAMOUNTS
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
            and SALESORDERITEM.TYPECODE = 14;
    end;

    -- Step 2: Order-level discounts (including Adjustable)

    with ITEMDISCOUNTS_CTE as (
        select
            SALESORDERITEMID,
            sum(DISCOUNTEDAMOUNT) as AMOUNT
        from @PRORATEDITEMAMOUNTS
        group by SALESORDERITEMID
    )
    insert into @PRORATEDITEMAMOUNTS
    select
        SALESORDERITEM.ID,
        PRORATEDAMOUNTS.ID,
        SALESORDERITEM.PRICE,
        PRORATEDAMOUNTS.AMOUNT,
        0,
        0
    from dbo.SALESORDERITEM
    inner join dbo.SALESORDERITEMORDERDISCOUNTDETAIL ORDERDISCOUNTPERITEM on ORDERDISCOUNTPERITEM.SALESORDERITEMID = SALESORDERITEM.ID
    left join ITEMDISCOUNTS_CTE DISCOUNTSALREADYDISTRIBUTED on DISCOUNTSALREADYDISTRIBUTED.SALESORDERITEMID = SALESORDERITEM.ID
    cross apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
        SALESORDERITEM.QUANTITY * SALESORDERITEM.PRICE - coalesce(DISCOUNTSALREADYDISTRIBUTED.AMOUNT, 0),
        ORDERDISCOUNTPERITEM.AMOUNT,
        2,
        (
            select
                T.ID,
                (T.PRICE - coalesce(DISCOUNTAMOUNTS.DISCOUNTEDAMOUNT, 0)) as AMOUNT
            from dbo.TICKET T
            left join @PRORATEDITEMAMOUNTS DISCOUNTAMOUNTS on DISCOUNTAMOUNTS.ITEMID = T.ID
            where T.SALESORDERITEMTICKETID = SALESORDERITEM.ID
            for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
        )
    ) PRORATEDAMOUNTS
    where SALESORDERITEM.SALESORDERID = @SALESORDERID
        and SALESORDERITEM.TYPECODE = 0

    union all

    -- Merchandise

    select
        SALESORDERITEM.ID,
        PRORATEDAMOUNTS.ID,
        SALESORDERITEM.PRICE,
        PRORATEDAMOUNTS.AMOUNT,
        0,
        14
    from dbo.SALESORDERITEM
    inner join dbo.SALESORDERITEMORDERDISCOUNTDETAIL ORDERDISCOUNTPERITEM on ORDERDISCOUNTPERITEM.SALESORDERITEMID = SALESORDERITEM.ID
    left join ITEMDISCOUNTS_CTE DISCOUNTSALREADYDISTRIBUTED on DISCOUNTSALREADYDISTRIBUTED.SALESORDERITEMID = SALESORDERITEM.ID
    cross apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
        SALESORDERITEM.QUANTITY * SALESORDERITEM.PRICE - coalesce(DISCOUNTSALREADYDISTRIBUTED.AMOUNT, 0),
        ORDERDISCOUNTPERITEM.AMOUNT,
        2,
        (
            select
                MERCHANDISE.MERCHANDISEUNITID ID,
                (SALESORDERITEM.PRICE - coalesce(DISCOUNTAMOUNTS.DISCOUNTEDAMOUNT, 0)) as AMOUNT
            from @MERCHANDISEUNITS MERCHANDISE
            left join @PRORATEDITEMAMOUNTS DISCOUNTAMOUNTS on DISCOUNTAMOUNTS.ITEMID = MERCHANDISE.MERCHANDISEUNITID
            where MERCHANDISE.SALESORDERITEMID = SALESORDERITEM.ID
            for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
        )
    ) PRORATEDAMOUNTS
    where SALESORDERITEM.SALESORDERID = @SALESORDERID
        and SALESORDERITEM.TYPECODE = 14;


    -- Store calculations directly on the TICKET record

    update dbo.TICKET set
        AMOUNTPAID = TICKET.PRICE - coalesce(TICKETDISCOUNTS.ITEMLEVELDISCOUNTEDAMOUNT + TICKETDISCOUNTS.ORDERLEVELDISCOUNTEDAMOUNT, 0),
        ITEMLEVELDISCOUNTSAPPLIED = coalesce(TICKETDISCOUNTS.ITEMLEVELDISCOUNTEDAMOUNT, 0),
        ORDERLEVELDISCOUNTSAPPLIED = coalesce(TICKETDISCOUNTS.ORDERLEVELDISCOUNTEDAMOUNT, 0),
        CHANGEDBYID = @CHANGEAGENTID,
        DATECHANGED = @CURRENTDATE
    from dbo.TICKET
    inner join dbo.SALESORDERITEM on SALESORDERITEM.ID = TICKET.SALESORDERITEMTICKETID
    left join (
        select
            ITEMID,
            sum(case when ISITEMLEVEL = 1 then DISCOUNTEDAMOUNT else 0 end) ITEMLEVELDISCOUNTEDAMOUNT,
            sum(case when ISITEMLEVEL = 0 then DISCOUNTEDAMOUNT else 0 end) ORDERLEVELDISCOUNTEDAMOUNT
        from @PRORATEDITEMAMOUNTS
        where TYPECODE = 0
        group by ITEMID
    ) TICKETDISCOUNTS on TICKET.ID = TICKETDISCOUNTS.ITEMID
    where SALESORDERITEM.SALESORDERID = @SALESORDERID;


    insert into dbo.SALESORDERITEMMERCHANDISEUNIT
        (ID, SALESORDERITEMMERCHANDISEID, AMOUNTPAID, ITEMLEVELDISCOUNTSAPPLIED, ORDERLEVELDISCOUNTSAPPLIED, REFUNDEDAMOUNT, ADDEDBYID, CHANGEDBYID, DATEADDED, DATECHANGED)
    select
        MERCHANDISE.MERCHANDISEUNITID,
        MERCHANDISE.SALESORDERITEMID,
        MERCHANDISE.PRICE - coalesce(APPLIEDDISCOUNTS.ITEMLEVELDISCOUNTEDAMOUNT, 0) - coalesce(APPLIEDDISCOUNTS.ORDERLEVELDISCOUNTEDAMOUNT, 0),
        coalesce(APPLIEDDISCOUNTS.ITEMLEVELDISCOUNTEDAMOUNT, 0),
        coalesce(APPLIEDDISCOUNTS.ORDERLEVELDISCOUNTEDAMOUNT, 0),
        0,
        @CHANGEAGENTID,
        @CHANGEAGENTID,
        @CURRENTDATE,
        @CURRENTDATE
    from @MERCHANDISEUNITS MERCHANDISE
    left join (
        select
            ITEMID,
            coalesce(sum(case when ISITEMLEVEL = 1 then DISCOUNTEDAMOUNT else 0 end), 0) ITEMLEVELDISCOUNTEDAMOUNT,
            coalesce(sum(case when ISITEMLEVEL = 0 then DISCOUNTEDAMOUNT else 0 end), 0) ORDERLEVELDISCOUNTEDAMOUNT
        from @PRORATEDITEMAMOUNTS
        where TYPECODE = 14
        group by ITEMID
    ) APPLIEDDISCOUNTS on MERCHANDISE.MERCHANDISEUNITID = APPLIEDDISCOUNTS.ITEMID;

    -- Mark the order as having its discounts distributed to prevent recalculating them later (which could calculate differently and cause data corruption).

    update dbo.SALESORDER
    set
        ITEMDISCOUNTSCALCULATED = 1,
        CHANGEDBYID = @CHANGEAGENTID,
        DATECHANGED = @CURRENTDATE
    where ID = @SALESORDERID;
end