In response to a discussion on a thread about gift card reporting I engaged in a bit of intellectual curiosity and wrote a query to display gift card data knowing I would need to write one for the aquarium at some point in the future based on the limitations of the canned reports. That bill has now come due and below is a more polished (but not finalized) version of the same query as driven by the needs of our organization. If Tessitura ever enables gift card recharging functionality it will support that as well with no changes needed.
So far we only have 2.9K gift cards in the system, but have over 1.8 million payment records and the query runs in under a second for all the different options.
Here is a sample of the results:
Here is the query (requires SQL Server 2012 or newer):
Declare@StartTXDate date = '12/2/2020',@EndTXDate datetime = '12/30/2020',@ResultType tinyint = 0--0 = All--1 = GC with Balance--2 = Unused--3 = Partial Use--4 = No Balance--97 = Historical--98 = Use Date--99 = New CardsDeclare@rStartTXDate date ,@rEndTXDate datetime ,@rResultType tinyint set @rStartTXDate = isnull(@StartTXDate , '01/01/1900')Set @rEndTXDate = dbo.lfn_MBA_EOD(isnull(@EndTXDate , dateadd(Day,-1,Getdate())))Set @rResultType = ISNULL (@ResultType,1)Set ArithAbort Onset nocount onSET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDdeclare @tblGC table(GcNo varchar(16),PaymentNo int INDEX idxPaymentNo CLUSTERED,TXDate datetime INDEX idxTXDate NONCLUSTERED,CustomerNo int,transaction_no int,PurchAmt money,RedemAmt money,SeqNo intprimary key (Paymentno, SeqNo))insert into @tblGC Select g.gc_no , p.payment_no, pmt_dt as TXDate , p.customer_no , transaction_no ,abs(iif( pmt_amt < 0, pmt_amt ,0)) as Purchase ,abs(iif( pmt_amt > 0, pmt_amt ,0)) as Redemption ,ROW_NUMBER () over (partition by p.Payment_no order by transaction_No) as SeqNofrom T_PAYMENT as P inner join T_GC as G on G.payment_no = p.payment_no where (p.pmt_dt <= @rEndTXDate and @rResultType >= 90)or @rResultType < 90Select f.*,isnull( Stuff((Select ' ~ ' + FORMAT( TXDate, 'G', 'en-US' ) + ' - ' + FORMAT( PurchAmt, 'C2', 'en-US' ) from @tblGC where PaymentNo = f.PaymentNo and PurchAmt > 0 order by SeqNo FOR XML PATH('')), 1, 3,''),'' ) as ChargeHistory,isnull( Stuff((Select ' ~ ' + FORMAT( TXDate, 'G', 'en-US' ) + ' - ' + FORMAT( RedemAmt, 'C2', 'en-US' ) from @tblGC where PaymentNo = f.PaymentNo and RedemAmt > 0 order by SeqNo FOR XML PATH('')), 1, 3,''),'' ) as UsageHistory, isnull(pc.fname,'') as PurchFirstName , isnull(pc.lname,'') as PurchLastName , isnull(LC.fname,'') as LastUseFirstName , isnull(Lc.lname,'') as LastUseLastName from(Select d.*,isnull((Select CustomerNo from @tblGC as G where g.SeqNo = d.MaxSeq and g.PaymentNo = d.PaymentNo and RedemAmt > 0 ),0) as LastUseCustomer ,PurchAmt - RedemAmt as Balance from (Select PaymentNo , GcNo ,Max(iif(SeqNo = 1 and PurchAmt > 0,CustomerNo,0)) as Purchaser , Max(SeqNo) MaxSeq ,Max(iif(SeqNo = 1 and PurchAmt > 0,purchAmt,0)) as OriginalAmount ,Sum(PurchAmt) as PurchAmt , sum(RedemAmt) as RedemAmt ,min(iif(SeqNo = 1,TXDate,null)) as PurchaseDate, Max(TXDate) as LastTX ,Sum(iif( RedemAmt > 0,1,0)) as UseCount ,Max(IIf(RedemAmt > 0, TXDate, null)) as LastUsed from @tblGC group by PaymentNo , GcNo ) as D) as Fleft join T_CUSTOMER as PC on f.Purchaser = pc.customer_no left join T_CUSTOMER as LC on f.LastUseCustomer = lc.customer_no Where (@rResultType = 0) or (@rResultType = 1 and Balance <> 0) or (@rResultType = 2 and RedemAmt = 0) or (@rResultType = 3 and Balance !=0 and RedemAmt > 0) or (@rResultType = 4 and Balance = 0) or (@rResultType = 97 and LastTX <= @rEndTXDate ) or (@rResultType = 98 and LastUsed between @rStartTXDate and @rEndTXDate ) or (@rResultType = 99 and PurchaseDate between @rStartTXDate and @rEndTXDate )order by PurchaseDate , GcNo
The function lfn_MBA_EOD just calculates the end of day to save me from having to repeat the same logic over and over again:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LFN_MBA_EOD]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))drop function [dbo].[LFN_MBA_EOD]GoCREATE FUNCTION [dbo].[LFN_MBA_EOD](@DateValue datetime)RETURNS datetimeASBEGIN RETURN dateadd(ms,-3,cast(dateadd(day,1,cast(isnull(@DateValue,getdate()) as date)) as datetime)) END
I love this! If I'm understanding this code correctly, this would allow us to view historic gift card purchases (even if the card has no balance remaining), correct?
Thank you for sharing!
Yes, the @ResultType variable controls what data to display.
0 - All GC at their current status
1 - Shows any GC with a balance
2 - Any GC that have 0 usage
3 - Any that have been used but still have a balance
4- All with no balance left
97 - Displays what the activity on the GC was up until the variable specified with the @EndTxDate variable. This allows backwards looking at GC's to see what the balance and activity may have been on a given date
98 - Pulls GC used in a given date range
99 - Pulls any new GC for the given date range
Thanks Ronald. That's great. I developed a report (one of my first) to look at Gift Certs and their redemption which is a lot more basic than yours. I'm keep to give yours a spin.
I just want to say thank you. I had this exact question from my CFO re: auditor data on Gift Vouchers and this is a New Years wonder. Works like a dream.
Thanks, I tried to cover as many use cases as possible.
One thing I thought about doing was that is not in the results was adding orders to the report, but that would have added T_Transaction to the query and didn't want to add the overhead to the query. If that proves to be an issue, worst case I can always put a link to a detail report on the report when they click on the GC number.
It would be nice if the Order_No was on in T_Payment as well since there are a number of payment based queries where order number was needed and I had to loop into t_Trasnaction just to get the order number.
Is there any reason to believe that the much-discussed eventual joining of orders and contributions together in some future version of Tessitura would include solving that particular issue? Because I have wondered if there was a way to get order_no on T_PAYMENT myself, too.
No idea, but if they are going to be taking that step it would be nice if they also started treating gift cards like a product and a payment method instead of just as a payment method as well as storing detail in T_Transaction not summary data.
Fully on board with that, too. The fact that they exist as receipts is quite weird, all things considered. I get where that idea came from, but as time goes on, it makes less and less sense.