USP_SALESORDER_APPLYITEMDISCOUNTS

Applies item discounts to a sales order.

Parameters

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

Definition

Copy


CREATE procedure [dbo].[USP_SALESORDER_APPLYITEMDISCOUNTS]
(
    @SALESORDERID uniqueidentifier,
    @CHANGEAGENTID uniqueidentifier
)
as
    set nocount on;

    declare @CURRENTDATE datetime = getdate();

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

    declare @SALESORDERITEMITEMDISCOUNTOPTION xml;
    declare @DISCOUNTORDERITEMS_BYGROUP dbo.UDT_APPLYDISCOUNTS_DISCOUNTORDERITEM;

    declare @SOIITEMDISCOUNTOPTION table (
        ID uniqueidentifier not null,
        SALESORDERITEMID uniqueidentifier not null,
        DISCOUNTID uniqueidentifier not null,
        DISCOUNTNAME nvarchar(255) not null,
        DISCOUNTAMOUNT money not null,
        OPTIONID uniqueidentifier not null,
        NUMBEROFDISCOUNTEDITEMS int not null
    );

    -- Save existing, superseded discounts so they don't magically disappear and confuse people.

    declare @EXISTINGDISCOUNTS table (DISCOUNTID uniqueidentifier, ORIGINALID uniqueidentifier, APPLICATIONTYPECODE tinyint, ORIGINALNAME nvarchar(100));

    if exists (
        select 1
        from dbo.SALESORDERITEMITEMDISCOUNT with (nolock)
        inner join dbo.SALESORDERITEM with (nolock) on SALESORDERITEM.ID = SALESORDERITEMITEMDISCOUNT.SALESORDERITEMID
        where SALESORDERITEM.SALESORDERID = @SALESORDERID
    ) begin
        insert into @EXISTINGDISCOUNTS
        select
            DISCOUNT.ID,
            DISCOUNT.ORIGINALDISCOUNTID,
            DISCOUNT.APPLICATIONTYPECODE,
            ITEMDISCOUNT.DISCOUNTNAME
        from dbo.SALESORDERITEMITEMDISCOUNT ITEMDISCOUNT
        inner join dbo.SALESORDERITEM on SALESORDERITEM.ID = ITEMDISCOUNT.SALESORDERITEMID
        inner join dbo.DISCOUNT on DISCOUNT.ID = ITEMDISCOUNT.DISCOUNTID
        where
            SALESORDERITEM.SALESORDERID = @SALESORDERID
            and DISCOUNT.SUPERSEDEDBYID is not null;

        delete from [dbo].[SALESORDERITEMITEMDISCOUNT] with (rowlock)
            where SALESORDERITEMID in (select ID from dbo.SALESORDERITEM with (nolock) where SALESORDERID = @SALESORDERID);
    end

    declare @PARENTDISCOUNTSCENARIOID uniqueidentifier = newid();

    declare @DISCOUNTORDERITEMSWITHMERCHANDISE table (
        PARENTDISCOUNTSCENARIOID uniqueidentifier,
        PARENTID uniqueidentifier,
        PARENTDISCOUNTSCENARIODISCOUNTGROUPID uniqueidentifier,
        ORDERITEMID uniqueidentifier,
        TYPECODE tinyint,
        DESCRIPTION nvarchar(255),
        PROGRAMID uniqueidentifier,
        EVENTID uniqueidentifier,
        PRICETYPECODEID uniqueidentifier,
        QUANTITY decimal(20,4),
        UNITPRICE money,
        MERCHANDISEDEPARTMENTID uniqueidentifier,
        MERCHANDISEPRODUCTINSTANCEID uniqueidentifier,
        MERCHANDISEPRODUCTID uniqueidentifier,
        ISDISCOUNTABLE bit
    )

    insert into @DISCOUNTORDERITEMSWITHMERCHANDISE
    select
        @PARENTDISCOUNTSCENARIOID as PARENTDISCOUNTSCENARIOID,
        @PARENTDISCOUNTSCENARIOID as PARENTID,
        null as PARENTDISCOUNTSCENARIODISCOUNTGROUPID,
        SALESORDERITEM.ID as ORDERITEMID,
        SALESORDERITEM.TYPECODE as TYPECODE,
        SALESORDERITEM.DESCRIPTION as DESCRIPTION,
        SALESORDERITEMTICKET.PROGRAMID as PROGRAMID,
        isnull(SALESORDERITEMTICKET.EVENTID, SALESORDERITEMTICKET.PROGRAMID) as EVENTID,
        SALESORDERITEMTICKET.PRICETYPECODEID as PRICETYPECODEID,
        SALESORDERITEM.QUANTITY as QUANTITY,
        SALESORDERITEM.PRICE as UNITPRICE,
        SALESORDERITEMMERCHANDISE.MERCHANDISEDEPARTMENTID as MERCHANDISEDEPARTMENTID,
        SALESORDERITEMMERCHANDISE.MERCHANDISEPRODUCTINSTANCEID as MERCHANDISEPRODUCTINSTANCEID,
        MERCHANDISEPRODUCTINSTANCE.MERCHANDISEPRODUCTID as MERCHANDISEPRODUCTID,
        isnull(MERCHANDISEPRODUCT.ISDISCOUNTABLE, 1) as ISDISCOUNTABLE
    from
        dbo.SALESORDERITEM
    left outer join
        dbo.SALESORDERITEMTICKET on SALESORDERITEM.ID = SALESORDERITEMTICKET.ID
    left outer join
        dbo.SALESORDERITEMTICKETCOMBINATION on SALESORDERITEMTICKET.ID = SALESORDERITEMTICKETCOMBINATION.ID
    left outer join
        dbo.SALESORDERITEMMERCHANDISE on SALESORDERITEMMERCHANDISE.ID = SALESORDERITEM.ID
    left outer join
        dbo.MERCHANDISEPRODUCTINSTANCE on MERCHANDISEPRODUCTINSTANCE.ID = SALESORDERITEMMERCHANDISE.MERCHANDISEPRODUCTINSTANCEID
    left outer join
        dbo.MERCHANDISEPRODUCT on MERCHANDISEPRODUCT.ID = MERCHANDISEPRODUCTINSTANCE.MERCHANDISEPRODUCTID
    where
        SALESORDERID = @SALESORDERID and
        SALESORDERITEMTICKETCOMBINATION.TICKETCOMBINATIONID is null and
        SALESORDERITEM.TYPECODE in (0, 14)  -- Ticket, Merchandise


    if @@rowcount = 0 begin
        -- We've already deleted existing discounts

        -- and there are no new discountable items

        return 0;
    end

    declare @SALESMETHODID uniqueidentifier;

    select @SALESMETHODID = ID
    from dbo.SALESMETHOD
    where TYPECODE = (select SALESMETHODTYPECODE from dbo.SALESORDER where ID = @SALESORDERID);

    declare @SALESMETHODAVAILABLEITEMDISCOUNTS table (ID uniqueidentifier, APPLICATIONTYPECODE tinyint);

    insert into @SALESMETHODAVAILABLEITEMDISCOUNTS (ID, APPLICATIONTYPECODE)
    select
        AVAILABLEDISCOUNTS.ID,
        AVAILABLEDISCOUNTS.APPLICATIONTYPECODE
    from dbo.UFN_SALESMETHOD_AVAILABLEDISCOUNTS(@SALESMETHODID, 1) AVAILABLEDISCOUNTS -- applies to items

    inner join dbo.DISCOUNT on AVAILABLEDISCOUNTS.ID = DISCOUNT.ID
    left join @EXISTINGDISCOUNTS EXISTINGDISCOUNTS on EXISTINGDISCOUNTS.ORIGINALID = DISCOUNT.ORIGINALDISCOUNTID
    where EXISTINGDISCOUNTS.DISCOUNTID is null

    -- Ignore new version of discount and use the older version that was already on the order

    insert into @SALESMETHODAVAILABLEITEMDISCOUNTS (ID, APPLICATIONTYPECODE)
    select DISCOUNTID, APPLICATIONTYPECODE
    from @EXISTINGDISCOUNTS;

    --find active, available and eligible discounts for sales order

    declare @SALESORDERAVAILABLEDISCOUNTS dbo.UDT_APPLYDISCOUNTS_SALESORDERAVAILABLEDISCOUNT;

    insert into @SALESORDERAVAILABLEDISCOUNTS
    select
        newid(),
        [DISCOUNT].[ID],
        case
            when DISCOUNT.APPLICATIONTYPECODE = 0 then  -- Automatically

                0
            else
                1
        end
    from
        @SALESMETHODAVAILABLEITEMDISCOUNTS as [DISCOUNT]
    left outer join
        dbo.SALESORDERMANUALDISCOUNT on SALESORDERMANUALDISCOUNT.DISCOUNTID = DISCOUNT.ID and SALESORDERMANUALDISCOUNT.SALESORDERID = @SALESORDERID
    where
        (
            [DISCOUNT].[APPLICATIONTYPECODE] = 0  -- Automatically

            or SALESORDERMANUALDISCOUNT.DISCOUNTID is not null
        )
        and [dbo].[UFN_DISCOUNT_ELIGIBLEFORORDER]([DISCOUNT].[ID], @SALESORDERID) = 1;  -- This may be an issue if there are a lot of discounts


    if @@rowcount = 0 begin
        -- There are no available item discounts

        return 0;
    end

    declare @DISCOUNTORDERITEMSXML xml = (
        select
            PARENTDISCOUNTSCENARIOID,
            PARENTID,
            PARENTDISCOUNTSCENARIODISCOUNTGROUPID,
            ORDERITEMID,
            TYPECODE,
            DESCRIPTION,
            PROGRAMID,
            EVENTID,
            PRICETYPECODEID,
            QUANTITY,
            UNITPRICE,
            MERCHANDISEDEPARTMENTID,
            MERCHANDISEPRODUCTINSTANCEID,
            MERCHANDISEPRODUCTID,
            ISDISCOUNTABLE
        from @DISCOUNTORDERITEMSWITHMERCHANDISE
        for xml raw('ITEM'),type,root('DISCOUNTORDERITEMSXML'),binary base64
    );

    -- compute hash outside UFN_DISCOUNTRESULTS_FINDINCACHE so that we don't have to recompute it when we add new results to the cache (bottom of this SP)

    declare @SCENARIOHASH varbinary(20) = dbo.UFN_DISCOUNTRESULTS_COMPUTEHASH(@SALESORDERID, @SALESORDERAVAILABLEDISCOUNTS, @DISCOUNTORDERITEMSXML);
    if @SCENARIOHASH is not null and exists (select 1 from dbo.CACHEDDISCOUNTRESULT where INPUTHASH = @SCENARIOHASH)
    begin
        insert into dbo.SALESORDERITEMITEMDISCOUNT
        (
            ID,
            SALESORDERITEMID,
            DISCOUNTID,
            DISCOUNTNAME,
            AMOUNT,
            NUMBEROFDISCOUNTEDITEMS,
            ADDEDBYID,
            CHANGEDBYID,
            DATEADDED,
            DATECHANGED
        )
        select
            newid(),
            CACHEDDISCOUNTRESULT.SALESORDERITEMID,
            CACHEDDISCOUNTRESULT.DISCOUNTID,
            coalesce(EXISTINGDISCOUNTS.ORIGINALNAME, DISCOUNT.NAME) DISCOUNTNAME,
            CACHEDDISCOUNTRESULT.AMOUNT,
            CACHEDDISCOUNTRESULT.NUMBEROFDISCOUNTEDITEMS,
            @CHANGEAGENTID,
            @CHANGEAGENTID,
            @CURRENTDATE,
            @CURRENTDATE
        from
        (
            select CACHEDTICKETDISCOUNT.DISCOUNTID, SALESORDERITEM.ID SALESORDERITEMID, CACHEDTICKETDISCOUNT.AMOUNT, CACHEDTICKETDISCOUNT.NUMBEROFDISCOUNTEDITEMS
            from dbo.CACHEDDISCOUNTRESULTAPPLICATION CACHEDTICKETDISCOUNT
            inner join dbo.SALESORDERITEMTICKET TICKETITEM
                on TICKETITEM.PROGRAMID = CACHEDTICKETDISCOUNT.PROGRAMID
                    and (TICKETITEM.EVENTID is null or TICKETITEM.EVENTID = CACHEDTICKETDISCOUNT.EVENTID)
                    and TICKETITEM.PRICETYPECODEID = CACHEDTICKETDISCOUNT.PRICETYPECODEID
            inner join dbo.SALESORDERITEM on SALESORDERITEM.ID = TICKETITEM.ID
            where CACHEDTICKETDISCOUNT.INPUTHASH = @SCENARIOHASH
                and SALESORDERITEM.SALESORDERID = @SALESORDERID

            union all

            select CACHEDMERCHDISCOUNT.DISCOUNTID, SALESORDERITEM.ID SALESORDERITEMID, CACHEDMERCHDISCOUNT.AMOUNT, CACHEDMERCHDISCOUNT.NUMBEROFDISCOUNTEDITEMS
            from dbo.CACHEDDISCOUNTRESULTAPPLICATION CACHEDMERCHDISCOUNT
            inner join dbo.SALESORDERITEMMERCHANDISE MERCHITEM on MERCHITEM.MERCHANDISEPRODUCTINSTANCEID = CACHEDMERCHDISCOUNT.MERCHANDISEPRODUCTINSTANCEID
            inner join dbo.SALESORDERITEM on SALESORDERITEM.ID = MERCHITEM.ID
            where CACHEDMERCHDISCOUNT.INPUTHASH = @SCENARIOHASH
                and SALESORDERITEM.SALESORDERID = @SALESORDERID

        ) CACHEDDISCOUNTRESULT
        left join dbo.DISCOUNT on DISCOUNT.ID = CACHEDDISCOUNTRESULT.DISCOUNTID
        left join @EXISTINGDISCOUNTS EXISTINGDISCOUNTS on EXISTINGDISCOUNTS.DISCOUNTID = CACHEDDISCOUNTRESULT.DISCOUNTID;

        update dbo.CACHEDDISCOUNTRESULT
        set LASTUSED = @CURRENTDATE, TIMESUSED += 1, CACHEUSAGETIME = cast(getdate() - @CURRENTDATE as time(4))
        where INPUTHASH = @SCENARIOHASH;

        return 0;
    end

    declare @DISCOUNTOPTIONID uniqueidentifier = newid();
    exec [dbo].[USP_SALESORDER_APPLYITEMDISCOUNTS_NONGROUPSIZEDISCOUNTS] @SALESORDERID, @DISCOUNTOPTIONID, @SALESORDERAVAILABLEDISCOUNTS, @CHANGEAGENTID, @SALESORDERITEMITEMDISCOUNTOPTION output, @DISCOUNTORDERITEMSXML;

    insert into @SOIITEMDISCOUNTOPTION
    (
        ID,
        SALESORDERITEMID,
        DISCOUNTID,
        DISCOUNTNAME,
        DISCOUNTAMOUNT,
        OPTIONID,
        NUMBEROFDISCOUNTEDITEMS
    )
    select
        T.item.value('(@ID)[1]','uniqueidentifier'),
        T.item.value('(@SALESORDERITEMID)[1]','uniqueidentifier'),
        T.item.value('(@DISCOUNTID)[1]','uniqueidentifier'),
        T.item.value('(@DISCOUNTNAME)[1]','nvarchar(255)'),
        T.item.value('(@DISCOUNTAMOUNT)[1]','money'),
        T.item.value('(@OPTIONID)[1]','uniqueidentifier'),
        T.item.value('(@NUMBEROFDISCOUNTEDITEMS)[1]','integer')
    from
        @SALESORDERITEMITEMDISCOUNTOPTION.nodes('/SALESORDERITEMITEMDISCOUNTOPTION/ITEM') T(item);

    if exists -- Short circuiting extra calls to calculate item stored procedures

    (
        select *
        from @SALESORDERAVAILABLEDISCOUNTS SALESORDERAVAILABLEDISCOUNTS
        inner join dbo.DISCOUNT on SALESORDERAVAILABLEDISCOUNTS.DISCOUNTID = DISCOUNT.ID
        where DISCOUNT.DISCOUNTTYPECODE = 2
    )
    begin
        declare @DISCOUNTORDERITEMSXML_BYGROUP xml;

        /*
            Contains Merchandise information for group discounts.
        */
        set @DISCOUNTORDERITEMSXML_BYGROUP =
        (

            select
                DISCOUNTID,
                DISCOUNTGROUPID,
                CALCULATIONTYPECODE,
                UNITDISCOUNTAMOUNT,
                UNITPRICE,
                ORDERITEMID,
                PRICETYPECODEID,
                MERCHANDISEPRODUCTID,
                DISCOUNTNAME,
                QUANTITY
            from
            (
            select
                DISCOUNT.ID DISCOUNTID,
                DISCOUNTGROUP.ID DISCOUNTGROUPID,
                DISCOUNT.CALCULATIONTYPECODE CALCULATIONTYPECODE,
                case
                    when DISCOUNT.CALCULATIONTYPECODE = 0 then
                        DISCOUNT.MERCHANDISEAMOUNT
                else
                    DISCOUNT.MERCHANDISEPERCENT
                end
                UNITDISCOUNTAMOUNT,
                DISCOUNTORDERITEMSWITHMERCHANDISE.UNITPRICE,
                DISCOUNT.DISCOUNTMERCHANDISEFORCODE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID,
                null PRICETYPECODEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTID,
                DISCOUNT.NAME DISCOUNTNAME,
                DISCOUNTORDERITEMSWITHMERCHANDISE.QUANTITY
            from @SALESORDERAVAILABLEDISCOUNTS SALESORDERAVAILABLEDISCOUNTS
            inner join dbo.DISCOUNT on
                SALESORDERAVAILABLEDISCOUNTS.DISCOUNTID = DISCOUNT.ID
            inner join dbo.DISCOUNTGROUP on
                DISCOUNT.ID = DISCOUNTGROUP.DISCOUNTID
            inner join dbo.DISCOUNTGROUPDETAIL on
                DISCOUNTGROUP.ID = DISCOUNTGROUPDETAIL.DISCOUNTGROUPID
            outer apply
                @DISCOUNTORDERITEMSWITHMERCHANDISE as DISCOUNTORDERITEMSWITHMERCHANDISE
            where
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTINSTANCEID is not null and
                DISCOUNT.DISCOUNTTYPECODE = 2 and  -- By quantity

                DISCOUNTGROUPDETAIL.DISCOUNTGROUPDETAILAPPLICATIONCODE = 0 and  -- All discountable merchandise

                DISCOUNTGROUPDETAIL.DISCOUNTEDITEM = 1 and
                DISCOUNTORDERITEMSWITHMERCHANDISE.ISDISCOUNTABLE = 1

            union

            select
                DISCOUNT.ID DISCOUNTID,
                DISCOUNTGROUP.ID DISCOUNTGROUPID,
                DISCOUNT.CALCULATIONTYPECODE CALCULATIONTYPECODE,
                case
                    when DISCOUNT.CALCULATIONTYPECODE = 0 then
                        DISCOUNT.MERCHANDISEAMOUNT
                else
                    DISCOUNT.MERCHANDISEPERCENT
                end
                UNITDISCOUNTAMOUNT,
                DISCOUNTORDERITEMSWITHMERCHANDISE.UNITPRICE,
                DISCOUNT.DISCOUNTMERCHANDISEFORCODE DISCOUNTMERCHANDISEFORCODE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID,
                null PRICETYPECODEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTID,
                DISCOUNT.NAME DISCOUNTNAME,
                DISCOUNTORDERITEMSWITHMERCHANDISE.QUANTITY
            from @SALESORDERAVAILABLEDISCOUNTS SALESORDERAVAILABLEDISCOUNTS
            inner join dbo.DISCOUNT on
                SALESORDERAVAILABLEDISCOUNTS.DISCOUNTID = DISCOUNT.ID
            inner join dbo.DISCOUNTGROUP on
                DISCOUNT.ID = DISCOUNTGROUP.DISCOUNTID
            inner join dbo.DISCOUNTGROUPDETAIL on
                DISCOUNTGROUP.ID = DISCOUNTGROUPDETAIL.DISCOUNTGROUPID
            inner join dbo.DISCOUNTGROUPDETAILMERCHANDISEDEPARTMENT on
                DISCOUNTGROUPDETAILMERCHANDISEDEPARTMENT.ID = DISCOUNTGROUPDETAIL.ID
            inner join
                @DISCOUNTORDERITEMSWITHMERCHANDISE DISCOUNTORDERITEMSWITHMERCHANDISE on
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEDEPARTMENTID = DISCOUNTGROUPDETAILMERCHANDISEDEPARTMENT.MERCHANDISEDEPARTMENTID
            where DISCOUNT.DISCOUNTTYPECODE = 2 and  -- By quantity

                DISCOUNTGROUPDETAIL.DISCOUNTGROUPDETAILAPPLICATIONCODE = 1 and  -- Merchandise department

                DISCOUNTGROUPDETAIL.DISCOUNTEDITEM = 1 and
                DISCOUNTORDERITEMSWITHMERCHANDISE.ISDISCOUNTABLE = 1

            union

            select
                DISCOUNT.ID DISCOUNTID,
                DISCOUNTGROUP.ID DISCOUNTGROUPID,
                DISCOUNT.CALCULATIONTYPECODE CALCULATIONTYPECODE,
                case
                    when DISCOUNT.CALCULATIONTYPECODE = 0 then
                        DISCOUNT.MERCHANDISEAMOUNT
                else
                    DISCOUNT.MERCHANDISEPERCENT
                end
                UNITDISCOUNTAMOUNT,
                DISCOUNTORDERITEMSWITHMERCHANDISE.UNITPRICE,
                DISCOUNT.DISCOUNTMERCHANDISEFORCODE DISCOUNTMERCHANDISEFORCODE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID,
                null PRICETYPECODEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTID,
                DISCOUNT.NAME DISCOUNTNAME,
                DISCOUNTORDERITEMSWITHMERCHANDISE.QUANTITY
            from @SALESORDERAVAILABLEDISCOUNTS SALESORDERAVAILABLEDISCOUNTS
            inner join dbo.DISCOUNT on
                SALESORDERAVAILABLEDISCOUNTS.DISCOUNTID = DISCOUNT.ID
            inner join dbo.DISCOUNTGROUP on
                DISCOUNT.ID = DISCOUNTGROUP.DISCOUNTID
            inner join dbo.DISCOUNTGROUPDETAIL on
                DISCOUNTGROUP.ID = DISCOUNTGROUPDETAIL.DISCOUNTGROUPID
            inner join dbo.DISCOUNTGROUPDETAILMERCHANDISEITEM on
                DISCOUNTGROUPDETAILMERCHANDISEITEM.ID = DISCOUNTGROUPDETAIL.ID
            inner join @DISCOUNTORDERITEMSWITHMERCHANDISE as DISCOUNTORDERITEMSWITHMERCHANDISE on
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTID = DISCOUNTGROUPDETAILMERCHANDISEITEM.MERCHANDISEITEMID
            where DISCOUNT.DISCOUNTTYPECODE = 2 and  -- By quantity

                DISCOUNTGROUPDETAIL.DISCOUNTGROUPDETAILAPPLICATIONCODE = 2 and  -- Merchandise

                DISCOUNTGROUPDETAIL.DISCOUNTEDITEM = 1 and
                DISCOUNTORDERITEMSWITHMERCHANDISE.ISDISCOUNTABLE = 1
            ) ORDERITEMS
            for xml raw('ITEM'),type,root('DISCOUNTORDERITEMSXML_BYGROUP'),binary base64
        )

        insert into @DISCOUNTORDERITEMS_BYGROUP
            select distinct
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTDISCOUNTSCENARIOID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTDISCOUNTSCENARIODISCOUNTGROUPID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID,
                @SALESORDERID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.TYPECODE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.DESCRIPTION,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PROGRAMID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.EVENTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PRICETYPECODEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.QUANTITY,
                DISCOUNTORDERITEMSWITHMERCHANDISE.UNITPRICE
            from @DISCOUNTORDERITEMSWITHMERCHANDISE as DISCOUNTORDERITEMSWITHMERCHANDISE
            inner join [dbo].[DISCOUNTGROUPDETAILPROGRAM]
                on [DISCOUNTGROUPDETAILPROGRAM].[PROGRAMID] = DISCOUNTORDERITEMSWITHMERCHANDISE.PROGRAMID
            inner join [dbo].[DISCOUNTGROUPDETAIL]
                on [DISCOUNTGROUPDETAIL].[ID] = [DISCOUNTGROUPDETAILPROGRAM].[ID]
            inner join [dbo].[DISCOUNTGROUP]
                on [DISCOUNTGROUP].[ID] = [DISCOUNTGROUPDETAIL].[DISCOUNTGROUPID]
            inner join [dbo].[DISCOUNT]
                on [DISCOUNTGROUP].[DISCOUNTID] = [DISCOUNT].[ID]
            inner join @SALESORDERAVAILABLEDISCOUNTS [SOAD]
                on [SOAD].[DISCOUNTID] = [DISCOUNT].[ID]
            where [DISCOUNT].[DISCOUNTTYPECODE] = 2;  -- By quantity


        set @DISCOUNTOPTIONID = newid()
        set @SALESORDERITEMITEMDISCOUNTOPTION = null

        exec [dbo].[USP_SALESORDER_APPLYITEMDISCOUNTS_GROUPSIZEDISCOUNTS] @SALESORDERID, @DISCOUNTOPTIONID, @SALESORDERAVAILABLEDISCOUNTS, @DISCOUNTORDERITEMS_BYGROUP, @CHANGEAGENTID, @SALESORDERITEMITEMDISCOUNTOPTION output, @DISCOUNTORDERITEMSXML_BYGROUP;

        -- Put all qualifying items into @SOIITEMDISCOUNTOPTION

        insert into @SOIITEMDISCOUNTOPTION
        (
            ID,
            SALESORDERITEMID,
            DISCOUNTID,
            DISCOUNTNAME,
            DISCOUNTAMOUNT,
            OPTIONID,
            NUMBEROFDISCOUNTEDITEMS
        )
            select
                T.item.value('(@ID)[1]','uniqueidentifier'),
                T.item.value('(@SALESORDERITEMID)[1]','uniqueidentifier'),
                T.item.value('(@DISCOUNTID)[1]','uniqueidentifier'),
                T.item.value('(@DISCOUNTNAME)[1]','nvarchar(255)'),
                T.item.value('(@DISCOUNTAMOUNT)[1]','money'),
                T.item.value('(@OPTIONID)[1]','uniqueidentifier'),
                T.item.value('(@NUMBEROFDISCOUNTEDITEMS)[1]','integer')
            from
                @SALESORDERITEMITEMDISCOUNTOPTION.nodes('/SALESORDERITEMITEMDISCOUNTOPTION/ITEM') T(item);


        declare @DISCOUNTORDERITEMS_EXCLUDINGGROUPS_XML xml = (
            select
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTDISCOUNTSCENARIOID ,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PARENTDISCOUNTSCENARIODISCOUNTGROUPID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.TYPECODE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.DESCRIPTION,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PROGRAMID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.EVENTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.PRICETYPECODEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.QUANTITY,
                DISCOUNTORDERITEMSWITHMERCHANDISE.UNITPRICE,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEDEPARTMENTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTINSTANCEID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.MERCHANDISEPRODUCTID,
                DISCOUNTORDERITEMSWITHMERCHANDISE.ISDISCOUNTABLE
            from @DISCOUNTORDERITEMSWITHMERCHANDISE as DISCOUNTORDERITEMSWITHMERCHANDISE
            where
                DISCOUNTORDERITEMSWITHMERCHANDISE.ORDERITEMID not in (
                    select SALESORDERITEMID
                    from @SOIITEMDISCOUNTOPTION
                    where OPTIONID = @DISCOUNTOPTIONID
                )
            for xml raw('ITEM'),type,root('DISCOUNTORDERITEMSXML'),binary base64
        )

        -- Remove all the discounts that aren't really discounts

        -- (we do this after the previous insert statement because $0 discounts still count

        -- towards the required number for group discounts).

        delete from @SOIITEMDISCOUNTOPTION where [DISCOUNTAMOUNT] <= 0;

        set @SALESORDERITEMITEMDISCOUNTOPTION = null
        exec [dbo].[USP_SALESORDER_APPLYITEMDISCOUNTS_NONGROUPSIZEDISCOUNTS] @SALESORDERID, @DISCOUNTOPTIONID, @SALESORDERAVAILABLEDISCOUNTS, @CHANGEAGENTID, @SALESORDERITEMITEMDISCOUNTOPTION output, @DISCOUNTORDERITEMS_EXCLUDINGGROUPS_XML;

        insert into @SOIITEMDISCOUNTOPTION
        (
            ID,
            SALESORDERITEMID,
            DISCOUNTID,
            DISCOUNTNAME,
            DISCOUNTAMOUNT,
            OPTIONID,
            NUMBEROFDISCOUNTEDITEMS
        )
            select
                T.item.value('(@ID)[1]','uniqueidentifier'),
                T.item.value('(@SALESORDERITEMID)[1]','uniqueidentifier'),
                T.item.value('(@DISCOUNTID)[1]','uniqueidentifier'),
                T.item.value('(@DISCOUNTNAME)[1]','nvarchar(255)'),
                T.item.value('(@DISCOUNTAMOUNT)[1]','money'),
                T.item.value('(@OPTIONID)[1]','uniqueidentifier'),
                T.item.value('(@NUMBEROFDISCOUNTEDITEMS)[1]','integer')
            from
                @SALESORDERITEMITEMDISCOUNTOPTION.nodes('/SALESORDERITEMITEMDISCOUNTOPTION/ITEM') T(item);
    end

    declare @SELECTEDOPTIONID uniqueidentifier = null;

    select top(1)
        @SELECTEDOPTIONID = [OPTIONID]
    from @SOIITEMDISCOUNTOPTION
    group by [OPTIONID]
    order by sum([DISCOUNTAMOUNT]) desc;

    insert into [dbo].[SALESORDERITEMITEMDISCOUNT]
    (
        [ID],
        [SALESORDERITEMID],
        [DISCOUNTID],
        [DISCOUNTNAME],
        [AMOUNT],
        [NUMBEROFDISCOUNTEDITEMS],
        [ADDEDBYID],
        [CHANGEDBYID],
        [DATEADDED],
        [DATECHANGED]
    )
    select
        newid(),
        SALESORDERITEMID,
        ITEMDISCOUNTOPTION.DISCOUNTID,
        coalesce(EXISTINGDISCOUNTS.ORIGINALNAME, ITEMDISCOUNTOPTION.DISCOUNTNAME),
        DISCOUNTAMOUNT,
        NUMBEROFDISCOUNTEDITEMS,
        @CHANGEAGENTID,
        @CHANGEAGENTID,
        @CURRENTDATE,
        @CURRENTDATE
    from @SOIITEMDISCOUNTOPTION ITEMDISCOUNTOPTION
    left join @EXISTINGDISCOUNTS EXISTINGDISCOUNTS on EXISTINGDISCOUNTS.DISCOUNTID = ITEMDISCOUNTOPTION.DISCOUNTID
    where ITEMDISCOUNTOPTION.OPTIONID = @SELECTEDOPTIONID;

    -- Recheck existence of scenario in cache to prevent adding a duplicate, which will fail the unique index on the next access of this cache entry (because the original and duplicate will be both updated with the same LASTUSED)

    if @SCENARIOHASH is not null and not exists (select 1 from dbo.CACHEDDISCOUNTRESULT where INPUTHASH = @SCENARIOHASH)
    begin
        declare @ELAPSEDTIME time(4) = cast(getdate() - @CURRENTDATE as time(4));
        exec dbo.USP_DISCOUNTRESULTS_ADDTOCACHE @SCENARIOHASH, @SALESORDERAVAILABLEDISCOUNTS, @SALESORDERID, @ELAPSEDTIME, @CHANGEAGENTID;
    end

    return 0;