From bd02774db9fa9305101df2479aaac3002478fbcf Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 29 Nov 2015 13:58:10 -0600 Subject: [PATCH] GiftCards are purchable --- db/manifest | 3 +- db/up/purchasable_gift_cards.sql | 24 ++ ruby/lib/jam_ruby.rb | 2 + ruby/lib/jam_ruby/models/affiliate_partner.rb | 18 +- ruby/lib/jam_ruby/models/anonymous_user.rb | 5 + ruby/lib/jam_ruby/models/gift_card.rb | 13 +- .../lib/jam_ruby/models/gift_card_purchase.rb | 17 ++ ruby/lib/jam_ruby/models/gift_card_type.rb | 66 +++++ ruby/lib/jam_ruby/models/jam_track.rb | 3 + .../models/recurly_transaction_web_hook.rb | 48 +--- ruby/lib/jam_ruby/models/sale.rb | 188 +++++++------- ruby/lib/jam_ruby/models/sale_line_item.rb | 14 +- ruby/lib/jam_ruby/models/shopping_cart.rb | 42 ++- ruby/lib/jam_ruby/models/user.rb | 7 + ruby/spec/factories.rb | 11 +- .../recurly_transaction_web_hook_spec.rb | 4 +- .../jam_ruby/models/sale_line_item_spec.rb | 18 +- ruby/spec/jam_ruby/models/sale_spec.rb | 102 ++++++-- .../jam_ruby/models/shopping_cart_spec.rb | 34 ++- ruby/spec/spec_helper.rb | 4 +- .../assets/javascripts/checkout_complete.js | 10 + web/app/assets/javascripts/checkout_order.js | 22 +- web/app/assets/javascripts/jam_rest.js | 12 + .../landing/GiftCardLandingPage.js.jsx.coffee | 93 +++++++ .../stores/UserStore.js.coffee | 4 +- .../client/checkout_complete.css.scss | 4 + .../landings/individual_jamtrack.css.scss | 35 ++- web/app/controllers/api_recurly_controller.rb | 11 +- .../api_shopping_carts_controller.rb | 28 ++ web/app/controllers/landings_controller.rb | 23 ++ .../api_shopping_carts/add_gift_card.rabl | 5 + .../clients/_checkout_complete.html.slim | 2 + .../views/clients/_checkout_order.html.slim | 2 +- .../views/clients/_checkout_payment.html.slim | 3 +- .../views/clients/_shopping_cart.html.haml | 2 +- .../views/landings/buy_gift_card.html.slim | 24 ++ web/config/routes.rb | 2 + .../controllers/api_users_controller_spec.rb | 10 +- web/spec/factories.rb | 2 +- web/spec/features/checkout_spec.rb | 2 - web/spec/features/gift_card_landing_spec.rb | 245 ++++++++++++++++++ web/spec/features/redeem_giftcard_spec.rb | 22 +- web/spec/managers/user_manager_spec.rb | 2 +- 43 files changed, 949 insertions(+), 239 deletions(-) create mode 100644 db/up/purchasable_gift_cards.sql create mode 100644 ruby/lib/jam_ruby/models/gift_card_purchase.rb create mode 100644 ruby/lib/jam_ruby/models/gift_card_type.rb create mode 100644 web/app/assets/javascripts/react-components/landing/GiftCardLandingPage.js.jsx.coffee create mode 100644 web/app/views/api_shopping_carts/add_gift_card.rabl create mode 100644 web/app/views/landings/buy_gift_card.html.slim create mode 100644 web/spec/features/gift_card_landing_spec.rb diff --git a/db/manifest b/db/manifest index 825013c73..40053da47 100755 --- a/db/manifest +++ b/db/manifest @@ -312,4 +312,5 @@ track_downloads.sql jam_track_lang_idx.sql giftcard.sql add_description_to_crash_dumps.sql -acappella.sql \ No newline at end of file +acappella.sql +purchasable_gift_cards.sql \ No newline at end of file diff --git a/db/up/purchasable_gift_cards.sql b/db/up/purchasable_gift_cards.sql new file mode 100644 index 000000000..9ef8d29fa --- /dev/null +++ b/db/up/purchasable_gift_cards.sql @@ -0,0 +1,24 @@ + + +CREATE TABLE gift_card_types ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + card_type VARCHAR(64) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +INSERT INTO gift_card_types (id, card_type) VALUES ('jam_tracks_5', 'jam_tracks_5'); +INSERT INTO gift_card_types (id, card_type) VALUES ('jam_tracks_10', 'jam_tracks_10'); + +CREATE TABLE gift_card_purchases ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE SET NULL, + gift_card_type_id VARCHAR(64) REFERENCES gift_card_types(id) ON DELETE SET NULL, + recurly_adjustment_uuid VARCHAR(500), + recurly_adjustment_credit_uuid VARCHAR(500), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + + +ALTER TABLE sale_line_items ADD COLUMN gift_card_purchase_id VARCHAR(64) REFERENCES gift_card_purchases(id); diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index 1cbb0cdab..b3772ec14 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -254,6 +254,8 @@ require "jam_ruby/models/band_search" require "jam_ruby/import/tency_stem_mapping" require "jam_ruby/models/jam_track_search" require "jam_ruby/models/gift_card" +require "jam_ruby/models/gift_card_purchase" +require "jam_ruby/models/gift_card_type" include Jampb diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index db41ddd12..e88aed9c7 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -119,18 +119,16 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base end def should_attribute_sale?(shopping_cart) - if shopping_cart.is_jam_track? - if created_within_affiliate_window(shopping_cart.user, Time.now) - product_info = shopping_cart.product_info - # subtract the total quantity from the freebie quantity, to see how much we should attribute to them - real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i - {fee_in_cents: (1.99 * 100 * real_quantity * rate.to_f).round} - else - false - end + + if created_within_affiliate_window(shopping_cart.user, Time.now) + product_info = shopping_cart.product_info + # subtract the total quantity from the freebie quantity, to see how much we should attribute to them + real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i + {fee_in_cents: (product_info[:price] * 100 * real_quantity * rate.to_f).round} else - raise 'shopping cart type not implemented yet' + false end + end def cumulative_earnings_in_dollars diff --git a/ruby/lib/jam_ruby/models/anonymous_user.rb b/ruby/lib/jam_ruby/models/anonymous_user.rb index 9d711b349..55c788e90 100644 --- a/ruby/lib/jam_ruby/models/anonymous_user.rb +++ b/ruby/lib/jam_ruby/models/anonymous_user.rb @@ -15,10 +15,15 @@ module JamRuby ShoppingCart.where(anonymous_user_id: @id).order('created_at DESC') end + def destroy_all_shopping_carts ShoppingCart.destroy_all(anonymous_user_id: @id) end + def destroy_jam_track_shopping_carts + ShoppingCart.destroy_all(anonymous_user_id: @id, cart_type: JamTrack::PRODUCT_TYPE) + end + def admin false end diff --git a/ruby/lib/jam_ruby/models/gift_card.rb b/ruby/lib/jam_ruby/models/gift_card.rb index 8a8469a27..0dc5f94e8 100644 --- a/ruby/lib/jam_ruby/models/gift_card.rb +++ b/ruby/lib/jam_ruby/models/gift_card.rb @@ -1,14 +1,15 @@ +# represents the gift card you hold in your hand module JamRuby class GiftCard < ActiveRecord::Base @@log = Logging.logger[GiftCard] + JAM_TRACKS_5 = 'jam_tracks_5' JAM_TRACKS_10 = 'jam_tracks_10' - JAM_TRACKS_20 = 'jam_tracks_20' CARD_TYPES = [ - JAM_TRACKS_10, - JAM_TRACKS_20 + JAM_TRACKS_5, + JAM_TRACKS_10 ] @@ -21,10 +22,10 @@ module JamRuby def check_gifted if user && user_id_changed? - if card_type == JAM_TRACKS_10 + if card_type == JAM_TRACKS_5 + user.gifted_jamtracks += 5 + elsif card_type == JAM_TRACKS_10 user.gifted_jamtracks += 10 - elsif card_type == JAM_TRACKS_20 - user.gifted_jamtracks += 20 else raise "unknown card type #{card_type}" end diff --git a/ruby/lib/jam_ruby/models/gift_card_purchase.rb b/ruby/lib/jam_ruby/models/gift_card_purchase.rb new file mode 100644 index 000000000..0cfb00807 --- /dev/null +++ b/ruby/lib/jam_ruby/models/gift_card_purchase.rb @@ -0,0 +1,17 @@ +# reperesents the gift card you buy from the site (but physical gift card is modeled by GiftCard) +module JamRuby + class GiftCardPurchase < ActiveRecord::Base + + @@log = Logging.logger[GiftCardPurchase] + + attr_accessible :user, :gift_card_type + + def name + gift_card_type.sale_display + end + + # who purchased the card? + belongs_to :user, class_name: "JamRuby::User" + belongs_to :gift_card_type, class_name: "JamRuby::GiftCardType" + end +end diff --git a/ruby/lib/jam_ruby/models/gift_card_type.rb b/ruby/lib/jam_ruby/models/gift_card_type.rb new file mode 100644 index 000000000..e6336a9b1 --- /dev/null +++ b/ruby/lib/jam_ruby/models/gift_card_type.rb @@ -0,0 +1,66 @@ +# reperesents the gift card you buy from the site (but physical gift card is modeled by GiftCard) +module JamRuby + class GiftCardType < ActiveRecord::Base + + @@log = Logging.logger[GiftCardType] + + PRODUCT_TYPE = 'GiftCardType' + + JAM_TRACKS_5 = 'jam_tracks_5' + JAM_TRACKS_10 = 'jam_tracks_10' + CARD_TYPES = + [ + JAM_TRACKS_5, + JAM_TRACKS_10 + ] + + validates :card_type, presence: true, inclusion: {in: CARD_TYPES} + + def self.jam_track_5 + GiftCardType.find(JAM_TRACKS_5) + end + + def self.jam_track_10 + GiftCardType.find(JAM_TRACKS_10) + end + + def name + sale_display + end + + def price + if card_type == JAM_TRACKS_5 + 10.00 + elsif card_type == JAM_TRACKS_10 + 20.00 + else + raise "unknown card type #{card_type}" + end + end + + + def sale_display + if card_type == JAM_TRACKS_5 + 'JamTracks Gift Card (5)' + elsif card_type == JAM_TRACKS_10 + 'JamTracks Gift Card (10)' + else + raise "unknown card type #{card_type}" + end + end + + def plan_code + if card_type == JAM_TRACKS_5 + "jamtrack-giftcard-5" + elsif card_type == JAM_TRACKS_10 + "jamtrack-giftcard-10" + else + raise "unknown card type #{card_type}" + end + end + + def sales_region + 'Worldwide' + end + end +end diff --git a/ruby/lib/jam_ruby/models/jam_track.rb b/ruby/lib/jam_ruby/models/jam_track.rb index aea976d89..9303af65b 100644 --- a/ruby/lib/jam_ruby/models/jam_track.rb +++ b/ruby/lib/jam_ruby/models/jam_track.rb @@ -155,6 +155,9 @@ module JamRuby true end + def sale_display + "JamTrack: " + name + end def duplicate_positions? counter = {} jam_track_tracks.each do |track| diff --git a/ruby/lib/jam_ruby/models/recurly_transaction_web_hook.rb b/ruby/lib/jam_ruby/models/recurly_transaction_web_hook.rb index 9d0fc9bd4..006611f61 100644 --- a/ruby/lib/jam_ruby/models/recurly_transaction_web_hook.rb +++ b/ruby/lib/jam_ruby/models/recurly_transaction_web_hook.rb @@ -92,53 +92,15 @@ module JamRuby transaction.save! # now that we have the transaction saved, we also need to delete the jam_track_right if this is a refund, or voided - - if transaction.transaction_type == 'refund' || transaction.transaction_type == 'void' sale = Sale.find_by_recurly_invoice_id(transaction.invoice_id) - if sale && sale.is_jam_track_sale? - if sale.sale_line_items.length == 1 - if sale.recurly_total_in_cents == transaction.amount_in_cents - line_item = sale.sale_line_items[0] - jam_track = line_item.product - jam_track_right = jam_track.right_for_user(transaction.user) if jam_track - if jam_track_right - line_item.affiliate_refunded = true - line_item.affiliate_refunded_at = Time.now - line_item.save! + if sale + AdminMailer.recurly_alerts(transaction.user, { + subject: "ACTION REQUIRED: #{transaction.user.email} has refund on invoice", + body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks" + }).deliver - jam_track_right.destroy - - # associate which JamTrack we assume this is related to in this one success case - transaction.jam_track = jam_track - transaction.save! - - AdminMailer.recurly_alerts(transaction.user, { - subject: "NOTICE: #{transaction.user.email} has had JamTrack: #{jam_track.name} revoked", - body: "A #{transaction.transaction_type} event came from Recurly for sale with Recurly invoice ID #{sale.recurly_invoice_id}. We deleted their right to the track in our own database as a result." - }).deliver - else - AdminMailer.recurly_alerts(transaction.user, { - subject: "NOTICE: #{transaction.user.email} got a refund, but unable to find JamTrackRight to delete", - body: "This should just mean the user already has no rights to the JamTrackRight when the refund came in. Not a big deal, but sort of weird..." - }).deliver - end - - else - AdminMailer.recurly_alerts(transaction.user, { - subject: "ACTION REQUIRED: #{transaction.user.email} got a refund it was not for total value of a JamTrack sale", - body: "We received a #{transaction.transaction_type} notice for an amount that was not the same as the original sale. So, no action was taken in the database. sale total: #{sale.recurly_total_in_cents}, refund amount: #{transaction.amount_in_cents}" - }).deliver - end - - - else - AdminMailer.recurly_alerts(transaction.user, { - subject: "ACTION REQUIRED: #{transaction.user.email} has refund on invoice with multiple JamTracks", - body: "You will have to manually revoke any JamTrackRights in our database for the appropriate JamTracks" - }).deliver - end else AdminMailer.recurly_alerts(transaction.user, { subject: "ACTION REQUIRED: #{transaction.user.email} has refund with no correlator to sales", diff --git a/ruby/lib/jam_ruby/models/sale.rb b/ruby/lib/jam_ruby/models/sale.rb index 6e3bb7cfb..9b0fb2047 100644 --- a/ruby/lib/jam_ruby/models/sale.rb +++ b/ruby/lib/jam_ruby/models/sale.rb @@ -69,28 +69,6 @@ module JamRuby } end - def self.preview_invoice(current_user, shopping_carts) - - line_items = {jam_tracks: []} - shopping_carts_jam_tracks = [] - shopping_carts_subscriptions = [] - shopping_carts.each do |shopping_cart| - - if shopping_cart.is_jam_track? - shopping_carts_jam_tracks << shopping_cart - else - # XXX: this may have to be revisited when we actually have something other than JamTracks for puchase - shopping_carts_subscriptions << shopping_cart - end - end - - jam_track_items = preview_invoice_jam_tracks(current_user, shopping_carts_jam_tracks) - line_items[:jam_tracks] = jam_track_items if jam_track_items - - # TODO: process shopping_carts_subscriptions - - line_items - end def self.ios_purchase(current_user, jam_track, receipt) jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| @@ -105,19 +83,14 @@ module JamRuby def self.place_order(current_user, shopping_carts) sales = [] - shopping_carts_jam_tracks = [] - shopping_carts_subscriptions = [] - shopping_carts.each do |shopping_cart| - if shopping_cart.is_jam_track? - shopping_carts_jam_tracks << shopping_cart - else - # XXX: this may have to be revisited when we actually have something other than JamTracks for puchase - shopping_carts_subscriptions << shopping_cart - end + + if Sale.is_mixed(shopping_carts) + # the controller checks this too; this is just an extra-level of sanity checking + return sales end - jam_track_sale = order_jam_tracks(current_user, shopping_carts_jam_tracks) + jam_track_sale = order_jam_tracks(current_user, shopping_carts) sales << jam_track_sale if jam_track_sale # TODO: process shopping_carts_subscriptions @@ -125,18 +98,10 @@ module JamRuby sales end - def self.preview_invoice_jam_tracks(current_user, shopping_carts_jam_tracks) - ### XXX TODO; - # we currently use a fake plan in Recurly to estimate taxes using the Pricing.Attach metod in Recurly.js - - # if we were to implement this the right way (ensure adjustments are on the account as necessary), then it would be better (more correct) - # just a pain to implement - end - - def self.is_only_freebie(shopping_carts_jam_tracks) + def self.is_only_freebie(shopping_carts) free = true - shopping_carts_jam_tracks.each do |cart| + shopping_carts.each do |cart| free = cart.product_info[:free] if !free @@ -162,7 +127,23 @@ module JamRuby # this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed) # it may return an nil sale if the JamTrack(s) specified by the shopping carts are already owned - def self.order_jam_tracks(current_user, shopping_carts_jam_tracks) + def self.order_jam_tracks(current_user, shopping_carts) + + shopping_carts_jam_tracks = [] + shopping_carts_subscriptions = [] + shopping_carts_gift_cards = [] + + shopping_carts.each do |shopping_cart| + if shopping_cart.is_jam_track? + shopping_carts_jam_tracks << shopping_cart + elsif shopping_cart.is_gift_card? + shopping_carts_gift_cards << shopping_cart + else + # XXX: this may have to be revisited when we actually have something other than JamTracks for puchase + raise "unknown shopping cart type #{shopping_cart.cart_type}" + shopping_carts_subscriptions << shopping_cart + end + end client = RecurlyClient.new @@ -171,8 +152,8 @@ module JamRuby sale = create_jam_track_sale(current_user) if sale.valid? - if is_only_freebie(shopping_carts_jam_tracks) - sale.process_jam_tracks(current_user, shopping_carts_jam_tracks, nil) + if is_only_freebie(shopping_carts) + sale.process_shopping_carts(current_user, shopping_carts, nil) sale.recurly_subtotal_in_cents = 0 sale.recurly_tax_in_cents = 0 @@ -187,11 +168,13 @@ module JamRuby return sale end - sale_line_item = sale.sale_line_items[0] - sale_line_item.recurly_tax_in_cents = 0 - sale_line_item.recurly_total_in_cents = 0 - sale_line_item.recurly_currency = 'USD' - sale_line_item.recurly_discount_in_cents = 0 + sale.sale_line_items.each do |sale_line_item| + sale_line_item = sale.sale_line_items[0] + sale_line_item.recurly_tax_in_cents = 0 + sale_line_item.recurly_total_in_cents = 0 + sale_line_item.recurly_currency = 'USD' + sale_line_item.recurly_discount_in_cents = 0 + end sale.save else @@ -201,7 +184,7 @@ module JamRuby purge_pending_adjustments(account) - created_adjustments = sale.process_jam_tracks(current_user, shopping_carts_jam_tracks, account) + created_adjustments = sale.process_shopping_carts(current_user, shopping_carts, account) # now invoice the sale ... almost done @@ -257,13 +240,13 @@ module JamRuby sale end - def process_jam_tracks(current_user, shopping_carts_jam_tracks, account) + def process_shopping_carts(current_user, shopping_carts, account) created_adjustments = [] begin - shopping_carts_jam_tracks.each do |shopping_cart| - process_jam_track(current_user, shopping_cart, account, created_adjustments) + shopping_carts.each do |shopping_cart| + process_shopping_cart(current_user, shopping_cart, account, created_adjustments) end rescue Recurly::Error, NoMethodError => x # rollback any adjustments created if error @@ -279,7 +262,7 @@ module JamRuby end - def process_jam_track(current_user, shopping_cart, account, created_adjustments) + def process_shopping_cart(current_user, shopping_cart, account, created_adjustments) recurly_adjustment_uuid = nil recurly_adjustment_credit_uuid = nil @@ -287,15 +270,20 @@ module JamRuby shopping_cart.reload # get the JamTrack in this shopping cart - jam_track = shopping_cart.cart_product + cart_product = shopping_cart.cart_product - if jam_track.right_for_user(current_user) - # if the user already owns the JamTrack, we should just skip this cart item, and destroy it - # if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop - ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart) - return + if shopping_cart.is_jam_track? + jam_track = cart_product + if jam_track.right_for_user(current_user) + # if the user already owns the JamTrack, we should just skip this cart item, and destroy it + # if this occurs, we have to reload every shopping_cart as we iterate. so, we do at the top of the loop + ShoppingCart.remove_jam_track_from_cart(current_user, shopping_cart) + return + end end + + if account # ask the shopping cart to create the correct Recurly adjustment attributes for a JamTrack adjustments = shopping_cart.create_adjustment_attributes(current_user) @@ -328,45 +316,69 @@ module JamRuby # if the sale line item is invalid, blow up the transaction unless sale_line_item.valid? - @log.error("sale item invalid! #{sale_line_item.errors.inspect}") + @@log.error("sale item invalid! #{sale_line_item.errors.inspect}") puts("sale item invalid! #{sale_line_item.errors.inspect}") Stats.write('web.recurly.purchase.sale_invalid', {message: sale_line_item.errors.to_s, value: 1}) raise RecurlyClientError.new(sale_line_item.errors) end - # create a JamTrackRight (this needs to be in a transaction too to make sure we don't make these by accident) - jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| - jam_track_right.redeemed = shopping_cart.free? - end + if shopping_cart.is_jam_track? + jam_track = cart_product - # also if the purchase was a free one, then: - # first, mark the free has_redeemable_jamtrack field if that's still true - # and if still they have more free things, then redeem the giftable_jamtracks - if shopping_cart.free? - if user.has_redeemable_jamtrack - User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false) - current_user.has_redeemable_jamtrack = false - else - User.where(id: current_user.id).update_all(gifted_jamtracks: current_user.gifted_jamtracks - 1) - current_user.gifted_jamtracks = current_user.gifted_jamtracks - 1 + # create a JamTrackRight (this needs to be in a transaction too to make sure we don't make these by accident) + jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| + jam_track_right.redeemed = shopping_cart.free? end - end - - # this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path - if jam_track_right.recurly_adjustment_uuid != recurly_adjustment_uuid - jam_track_right.recurly_adjustment_uuid = recurly_adjustment_uuid - jam_track_right.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid - unless jam_track_right.save - raise RecurlyClientError.new(jam_track_right.errors) + # also if the purchase was a free one, then: + # first, mark the free has_redeemable_jamtrack field if that's still true + # and if still they have more free things, then redeem the giftable_jamtracks + if shopping_cart.free? + if user.has_redeemable_jamtrack + User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false) + current_user.has_redeemable_jamtrack = false + else + User.where(id: current_user.id).update_all(gifted_jamtracks: current_user.gifted_jamtracks - 1) + current_user.gifted_jamtracks = current_user.gifted_jamtracks - 1 + end end + + + # this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path + if jam_track_right.recurly_adjustment_uuid != recurly_adjustment_uuid + jam_track_right.recurly_adjustment_uuid = recurly_adjustment_uuid + jam_track_right.recurly_adjustment_credit_uuid = recurly_adjustment_credit_uuid + unless jam_track_right.save + raise RecurlyClientError.new(jam_track_right.errors) + end + end + + # blow up the transaction if the JamTrackRight did not get created + raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any? + + elsif shopping_cart.is_gift_card? + gift_card_type = cart_product + raise "gift card is null" if gift_card_type.nil? + raise if current_user.nil? + + shopping_cart.quantity.times do |item| + gift_card_purchase = GiftCardPurchase.new( + { + user: current_user, + gift_card_type: gift_card_type + }) + + unless gift_card_purchase.save + raise RecurlyClientError.new(gift_card_purchase.errors) + end + end + + else + raise 'unknown shopping cart type: ' + shopping_cart.cart_type end # delete the shopping cart; it's been dealt with shopping_cart.destroy if shopping_cart - - # blow up the transaction if the JamTrackRight did not get created - raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any? end @@ -396,7 +408,7 @@ module JamRuby def self.create_jam_track_sale(user) sale = Sale.new sale.user = user - sale.sale_type = JAMTRACK_SALE + sale.sale_type = JAMTRACK_SALE # gift cards and jam tracks are sold with this type of sale sale.order_total = 0 sale.save sale diff --git a/ruby/lib/jam_ruby/models/sale_line_item.rb b/ruby/lib/jam_ruby/models/sale_line_item.rb index 7d744cfe5..5cbe5ad87 100644 --- a/ruby/lib/jam_ruby/models/sale_line_item.rb +++ b/ruby/lib/jam_ruby/models/sale_line_item.rb @@ -4,14 +4,16 @@ module JamRuby JAMBLASTER = 'JamBlaster' JAMCLOUD = 'JamCloud' JAMTRACK = 'JamTrack' + GIFTCARD = 'GiftCardType' belongs_to :sale, class_name: 'JamRuby::Sale' belongs_to :jam_track, class_name: 'JamRuby::JamTrack' belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight' + belongs_to :gift_card, class_name: 'JamRuby::GiftCard' belongs_to :affiliate_referral, class_name: 'JamRuby::AffiliatePartner', foreign_key: :affiliate_referral_id has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid' - validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]} + validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK, GIFTCARD]} validates :unit_price, numericality: {only_integer: false} validates :quantity, numericality: {only_integer: true} validates :free, numericality: {only_integer: true} @@ -21,9 +23,19 @@ module JamRuby validates :recurly_plan_code, presence:true validates :sale, presence:true + def is_jam_track? + product_type == JAMTRACK + end + + def is_gift_card? + product_type == GIFTCARD + end + def product if product_type == JAMTRACK JamTrack.find_by_id(product_id) + elsif product_type == GIFTCARD + GiftCardType.find_by_id(product_id) else raise 'unsupported product type' end diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb index c113c5a99..fb531c2bd 100644 --- a/ruby/lib/jam_ruby/models/shopping_cart.rb +++ b/ruby/lib/jam_ruby/models/shopping_cart.rb @@ -28,7 +28,7 @@ module JamRuby def product_info product = self.cart_product - {name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region} unless product.nil? + {type: cart_type, name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region, sale_display:product.sale_display} unless product.nil? end # multiply quantity by price @@ -90,6 +90,7 @@ module JamRuby end def self.create user, product, quantity = 1, mark_redeem = false + cart = ShoppingCart.new if user.is_a?(User) cart.user = user @@ -110,40 +111,42 @@ module JamRuby cart_type == JamTrack::PRODUCT_TYPE end + def is_gift_card? + cart_type == GiftCardType::PRODUCT_TYPE + end # returns an array of adjustments for the shopping cart def create_adjustment_attributes(current_user) - raise "not a jam track" unless is_jam_track? + raise "not a jam track or gift card" unless is_jam_track? || is_gift_card? info = self.product_info if free? - - puts "GOT A FREEBIE!" # create the credit, then the pseudo charge [ { accounting_code: PURCHASE_FREE_CREDIT, currency: 'USD', unit_amount_in_cents: -(info[:total_price] * 100).to_i, - description: "JamTrack: " + info[:name] + " (Credit)", + description: info[:sale_display] + " (Credit)", tax_exempt: true }, { accounting_code: PURCHASE_FREE, currency: 'USD', unit_amount_in_cents: (info[:total_price] * 100).to_i, - description: "JamTrack: " + info[:name], + description: info[:sale_display], tax_exempt: true } ] else + [ { accounting_code: PURCHASE_NORMAL, currency: 'USD', unit_amount_in_cents: (info[:total_price] * 100).to_i, - description: "JamTrack: " + info[:name], + description: info[:sale_display], tax_exempt: false } ] @@ -152,8 +155,13 @@ module JamRuby def self.move_to_user(user, anonymous_user, shopping_carts) shopping_carts.each do |shopping_cart| - mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user) - cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem) + if shopping_cart.is_jam_track? + mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user) + cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem) + else + cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, false) + end + end anonymous_user.destroy_all_shopping_carts @@ -197,7 +205,7 @@ module JamRuby if clear # if you are an anonymous user, we make sure there is nothing else in your shopping cart ... keep it clean for the 'new user rummaging around for a freebie scenario' - any_user.destroy_all_shopping_carts + any_user.destroy_jam_track_shopping_carts any_user.reload end @@ -207,6 +215,14 @@ module JamRuby cart end + def self.add_item_to_cart(any_user, item) + cart = nil + ShoppingCart.transaction do + cart = ShoppingCart.create(any_user, item, 1, false) + end + cart + end + # deletes a jam track from the shopping cart, updating redeem flag as necessary def self.remove_jam_track_from_cart(any_user, cart) ShoppingCart.transaction do @@ -236,6 +252,12 @@ module JamRuby end end + def self.remove_item_from_cart(any_user, cart) + ShoppingCart.transaction do + cart.destroy + end + end + # if the number of items in the shopping cart is less than gifted_jamtracks on the user, then fix them all up def self.apply_gifted_jamtracks(user) jam_track_carts = user.shopping_carts.where(cart_type:JamTrack::PRODUCT_TYPE) diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index f802954ea..907cd9776 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -150,6 +150,8 @@ module JamRuby # gift cards has_many :gift_cards, :class_name=> "JamRuby::GiftCard" + has_many :gift_card_purchases, :class_name=> "JamRuby::GiftCardPurchase" + # affiliate_partner has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id, inverse_of: :partner_user @@ -1678,6 +1680,11 @@ module JamRuby ShoppingCart.where("user_id=?", self).destroy_all end + def destroy_jam_track_shopping_carts + ShoppingCart.destroy_all(anonymous_user_id: @id, cart_type: JamTrack::PRODUCT_TYPE) + end + + def unsubscribe_token self.class.create_access_token(self) end diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 1a9097e68..024ecdc20 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -862,7 +862,16 @@ FactoryGirl.define do factory :gift_card, class: 'JamRuby::GiftCard' do sequence(:code) {n.to_s} - card_type = JamRuby::GiftCard::JAM_TRACKS_10 + card_type JamRuby::GiftCardType::JAM_TRACKS_5 + end + + factory :gift_card_type, class: 'JamRuby::GiftCardType' do + card_type JamRuby::GiftCardType::JAM_TRACKS_5 + end + + factory :gift_card_purchase, class: 'JamRuby::GiftCardPurchase' do + + association :user, factory: :user end end diff --git a/ruby/spec/jam_ruby/models/recurly_transaction_web_hook_spec.rb b/ruby/spec/jam_ruby/models/recurly_transaction_web_hook_spec.rb index ba89aee20..bce945d20 100644 --- a/ruby/spec/jam_ruby/models/recurly_transaction_web_hook_spec.rb +++ b/ruby/spec/jam_ruby/models/recurly_transaction_web_hook_spec.rb @@ -135,7 +135,7 @@ describe RecurlyTransactionWebHook do RecurlyTransactionWebHook.create_from_xml(document) - JamTrackRight.find_by_id(jam_track_right.id).should be_nil + JamTrackRight.find_by_id(jam_track_right.id).should_not be_nil end it "deletes jam_track_right when voided" do @@ -154,7 +154,7 @@ describe RecurlyTransactionWebHook do RecurlyTransactionWebHook.create_from_xml(document) - JamTrackRight.find_by_id(jam_track_right.id).should be_nil + JamTrackRight.find_by_id(jam_track_right.id).should_not be_nil end end diff --git a/ruby/spec/jam_ruby/models/sale_line_item_spec.rb b/ruby/spec/jam_ruby/models/sale_line_item_spec.rb index 334166734..4d0340259 100644 --- a/ruby/spec/jam_ruby/models/sale_line_item_spec.rb +++ b/ruby/spec/jam_ruby/models/sale_line_item_spec.rb @@ -6,6 +6,7 @@ describe SaleLineItem do let(:user) {FactoryGirl.create(:user)} let(:user2) {FactoryGirl.create(:user)} let(:jam_track) {FactoryGirl.create(:jam_track)} + let(:gift_card) {FactoryGirl.create(:gift_card_type, card_type: GiftCardType::JAM_TRACKS_10)} describe "associations" do @@ -23,7 +24,7 @@ describe SaleLineItem do describe "state" do - it "success" do + it "jam track success" do sale = Sale.create_jam_track_sale(user) shopping_cart = ShoppingCart.create(user, jam_track) sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid', nil, nil) @@ -37,5 +38,20 @@ describe SaleLineItem do success: true }) end + + it "gift card success" do + sale = Sale.create_jam_track_sale(user) + shopping_cart = ShoppingCart.create(user, gift_card) + sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid', nil, nil) + transaction = FactoryGirl.create(:recurly_transaction_web_hook, subscription_id: 'some_recurly_uuid') + + sale_line_item.reload + sale_line_item.state.should eq({ + void: false, + refund: false, + fail: false, + success: true + }) + end end end diff --git a/ruby/spec/jam_ruby/models/sale_spec.rb b/ruby/spec/jam_ruby/models/sale_spec.rb index 6119a5973..fc89145cb 100644 --- a/ruby/spec/jam_ruby/models/sale_spec.rb +++ b/ruby/spec/jam_ruby/models/sale_spec.rb @@ -7,6 +7,7 @@ describe Sale do let(:jam_track) {FactoryGirl.create(:jam_track)} let(:jam_track2) {FactoryGirl.create(:jam_track)} let(:jam_track3) {FactoryGirl.create(:jam_track)} + let(:gift_card) {GiftCardType.jam_track_5} def assert_free_line_item(sale_line_item, jamtrack) sale_line_item.recurly_tax_in_cents.should be_nil @@ -68,6 +69,7 @@ describe Sale do let(:jamtrack3) { FactoryGirl.create(:jam_track) } let(:jamtrack4) { FactoryGirl.create(:jam_track) } let(:jam_track_price_in_cents) { (jamtrack.price * 100).to_i } + let(:gift_card_price_in_cents) { (gift_card.price * 100).to_i } let(:client) { RecurlyClient.new } let(:billing_info) { info = {} @@ -95,6 +97,77 @@ describe Sale do end end + it "for a gift card" do + shopping_cart = ShoppingCart.create user, gift_card, 1, false + client.find_or_create_account(user, billing_info) + + sales = Sale.place_order(user, [shopping_cart]) + + user.reload + user.sales.length.should eq(1) + + sales.should eq(user.sales) + sale = sales[0] + sale.recurly_invoice_id.should_not be_nil + + sale.recurly_subtotal_in_cents.should eq(gift_card_price_in_cents) + sale.recurly_tax_in_cents.should eq(0) + sale.recurly_total_in_cents.should eq(gift_card_price_in_cents) + sale.recurly_currency.should eq('USD') + + sale.order_total.should eq(gift_card.price) + sale.sale_line_items.length.should == 1 + sale_line_item = sale.sale_line_items[0] + # validate we are storing pricing info from recurly + sale_line_item.recurly_tax_in_cents.should eq(0) + sale_line_item.recurly_total_in_cents.should eq(gift_card_price_in_cents) + sale_line_item.recurly_currency.should eq('USD') + sale_line_item.recurly_discount_in_cents.should eq(0) + sale_line_item.product_type.should eq(GiftCardType::PRODUCT_TYPE) + sale_line_item.unit_price.should eq(gift_card.price) + sale_line_item.quantity.should eq(1) + sale_line_item.free.should eq(0) + sale_line_item.sales_tax.should be_nil + sale_line_item.shipping_handling.should eq(0) + sale_line_item.recurly_plan_code.should eq(gift_card.plan_code) + sale_line_item.product_id.should eq(gift_card.id) + sale_line_item.recurly_subscription_uuid.should be_nil + sale_line_item.recurly_adjustment_uuid.should_not be_nil + sale_line_item.recurly_adjustment_credit_uuid.should be_nil + sale_line_item.recurly_adjustment_uuid.should_not be_nil + + # verify subscription is in Recurly + recurly_account = client.get_account(user) + adjustments = recurly_account.adjustments + adjustments.should_not be_nil + adjustments.should have(1).items + purchase= adjustments[0] + purchase.unit_amount_in_cents.should eq((gift_card.price * 100).to_i) + purchase.accounting_code.should eq(ShoppingCart::PURCHASE_NORMAL) + purchase.description.should eq("JamTracks Gift Card (5)") + purchase.state.should eq('invoiced') + purchase.uuid.should eq(sale_line_item.recurly_adjustment_uuid) + + invoices = recurly_account.invoices + invoices.should have(1).items + invoice = invoices[0] + invoice.uuid.should eq(sale.recurly_invoice_id) + invoice.line_items.should have(1).items # should have single adjustment associated + invoice.line_items[0].should eq(purchase) + invoice.subtotal_in_cents.should eq((gift_card.price * 100).to_i) + invoice.total_in_cents.should eq((gift_card.price * 100).to_i) + invoice.state.should eq('collected') + + # verify jam_track_rights data + user.gift_card_purchases.should_not be_nil + user.gift_card_purchases.should have(1).items + user.gift_card_purchases.last.gift_card_type.should eq(GiftCardType.jam_track_5) + user.has_redeemable_jamtrack.should be_true + + sale_line_item.affiliate_referral.should be_nil + sale_line_item.affiliate_referral_fee_in_cents.should be_nil + end + it "for a free jam track" do shopping_cart = ShoppingCart.create user, jamtrack, 1, true @@ -195,48 +268,25 @@ describe Sale do # OK! Now make a second purchase; this time, buy one free, one not free shopping_cart3 = ShoppingCart.create user, jamtrack3, 1, true - shopping_cart4 = ShoppingCart.create user, jamtrack4, 1, false client.find_or_create_account(user, billing_info) - sales = Sale.place_order(user, [shopping_cart3, shopping_cart4]) + sales = Sale.place_order(user, [shopping_cart3]) user.reload user.sales.length.should eq(2) sale = sales[0] sale.reload - sale.recurly_invoice_id.should_not be_nil + sale.recurly_invoice_id.should be_nil sale.recurly_subtotal_in_cents.should eq(0) sale.recurly_tax_in_cents.should eq(0) sale.recurly_total_in_cents.should eq(0) sale.recurly_currency.should eq('USD') sale.order_total.should eq(0) - sale.sale_line_items.length.should == 2 + sale.sale_line_items.length.should == 1 assert_free_line_item(sale.sale_line_items[0], jamtrack3) - - paid_right = JamTrackRight.where(user_id:user.id).where(jam_track_id: jamtrack4.id).first - - sale_line_item.recurly_total_in_cents.should eq(jam_track_price_in_cents) - sale_line_item.recurly_currency.should eq('USD') - sale_line_item.recurly_discount_in_cents.should eq(0) - sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE) - sale_line_item.unit_price.should eq(jamtrack4.price) - sale_line_item.quantity.should eq(1) - sale_line_item.free.should eq(0) - sale_line_item.sales_tax.should be_nil - sale_line_item.shipping_handling.should eq(0) - sale_line_item.recurly_plan_code.should eq(jamtrack4.plan_code) - sale_line_item.product_id.should eq(jamtrack.id) - sale_line_item.recurly_subscription_uuid.should be_nil - sale_line_item.recurly_adjustment_uuid.should_not be_nil - sale_line_item.recurly_adjustment_credit_uuid.should be_nil - sale_line_item.recurly_adjustment_uuid.should eq(paid_right.recurly_adjustment_uuid) - - user.has_redeemable_jamtrack.should be_false - user.gifted_jamtracks.should eq(0) - end it "for a free jam track with an affiliate association" do diff --git a/ruby/spec/jam_ruby/models/shopping_cart_spec.rb b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb index 58a528c7b..8d3b724b1 100644 --- a/ruby/spec/jam_ruby/models/shopping_cart_spec.rb +++ b/ruby/spec/jam_ruby/models/shopping_cart_spec.rb @@ -10,6 +10,8 @@ describe ShoppingCart do let(:jam_track5) { FactoryGirl.create(:jam_track) } let(:jam_track6) { FactoryGirl.create(:jam_track) } let(:jam_track7) { FactoryGirl.create(:jam_track) } + let(:gift_card) {FactoryGirl.create(:gift_card_type)} + let(:gift_card2) {FactoryGirl.create(:gift_card_type)} before(:each) do ShoppingCart.delete_all @@ -29,7 +31,6 @@ describe ShoppingCart do user.shopping_carts[0].quantity.should == 1 end - it "maintains only one free JamTrack in ShoppingCart" do cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true) cart1.should_not be_nil @@ -39,8 +40,12 @@ describe ShoppingCart do cart2.errors.any?.should be_false user.reload user.shopping_carts.length.should eq(1) - cart3 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true) - cart3.errors.any?.should be_false + cart3 = ShoppingCart.add_item_to_cart(user, gift_card) + cart3.errors.any?.should be_true + user.reload + user.shopping_carts.length.should eq(1) + cart4 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true) + cart4.errors.any?.should be_false user.reload user.shopping_carts.length.should eq(1) end @@ -56,6 +61,10 @@ describe ShoppingCart do cart2.errors.any?.should be_true end + it "a second giftcard just adds quantity" do + + end + describe "redeemable behavior" do it "removes redeemable item to shopping cart (maintains only one in cart)" do @@ -153,6 +162,25 @@ describe ShoppingCart do end end + describe "gift cards" do + it "can not add multiple of same type" do + cart1 = ShoppingCart.add_item_to_cart(user, gift_card) + cart1.should_not be_nil + cart1.errors.any?.should be_false + + user.reload + user.has_redeemable_jamtrack = true + user.shopping_carts.length.should eq(1) + user.shopping_carts[0].quantity.should eql(1) + + cart2 = ShoppingCart.add_item_to_cart(user, gift_card) + cart2.should_not be_nil + # it's the same type, so it's blocked + cart2.errors.any?.should be_true + cart2.errors[:cart_id].should eq(["has already been taken"]) + end + end + describe "mixed" do it "non-free then free" do # you shouldn't be able to add a free after a non-free diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 93ec5dda7..7f189e159 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -95,13 +95,13 @@ end config.before(:suite) do DatabaseCleaner.strategy = :transaction - DatabaseCleaner.clean_with(:deletion, {pre_count: true, reset_ids:false, :except => %w[instruments genres icecast_server_groups jamcompany jamisp geoipblocks geoipisp geoiplocations cities regions countries generic_state spatial_ref_sys] }) + DatabaseCleaner.clean_with(:deletion, {pre_count: true, reset_ids:false, :except => %w[gift_card_types instruments genres icecast_server_groups jamcompany jamisp geoipblocks geoipisp geoiplocations cities regions countries generic_state spatial_ref_sys] }) end config.around(:each) do |example| # set no_transaction: true as metadata on your test to use deletion strategy instead if example.metadata[:no_transaction] - DatabaseCleaner.strategy = :deletion, {pre_count: true, reset_ids:false, :except => %w[instruments genres icecast_server_groups jamcompany jamisp geoipblocks geoipisp geoiplocations cities regions countries generic_state spatial_ref_sys] } + DatabaseCleaner.strategy = :deletion, {pre_count: true, reset_ids:false, :except => %w[gift_card_types instruments genres icecast_server_groups jamcompany jamisp geoipblocks geoipisp geoiplocations cities regions countries generic_state spatial_ref_sys] } else DatabaseCleaner.strategy = :transaction end diff --git a/web/app/assets/javascripts/checkout_complete.js b/web/app/assets/javascripts/checkout_complete.js index 4f1e96110..aa52214b9 100644 --- a/web/app/assets/javascripts/checkout_complete.js +++ b/web/app/assets/javascripts/checkout_complete.js @@ -15,6 +15,7 @@ var $templatePurchasedJamTrack = null; var $thanksPanel = null; var $jamTrackInBrowser = null; + var $giftCardPurchased = null; var $purchasedJamTrack = null; var $purchasedJamTrackHeader = null; var $purchasedJamTracks = null; @@ -75,9 +76,17 @@ else { $thanksPanel.removeClass('hidden') handleJamTracksPurchased(purchaseResponse.jam_tracks) + handleGiftCardsPurchased(purchaseResponse.gift_cards) } } + + function handleGiftCardsPurchased(gift_cards) { + // were any GiftCards purchased? + if(gift_cards && gift_cards.length > 0) { + $giftCardPurchased.removeClass('hidden') + } + } function handleJamTracksPurchased(jamTracks) { // were any JamTracks purchased? var jamTracksPurchased = jamTracks && jamTracks.length > 0; @@ -194,6 +203,7 @@ $templatePurchasedJamTrack = $('#template-purchased-jam-track'); $thanksPanel = $screen.find(".thanks-panel"); $jamTrackInBrowser = $screen.find(".thanks-detail.jam-tracks-in-browser"); + $giftCardPurchased = $screen.find('.thanks-detail.gift-card') $purchasedJamTrack = $thanksPanel.find(".thanks-detail.purchased-jam-track"); $purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header"); $purchasedJamTracks = $purchasedJamTrack.find(".purchased-list") diff --git a/web/app/assets/javascripts/checkout_order.js b/web/app/assets/javascripts/checkout_order.js index 168b965d6..a69c0a39c 100644 --- a/web/app/assets/javascripts/checkout_order.js +++ b/web/app/assets/javascripts/checkout_order.js @@ -135,15 +135,7 @@ } } - function displayTax(effectiveQuantity, item_tax, total_with_tax) { - var totalTax = 0; - var totalPrice = 0; - - var unitTax = item_tax * effectiveQuantity; - totalTax += unitTax; - - var totalUnitPrice = total_with_tax * effectiveQuantity; - totalPrice += totalUnitPrice; + function displayTax(totalTax, totalPrice) { $screen.find('.order-right-page .order-items-value.taxes').text('$' + totalTax.toFixed(2)) $screen.find('.order-right-page .order-items-value.grand-total').text('$' + totalPrice.toFixed(2)) @@ -181,8 +173,16 @@ taxRate = 0.0825; } - var unitTax = 1.99 * taxRate; - displayTax(effectiveQuantity, unitTax, 1.99 + unitTax) + var estimatedTax = 0; + var estimatedTotal = 0; + + context._.each(carts, function(cart) { + var cart_quantity = cart.product_info.quantity - cart.product_info.marked_for_redeem + estimatedTax += cart.product_info.price * cart_quantity * taxRate; + estimatedTotal += cart.product_info.price * cart_quantity; + }) + + displayTax(Math.round(estimatedTax*100)/100, Math.round((estimatedTotal + estimatedTax)*100)/100) } else { checkoutUtils.configureRecurly() diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index b2d7a604a..10461d496 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -1805,6 +1805,17 @@ return deferred } + function addGiftCardToShoppingCart(options) { + var deferred = $.ajax({ + type: "POST", + url: '/api/shopping_carts/add_gift_card?' + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + + return deferred + } + function getShoppingCarts() { // the need for the time de-duplicator indicates we are doing something wrong on the server return $.ajax({ @@ -2188,6 +2199,7 @@ this.enqueueJamTrack = enqueueJamTrack; this.getBackingTracks = getBackingTracks; this.addJamtrackToShoppingCart = addJamtrackToShoppingCart; + this.addGiftCardToShoppingCart = addGiftCardToShoppingCart; this.getShoppingCarts = getShoppingCarts; this.removeShoppingCart = removeShoppingCart; this.clearShoppingCart = clearShoppingCart; diff --git a/web/app/assets/javascripts/react-components/landing/GiftCardLandingPage.js.jsx.coffee b/web/app/assets/javascripts/react-components/landing/GiftCardLandingPage.js.jsx.coffee new file mode 100644 index 000000000..e55cceb26 --- /dev/null +++ b/web/app/assets/javascripts/react-components/landing/GiftCardLandingPage.js.jsx.coffee @@ -0,0 +1,93 @@ +context = window +rest = context.JK.Rest() + +@GiftCardLandingPage = React.createClass({ + + render: () -> + + if this.state.done + ctaButtonText10 = 'sending you in...' + ctaButtonText20 = 'sending you in...' + else if this.state.processing + ctaButtonText10 = 'hold on...' + ctaButtonText20 = 'hold on...' + else + ctaButtonText10 = `ADD $10 CARD
TO CART
` + ctaButtonText20 = `ADD $20 CARD
TO CART
` + + + ctaButtons = + `
+ + +
` + + + `
+
+
+ gift card +

