USP_CREDIT_ADDMEMBERSHIPREFUND

Parameters

Parameter Parameter Type Mode Description
@ID uniqueidentifier INOUT
@CHANGEAGENTID uniqueidentifier IN
@MEMBERSHIPID uniqueidentifier IN
@MEMBERSHIPTRANSACTIONID uniqueidentifier IN
@ITEMS xml IN
@REFUNDMETHODS xml IN
@COMMENT nvarchar(500) IN
@CREDITREASONCODEID uniqueidentifier IN
@CURRENTAPPUSERID uniqueidentifier IN
@TOTAL money IN
@TIMESTAMP bigint IN
@ADDRESSID uniqueidentifier IN

Definition

Copy


CREATE procedure dbo.USP_CREDIT_ADDMEMBERSHIPREFUND
(
    @ID uniqueidentifier = null output,
    @CHANGEAGENTID uniqueidentifier = null,
    @MEMBERSHIPID uniqueidentifier,
    @MEMBERSHIPTRANSACTIONID uniqueidentifier = null,
    @ITEMS xml = null,
    @REFUNDMETHODS xml = null,
    @COMMENT nvarchar(500) = '',
    @CREDITREASONCODEID uniqueidentifier = null,
    @CURRENTAPPUSERID uniqueidentifier = null,
    @TOTAL money = null,  -- Maximum refundable amount (i.e. total value of the membership transaction including add-ons)

    @TIMESTAMP bigint = null,
    @ADDRESSID uniqueidentifier = null
)
as
begin
    set nocount on;

    if @ID is null
        set @ID = newid();

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

    declare @CURRENTDATE datetime = getdate();
    declare @POSTSTATUSCODE tinyint;

    --Make sure nobody else has done a refund since the form was opened.

    if @TIMESTAMP <> coalesce((select TSLONG from dbo.MEMBERSHIPTRANSACTION where ID = @MEMBERSHIPTRANSACTIONID), -1) begin
        if @MEMBERSHIPTRANSACTIONID is null or @MEMBERSHIPTRANSACTIONID = '00000000-0000-0000-0000-000000000000'
            raiserror('ERR_TRANSACTIONID_MEMBERSHIPNOTSELECTED', 13, 1);
        else
            raiserror('ERR_ITEMS_ORDERREFUND_HASNEWERMEMBERSHIPTRANSACTIONS', 13, 1);

        return 1;
    end
    else begin
        update dbo.MEMBERSHIPTRANSACTION
        set DATECHANGED = getdate(), CHANGEDBYID = @CHANGEAGENTID
        where ID = @MEMBERSHIPTRANSACTIONID;

        select @POSTSTATUSCODE = LI.POSTSTATUSCODE
        from dbo.MEMBERSHIPTRANSACTION MT
        inner join dbo.FINANCIALTRANSACTIONLINEITEM LI on LI.ID = MT.REVENUESPLITID
        where MT.ID = @MEMBERSHIPTRANSACTIONID;
    end

    -- Get refund items from input parameter @ITEMS

    declare @CREDITITEMS table(
        ID uniqueidentifier,
        SALESORDERITEMID uniqueidentifier,
        PRICE money,
        ITEMDISCOUNTS money,  -- This represents promos to reverse, which is not included in the @ITEMS xml (determined later).

        AMOUNTTOREFUND money,  -- This will be the BASEAMOUNT on the refund line item.

        TYPECODE tinyint,
        [DESCRIPTION] nvarchar(255),
        ITEMID uniqueidentifier,
        REVENUESPLITID uniqueidentifier,
        CONTRIBUTEDREVENUE money,
        WILLBECANCELLED bit
    );

    insert into @CREDITITEMS
    select
        newid(),
        case
            when T.item.value('(@SALESORDERITEMID)[1]','uniqueidentifier') = '00000000-0000-0000-0000-000000000000'
                then null
            else
                T.item.value('(@SALESORDERITEMID)[1]','uniqueidentifier')
        end SALESORDERITEMID,
        T.item.value('(@PRICE)[1]','money') PRICE,
        0 ITEMDISCOUNTS,
        T.item.value('(@AMOUNTTOREFUND)[1]','money') AMOUNTTOREFUND,
        T.item.value('(@TYPECODE)[1]','tinyint') TYPECODE,
        T.item.value('(@DESCRIPTION)[1]','nvarchar(100)') [DESCRIPTION],
        case
            when T.item.value('(@ITEMID)[1]','uniqueidentifier') = '00000000-0000-0000-0000-000000000000'
                then null
            else
                T.item.value('(@ITEMID)[1]','uniqueidentifier')
        end ITEMID,
        case
            when T.item.value('(@REVENUESPLITID)[1]','uniqueidentifier') = '00000000-0000-0000-0000-000000000000'
                then null
            else
                T.item.value('(@REVENUESPLITID)[1]','uniqueidentifier')
        end REVENUESPLITID,
        0 CONTRIBUTEDREVENUE,
        T.item.value('(@WILLBECANCELLED)[1]', 'bit') WILLBECANCELLED
    from @ITEMS.nodes('/ITEMS/ITEM') T(item)
    where T.item.value('(@INCLUDE)[1]','bit') = 1;

    -- If the membership was sold through Sales, we may have discounts to reverse.

    -- This must be a sum, because tickets may be applied along with a normal promotion.

    update CREDITITEMS
    set
        ITEMDISCOUNTS = coalesce(PROMOS.AMOUNT, 0)
    from @CREDITITEMS CREDITITEMS
    inner join dbo.SALESORDERITEM MEMBERSHIPITEM on MEMBERSHIPITEM.ID = CREDITITEMS.SALESORDERITEMID
    cross apply (
        select sum(AMOUNT) AMOUNT
        from dbo.SALESORDERITEMMEMBERSHIPITEMPROMOTION
        where SALESORDERITEMID = MEMBERSHIPITEM.ID
    ) PROMOS
    where MEMBERSHIPITEM.PRICE = CREDITITEMS.AMOUNTTOREFUND + PROMOS.AMOUNT;


    /******** Validate refund items and refund payments *******/

    declare @REFUNDEDITEMTOTAL money = (select sum(AMOUNTTOREFUND) from @CREDITITEMS);

    -- Error if no items were selected

    if not exists (select 1 from @CREDITITEMS) begin
        raiserror('ERR_ITEMS_NONESELECTED', 13, 1);
        return 1;
    end

    -- Error if the quantity refunding for any item is greater than the quantity available

    -- -- (original quantity - already refunded quantity - quantity we're refunding right now)

    if exists (
        select 1
        from (
            select ITEMID, count(ID) NUMREFUNDING
            from @CREDITITEMS
            group by ITEMID
        ) CREDITITEMS_GROUPED
        inner join dbo.MEMBERSHIPADDON on MEMBERSHIPADDON.ID = CREDITITEMS_GROUPED.ITEMID
        outer apply (
            select coalesce(sum(PREEXISTINGREFUNDLI.QUANTITY),0) QUANTITY
            from dbo.FINANCIALTRANSACTIONLINEITEM PREEXISTINGREFUNDLI
            inner join dbo.FINANCIALTRANSACTION REFUND on REFUND.ID = PREEXISTINGREFUNDLI.FINANCIALTRANSACTIONID
            where PREEXISTINGREFUNDLI.SOURCELINEITEMID = MEMBERSHIPADDON.REVENUESPLITID
                and REFUND.TYPECODE = 23
        ) PREEXISTINGREFUNDS
        where MEMBERSHIPADDON.QUANTITY - PREEXISTINGREFUNDS.QUANTITY - CREDITITEMS_GROUPED.NUMREFUNDING < 0
    ) or exists (
        select 1
        from @CREDITITEMS CREDITITEMS
        inner join dbo.MEMBERSHIPTRANSACTION MT on MT.ID = CREDITITEMS.ITEMID
        inner join dbo.FINANCIALTRANSACTIONLINEITEM MEMBERSHIPREFUNDLI on MEMBERSHIPREFUNDLI.SOURCELINEITEMID = MT.REVENUESPLITID
        inner join dbo.FINANCIALTRANSACTION REFUND on REFUND.ID = MEMBERSHIPREFUNDLI.FINANCIALTRANSACTIONID
        where REFUND.TYPECODE = 23
    )
    begin
        raiserror('ERR_ITEMS_EXCEEDSQUANTITYAVAILABLE', 13, 1);
        return 1;
    end

    -- Error if the amount refunding is less than zero for any of the items

    if exists (select 1 from @CREDITITEMS where AMOUNTTOREFUND < 0)
    begin
        raiserror('ERR_ITEMS_NEGATIVEAMOUNT', 13, 1);
        return 1;
    end

    -- Error if the payments do not add up to the refund total

    if coalesce((select sum(T.item.value('(@AMOUNTREFUNDING)[1]','money')) from @REFUNDMETHODS.nodes('/REFUNDMETHODS/ITEM') T(item)),0) <> @REFUNDEDITEMTOTAL
    begin
        raiserror('ERR_REFUNDMETHODS_DIFFERENTFROMTOTALAMOUNT', 13, 1);
        return 1;
    end

    -- Error if the refund amount is greater than the total available to refund

    if @REFUNDEDITEMTOTAL > @TOTAL
    begin
        raiserror('ERR_REFUNDAMOUNT_EXCEEDSLIMIT', 13, 1);
        return 1;
    end

    -- Error if there are any later membership transactions

    if @MEMBERSHIPTRANSACTIONID <> (
        select top 1 ID
        from dbo.MEMBERSHIPTRANSACTION
        where MEMBERSHIPID = @MEMBERSHIPID
        order by DATEADDED desc
    )
    begin
        raiserror('ERR_ITEMS_MEMBERSHIPREFUND_HASNEWERMEMBERSHIPTRANSACTIONS', 13, 1);
        return 1;
    end

    -- Error if there are memberships with contributed revenue that are not being fully refunded

    if exists (
        select 1 from @CREDITITEMS MEMBERSHIPITEM
        inner join dbo.MEMBERSHIPTRANSACTION REFUNDINGTRANSACTION on REFUNDINGTRANSACTION.ID = MEMBERSHIPITEM.ITEMID
        inner join dbo.FINANCIALTRANSACTIONLINEITEM MEMBERSHIPLI on MEMBERSHIPLI.ID = REFUNDINGTRANSACTION.REVENUESPLITID
        inner join dbo.FINANCIALTRANSACTIONLINEITEM CONTRIBUTEDLI on CONTRIBUTEDLI.SOURCELINEITEMID = MEMBERSHIPLI.ID
        inner join dbo.REVENUESPLIT_EXT EXT
            on (EXT.ID = CONTRIBUTEDLI.ID and EXT.TYPECODE = 0 and EXT.APPLICATIONCODE = 0 and CONTRIBUTEDLI.TYPECODE = 0)  -- Standard donation line item whose source is the membership line item

        group by MEMBERSHIPLI.ID, MEMBERSHIPLI.BASEAMOUNT, MEMBERSHIPITEM.AMOUNTTOREFUND, MEMBERSHIPITEM.ITEMDISCOUNTS
        having MEMBERSHIPLI.BASEAMOUNT + sum(CONTRIBUTEDLI.BASEAMOUNT) <> MEMBERSHIPITEM.AMOUNTTOREFUND + MEMBERSHIPITEM.ITEMDISCOUNTS
    )
    begin
        raiserror('ERR_ITEMS_MEMBERSHIPPARTIALREFUND_HASCONTRIBUTEDREVENUE', 13, 1);
        return 1;
    end

    -- If there are memberships with add-ons that are also on this transaction and the addons are not all being refunded

    if exists (
        select 1 from @CREDITITEMS MEMBERSHIPITEM
        inner join dbo.MEMBERSHIPTRANSACTION REFUNDINGTRANSACTION on REFUNDINGTRANSACTION.ID = MEMBERSHIPITEM.ITEMID
        inner join dbo.MEMBERSHIPADDON ADDONSINTRANSACTION on ADDONSINTRANSACTION.MEMBERSHIPTRANSACTIONID = REFUNDINGTRANSACTION.ID
        where 
            MEMBERSHIPITEM.WILLBECANCELLED = 1
            and (select count(*) from @CREDITITEMS where ITEMID = ADDONSINTRANSACTION.ID) <> (ADDONSINTRANSACTION.QUANTITY - ADDONSINTRANSACTION.NUMCANCELLED)
    )
    begin
        raiserror('BBERR_ITEMS_MEMBERSHIPCANCEL_NOTREFUNDINGADDONS', 13, 1);
        return 1;
    end

    -- Find contributed revenue:

    -- 1. Find the contributory line items (donation line items whose sourcelineitemid is the revenuesplitid of the membership we're trying to refund).

    -- 2. For discounted memberships sold through Sales, prorate the discount across the contributory line items and subtract it from the membership line item.

    -- 3. Subtract the contributory amount from the amount to refund of the membership item.


    declare @CONTRIBUTEDREVENUE table (
        CONTRIBUTEDLINEITEMID uniqueidentifier,
        SOURCELINEITEMID uniqueidentifier,
        TYPECODE tinyint,
        [DESCRIPTION] nvarchar(200),
        ORIGINALAMOUNT money,
        DISCOUNTS money
    );

    -- Find contributed revenue.

    insert into @CONTRIBUTEDREVENUE
    select
        CONTRIBUTEDLI.ID CONTRIBUTEDLINEITEMID,
        CONTRIBUTEDLI.SOURCELINEITEMID,
        2 TYPECODE,
        CONTRIBUTEDLI.[DESCRIPTION],
        CONTRIBUTEDLI.BASEAMOUNT ORIGINALAMOUNT,
        null DISCOUNTS
    from @CREDITITEMS CREDITITEMS
    inner join dbo.FINANCIALTRANSACTIONLINEITEM CONTRIBUTEDLI on CONTRIBUTEDLI.SOURCELINEITEMID = CREDITITEMS.REVENUESPLITID
    inner join dbo.REVENUESPLIT_EXT CONTRIBUTEDLI_EXT on CONTRIBUTEDLI_EXT.ID = CONTRIBUTEDLI.ID
    where
        CONTRIBUTEDLI_EXT.TYPECODE = 0  -- Gift

        and CONTRIBUTEDLI_EXT.APPLICATIONCODE = 0  -- Application type is "Donation" (not "Membership", even though ER's contributed revenue applies to "Event Registration")

        and CONTRIBUTEDLI.TYPECODE = 0;  -- Standard


    if @@ROWCOUNT > 0
    begin
        -- Set the contributed amount on items with partially contributed revenue.

        update @CREDITITEMS
        set CONTRIBUTEDREVENUE = coalesce((select sum(ORIGINALAMOUNT) from @CONTRIBUTEDREVENUE where SOURCELINEITEMID = CI.REVENUESPLITID),0)
        from @CREDITITEMS CI;

        -- Prorate discounts across contributed items.

        -- This could be more efficient (when discount is more than contributed amount, no proration is necessary, because the full contributory amount is discounted), but that's not important.

        update @CONTRIBUTEDREVENUE
        set DISCOUNTS = PRORATEDAMOUNTS.AMOUNT
        from @CREDITITEMS CREDITITEMS
        cross apply dbo.UFN_SPLITS_PRORATEAMOUNTS(
            CREDITITEMS.CONTRIBUTEDREVENUE,
            (select case when CREDITITEMS.ITEMDISCOUNTS > CREDITITEMS.CONTRIBUTEDREVENUE then CREDITITEMS.CONTRIBUTEDREVENUE else CREDITITEMS.ITEMDISCOUNTS end),
            2,
            (
                select CONTRIBUTEDREVENUE.CONTRIBUTEDLINEITEMID ID, CONTRIBUTEDREVENUE.ORIGINALAMOUNT AMOUNT
                from @CONTRIBUTEDREVENUE CONTRIBUTEDREVENUE
                where CONTRIBUTEDREVENUE.SOURCELINEITEMID = CREDITITEMS.REVENUESPLITID
                for xml raw('ITEM'),type,elements,root('AMOUNTSTOPRORATE'),binary base64
            )
        ) PRORATEDAMOUNTS
        where PRORATEDAMOUNTS.ID = CONTRIBUTEDLINEITEMID;

        -- Reduce the discount amount for partially contributed items (since the discounts have been moved to the contributory line items).

        -- Also reduce the refund amount to account for discounts in excess of the contributory revenue.

        update @CREDITITEMS
        set
            AMOUNTTOREFUND = case when CONTRIBUTEDREVENUE > ITEMDISCOUNTS then PRICE - CONTRIBUTEDREVENUE else AMOUNTTOREFUND end,
            ITEMDISCOUNTS = case when CONTRIBUTEDREVENUE > ITEMDISCOUNTS then 0 else ITEMDISCOUNTS - CONTRIBUTEDREVENUE end
        where CONTRIBUTEDREVENUE > 0;

        -- At this point:

        -- -- 1. The ITEMDISCOUNTS of a partially contributed credit item will be max(0, discounted amount - contributed amount)

        -- -- 2. The AMOUNTTOREFUND of a partially contributed credit item will be PRICE - max(contributed amount, discounted amount).

        -- Otherwise the refund GL will not be generated correctly.

    end


    declare @INS table
    (
        ID uniqueidentifier,
        CREDITID uniqueidentifier,
        SALESORDERITEMID uniqueidentifier,
        PRICE money,
        DISCOUNTS money,
        TYPECODE tinyint,
        [DESCRIPTION] nvarchar(700),
        SOURCELINEITEMID uniqueidentifier,
        REFUNDAMOUNT money,
        UNITVALUE money
    );

    -- This may not be the most intuitive data set-up in the world (particularly the roles that UNITVALUE and DISCOUNTS play in GL creation).

    -- However, the alternatives are breaking backward-compatibility of refund data or converting all existing refunds into something that makes more sense,

    -- neither of which we have time to do right now.

    insert into @INS
    select
        ID,
        @ID,
        SALESORDERITEMID,
        PRICE,
        ITEMDISCOUNTS,
        TYPECODE,
        [DESCRIPTION],
        REVENUESPLITID,
        AMOUNTTOREFUND,
        AMOUNTTOREFUND + CONTRIBUTEDREVENUE + ITEMDISCOUNTS  -- GL is wack.

    from @CREDITITEMS

    union all

    -- Contributory portions of memberships

    select
        newid(),
        @ID,
        null SALESORDERITEMID,
        ORIGINALAMOUNT PRICE,
        DISCOUNTS,
        TYPECODE,
        [DESCRIPTION],
        CONTRIBUTEDLINEITEMID,
        ORIGINALAMOUNT - DISCOUNTS REFUNDAMOUNT,
        ORIGINALAMOUNT UNITVALUE
    from @CONTRIBUTEDREVENUE
    where ORIGINALAMOUNT - DISCOUNTS <> 0;


    begin try
        -- Bug #167166 - MDC

        -- Weird that the @TRANSACTIONDATE is being inserted into a datetimeoffset column. Always has offset 0.

        declare @TRANSACTIONDATE datetime =  dbo.UFN_TIMEZONEENTRY_GETSYSTEMDEFAULTDATEFROMUTC(getutcdate());

        declare @CURRENCYID uniqueidentifier = (select ID from dbo.CURRENCY where ISORGANIZATIONCURRENCY = 1);
        declare @PDACCOUNTSYSTEMID uniqueidentifier;
        declare @ALLOWGLDISTRIBUTIONS bit;

        select
            @PDACCOUNTSYSTEMID = ID,
            @ALLOWGLDISTRIBUTIONS = ALLOWGLDISTRIBUTIONS
        from
            dbo.UFN_PDACCOUNTSYSTEM_DEFAULTORSYSTEM();

        declare @CONSTITUENTID uniqueidentifier = (
            select top 1 FT.CONSTITUENTID
            from dbo.MEMBERSHIPTRANSACTION MT
            inner join dbo.FINANCIALTRANSACTIONLINEITEM FTLI on FTLI.ID = MT.REVENUESPLITID
            inner join dbo.FINANCIALTRANSACTION FT on FT.ID = FTLI.FINANCIALTRANSACTIONID
            where MT.MEMBERSHIPID = @MEMBERSHIPID
            order by MT.TRANSACTIONDATE, MT.DATEADDED desc
        );

        -- Get sales order ID for refunded item if it is in a sales order

        declare @ORDERID uniqueidentifier = (
            select top 1 SALESORDER.ID
            from @CREDITITEMS CREDITITEMS
            inner join dbo.SALESORDERITEM on SALESORDERITEM.ID = CREDITITEMS.SALESORDERITEMID
            inner join dbo.SALESORDER on SALESORDER.ID = SALESORDERITEM.SALESORDERID
        );


        insert into dbo.FINANCIALTRANSACTION
        (
            ID,
            TRANSACTIONAMOUNT,
            BASEAMOUNT,
            ORGAMOUNT,
            TRANSACTIONCURRENCYID,
            TYPECODE,
            PDACCOUNTSYSTEMID,
            [DESCRIPTION],
            [DATE],
            POSTDATE,
            POSTSTATUSCODE,
            PARENTID,
            APPUSERID,
            CONSTITUENTID,
            DATEADDED, DATECHANGED, ADDEDBYID, CHANGEDBYID
        )
        values
        (
            @ID,
            @REFUNDEDITEMTOTAL,
            @REFUNDEDITEMTOTAL,
            @REFUNDEDITEMTOTAL,
            @CURRENCYID,
            23, --Refund

            @PDACCOUNTSYSTEMID,
            @COMMENT,
            @TRANSACTIONDATE,
            case when @ALLOWGLDISTRIBUTIONS = 1 and isnull(@POSTSTATUSCODE, 1) != 3 then cast(@TRANSACTIONDATE as date) else null end,
            case when @ALLOWGLDISTRIBUTIONS = 1 and isnull(@POSTSTATUSCODE, 1) != 3 then 1 else 3 end,
            (select REVENUEID from dbo.SALESORDER where ID = @ORDERID),
            @CURRENTAPPUSERID,
            @CONSTITUENTID,
            @CURRENTDATE, @CURRENTDATE, @CHANGEAGENTID, @CHANGEAGENTID
        );

        insert into dbo.CREDIT_EXT
        (
            ID,
            SALESORDERID,
            CREDITREASONCODEID,
            DATEADDED, DATECHANGED, ADDEDBYID, CHANGEDBYID
        )
        values
        (
            @ID,
            @ORDERID,
            case @CREDITREASONCODEID
                when '00000000-0000-0000-0000-000000000000' then null
                else @CREDITREASONCODEID
            end,
            @CURRENTDATE, @CURRENTDATE, @CHANGEAGENTID, @CHANGEAGENTID
        );

        exec dbo.USP_CREDIT_ADDCREDITPAYMENTS @ID, @REFUNDMETHODS, @CHANGEAGENTID, @CURRENTAPPUSERID, @ADDRESSID;

        insert into dbo.FINANCIALTRANSACTIONLINEITEM
        (
            ID,
            FINANCIALTRANSACTIONID,
            UNITVALUE,
            QUANTITY,
            [DESCRIPTION],
            TRANSACTIONAMOUNT,
            BASEAMOUNT,
            ORGAMOUNT,
            POSTDATE,
            POSTSTATUSCODE,
            TYPECODE,
            SOURCELINEITEMID,
            ADDEDBYID, CHANGEDBYID, DATEADDED, DATECHANGED
        )
        select
            I.ID,
            I.CREDITID,
            I.UNITVALUE,
            1 QUANTITY,
            I.[DESCRIPTION],
            I.REFUNDAMOUNT,
            I.REFUNDAMOUNT,
            I.REFUNDAMOUNT,
            FT.POSTDATE,
            FT.POSTSTATUSCODE,
            case I.TYPECODE
                when 3 then 7 --Fee

                when 4 then 8 --Tax

                when 5 then 5 --Discount

                else 0
            end,
            I.SOURCELINEITEMID,
            @CHANGEAGENTID, @CHANGEAGENTID, @CURRENTDATE, @CURRENTDATE
        from @INS I
        inner join dbo.FINANCIALTRANSACTION FT on FT.ID = I.CREDITID;

        insert into dbo.CREDITITEM_EXT
        (
            ID,
            CREDITID,
            DISCOUNTS,
            FEES,
            GROUPID,
            GROUPTYPECODE,
            SALESORDERITEMID,
            TYPECODE,
            ADDEDBYID, CHANGEDBYID, DATEADDED, DATECHANGED
        )
        select
            ID,
            CREDITID,
            DISCOUNTS,
            0 FEES,
            null GROUPID,
            0 GROUPTYPECODE,
            SALESORDERITEMID,
            TYPECODE,
            @CHANGEAGENTID, @CHANGEAGENTID, @CURRENTDATE, @CURRENTDATE
        from @INS;

        -- Update ticket applied to membership fields for tickets that were applied to a fully refunded membership.

        update dbo.TICKET
        set
            TICKET.APPLIEDTOMEMBERSHIP = 0,
            TICKET.APPLIEDTOMEMBERSHIPSALESORDERID = null
        from dbo.TICKET
        inner join dbo.SALESORDERITEMMEMBERSHIPITEMPROMOTIONAPPLIEDITEMTICKET APPLIEDTICKETS on TICKET.ID = APPLIEDTICKETS.TICKETID
        inner join dbo.SALESORDERITEMMEMBERSHIPITEMPROMOTIONAPPLIEDITEM APPLIEDITEM on APPLIEDTICKETS.SALESORDERITEMMEMBERSHIPITEMPROMOTIONAPPLIEDITEMID = APPLIEDITEM.ID
        inner join dbo.SALESORDERITEMMEMBERSHIPITEMPROMOTION PROMOTION on APPLIEDITEM.SALESORDERITEMMEMBERSHIPITEMPROMOTIONID = PROMOTION.ID
        inner join @CREDITITEMS CREDITITEMS on PROMOTION.SALESORDERITEMID = CREDITITEMS.SALESORDERITEMID
        where CREDITITEMS.AMOUNTTOREFUND = CREDITITEMS.PRICE - CREDITITEMS.ITEMDISCOUNTS
            or CREDITITEMS.CONTRIBUTEDREVENUE > 0;  -- hacky; memberships with contributed revenue must be fully refunded, so we know to unmark the ticket as applied.



        --Add credit membership items

        insert into dbo.CREDITITEMMEMBERSHIP
        (
            ID,
            MEMBERSHIPID,
            MEMBERSHIPPROGRAMID,
            MEMBERSHIPPROGRAMNAME,
            MEMBERSHIPLEVELID,
            MEMBERSHIPLEVELTERMID,
            MEMBERSHIPLEVELTYPECODEID,
            NUMBEROFCHILDREN,
            COMMENTS,
            ISGIFT,
            GIVENBYID,
            EXPIRATIONDATE,
            MEMBERS,
            ACTIONCODE,
            ADDEDBYID,
            CHANGEDBYID,
            DATEADDED,
            DATECHANGED,
            WILLBECANCELLED
        )
        select
            CREDITITEMS.ID,
            MEMBERSHIPTRANSACTION.MEMBERSHIPID,
            MEMBERSHIP.MEMBERSHIPPROGRAMID,
            (select NAME from dbo.MEMBERSHIPPROGRAM where ID = MEMBERSHIP.MEMBERSHIPPROGRAMID),
            MEMBERSHIPTRANSACTION.MEMBERSHIPLEVELID,
            MEMBERSHIPTRANSACTION.MEMBERSHIPLEVELTERMID,
            MEMBERSHIPTRANSACTION.MEMBERSHIPLEVELTYPECODEID,
            MEMBERSHIPTRANSACTION.NUMBEROFCHILDREN,
            MEMBERSHIPTRANSACTION.COMMENTS,
            MEMBERSHIPTRANSACTION.ISGIFT,
            MEMBERSHIPTRANSACTION.DONORID,
            MEMBERSHIPTRANSACTION.EXPIRATIONDATE,
            (
                select dbo.UFN_CONSTITUENT_BUILDNAME(MEMBER.CONSTITUENTID) MEMBERNAME
                from dbo.MEMBER
                where MEMBER.MEMBERSHIPID = MEMBERSHIP.ID
                for xml raw('ITEM'),type,elements,root('MEMBERSHIPMEMBERS'),binary base64
            ),
            MEMBERSHIPTRANSACTION.ACTIONCODE,
            @CHANGEAGENTID,
            @CHANGEAGENTID,
            @CURRENTDATE,
            @CURRENTDATE,
            CREDITITEMS.WILLBECANCELLED
        from dbo.MEMBERSHIPTRANSACTION
        inner join dbo.MEMBERSHIP on MEMBERSHIPTRANSACTION.MEMBERSHIPID = MEMBERSHIP.ID
        inner join @CREDITITEMS CREDITITEMS on CREDITITEMS.ITEMID = MEMBERSHIPTRANSACTION.ID
        where MEMBERSHIPTRANSACTION.ID = @MEMBERSHIPTRANSACTIONID;

        -- Add membership promotions (if the full amount paid for the membership is being returned)

        insert into dbo.CREDITITEMMEMBERSHIPITEMPROMOTION
        (
            CREDITITEMID,
            SALESORDERITEMMEMBERSHIPITEMPROMOTIONID,
            AMOUNT,
            ADDEDBYID,
            CHANGEDBYID,
            DATEADDED,
            DATECHANGED
        )
        select distinct
            REFUNDEDMEMBERSHIPLI.ID,
            SALESORDERITEMMEMBERSHIPITEMPROMOTION.ID,
            SALESORDERITEMMEMBERSHIPITEMPROMOTION.AMOUNT, --Right now, the entire promotion has to be returned

            @CHANGEAGENTID,
            @CHANGEAGENTID,
            @CURRENTDATE,
            @CURRENTDATE
        from dbo.SALESORDERITEMMEMBERSHIPITEMPROMOTION
        inner join dbo.CREDITITEM_EXT as EXT on EXT.SALESORDERITEMID = SALESORDERITEMMEMBERSHIPITEMPROMOTION.SALESORDERITEMID
        inner join dbo.FINANCIALTRANSACTIONLINEITEM as REFUNDEDMEMBERSHIPLI on REFUNDEDMEMBERSHIPLI.ID = EXT.ID
        where REFUNDEDMEMBERSHIPLI.FINANCIALTRANSACTIONID = @ID
            and (
                EXT.DISCOUNTS > 0  -- Unfortunately, this is 0 when the discount has moved to contributed revenue.

                or exists (        -- Instead, link to the original thing we were trying to refund to determine whether we reversed discounts.

                    select 1
                    from @CONTRIBUTEDREVENUE CR
                    where CR.SOURCELINEITEMID = REFUNDEDMEMBERSHIPLI.SOURCELINEITEMID
                        and DISCOUNTS > 0
                )
            );


        if @ALLOWGLDISTRIBUTIONS = 1 and isnull(@POSTSTATUSCODE, 1) != 3 begin
            exec dbo.USP_REFUND_CREATEGLDISTRIBUTION @ID, @CHANGEAGENTID, @CURRENTDATE;
            exec dbo.USP_REFUND_CREATEDISCOUNTGLDISTRIBUTION @ID, @CHANGEAGENTID, @CURRENTDATE;
        end

        -- (Updating the membership transaction, members etc. now occurs after the refund and depends on user interaction).


    end try
    begin catch
        exec dbo.USP_RAISE_ERROR;
        return 1;
    end catch

    return 0;
end