From 04aa1a9b3c5a3c986151c2533be7676b3efdff86 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 23 Mar 2015 08:52:52 -0500 Subject: [PATCH] * wip --- ruby/lib/jam_ruby/models/shopping_cart.rb | 33 ++- web/app/assets/javascripts/checkout_order.js | 30 ++- web/app/assets/javascripts/shopping_cart.js | 4 +- .../client/checkout_order.css.scss | 2 +- .../api_shopping_carts_controller.rb | 21 +- .../views/clients/_checkout_order.html.slim | 22 +- web/spec/features/checkout_spec.rb | 239 +++++++++++++++++- 7 files changed, 305 insertions(+), 46 deletions(-) diff --git a/ruby/lib/jam_ruby/models/shopping_cart.rb b/ruby/lib/jam_ruby/models/shopping_cart.rb index 4faffae08..66c2572e1 100644 --- a/ruby/lib/jam_ruby/models/shopping_cart.rb +++ b/ruby/lib/jam_ruby/models/shopping_cart.rb @@ -8,19 +8,20 @@ module JamRuby validates :cart_id, presence: true validates :cart_type, presence: true validates :cart_class_name, presence: true - validates :marked_for_redeem, numericality: {only_integer: true} + validates :marked_for_redeem, numericality: {only_integer: true} default_scope order('created_at DESC') def product_info product = self.cart_product - {name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem:marked_for_redeem} unless product.nil? + {name: product.name, price: product.price, product_id: cart_id, plan_code: product.plan_code, total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem} unless product.nil? end # multiply (quantity - redeemable) by price def total_price(product) (quantity - marked_for_redeem) * product.price end + def cart_product self.cart_class_name.classify.constantize.find_by_id self.cart_id unless self.cart_class_name.blank? end @@ -57,7 +58,7 @@ module JamRuby mark_redeem = false if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack mark_redeem = true # start out assuming we can redeem... - shopping_carts.each do |shopping_cart| + any_user.shopping_carts.each do |shopping_cart| # but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true if shopping_cart.cart_type == product.class::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0 mark_redeem = false @@ -67,5 +68,31 @@ module JamRuby end mark_redeem end + + # adds a jam_track to cart, checking for promotions + def self.add_jam_track_to_cart(any_user, jam_track) + ShoppingCart.transaction do + mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user) + @cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem) + end + 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 + cart.destroy + # check if we should move the redemption + mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user) + + carts = any_user.shopping_carts + + # if we find any carts on the account, mark one redeemable + if mark_redeem && carts.length > 0 + carts[0].redeem(mark_redeem) + carts[0].save + end + end + + end end end \ No newline at end of file diff --git a/web/app/assets/javascripts/checkout_order.js b/web/app/assets/javascripts/checkout_order.js index 5ca574c7e..c6a45062e 100644 --- a/web/app/assets/javascripts/checkout_order.js +++ b/web/app/assets/javascripts/checkout_order.js @@ -28,6 +28,7 @@ var $backBtn = null; var $orderPrompt = null; var $emptyCartPrompt = null; + var $noAccountInfoPrompt = null; function beforeShow() { @@ -56,9 +57,10 @@ function beforeShowOrder() { $orderPrompt.addClass('hidden') $emptyCartPrompt.addClass('hidden') + $noAccountInfoPrompt.addClass('hidden') $orderPanel.removeClass("hidden") $thanksPanel.addClass("hidden") - $orderContent.find(".place-order").addClass('disabled').off('click', placeOrder) + $screen.find(".place-order").addClass('disabled').off('click', placeOrder) step = 3; renderNavigation(); populateOrderPage(); @@ -77,6 +79,7 @@ .fail(function(jqXHR) { if(jqXHR.status == 404) { // no account for this user + $noAccountInfoPrompt.removeClass('hidden') app.notify({ title: "No account information", text: "Please restart the checkout process." }, null, @@ -97,9 +100,19 @@ $.each(carts, function(index, cart) { sub_total += parseFloat(cart.product_info.total_price) }); - //data.grand_total = (sub_total + taxes).toFixed(2) - data.sub_total = sub_total.toFixed(2) - //data.taxes = taxes.toFixed(2) + if(carts.length == 0) { + data.grand_total = '-.--' + data.sub_total = '-.--' + data.taxes = '-.--' + data.shipping_handling = '-.--' + } + else { + data.grand_total = 'Calculating...' + data.sub_total = '$' + sub_total.toFixed(2) + data.taxes = 'Calculating...' + data.shipping_handling = '$0.00' + } + data.carts = carts data.billing_info = recurlyAccountInfo.billing_info data.shipping_info = recurlyAccountInfo.address @@ -121,12 +134,14 @@ if(carts.length == 0) { $orderPrompt.addClass('hidden') $emptyCartPrompt.removeClass('hidden') + $noAccountInfoPrompt.addClass('hidden') $placeOrder.addClass('disabled') } else { logger.debug("cart has " + carts.length + " items in it") $orderPrompt.removeClass('hidden') $emptyCartPrompt.addClass('hidden') + $noAccountInfoPrompt.addClass('hidden') $placeOrder.removeClass('disabled').on('click', placeOrder) var planPricing = {} @@ -184,7 +199,7 @@ if(allResolved) { $screen.find('.order-right-page .order-items-value.taxes').text('$' + totalTax.toFixed(2)) - $screen.find('.order-right-page .order-items-value.order-total').text('$' + totalPrice.toFixed(2)) + $screen.find('.order-right-page .order-items-value.grand-total').text('$' + totalPrice.toFixed(2)) } else { @@ -349,8 +364,9 @@ $purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header"); $purchasedJamTracks = $purchasedJamTrack.find(".purchased-list") $backBtn = $screen.find('.back'); - $orderPrompt = $screen.find('.order-prompt'); - $emptyCartPrompt = $screen.find('.empty-cart-prompt'); + $orderPrompt = $screen.find('.order-prompt'); + $emptyCartPrompt = $screen.find('.empty-cart-prompt'); + $noAccountInfoPrompt = $screen.find('.no-account-info-prompt'); $orderContent = $orderPanel.find(".order-content"); if ($screen.length == 0) throw "$screen must be specified"; diff --git a/web/app/assets/javascripts/shopping_cart.js b/web/app/assets/javascripts/shopping_cart.js index eb782bd56..86157b96d 100644 --- a/web/app/assets/javascripts/shopping_cart.js +++ b/web/app/assets/javascripts/shopping_cart.js @@ -34,15 +34,13 @@ } else { app.user().done(function(user) { - if(user.reuse_card) { + if(user.has_recurly_account && user.reuse_card) { window.location = '/client#/checkoutOrder'; } else { window.location = '/client#/checkoutPayment'; } - }) - } } diff --git a/web/app/assets/stylesheets/client/checkout_order.css.scss b/web/app/assets/stylesheets/client/checkout_order.css.scss index d1fc81380..92db40291 100644 --- a/web/app/assets/stylesheets/client/checkout_order.css.scss +++ b/web/app/assets/stylesheets/client/checkout_order.css.scss @@ -196,7 +196,7 @@ background-color:#262626; @include border_box_sizing; - .order-total { + .grand-total { color: #ed3618; } } diff --git a/web/app/controllers/api_shopping_carts_controller.rb b/web/app/controllers/api_shopping_carts_controller.rb index bfc3eb750..761c3dc81 100644 --- a/web/app/controllers/api_shopping_carts_controller.rb +++ b/web/app/controllers/api_shopping_carts_controller.rb @@ -16,11 +16,7 @@ class ApiShoppingCartsController < ApiController raise StateError, "Invalid JamTrack." end - ShoppingCart.transaction do - mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user) - @cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem) - end - + @cart = ShoppingCart.add_jam_track_to_cart(any_user, jam_track) if @cart.errors.any? response.status = :unprocessable_entity @@ -50,20 +46,7 @@ class ApiShoppingCartsController < ApiController @cart = any_user.shopping_carts.find_by_id(params[:id]) raise StateError, "Invalid Cart." if @cart.nil? - ShoppingCart.transaction do - @cart.destroy - - # check if we should move the redemption - mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user) - - carts = any_user.shopping_carts - - # if we find any carts on the account, mark one redeemable - if mark_redeem && carts.length > 0 - carts[0].redeem(mark_redeem) - carts[0].save - end - end + ShoppingCart.remove_jam_track_from_cart(any_user, @cart) respond_with responder: ApiResponder, :status => 204 end diff --git a/web/app/views/clients/_checkout_order.html.slim b/web/app/views/clients/_checkout_order.html.slim index dadca75b5..47e37bc72 100644 --- a/web/app/views/clients/_checkout_order.html.slim +++ b/web/app/views/clients/_checkout_order.html.slim @@ -17,6 +17,10 @@ div layout="screen" layout-id="checkoutOrder" id="checkoutOrderScreen" class="sc | You have nothing in your cart. You can go browse for JamTracks  a href="/client#/jamtrack" here | . + p.no-account-info-prompt.hidden + | You have no billing info. Please go back to the  + a href="/client#/checkoutPayment" Payment + |  page to enter your billing info. .order-content @@ -122,22 +126,22 @@ script type='text/template' id='template-order-content' .clearall .billing-caption ORDER SUMMARY: - .order-items-header Order items: - .order-items-value ${{data.sub_total}} + .order-items-header.order-total Order items: + .order-items-value.order-total= "{{data.sub_total}}" .clearall - .order-items-header Shipping & handling: - .order-items-value $0.00 + .order-items-header.shipping-handling Shipping & handling: + .order-items-value.shipping-handling= "{{data.shipping_handling}}" .clearall .line - .order-items-header Total before tax: - .order-items-value ${{data.sub_total}} + .order-items-header.sub-total Total before tax: + .order-items-value.sub-total= "{{data.sub_total}}" .clearall .order-items-header.taxes Taxes: - .order-items-value.taxes Calculating... + .order-items-value.taxes= "{{data.taxes}}" .clearall .line - .order-items-header.order-total Order total: - .order-items-value.order-total Calculating... + .order-items-header.grand-total Order total: + .order-items-value.grand-total= "{{data.grand_total}}" .clearall .order-help span By placing your order, you agree to JamKazam's diff --git a/web/spec/features/checkout_spec.rb b/web/spec/features/checkout_spec.rb index 1099fc0ad..3f9e27d5a 100644 --- a/web/spec/features/checkout_spec.rb +++ b/web/spec/features/checkout_spec.rb @@ -3,12 +3,45 @@ require 'spec_helper' describe "Checkout", :js => true, :type => :feature, :capybara_feature => true do let(:user) { FactoryGirl.create(:user) } - let(:jamtrack_acdc_backinblack) { FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack') } + let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack } + let(:jamtrack_pearljam_evenflow) { @jamtrack_pearljam_evenflow } + + 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' + } + } + + def create_account(user, billing_info) + @recurlyClient.create_account(user, billing_info) + @created_accounts << user + end before(:all) do Capybara.javascript_driver = :poltergeist Capybara.current_driver = Capybara.javascript_driver - Capybara.default_wait_time = 30 # these tests are SLOOOOOW + Capybara.default_wait_time = 60 # these tests are SLOOOOOW + + @recurlyClient = RecurlyClient.new + @created_accounts = [] + + @jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack') + @jamtrack_pearljam_evenflow = FactoryGirl.create(:jam_track, name: 'Even Flow', original_artist: 'Pearl Jam', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-pearljam-evenflow') + + # make sure plans are there + @recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack) + @recurlyClient.create_jam_track_plan(@jamtrack_pearljam_evenflow) unless @recurlyClient.find_jam_track_plan(@jamtrack_pearljam_evenflow) + end @@ -22,13 +55,28 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d stub_const("APP_CONFIG", web_config) end + after(:each) do + @created_accounts.each do |user| + if user.recurly_code + begin + @account = Recurly::Account.find(user.recurly_code) + if @account.present? + @account.destroy + end + rescue + end + end + end + + end + def verify_nav(selected) 3.times do |i| badge = i + 1 - if i == selected + if badge == selected find('.badge-number', text:badge) else find('.badge-number.disabled', text:badge) @@ -222,14 +270,197 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d it "payment shows saved card info correctly if user has billing info and reuse_card set to true" do + puts "jamtrack_acdc_backinblack #{jamtrack_acdc_backinblack}" + ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack) + user.reuse_card = true - user.recurly_code = user.id + @recurlyClient.create_account(user, billing_info) + user.has_redeemable_jamtrack = true + user.save! + + fast_signin(user, '/client#/checkoutPayment') + + # ok, the user has a free jamtrack... verify that display confirms this + find('p.payment-prompt.free-jamtrack') + + # verify that all billing info looks disabled + have_field('billing-first-name', disabled: true) + have_field('billing-last-name', disabled: true) + have_field('billing-address1', disabled: true) + have_field('billing-address2', disabled: true) + have_field('billing-city', disabled: true) + have_field('billing-state', disabled: true) + have_field('billing-zip', disabled: true) + have_field('billing-country', disabled: true) + have_field('card-number', disabled: true) + have_field('card_expire-date_2i', disabled: true) + have_field('card_expire-date_1i', disabled: true) + have_field('card-number', disabled: true) + have_field('card-verify', disabled: true) + + # verify that the use current card checkbox is checked, and that the 'save card' checkbox is checking + find('#reuse-existing-card').checked?.should be_true + find('#save-card:checked').checked?.should be_true + + # then uncheck 'reuse-existing-card', which should re-enable all the fields that were just disabled + find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click) + + # verify that all billing info looks enabled now, since 'reuse-existing-card' was unchecked + have_field('billing-first-name', disabled: false) + have_field('billing-last-name', disabled: false) + have_field('billing-address1', disabled: false) + have_field('billing-address2', disabled: false) + have_field('billing-city', disabled: false) + have_field('billing-state', disabled: false) + have_field('billing-zip', disabled: false) + have_field('billing-country', disabled: false) + have_field('card-number', disabled: false) + have_field('card_expire-date_2i', disabled: false) + have_field('card_expire-date_1i', disabled: false) + have_field('card-number', disabled: false) + have_field('card-verify', disabled: false) + + # ok, we want to fiddle some values, and later prove that they will be ignored once we set reuse-existing-card back to checked + fill_in 'billing-first-name', with: 'Bobby' + fill_in 'billing-last-name', with: 'Junk' + fill_in 'billing-address1', with: '10702 Buckthorn Drive' + + # flip it back to reuse existing + find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click) + + # hit next... we should move on to the payment screen + + # try to submit, and see order page + find('#payment-info-next').trigger(:click) + + # find empty shopping cart prompt notice + find('p.order-prompt') + + account = @recurlyClient.get_account(user) + account.billing_info.address1.should eq(billing_info[:address1]) + account.billing_info.first_name.should eq(billing_info[:first_name]) + account.billing_info.last_name.should eq(billing_info[:last_name]) + + find('.order-items-value.sub-total', text:'0.00') + find('.order-items-value.taxes', text:'0.00') + find('.order-items-value.order-total', text:'0.00') + end + + it "payment allows user to enter new billing info" do + + ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack) + + user.reuse_card = true + @recurlyClient.create_account(user, billing_info) + user.has_redeemable_jamtrack = true + user.save! + + fast_signin(user, '/client#/checkoutPayment') + + # ok, the user has a free jamtrack... verify that display confirms this + find('p.payment-prompt.free-jamtrack') + + # verify that the use current card checkbox is checked, and that the 'save card' checkbox is checking + find('#reuse-existing-card').checked?.should be_true + find('#save-card:checked').checked?.should be_true + + # then uncheck 'reuse-existing-card', which should re-enable all the fields that were just disabled + find('.reuse-existing-card-checkbox ins.iCheck-helper').trigger(:click) + + # ok, we want to fiddle some values, and later prove that they will be ignored once we set reuse-existing-card back to checked + fill_in 'billing-first-name', with: 'Bobby' + fill_in 'billing-last-name', with: 'Junk' + fill_in 'billing-address1', with: '10702 Buckthorn Drive' + fill_in 'card-number', with: '4111111111111111' + fill_in 'card-verify', with: '013' + + # hit next... we should move on to the payment screen + + # try to submit, and see order page + find('#payment-info-next').trigger(:click) + + # find empty shopping cart prompt notice + find('p.order-prompt') + + account = @recurlyClient.get_account(user) + account.billing_info.first_name.should eq('Bobby') + account.billing_info.last_name.should eq('Junk') + account.billing_info.address1.should eq('10702 Buckthorn Drive') + + find('.order-items-value.sub-total', text:'0.00') + find('.order-items-value.taxes', text:'0.00') + find('.order-items-value.order-total', text:'0.00') + end + + + it "user with no redeemable jamtrack is not show the free-jamtrack prompt" do user.has_redeemable_jamtrack = false user.save! fast_signin(user, '/client#/checkoutPayment') + # ok, the user has a free jamtrack... verify that display confirms this find('p.payment-prompt.no-free-jamtrack') + end + end + + describe "Checkout Order" do + it "shows empty cart notice" do + + + fast_signin(user, '/client#/checkoutOrder') + + # the user should be toldy they have a empty cart + find('p.empty-cart-prompt') + find('.order-items-value.order-total', text:'-.--') + find('.order-items-value.sub-total', text:'-.--') + find('.order-items-value.taxes', text:'-.--') + find('.order-items-value.order-total', text:'-.--') + + # verify that both Place Your Orders are disabled + find('.order-content .place-order.disabled') + find('.order-panel .place-order.disabled') + end + + it "shows one free item correctly" do + + ShoppingCart.add_jam_track_to_cart(user, jamtrack_acdc_backinblack) + + @recurlyClient.create_account(user, billing_info) + + user.reuse_card = true + user.has_redeemable_jamtrack = true + user.save! + + fast_signin(user, '/client#/checkoutOrder') + + find('p.order-prompt') + find('.order-items-value.order-total', text:'$0.00') + find('.order-items-value.shipping-handling', text:'$0.00') + find('.order-items-value.sub-total', text:'$0.00') + find('.order-items-value.taxes', text:'$0.00') + find('.order-items-value.grand-total', text:'$0.00') + end + + it "shows one free, one not free item correctly" do + + end + + it "shows one non-free item correctly" do + + end + + it "shows two non-free items correctly" do + + end + end + + describe "Complete Checkout Flow" do + it "for anonyous user" do + + end + + it "for existing user" do end end