$10 or $20 JAMTRACKS GIFT CARDS

+

A PERFECT GIFT FOR THE HOLIDAYS

+
+
+
+ + +
+ Preview JamTrack +
+
+

Click the play buttons below to preview the master mix and 20-second samples of all the isolated tracks.

+
+ +
+

+ Get a $10 gift card (good for 5 songs) or a $20 gift card (good for 10 songs), and your happy + gift card getter can choose their favorites from our catalog of 3,700+ popular songs. +

+ {ctaButtons} + or browse our catalog of 3,700+ songs +
+
+
+
+

+ JamTracks by JamKazam are the best way to play along with your favorite songs. Far better and different than traditional + backing tracks, our JamTracks are complete multi-track professional recordings, with fully isolated tracks for each part of the music. + And our free app and Internet service are packed with features that give you unmatched creative freedom to learn, practice, record, play with others, and share your performances. +

+
+
` + + getInitialState: () -> + {processing:false} + + componentDidMount:() -> + $root = $(this.getDOMNode()) + +# add item to cart, create the user if necessary, and then place the order to get the free JamTrack. + ctaClick: (card_type, e) -> + e.preventDefault() + + return if @state.processing + + loggedIn = context.JK.currentUserId? + + rest.addGiftCardToShoppingCart({id: card_type}).done((response) => + + if loggedIn + @setState({done: true}) + context.location = '/client#/shoppingCart' + else + @setState({done: true}) + context.location = '/client#/checkoutPayment' + + ).fail((jqXHR, textStatus, errorMessage) => + if jqXHR.status == 422 + errors = JSON.parse(jqXHR.responseText) + cart_errors = errors?.errors?.cart_id + context.JK.app.ajaxError(jqXHR, textStatus, errorMessage) + @setState({processing:false}) + ) +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/react-components/stores/UserStore.js.coffee b/web/app/assets/javascripts/react-components/stores/UserStore.js.coffee index 6819fbf4d..c92bbe9d7 100644 --- a/web/app/assets/javascripts/react-components/stores/UserStore.js.coffee +++ b/web/app/assets/javascripts/react-components/stores/UserStore.js.coffee @@ -24,8 +24,8 @@ logger = context.JK.logger onModify: (changes) -> @user = $.extend({}, @user, changes) - @changed( - ) + @changed() + changed:() -> @trigger({user: @user}) } diff --git a/web/app/assets/stylesheets/client/checkout_complete.css.scss b/web/app/assets/stylesheets/client/checkout_complete.css.scss index 45c9b3951..154cb17bc 100644 --- a/web/app/assets/stylesheets/client/checkout_complete.css.scss +++ b/web/app/assets/stylesheets/client/checkout_complete.css.scss @@ -73,6 +73,10 @@ } } } + .thanks-detail.gift-card{ + margin-top: 20px; + } + .thanks-detail.purchased-jam-track { margin-top:20px; diff --git a/web/app/assets/stylesheets/landings/individual_jamtrack.css.scss b/web/app/assets/stylesheets/landings/individual_jamtrack.css.scss index c54f8bd2f..b48592f3b 100644 --- a/web/app/assets/stylesheets/landings/individual_jamtrack.css.scss +++ b/web/app/assets/stylesheets/landings/individual_jamtrack.css.scss @@ -162,6 +162,18 @@ body.web.individual_jamtrack { text-align: center; } + img.gift-card-preview { + width:300px; + float: left; + margin-left: -15px; // because image has black on the left, which you can't see on back background + margin-right: 20px; + margin-bottom: 20px; + } + p.gift-getter { + margin-top:20px; + line-height:125%; + } + img.app-preview { width: 340px; float: left; @@ -205,6 +217,9 @@ body.web.individual_jamtrack { .browse-all { color: #ffb800; + text-decoration: underline; + text-align: center; + display: block; } p { @@ -320,6 +335,11 @@ body.web.individual_jamtrack { padding: 10px; border-width: 0 0 $chunkyBorderWidth; + + &.gift-card { + border-width: 0 0 2px; + } + border-style: solid; border-color: $copy-color-on-dark; @@ -338,6 +358,9 @@ body.web.individual_jamtrack { margin-bottom:10px; } + .cta-buttons { + text-align:center; + } .cta-button { font-size: 24px; color: white; @@ -348,6 +371,13 @@ body.web.individual_jamtrack { width: 100%; border: 1px outset buttonface; font-family: Raleway, Arial, Helvetica, sans-serif; + + &.gift-card { + font-size:16px; + width:138px; + margin:15px 5px; + display:inline-block; + } } } @@ -385,12 +415,7 @@ body.web.individual_jamtrack { border: 1px outset buttonface; font-family: Raleway, Arial, Helvetica, sans-serif; } - .browse-all { - text-decoration: underline; - text-align: center; - display: block; - } .privacy-policy { text-decoration: underline; } diff --git a/web/app/controllers/api_recurly_controller.rb b/web/app/controllers/api_recurly_controller.rb index e051e17fc..1da2f62e0 100644 --- a/web/app/controllers/api_recurly_controller.rb +++ b/web/app/controllers/api_recurly_controller.rb @@ -124,7 +124,7 @@ class ApiRecurlyController < ApiController def place_order error=nil - response = {jam_tracks: []} + response = {jam_tracks: [], gift_cards: []} if Sale.is_mixed(current_user.shopping_carts) msg = "has free and non-free items. Try removing non-free items." @@ -136,11 +136,16 @@ class ApiRecurlyController < ApiController sales.each do |sale| - if sale.is_jam_track_sale? - sale.sale_line_items.each do |line_item| + sale.sale_line_items.each do |line_item| + if line_item.is_jam_track? jam_track = line_item.product jam_track_right = jam_track.right_for_user(current_user) response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version} + elsif line_item.is_gift_card? + gift_card = line_item.product + response[:gift_cards] << {name: gift_card.name, id: gift_card.id} + else + raise 'unknown sale line item type: ' + line_item.product_type end end end diff --git a/web/app/controllers/api_shopping_carts_controller.rb b/web/app/controllers/api_shopping_carts_controller.rb index 0d1256daf..e45429f86 100644 --- a/web/app/controllers/api_shopping_carts_controller.rb +++ b/web/app/controllers/api_shopping_carts_controller.rb @@ -30,6 +30,34 @@ class ApiShoppingCartsController < ApiController end end + def add_gift_card + gift_card_type = nil + + id = params[:id] + + if id && id.to_i == 5 + gift_card_type = 'jam_tracks_5' + elsif id && id.to_i == 10 + gift_card_type = 'jam_tracks_10' + end + + gift_card = GiftCardType.find_by_id(gift_card_type) + + # verify GiftCard exists + if gift_card.nil? + raise StateError, "Invalid JamTrack." + end + + @cart = ShoppingCart.add_item_to_cart(any_user, gift_card) + + if @cart.errors.any? + response.status = :unprocessable_entity + respond_with @cart + else + # let add_gift_card.rabl take over + end + end + def update_cart @cart = ShoppingCart.find_by_id params[:id] diff --git a/web/app/controllers/landings_controller.rb b/web/app/controllers/landings_controller.rb index 5e6cc538e..e566b6adb 100644 --- a/web/app/controllers/landings_controller.rb +++ b/web/app/controllers/landings_controller.rb @@ -213,5 +213,28 @@ class LandingsController < ApplicationController @landing_tag_play_learn_earn = true render 'redeem_giftcard', layout: 'web' end + + def buy_gift_card + @no_landing_tag = true + @landing_tag_play_learn_earn = true + @show_after_black_bar_border = true + @jam_track = JamTrack.find_by_slug('elton-john-rocket-man') + @jam_track = JamTrack.first unless @jam_track + + instrument_id = nil + instrument_name = nil + instrument_count = 0 + + band_jam_track_count = @jam_track.band_jam_track_count + jam_track_count = JamTrack.count + @title = individual_jamtrack_title(false, params[:generic], @jam_track) + @description = individual_jamtrack_desc(false, params[:generic], @jam_track) + @page_data = {jam_track: @jam_track, all_track_count: jam_track_count, band_track_count: band_jam_track_count, band: false, generic: params[:generic], instrument: instrument_name, instrument_id: instrument_id, instrument_count: instrument_count} + gon.jam_track_plan_code = @jam_track.plan_code if @jam_track + gon.generic = params[:generic] + gon.instrument_id = instrument_id + + render 'buy_gift_card', layout: 'web' + end end diff --git a/web/app/views/api_shopping_carts/add_gift_card.rabl b/web/app/views/api_shopping_carts/add_gift_card.rabl new file mode 100644 index 000000000..272eb3c6d --- /dev/null +++ b/web/app/views/api_shopping_carts/add_gift_card.rabl @@ -0,0 +1,5 @@ +extends "api_shopping_carts/show" + +node :show_free_jamtrack do + any_user.show_free_jamtrack? +end \ No newline at end of file diff --git a/web/app/views/clients/_checkout_complete.html.slim b/web/app/views/clients/_checkout_complete.html.slim index d6c09da27..eaf440ff3 100644 --- a/web/app/views/clients/_checkout_complete.html.slim +++ b/web/app/views/clients/_checkout_complete.html.slim @@ -19,6 +19,8 @@ div layout="screen" layout-id="checkoutComplete" id="checkoutCompleteScreen" cla br .thanks-detail We'll send you an email confirming your order shortly. br + .thanks-detail.gift-card.hidden + p Thank you for purchasing a JamTrack Gift Card! It will be mailed to you. .thanks-detail.jam-tracks-in-browser.hidden p To play your purchased JamTrack, launch the JamKazam application and open the JamTrack while in a session. a.download-jamkazam-wrapper.hidden href="/downloads" rel="external" diff --git a/web/app/views/clients/_checkout_order.html.slim b/web/app/views/clients/_checkout_order.html.slim index 1a62e7066..3726e782f 100644 --- a/web/app/views/clients/_checkout_order.html.slim +++ b/web/app/views/clients/_checkout_order.html.slim @@ -90,7 +90,7 @@ script type='text/template' id='template-order-content' = "{% _.each(data.carts, function(cart) { %}" .cart-item cart-id="{{cart.id}}" .cart-item-caption - = "{{cart.cart_type}}: {{cart.product_info.name}}" + = "{{cart.product_info.sale_display}}" = "{% if (cart.product_info.free) { %}" span.first-one-free | (first one free) diff --git a/web/app/views/clients/_checkout_payment.html.slim b/web/app/views/clients/_checkout_payment.html.slim index 1bf5794a4..5f1d1d323 100644 --- a/web/app/views/clients/_checkout_payment.html.slim +++ b/web/app/views/clients/_checkout_payment.html.slim @@ -10,8 +10,7 @@ div layout="screen" layout-id="checkoutPayment" id="checkoutPaymentScreen" class .checkout-navigation-bar .payment-wrapper p.payment-prompt.free-jamtrack.hidden - | Please enter your billing address and payment information below. You will not be billed for your first JamTrack, which is 100% free.  - | But we need this data to prevent fraud/abuse of those who would create multiple accounts to collect multiple free JamTracks.  + | Please enter your billing address and payment information below.  | You will not be billed for any charges of any kind without your explicit authorization.  | There are no "hidden" charges or fees, thank you! p.payment-prompt.no-free-jamtrack.hidden diff --git a/web/app/views/clients/_shopping_cart.html.haml b/web/app/views/clients/_shopping_cart.html.haml index 1db6fe56b..5ab90b902 100644 --- a/web/app/views/clients/_shopping_cart.html.haml +++ b/web/app/views/clients/_shopping_cart.html.haml @@ -40,7 +40,7 @@ = "{% _.each(data.carts, function(cart, index) { %}" %tr.cart-item{"cart-id" => "{{cart.id}}"} %td.cart-item-caption - {{cart.cart_type}}: {{cart.product_info.name}} + {{cart.product_info.sale_display}} %td.cart-item-price $ {{Number(cart.product_info.real_price).toFixed(2)}} = "{% if(index == data.carts.length - 1) { %}" diff --git a/web/app/views/landings/buy_gift_card.html.slim b/web/app/views/landings/buy_gift_card.html.slim new file mode 100644 index 000000000..cc96f704d --- /dev/null +++ b/web/app/views/landings/buy_gift_card.html.slim @@ -0,0 +1,24 @@ +- provide(:page_name, 'landing_page full individual_jamtrack') +- provide(:description, @description) +- provide(:title, @title) + += react_component 'GiftCardLandingPage', @page_data.to_json + +- content_for :after_black_bar do + .row.cta-row + h2 GET YOUR GIFT CARD NOW! + p And join 20,000+ other musicians who love our JamTracks. + p.cta-text Not sure if JamTracks are for you? Scroll down to learn more. + +- content_for :white_bar do + = react_component 'JamTrackLandingBottomPage', @page_data.to_json + +- content_for :red_bar do + .full-row + | Get your free JamTrack and start playing today! + +javascript: + $(document).on('JAMKAZAM_READY', function(e, data) { + var song = new JK.IndividualJamTrack(data.app, true); + song.initialize(); + }) diff --git a/web/config/routes.rb b/web/config/routes.rb index 8ce1181e5..cb96586fe 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -41,6 +41,7 @@ SampleApp::Application.routes.draw do match '/landing/jamtracks/:plan_code', to: 'landings#individual_jamtrack', via: :get, as: 'individual_jamtrack' match '/landing/jamtracks/band/:plan_code', to: 'landings#individual_jamtrack_band', via: :get, as: 'individual_jamtrack_band' match '/landing/jamtracks/:instrument/:plan_code', to: 'landings#individual_jamtrack', via: :get, as: 'individual_jamtrack_instrument' + match '/landing/gift-card', to: 'landings#buy_gift_card', via: :get, as: 'buy_gift_card' match '/affiliateProgram', to: 'landings#affiliate_program', via: :get, as: 'affiliate_program' @@ -276,6 +277,7 @@ SampleApp::Application.routes.draw do match '/shopping_carts' => 'api_shopping_carts#index', :via => :get match '/shopping_carts' => 'api_shopping_carts#remove_cart', :via => :delete match '/shopping_carts/clear_all' => 'api_shopping_carts#clear_all', :via => :delete + match '/shopping_carts/add_gift_card' => 'api_shopping_carts#add_gift_card', :via => :post # RSVP requests match '/rsvp_requests' => 'api_rsvp_requests#index', :via => :get diff --git a/web/spec/controllers/api_users_controller_spec.rb b/web/spec/controllers/api_users_controller_spec.rb index 6c7d83fdb..b57746459 100644 --- a/web/spec/controllers/api_users_controller_spec.rb +++ b/web/spec/controllers/api_users_controller_spec.rb @@ -24,7 +24,7 @@ describe ApiUsersController do gift_card.reload user.gift_cards.should eq([gift_card]) - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) gift_card.user.should eq(user) end @@ -42,7 +42,7 @@ describe ApiUsersController do gift_card.reload user.gift_cards.should eq([gift_card]) - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) gift_card.user.should eq(user) end @@ -78,7 +78,7 @@ describe ApiUsersController do gift_card.reload user.gift_cards.should eq([gift_card]) - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) gift_card.user.should eq(user) cart1.reload cart1.marked_for_redeem.should eq(1) @@ -100,7 +100,7 @@ describe ApiUsersController do gift_card.reload user.gift_cards.should eq([gift_card]) - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) gift_card.user.should eq(user) cart1.reload cart1.marked_for_redeem.should eq(1) @@ -125,7 +125,7 @@ describe ApiUsersController do gift_card.reload user.gift_cards.should eq([gift_card]) - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) gift_card.user.should eq(user) user.shopping_carts.each do |cart| cart.marked_for_redeem.should eq(0) diff --git a/web/spec/factories.rb b/web/spec/factories.rb index bc51f447e..99fbb3fbe 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -842,6 +842,6 @@ FactoryGirl.define do factory :gift_card, class: 'JamRuby::GiftCard' do sequence(:code) {|n| n.to_s} - card_type GiftCard::JAM_TRACKS_10 + card_type GiftCard::JAM_TRACKS_5 end end diff --git a/web/spec/features/checkout_spec.rb b/web/spec/features/checkout_spec.rb index 2ee4d81c7..24a1c23a9 100644 --- a/web/spec/features/checkout_spec.rb +++ b/web/spec/features/checkout_spec.rb @@ -827,8 +827,6 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d acdc_sale.free.should eq(0) acdc_sale.unit_price.should eq(1.99) acdc_sale.sale.should eq(sale) - - end it "for anonymous user with referral" do diff --git a/web/spec/features/gift_card_landing_spec.rb b/web/spec/features/gift_card_landing_spec.rb new file mode 100644 index 000000000..4e61e1cfc --- /dev/null +++ b/web/spec/features/gift_card_landing_spec.rb @@ -0,0 +1,245 @@ +require 'spec_helper' + +describe "Gift Card Landing", :js => true, :type => :feature, :capybara_feature => true do + + subject { page } + + before(:all) do + ShoppingCart.delete_all + JamTrackRight.delete_all + JamTrack.delete_all + JamTrackTrack.delete_all + JamTrackLicensor.delete_all + GiftCardPurchase.delete_all + GiftCard.delete_all + end + + before(:all) do + @jamtrack_rocketman = FactoryGirl.create(:jam_track, slug: 'elton-john-rocket-man', name: 'Rocket Man', original_artist: 'Elton John', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack') + end + + + let(:jamtrack_rocketman) {@jamtrack_rocketman} + let(:user) { FactoryGirl.create(:user, country: 'US') } + + let(:billing_info) { + { + first_name: 'Seth', + last_name: 'Call', + address1: '10704 Buckthorn Drive', + city: 'Austin', + state: 'Texas', + country: 'US', + zip: '78759', + number: '4111111111111111', + month: '08', + year: '2017', + verification_value: '012' + } + } + + it "logged out (5) and affiliate" do + partner = FactoryGirl.create(:affiliate_partner) + affiliate_params = partner.affiliate_query_params + visit "/landing/gift-card?" + affiliate_params + + find('h1.jam-track-name', '$10 or $20 JAMTRACKS GIFT CARDS') + find('h2.original-artist', 'A PERFECT GIFT FOR THE HOLIDAYS') + jamtrack_rocketman.jam_track_tracks.each do |track| + if track.master? + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: 'Master Mix') + else + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: track.instrument.description) + end + end + find('a.browse-all')['href'].should eq("/client?search=#/jamtrack/search") + find('button.five-jt', text: 'ADD $10 CARD TO CART').trigger(:click) + + + find('h1', text: 'check out') + + # fill out all billing info and account info + fill_in 'billing-first-name', with: 'Seth' + fill_in 'billing-last-name', with: 'Call' + fill_in 'billing-address1', with: '10704 Buckthorn Drive' + fill_in 'billing-city', with: 'Austin' + fill_in 'billing-state', with: 'Texas' + fill_in 'billing-zip', with: '78759' + fill_in 'card-number', with: '4111111111111111' + fill_in 'card-verify', with: '012' + + # fill in user/email/tos + fill_in 'email', with: 'bogus+gc1@jamkazam.com' + fill_in 'password', with: 'jam123' + find('#divJamKazamTos ins.iCheck-helper').trigger(:click) # accept TOS + + # try to submit, and see order page + find('#payment-info-next').trigger(:click) + + find('.order-items-value.sub-total', text:'10.00') + find('.order-items-value.taxes', text:'0.83') + find('.order-items-value.order-total', text:'$10.00') + find('.order-items-value.grand-total', text:'$10.83') + + # click the ORDER button + find('.place-order-center a.button-orange.place-order').trigger(:click) + + # and now we should see confirmation, and a notice that we are in a normal browser + find('.thanks-detail.gift-card') + + created_user = User.find_by_email('bogus+gc1@jamkazam.com') + + sleep 3 # challenge to all comers! WHY DO I HAVE TO SLEEP FOR THIS ASSERTION TO BE TRUE! GAH . and 1 second won't do it + + created_user.reload + created_user.has_redeemable_jamtrack.should be_true + created_user.gifted_jamtracks.should eq(0) + created_user.gift_card_purchases.length.should eq(1) + + # verify sales data + created_user.sales.length.should eq(1) + sale = created_user.sales.last + sale.sale_line_items.length.should eq(1) + line_item = sale.sale_line_items[0] + line_item.product_type.should eq('GiftCardType') + line_item.product_id.should eq('jam_tracks_5') + line_item.quantity.should eq(1) + line_item.free.should eq(0) + line_item.unit_price.should eq(10.00) + line_item.sale.should eq(sale) + line_item.affiliate_referral.should eq(partner) + line_item.affiliate_refunded.should be_false + line_item.affiliate_refunded_at.should be_nil + line_item.affiliate_referral_fee_in_cents.should eq(10.00 * partner.rate * 100) + end + + it "logged out (10)" do + visit "/landing/gift-card" + + find('h1.jam-track-name', '$10 or $20 JAMTRACKS GIFT CARDS') + find('h2.original-artist', 'A PERFECT GIFT FOR THE HOLIDAYS') + jamtrack_rocketman.jam_track_tracks.each do |track| + if track.master? + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: 'Master Mix') + else + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: track.instrument.description) + end + end + find('a.browse-all')['href'].should eq("/client?search=#/jamtrack/search") + find('button.ten-jt', text: 'ADD $20 CARD TO CART').trigger(:click) + + + find('h1', text: 'check out') + + # fill out all billing info and account info + fill_in 'billing-first-name', with: 'Seth' + fill_in 'billing-last-name', with: 'Call' + fill_in 'billing-address1', with: '10704 Buckthorn Drive' + fill_in 'billing-city', with: 'Austin' + fill_in 'billing-state', with: 'Texas' + fill_in 'billing-zip', with: '78759' + fill_in 'card-number', with: '4111111111111111' + fill_in 'card-verify', with: '012' + + # fill in user/email/tos + fill_in 'email', with: 'bogus+gc2@jamkazam.com' + fill_in 'password', with: 'jam123' + find('#divJamKazamTos ins.iCheck-helper').trigger(:click) # accept TOS + + # try to submit, and see order page + find('#payment-info-next').trigger(:click) + + find('.order-items-value.sub-total', text:'20.00') + find('.order-items-value.taxes', text:'1.65') + find('.order-items-value.order-total', text:'$20.00') + find('.order-items-value.grand-total', text:'$21.65') + + # click the ORDER button + find('.place-order-center a.button-orange.place-order').trigger(:click) + + # and now we should see confirmation, and a notice that we are in a normal browser + find('.thanks-detail.gift-card') + + created_user = User.find_by_email('bogus+gc2@jamkazam.com') + + sleep 3 # challenge to all comers! WHY DO I HAVE TO SLEEP FOR THIS ASSERTION TO BE TRUE! GAH . and 1 second won't do it + + created_user.reload + created_user.has_redeemable_jamtrack.should be_true + created_user.gifted_jamtracks.should eq(0) + created_user.gift_card_purchases.length.should eq(1) + + # verify sales data + created_user.sales.length.should eq(1) + sale = created_user.sales.last + sale.sale_line_items.length.should eq(1) + line_item = sale.sale_line_items[0] + line_item.product_type.should eq('GiftCardType') + line_item.product_id.should eq('jam_tracks_10') + line_item.quantity.should eq(1) + line_item.free.should eq(0) + line_item.unit_price.should eq(20.00) + line_item.sale.should eq(sale) + end + + it "logged in (5)" do + fast_signin(user,"/landing/gift-card") + + find('h1.jam-track-name', '$10 or $20 JAMTRACKS GIFT CARDS') + find('h2.original-artist', 'A PERFECT GIFT FOR THE HOLIDAYS') + jamtrack_rocketman.jam_track_tracks.each do |track| + if track.master? + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="other"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: 'Master Mix') + else + find('.tracks.previews[data-id="' + track.id + '"] img.instrument-icon[data-instrument-id="' + track.instrument.id + '"]') + find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text: track.instrument.description) + end + end + find('a.browse-all')['href'].should eq("/client?search=#/jamtrack/search") + find('button.five-jt', text: 'ADD $10 CARD TO CART').trigger(:click) + + # land in shopping cart because we are a user; log in + find('.proceed-checkout').trigger(:click) + + find('h1', text: 'check out') + + # fill out all billing info and account info + fill_in 'billing-first-name', with: 'Seth' + fill_in 'billing-last-name', with: 'Call' + fill_in 'billing-address1', with: '10704 Buckthorn Drive' + fill_in 'billing-city', with: 'Austin' + fill_in 'billing-state', with: 'Texas' + fill_in 'billing-zip', with: '78759' + fill_in 'card-number', with: '4111111111111111' + fill_in 'card-verify', with: '012' + + # try to submit, and see order page + find('#payment-info-next').trigger(:click) + + find('.order-items-value.sub-total', text:'10.00') + find('.order-items-value.taxes', text:'0.83') + find('.order-items-value.order-total', text:'$10.00') + find('.order-items-value.grand-total', text:'$10.83') + + # click the ORDER button + find('.place-order-center a.button-orange.place-order').trigger(:click) + + # and now we should see confirmation, and a notice that we are in a normal browser + find('.thanks-detail.gift-card') + + user.reload + + sleep 3 # challenge to all comers! WHY DO I HAVE TO SLEEP FOR THIS ASSERTION TO BE TRUE! GAH . and 1 second won't do it + + user.reload + user.has_redeemable_jamtrack.should be_true + user.gifted_jamtracks.should eq(0) + user.gift_card_purchases.length.should eq(1) + end + +end diff --git a/web/spec/features/redeem_giftcard_spec.rb b/web/spec/features/redeem_giftcard_spec.rb index f075e8d88..631fb8e3a 100644 --- a/web/spec/features/redeem_giftcard_spec.rb +++ b/web/spec/features/redeem_giftcard_spec.rb @@ -30,13 +30,13 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') user = User.find_by_email("gifter1@jamkazam.com") gift_card.reload gift_card.user.should eq(user) user.reload - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) end it "validates correctly" do @@ -58,13 +58,13 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') user = User.find_by_email("gifter2@jamkazam.com") gift_card.reload gift_card.user.should eq(user) user.reload - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) end it "converts shopping cart items to free" do @@ -89,7 +89,7 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') cart.reload cart.marked_for_redeem.should eq(1) @@ -108,12 +108,12 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') gift_card.reload gift_card.user.should eq(user1) user1.reload - user1.gifted_jamtracks.should eq(10) + user1.gifted_jamtracks.should eq(5) end end @@ -133,12 +133,12 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') gift_card.reload gift_card.user.should eq(user1) user1.reload - user1.gifted_jamtracks.should eq(10) + user1.gifted_jamtracks.should eq(5) end it "converts shopping cart items to free" do @@ -156,12 +156,12 @@ describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature = fill_in "code", with: gift_card.code find('button.redeem-giftcard').trigger(:click) find('.done-action a.go-browse').trigger(:click) - find('.no-free-jamtrack') + find('.free-jamtrack') gift_card.reload gift_card.user.should eq(user1) user1.reload - user1.gifted_jamtracks.should eq(10) + user1.gifted_jamtracks.should eq(5) cart.reload cart.marked_for_redeem.should eq(1) end diff --git a/web/spec/managers/user_manager_spec.rb b/web/spec/managers/user_manager_spec.rb index 974fbf031..b0747dac4 100644 --- a/web/spec/managers/user_manager_spec.rb +++ b/web/spec/managers/user_manager_spec.rb @@ -730,7 +730,7 @@ describe UserManager do gift_card.user.should eq(user) user = User.find(user.id) user.has_redeemable_jamtrack.should be_true - user.gifted_jamtracks.should eq(10) + user.gifted_jamtracks.should eq(5) user.gift_cards[0].should eq(gift_card) end