From eccea9d9ff960a47a5729790f0f8aee2f96acecc Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 6 Feb 2014 09:31:13 +1100 Subject: [PATCH 01/87] When removing a product's option type, remove its variants' associated option values --- app/models/spree/product_decorator.rb | 6 ++++ .../spree/product_option_type_decorator.rb | 10 +++++++ spec/models/spree/product_spec.rb | 29 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 app/models/spree/product_option_type_decorator.rb diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 5150a760c8..3f570bdd2f 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -1,4 +1,10 @@ Spree::Product.class_eval do + # We have an after_destroy callback on Spree::ProductOptionType. However, if we + # don't specify dependent => destroy on this association, it is not called. See: + # https://github.com/rails/rails/issues/7618 + has_many :option_types, :through => :product_option_types, :dependent => :destroy + + belongs_to :supplier, :class_name => 'Enterprise' has_many :product_distributions, :dependent => :destroy diff --git a/app/models/spree/product_option_type_decorator.rb b/app/models/spree/product_option_type_decorator.rb new file mode 100644 index 0000000000..e091b62efa --- /dev/null +++ b/app/models/spree/product_option_type_decorator.rb @@ -0,0 +1,10 @@ +Spree::ProductOptionType.class_eval do + after_destroy :remove_option_values + + def remove_option_values + self.product.variants_including_master.each do |variant| + option_values = variant.option_values.where(option_type_id: self.option_type) + variant.option_values.destroy *option_values + end + end +end diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 6c7dfb9091..7e8f679f26 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -412,6 +412,35 @@ module Spree end end + describe "option types" do + describe "removing an option type" do + it "removes the associated option values from all variants" do + # Given a product with a variant unit option type and values + p = create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) + v1 = create(:variant, product: p, unit_value: 100, option_values: []) + v2 = create(:variant, product: p, unit_value: 200, option_values: []) + + # And a custom option type and values + ot = create(:option_type, name: 'foo', presentation: 'foo') + p.option_types << ot + ov1 = create(:option_value, option_type: ot, name: 'One', presentation: 'One') + ov2 = create(:option_value, option_type: ot, name: 'Two', presentation: 'Two') + v1.option_values << ov1 + v2.option_values << ov2 + + # When we remove the custom option type + p.option_type_ids = p.option_type_ids.reject { |id| id == ot.id } + + # Then the associated option values should have been removed from the variants + v1.option_values(true).should_not include ov1 + v2.option_values(true).should_not include ov2 + + # And the option values themselves should still exist + Spree::OptionValue.where(id: [ov1.id, ov2.id]).count.should == 2 + end + end + end + describe "Stock filtering" do it "considers products that are on_demand as being in stock" do product = create(:simple_product, on_demand: true) From ed8727708de2e1b31247d7b93dcb566c312378f2 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 6 Feb 2014 13:03:40 +1100 Subject: [PATCH 02/87] Improving responsiveness, hopefully --- .../stylesheets/darkswarm/shop.css.sass | 39 ++++++++++++++----- app/views/shop/_products.html.haml | 2 +- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 59c39133b3..b86b91c9ce 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -29,6 +29,9 @@ shop location font-family: "AvenirBla_IE", "AvenirBla" padding-right: 16px + @media all and (max-width: 768px) + location, location + small + display: block #distributor_title float: left @@ -38,28 +41,37 @@ shop ordercycle @media all and (max-width: 768px) float: left + clear: left padding-bottom: 12px display: block float: right form.custom - width: 400px + //width: 400px text-align: right + margin-right: 1em + @media all and (max-width: 768px) + padding-left: 1em + padding-top: 1em & > strong line-height: 2.5 font-size: 1.29em padding-right: 14px + @media all and (max-width: 768px) + font-size: 1.2em .custom.dropdown width: 280px display: inline-block background: transparent border-width: 2px border-color: #666666 - font-size: 1.28em + font-size: 1em margin-bottom: 0 + @media all and (max-width: 768px) + font-size: 1.2em + width: 180px closing font-size: 0.875em display: block - float: right padding-top: 14px tabs @@ -87,7 +99,11 @@ shop products display: block - padding-top: 36px + padding-top: 2.3em + @media all and (max-width: 768px) + padding-top: 1em + input.button.right + float: left table table-layout: fixed width: 100% @@ -96,9 +112,9 @@ shop th line-height: 50px &.name - //width: 300px - &.notes - width: 140px + width: 330px + //&.notes + //width: 140px &.variant width: 100px &.quantity, &.bulk, &.price @@ -116,9 +132,12 @@ shop border-right: 0px td padding: 20px 0px - &.name img - float: left - margin-right: 30px + &.name + img + float: left + margin-right: 30px + div + min-width: 150px tr.product-description display: none diff --git a/app/views/shop/_products.html.haml b/app/views/shop/_products.html.haml index 57140531cd..98b6a7ecf1 100644 --- a/app/views/shop/_products.html.haml +++ b/app/views/shop/_products.html.haml @@ -48,7 +48,7 @@ %small{"ng-show" => "(product.variants.length > 0)"} from {{ product.price | currency }} %tr.product-description - %td{colspan: 6}{{ product.notes | truncate:80 }} + %td{colspan: 2}{{ product.notes | truncate:80 }} %tr{"ng-repeat" => "variant in product.variants", "ng-show" => "product.show_variants"} = render partial: "shop/variant" %input.button.right{type: :submit, value: "Check Out"} From a1f75aa55eb4ee12f548a6b7253790026095e386 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 6 Feb 2014 13:49:27 +1100 Subject: [PATCH 03/87] Fixing up the top menu a bit --- app/assets/stylesheets/darkswarm/shop.css.sass | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index b86b91c9ce..97d92ef1f7 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -88,14 +88,20 @@ shop margin: 0px 0px 0px 40px p max-width: 555px + @media all and (max-width: 768px) + height: auto !important & > .title, &.active > .title text-transform: uppercase line-height: 50px + @media all and (max-width: 768px) + line-height: 30px !important border: none &, &:hover background: none a padding: 0px 2.2em + @media all and (max-width: 768px) + line-height: inherit !important products display: block From 33691085bdb60db5e10c714e17ddd1ffc4345576 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 6 Feb 2014 13:43:28 +1100 Subject: [PATCH 04/87] Only perform scaling when a scale is present. Fixes items values not saving then loading as infinity. --- .../admin/bulk_product_update.js.coffee | 7 +++++-- .../unit/bulk_product_update_spec.js.coffee | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index c6028f21aa..81ad95f9ca 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -221,7 +221,10 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.variantUnitValue = (product, variant) -> if variant.unit_value - variant.unit_value / product.variant_unit_scale + if product.variant_unit_scale + variant.unit_value / product.variant_unit_scale + else + variant.unit_value else null @@ -376,7 +379,7 @@ productEditModule.controller "AdminProductEditCtrl", [ if match product = $scope.findProduct(product.id) variant.unit_value = parseFloat(match[1]) || null - variant.unit_value *= product.variant_unit_scale if variant.unit_value + variant.unit_value *= product.variant_unit_scale if variant.unit_value && product.variant_unit_scale variant.unit_description = match[3] diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index ef8a7c3cb2..9fe79278d4 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -460,6 +460,11 @@ describe "AdminProductEditCtrl", -> variant = {unit_value: 5} expect(scope.variantUnitValue(product, variant)).toEqual 5000 + it "returns the unscaled value when the product has no scale", -> + product = {} + variant = {unit_value: 5} + expect(scope.variantUnitValue(product, variant)).toEqual 5 + it "returns null when the variant has no unit_value", -> product = {} variant = {} @@ -672,6 +677,16 @@ describe "AdminProductEditCtrl", -> unit_description: '' unit_value_with_description: "250.5" + it "does not convert value when using a non-scaled unit", -> + testProduct = {id: 123} + testVariant = {unit_value_with_description: "12"} + scope.products = [testProduct] + scope.packVariant(testProduct, testVariant) + expect(testVariant).toEqual + unit_value: 12 + unit_description: '' + unit_value_with_description: "12" + describe "filtering products", -> beforeEach -> From f187041606a92d3a0ca5c0c5bab846a0e902709e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 6 Feb 2014 14:05:26 +1100 Subject: [PATCH 05/87] Do not display a space separating units and scaled values. eg. 1kg, 4 boxes --- app/models/spree/variant_decorator.rb | 7 +++++- spec/models/spree/variant_spec.rb | 32 +++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index 8150ce6529..de9a9fa9d0 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -54,13 +54,18 @@ Spree::Variant.class_eval do def option_value_name value, unit = option_value_value_unit + separator = value_scaled? ? '' : ' ' name_fields = [] - name_fields << "#{value} #{unit}" if value.present? && unit.present? + name_fields << "#{value}#{separator}#{unit}" if value.present? && unit.present? name_fields << unit_description if unit_description.present? name_fields.join ' ' end + def value_scaled? + self.product.variant_unit_scale.present? + end + def option_value_value_unit if unit_value.present? if %w(weight volume).include? self.product.variant_unit diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 930ba631ad..9dc0d70cf0 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -171,20 +171,48 @@ module Spree it "when description is blank" do v = Spree::Variant.new unit_description: nil v.stub(:option_value_value_unit) { %w(value unit) } - v.send(:option_value_name).should == "value unit" + v.stub(:value_scaled?) { true } + v.send(:option_value_name).should == "valueunit" end it "when description is present" do v = Spree::Variant.new unit_description: 'desc' v.stub(:option_value_value_unit) { %w(value unit) } - v.send(:option_value_name).should == "value unit desc" + v.stub(:value_scaled?) { true } + v.send(:option_value_name).should == "valueunit desc" end it "when value is blank and description is present" do v = Spree::Variant.new unit_description: 'desc' v.stub(:option_value_value_unit) { [nil, nil] } + v.stub(:value_scaled?) { true } v.send(:option_value_name).should == "desc" end + + it "spaces value and unit when value is unscaled" do + v = Spree::Variant.new unit_description: nil + v.stub(:option_value_value_unit) { %w(value unit) } + v.stub(:value_scaled?) { false } + v.send(:option_value_name).should == "value unit" + end + end + + describe "determining if a variant's value is scaled" do + it "returns true when the product has a scale" do + p = Spree::Product.new variant_unit_scale: 1000 + v = Spree::Variant.new + v.stub(:product) { p } + + v.send(:value_scaled?).should be_true + end + + it "returns false otherwise" do + p = Spree::Product.new + v = Spree::Variant.new + v.stub(:product) { p } + + v.send(:value_scaled?).should be_false + end end describe "generating option value's value and unit" do From aa9b5c84d92f329574961582ccbc95b0917bec0e Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 6 Feb 2014 16:14:29 +1100 Subject: [PATCH 06/87] Fix expected variant option type name/presentation --- spec/models/spree/variant_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb index 9dc0d70cf0..2031a4d22b 100644 --- a/spec/models/spree/variant_spec.rb +++ b/spec/models/spree/variant_spec.rb @@ -113,8 +113,8 @@ module Spree ov = Spree::OptionValue.last ov.option_type.should == @ot - ov.name.should == '10 g foo' - ov.presentation.should == '10 g foo' + ov.name.should == '10g foo' + ov.presentation.should == '10g foo' v.option_values.should include ov end @@ -139,8 +139,8 @@ module Spree ov = v.option_values.last ov.option_type.should == @ot - ov.name.should == '10 g foo' - ov.presentation.should == '10 g foo' + ov.name.should == '10g foo' + ov.presentation.should == '10g foo' v_orig.option_values.should include ov end From b1118735ecb6647d0d4b0488cdd7337f50b1c32f Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 6 Feb 2014 16:25:33 +1100 Subject: [PATCH 07/87] One CSS change, adding a test for le price adjustments --- app/assets/stylesheets/darkswarm/shop.css.sass | 2 ++ spec/features/consumer/shopping_spec.rb | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 97d92ef1f7..3781b380f8 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -142,6 +142,8 @@ shop img float: left margin-right: 30px + @media all and (max-width: 768px) + margin-right: 1em div min-width: 150px tr.product-description diff --git a/spec/features/consumer/shopping_spec.rb b/spec/features/consumer/shopping_spec.rb index 7a73801169..b7e085db10 100644 --- a/spec/features/consumer/shopping_spec.rb +++ b/spec/features/consumer/shopping_spec.rb @@ -116,9 +116,9 @@ feature "As a consumer I want to shop with a distributor", js: true do let(:oc) { create(:simple_order_cycle, distributors: [distributor]) } let(:product) { create(:simple_product) } let(:variant) { create(:variant, product: product) } + let(:exchange) { Exchange.find(oc.exchanges.to_enterprises(distributor).outgoing.first.id) } before do - exchange = Exchange.find(oc.exchanges.to_enterprises(distributor).outgoing.first.id) exchange.update_attribute :pickup_time, "frogs" exchange.variants << product.master exchange.variants << variant @@ -141,7 +141,17 @@ feature "As a consumer I want to shop with a distributor", js: true do find(".collapse").trigger "click" page.should_not have_text variant.options_text end - it "allows the user to expand variants" + + it "uses the adjusted price" do + enterprise_fee1 = create(:enterprise_fee, amount: 20) + enterprise_fee2 = create(:enterprise_fee, amount: 3) + exchange.enterprise_fees = [enterprise_fee1, enterprise_fee2] + exchange.save + + visit shop_path + select "frogs", :from => "order_cycle_id" + page.should have_content "$#{(product.price + 23.00)}" + end end describe "Filtering on hand and on demand products" do From 202f95a8fc33606eddc238f4dad8897b29690d9a Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 6 Feb 2014 17:14:14 +1100 Subject: [PATCH 08/87] Fix for broken percentage Enterprise Fees in place, no test yet --- app/models/order_cycle.rb | 4 ++-- spec/models/order_cycle_spec.rb | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 39fed33674..d710befc10 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -147,7 +147,8 @@ class OrderCycle < ActiveRecord::Base enterprise_fees_for(variant, distributor).sum do |fee| # Spree's Calculator interface accepts Orders or LineItems, # so we meet that interface with a struct. - line_item = OpenStruct.new variant: variant, quantity: 1 + # Amount is faked, this is a method on LineItem + line_item = OpenStruct.new variant: variant, quantity: 1, amount: (variant.price) fee[:enterprise_fee].compute_amount(line_item) end end @@ -159,7 +160,6 @@ class OrderCycle < ActiveRecord::Base enterprise_fees_for(variant, distributor).each { |fee| create_adjustment_for_fee line_item, fee[:enterprise_fee], fee[:label], fee[:role] } end - private # -- Fees diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index d5ee190efa..20b6da2feb 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -323,6 +323,9 @@ describe OrderCycle do order_cycle.fees_for(product.master, distributor).should == 23 end + + + it "sums percentage fees for the variant" end describe "creating adjustments for a line item" do From 1ca9a86042799036e66b249d6cb69e4870e0c5f7 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 11:09:37 +1100 Subject: [PATCH 09/87] Getting in a spec asserting that percentage-based fees are applied --- spec/models/order_cycle_spec.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 20b6da2feb..bd8d835d91 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -325,7 +325,19 @@ describe OrderCycle do end - it "sums percentage fees for the variant" + it "sums percentage fees for the variant" do + coordinator = create(:distributor_enterprise) + distributor = create(:distributor_enterprise) + order_cycle = create(:simple_order_cycle) + enterprise_fee1 = create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) + product = create(:simple_product, price: 10.00) + + create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, + enterprise_fees: [enterprise_fee1], variants: [product.master]) + + product.master.price.should == 10.00 + order_cycle.fees_for(product.master, distributor).should == 2.00 + end end describe "creating adjustments for a line item" do @@ -350,6 +362,7 @@ describe OrderCycle do ef3 = double(:enterprise_fee) incoming_exchange = double(:exchange, enterprise_fees: [ef1], incoming?: true) outgoing_exchange = double(:exchange, enterprise_fees: [ef2], incoming?: false) + oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } oc.stub(:coordinator_fees) { [ef3] } oc.stub(:adjustment_label_for) { 'label' } From 26e4adf7a6fd0260729684f784a18ee4f40a5ade Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 13:53:36 +1100 Subject: [PATCH 10/87] Adding the routes, controller, new action and some tests --- app/controllers/shop_controller.rb | 1 - config/routes.rb | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index a4fe70c71b..fd1926e102 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -34,7 +34,6 @@ class ShopController < BaseController private def set_distributor - unless @distributor = current_distributor redirect_to root_path end diff --git a/config/routes.rb b/config/routes.rb index 371620f0ec..bcbd37d7db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,13 +1,18 @@ Openfoodnetwork::Application.routes.draw do root :to => 'home#temp_landing_page' - resource :shop, controller: :shop do get :products post :order_cycle get :order_cycle end + namespace :shop do + resource :checkout, controller: :checkout do + get :new + end + end + resources :enterprises do collection do get :suppliers From cec0ad8a832d39cb839065c629e5496cdc775f3c Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 13:55:25 +1100 Subject: [PATCH 11/87] Adding ze boilerplate --- .../javascripts/shop/checkout.js.coffee | 3 ++ app/assets/stylesheets/shop/checkout.css.scss | 3 ++ app/controllers/shop/checkout_controller.rb | 24 ++++++++++ app/helpers/shop/checkout_helper.rb | 2 + app/views/shop/checkout/new.html.haml | 0 .../shop/checkout_controller_spec.rb | 21 +++++++++ .../consumer/shopping/checkout_spec.rb | 45 +++++++++++++++++++ spec/helpers/shop/checkout_helper_spec.rb | 15 +++++++ 8 files changed, 113 insertions(+) create mode 100644 app/assets/javascripts/shop/checkout.js.coffee create mode 100644 app/assets/stylesheets/shop/checkout.css.scss create mode 100644 app/controllers/shop/checkout_controller.rb create mode 100644 app/helpers/shop/checkout_helper.rb create mode 100644 app/views/shop/checkout/new.html.haml create mode 100644 spec/controllers/shop/checkout_controller_spec.rb create mode 100644 spec/features/consumer/shopping/checkout_spec.rb create mode 100644 spec/helpers/shop/checkout_helper_spec.rb diff --git a/app/assets/javascripts/shop/checkout.js.coffee b/app/assets/javascripts/shop/checkout.js.coffee new file mode 100644 index 0000000000..761567942f --- /dev/null +++ b/app/assets/javascripts/shop/checkout.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ diff --git a/app/assets/stylesheets/shop/checkout.css.scss b/app/assets/stylesheets/shop/checkout.css.scss new file mode 100644 index 0000000000..727d410b19 --- /dev/null +++ b/app/assets/stylesheets/shop/checkout.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Shop::Checkout controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb new file mode 100644 index 0000000000..1281f75b6b --- /dev/null +++ b/app/controllers/shop/checkout_controller.rb @@ -0,0 +1,24 @@ +class Shop::CheckoutController < BaseController + layout 'darkswarm' + + before_filter :set_distributor + before_filter :require_order_cycle + + def new + + end + + private + + def set_distributor + unless @distributor = current_distributor + redirect_to root_path + end + end + + def require_order_cycle + unless current_order_cycle + redirect_to shop_path + end + end +end diff --git a/app/helpers/shop/checkout_helper.rb b/app/helpers/shop/checkout_helper.rb new file mode 100644 index 0000000000..263dcc8321 --- /dev/null +++ b/app/helpers/shop/checkout_helper.rb @@ -0,0 +1,2 @@ +module Shop::CheckoutHelper +end diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/new.html.haml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/controllers/shop/checkout_controller_spec.rb b/spec/controllers/shop/checkout_controller_spec.rb new file mode 100644 index 0000000000..20e0b32c5d --- /dev/null +++ b/spec/controllers/shop/checkout_controller_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Shop::CheckoutController do + it "redirects home when no distributor is selected" do + get :new + response.should redirect_to root_path + end + + it "redirects to the shop when no order cycle is selected" do + controller.stub(:current_distributor).and_return(double(:distributor)) + get :new + response.should redirect_to shop_path + end + + it "renders when both distributor and order cycle is selected" do + controller.stub(:current_distributor).and_return(double(:distributor)) + controller.stub(:order_cycle).and_return(double(:order_cycle)) + get :new + response.should be_success + end +end diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb new file mode 100644 index 0000000000..3c328ca8b1 --- /dev/null +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +include AuthenticationWorkflow +include WebHelper + +feature "As a consumer I want to check out my cart", js: true do + describe "Attempting to access checkout without meeting the preconditions" do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } + + before do + create_enterprise_group_for distributor + end + + it "redirects to the homepage if no distributor is selected" do + visit "/shop/checkout" + current_path.should == root_path + end + + it "redirects to the shop page if we have a distributor but no order cycle selected" do + select_distributor + visit "/shop/checkout" + current_path.should == shop_path + end + + + it "renders checkout if we have distributor and order cycle selected" do + select_distributor + select_order_cycle + visit "/shop/checkout" + current_path.should == "/shop/checkout" + end + end +end + +def select_distributor + visit "/" + click_link distributor.name +end + +def select_order_cycle + exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) + visit "/shop" + select exchange.pickup_time, from: "order_cycle_id" +end diff --git a/spec/helpers/shop/checkout_helper_spec.rb b/spec/helpers/shop/checkout_helper_spec.rb new file mode 100644 index 0000000000..c9500ab0b6 --- /dev/null +++ b/spec/helpers/shop/checkout_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the Shop::CheckoutHelper. For example: +# +# describe Shop::CheckoutHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +describe Shop::CheckoutHelper do + pending "add some examples to (or delete) #{__FILE__}" +end From 63f85fef0efcb1c27b6b69cb6321cc7e22031578 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 14:34:49 +1100 Subject: [PATCH 12/87] Starting to get in the login stuff --- app/views/shop/checkout/_login.html.haml | 14 +++++ app/views/shop/checkout/_signup.html.haml | 13 +++++ app/views/shop/checkout/new.html.haml | 3 ++ .../consumer/shopping/checkout_spec.rb | 51 ++++++++++++++++--- 4 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 app/views/shop/checkout/_login.html.haml create mode 100644 app/views/shop/checkout/_signup.html.haml diff --git a/app/views/shop/checkout/_login.html.haml b/app/views/shop/checkout/_login.html.haml new file mode 100644 index 0000000000..7e5170805f --- /dev/null +++ b/app/views/shop/checkout/_login.html.haml @@ -0,0 +1,14 @@ +%h2 Login + += form_for Spree::User.new, :remote => true, :html => {'data-type' => :json}, :as => :spree_user, :url => spree.spree_user_session_path do |f| + %p + = f.label :email, t(:email) + = f.email_field :email, :class => 'title', :tabindex => 1, :id => "login_spree_user_email" + %p + = f.label :password, t(:password) + = f.password_field :password, :class => 'title', :tabindex => 2, :id => "login_spree_user_password" + %p + %label + = f.check_box :remember_me + = f.label :remember_me, t(:remember_me) + %p= f.submit t(:login), :class => 'button primary', :tabindex => 3, :id => "login_spree_user_remember_me" diff --git a/app/views/shop/checkout/_signup.html.haml b/app/views/shop/checkout/_signup.html.haml new file mode 100644 index 0000000000..449abeb892 --- /dev/null +++ b/app/views/shop/checkout/_signup.html.haml @@ -0,0 +1,13 @@ +%h2 Sign Up += form_for Spree::User.new, :as => :spree_user, :url => spree.spree_user_registration_path(@spree_user) do |f| + %p + = f.label :email, t(:email) + = f.email_field :email, :class => 'title', :id => "signup_spree_user_email" + %p + = f.label :password, t(:password) + = f.password_field :password, :class => 'title', :id => "signup_spree_user_password" + %p + = f.label :password_confirmation, t(:confirm_password) + = f.password_field :password_confirmation, :class => 'title', :id => "signup_spree_user_password_confirmation" + + = f.submit t(:create), :class => 'button' diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/new.html.haml index e69de29bb2..7081ab3b7c 100644 --- a/app/views/shop/checkout/new.html.haml +++ b/app/views/shop/checkout/new.html.haml @@ -0,0 +1,3 @@ +- unless spree_current_user + = render partial: "shop/checkout/login" + = render partial: "shop/checkout/signup" diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 3c328ca8b1..0800691fda 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -4,14 +4,14 @@ include AuthenticationWorkflow include WebHelper feature "As a consumer I want to check out my cart", js: true do + let(:distributor) { create(:distributor_enterprise) } + let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } + + before do + create_enterprise_group_for distributor + end + describe "Attempting to access checkout without meeting the preconditions" do - let(:distributor) { create(:distributor_enterprise) } - let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } - - before do - create_enterprise_group_for distributor - end - it "redirects to the homepage if no distributor is selected" do visit "/shop/checkout" current_path.should == root_path @@ -31,6 +31,43 @@ feature "As a consumer I want to check out my cart", js: true do current_path.should == "/shop/checkout" end end + + describe "Login behaviour" do + before do + select_distributor + select_order_cycle + end + + it "renders the login form if user is logged out" do + visit "/shop/checkout" + within "section[role='main']" do + page.should have_content "Login" + end + end + + it "does not not render the login form if user is logged in" do + login_to_consumer_section + visit "/shop/checkout" + within "section[role='main']" do + page.should_not have_content "Login" + end + end + + it "renders the signup link if user is logged out" do + visit "/shop/checkout" + within "section[role='main']" do + page.should have_content "Sign Up" + end + end + + it "does not not render the signup form if user is logged in" do + login_to_consumer_section + visit "/shop/checkout" + within "section[role='main']" do + page.should_not have_content "Sign Up" + end + end + end end def select_distributor From 7af1d72ef95b2f847b292869838aa4bdc02980f9 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 16:07:18 +1100 Subject: [PATCH 13/87] Working login and signup forms for checkout, specs, redirect back to checkout on success --- app/controllers/application_controller.rb | 5 ++++ .../user_sessions_controller_decorator.rb | 2 +- app/views/shop/checkout/_login.html.haml | 3 +- app/views/shop/checkout/_signup.html.haml | 2 +- app/views/shop/checkout/new.html.haml | 6 ++-- .../shop/checkout_controller_spec.rb | 2 +- .../consumer/shopping/checkout_spec.rb | 28 +++++++++++++++++++ spec/helpers/shop/checkout_helper_spec.rb | 15 ---------- 8 files changed, 41 insertions(+), 22 deletions(-) delete mode 100644 spec/helpers/shop/checkout_helper_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 88ec04f574..eb90094d48 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,6 +5,11 @@ class ApplicationController < ActionController::Base before_filter :load_data_for_sidebar before_filter :require_certified_hostname + + def after_sign_in_path_for(resource) + request.referer || root_path + end + private def load_data_for_menu @cms_site = Cms::Site.where(:identifier => 'open-food-network').first diff --git a/app/controllers/spree/user_sessions_controller_decorator.rb b/app/controllers/spree/user_sessions_controller_decorator.rb index 3f3345b54a..c0d5d72381 100644 --- a/app/controllers/spree/user_sessions_controller_decorator.rb +++ b/app/controllers/spree/user_sessions_controller_decorator.rb @@ -24,4 +24,4 @@ Spree::UserSessionsController.class_eval do end end end -end \ No newline at end of file +end diff --git a/app/views/shop/checkout/_login.html.haml b/app/views/shop/checkout/_login.html.haml index 7e5170805f..4509a9dea7 100644 --- a/app/views/shop/checkout/_login.html.haml +++ b/app/views/shop/checkout/_login.html.haml @@ -1,6 +1,5 @@ %h2 Login - -= form_for Spree::User.new, :remote => true, :html => {'data-type' => :json}, :as => :spree_user, :url => spree.spree_user_session_path do |f| += form_for Spree::User.new, :html => {'data-type' => :json}, :as => :spree_user, :url => spree.spree_user_session_path do |f| %p = f.label :email, t(:email) = f.email_field :email, :class => 'title', :tabindex => 1, :id => "login_spree_user_email" diff --git a/app/views/shop/checkout/_signup.html.haml b/app/views/shop/checkout/_signup.html.haml index 449abeb892..51b8265f19 100644 --- a/app/views/shop/checkout/_signup.html.haml +++ b/app/views/shop/checkout/_signup.html.haml @@ -10,4 +10,4 @@ = f.label :password_confirmation, t(:confirm_password) = f.password_field :password_confirmation, :class => 'title', :id => "signup_spree_user_password_confirmation" - = f.submit t(:create), :class => 'button' + = f.submit "Sign Up", :class => 'button' diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/new.html.haml index 7081ab3b7c..0ea78fdc71 100644 --- a/app/views/shop/checkout/new.html.haml +++ b/app/views/shop/checkout/new.html.haml @@ -1,3 +1,5 @@ - unless spree_current_user - = render partial: "shop/checkout/login" - = render partial: "shop/checkout/signup" + %section#checkout_login + = render partial: "shop/checkout/login" + %section#checkout_signup + = render partial: "shop/checkout/signup" diff --git a/spec/controllers/shop/checkout_controller_spec.rb b/spec/controllers/shop/checkout_controller_spec.rb index 20e0b32c5d..59b58b49b0 100644 --- a/spec/controllers/shop/checkout_controller_spec.rb +++ b/spec/controllers/shop/checkout_controller_spec.rb @@ -14,7 +14,7 @@ describe Shop::CheckoutController do it "renders when both distributor and order cycle is selected" do controller.stub(:current_distributor).and_return(double(:distributor)) - controller.stub(:order_cycle).and_return(double(:order_cycle)) + controller.stub(:current_order_cycle).and_return(create(:order_cycle)) get :new response.should be_success end diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 0800691fda..da0df4fb4f 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -33,6 +33,7 @@ feature "As a consumer I want to check out my cart", js: true do end describe "Login behaviour" do + let(:user) { create_enterprise_user } before do select_distributor select_order_cycle @@ -67,6 +68,33 @@ feature "As a consumer I want to check out my cart", js: true do page.should_not have_content "Sign Up" end end + + it "redirects to the checkout page when logging in from the checkout page" do + visit "/shop/checkout" + within "#checkout_login" do + fill_in "spree_user[email]", with: user.email + fill_in "spree_user[password]", with: user.password + click_button "Login" + end + current_path.should == "/shop/checkout" + within "section[role='main']" do + page.should_not have_content "Login" + end + end + + it "redirects to the checkout page when signing up from the checkout page" do + visit "/shop/checkout" + within "#checkout_signup" do + fill_in "spree_user[email]", with: "test@gmail.com" + fill_in "spree_user[password]", with: "password" + fill_in "spree_user[password_confirmation]", with: "password" + click_button "Sign Up" + end + current_path.should == "/shop/checkout" + within "section[role='main']" do + page.should_not have_content "Sign Up" + end + end end end diff --git a/spec/helpers/shop/checkout_helper_spec.rb b/spec/helpers/shop/checkout_helper_spec.rb deleted file mode 100644 index c9500ab0b6..0000000000 --- a/spec/helpers/shop/checkout_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -# Specs in this file have access to a helper object that includes -# the Shop::CheckoutHelper. For example: -# -# describe Shop::CheckoutHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end -describe Shop::CheckoutHelper do - pending "add some examples to (or delete) #{__FILE__}" -end From b11533a6299daf5d34d7bab644b8c115dc27058e Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 7 Feb 2014 16:52:18 +1100 Subject: [PATCH 14/87] Renaming ShopController to Shop::ShopController --- app/controllers/{ => shop}/shop_controller.rb | 6 +++--- app/views/shop/{ => shop}/_about_us.html.haml | 0 app/views/shop/{ => shop}/_contact_us.html.haml | 0 app/views/shop/{ => shop}/_last_order_cycle.html.haml | 0 app/views/shop/{ => shop}/_next_order_cycle.html.haml | 0 app/views/shop/{ => shop}/_order_cycle.rabl | 0 app/views/shop/{ => shop}/_order_cycles.html.haml | 6 +++--- app/views/shop/{ => shop}/_products.html.haml | 2 +- app/views/shop/{ => shop}/_variant.html.haml | 0 app/views/shop/{ => shop}/products.rabl | 0 app/views/shop/{ => shop}/show.html.haml | 8 ++++---- config/routes.rb | 2 +- spec/controllers/{ => shop}/shop_controller_spec.rb | 2 +- spec/features/consumer/{ => shopping}/shopping_spec.rb | 0 14 files changed, 13 insertions(+), 13 deletions(-) rename app/controllers/{ => shop}/shop_controller.rb (89%) rename app/views/shop/{ => shop}/_about_us.html.haml (100%) rename app/views/shop/{ => shop}/_contact_us.html.haml (100%) rename app/views/shop/{ => shop}/_last_order_cycle.html.haml (100%) rename app/views/shop/{ => shop}/_next_order_cycle.html.haml (100%) rename app/views/shop/{ => shop}/_order_cycle.rabl (100%) rename app/views/shop/{ => shop}/_order_cycles.html.haml (86%) rename app/views/shop/{ => shop}/_products.html.haml (98%) rename app/views/shop/{ => shop}/_variant.html.haml (100%) rename app/views/shop/{ => shop}/products.rabl (100%) rename app/views/shop/{ => shop}/show.html.haml (89%) rename spec/controllers/{ => shop}/shop_controller_spec.rb (99%) rename spec/features/consumer/{ => shopping}/shopping_spec.rb (100%) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop/shop_controller.rb similarity index 89% rename from app/controllers/shop_controller.rb rename to app/controllers/shop/shop_controller.rb index fd1926e102..e0862d205e 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop/shop_controller.rb @@ -1,4 +1,4 @@ -class ShopController < BaseController +class Shop::ShopController < BaseController layout "darkswarm" before_filter :set_distributor @@ -22,12 +22,12 @@ class ShopController < BaseController if request.post? if oc = OrderCycle.with_distributor(@distributor).active.find_by_id(params[:order_cycle_id]) current_order(true).set_order_cycle! oc - render partial: "shop/order_cycle" + render partial: "shop/shop/order_cycle" else render status: 404, json: "" end else - render partial: "shop/order_cycle" + render partial: "shop/shop/order_cycle" end end diff --git a/app/views/shop/_about_us.html.haml b/app/views/shop/shop/_about_us.html.haml similarity index 100% rename from app/views/shop/_about_us.html.haml rename to app/views/shop/shop/_about_us.html.haml diff --git a/app/views/shop/_contact_us.html.haml b/app/views/shop/shop/_contact_us.html.haml similarity index 100% rename from app/views/shop/_contact_us.html.haml rename to app/views/shop/shop/_contact_us.html.haml diff --git a/app/views/shop/_last_order_cycle.html.haml b/app/views/shop/shop/_last_order_cycle.html.haml similarity index 100% rename from app/views/shop/_last_order_cycle.html.haml rename to app/views/shop/shop/_last_order_cycle.html.haml diff --git a/app/views/shop/_next_order_cycle.html.haml b/app/views/shop/shop/_next_order_cycle.html.haml similarity index 100% rename from app/views/shop/_next_order_cycle.html.haml rename to app/views/shop/shop/_next_order_cycle.html.haml diff --git a/app/views/shop/_order_cycle.rabl b/app/views/shop/shop/_order_cycle.rabl similarity index 100% rename from app/views/shop/_order_cycle.rabl rename to app/views/shop/shop/_order_cycle.rabl diff --git a/app/views/shop/_order_cycles.html.haml b/app/views/shop/shop/_order_cycles.html.haml similarity index 86% rename from app/views/shop/_order_cycles.html.haml rename to app/views/shop/shop/_order_cycles.html.haml index 5c2986bedf..22463e677b 100644 --- a/app/views/shop/_order_cycles.html.haml +++ b/app/views/shop/shop/_order_cycles.html.haml @@ -1,7 +1,7 @@ %ordercycle{"ng-controller" => "OrderCycleCtrl"} :javascript - angular.module('Shop').value('orderCycleData', #{render "shop/order_cycle"}) + angular.module('Shop').value('orderCycleData', #{render "shop/shop/order_cycle"}) - if @order_cycles.empty? @@ -10,8 +10,8 @@ Please contact your hub directly to see if they accept late orders, or wait until the next cycle opens. - = render partial: "shop/next_order_cycle" - = render partial: "shop/last_order_cycle" + = render partial: "shop/shop/next_order_cycle" + = render partial: "shop/shop/last_order_cycle" - else %form.custom diff --git a/app/views/shop/_products.html.haml b/app/views/shop/shop/_products.html.haml similarity index 98% rename from app/views/shop/_products.html.haml rename to app/views/shop/shop/_products.html.haml index 98b6a7ecf1..85bd5cf7ac 100644 --- a/app/views/shop/_products.html.haml +++ b/app/views/shop/shop/_products.html.haml @@ -50,5 +50,5 @@ %tr.product-description %td{colspan: 2}{{ product.notes | truncate:80 }} %tr{"ng-repeat" => "variant in product.variants", "ng-show" => "product.show_variants"} - = render partial: "shop/variant" + = render partial: "shop/shop/variant" %input.button.right{type: :submit, value: "Check Out"} diff --git a/app/views/shop/_variant.html.haml b/app/views/shop/shop/_variant.html.haml similarity index 100% rename from app/views/shop/_variant.html.haml rename to app/views/shop/shop/_variant.html.haml diff --git a/app/views/shop/products.rabl b/app/views/shop/shop/products.rabl similarity index 100% rename from app/views/shop/products.rabl rename to app/views/shop/shop/products.rabl diff --git a/app/views/shop/show.html.haml b/app/views/shop/shop/show.html.haml similarity index 89% rename from app/views/shop/show.html.haml rename to app/views/shop/shop/show.html.haml index 6f1f0b0181..5063841152 100644 --- a/app/views/shop/show.html.haml +++ b/app/views/shop/shop/show.html.haml @@ -9,7 +9,7 @@ %small %a{href: "/"} Change location - = render partial: "shop/order_cycles" + = render partial: "shop/shop/order_cycles" -#%description @@ -44,9 +44,9 @@ %p Contact %products.row - = render partial: "shop/products" + = render partial: "shop/shop/products" #footer %section.row - = render partial: "shop/contact_us" - = render partial: "shop/about_us" + = render partial: "shop/shop/contact_us" + = render partial: "shop/shop/about_us" = render partial: "shared/copyright" diff --git a/config/routes.rb b/config/routes.rb index bcbd37d7db..479970737d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Openfoodnetwork::Application.routes.draw do root :to => 'home#temp_landing_page' - resource :shop, controller: :shop do + resource :shop, controller: "shop/shop" do get :products post :order_cycle get :order_cycle diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop/shop_controller_spec.rb similarity index 99% rename from spec/controllers/shop_controller_spec.rb rename to spec/controllers/shop/shop_controller_spec.rb index 6b539fdbef..f440da9805 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop/shop_controller_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ShopController do +describe Shop::ShopController do let(:d) { create(:distributor_enterprise) } it "redirects to the home page if no distributor is selected" do diff --git a/spec/features/consumer/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb similarity index 100% rename from spec/features/consumer/shopping_spec.rb rename to spec/features/consumer/shopping/shopping_spec.rb From c7c70252d02e640bb2dc84da255c805f83e029ac Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 11 Feb 2014 19:13:11 +1100 Subject: [PATCH 15/87] ofnTrackProduct and ofnTrackVariant accept nested properties as arguments --- .../admin/bulk_product_update.js.coffee | 25 +++++++++---------- .../unit/bulk_product_update_spec.js.coffee | 13 +++++++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 81ad95f9ca..ce198c4a74 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -20,30 +20,31 @@ productEditModule.directive "ofnDecimal", -> viewValue -productEditModule.directive "ofnTrackProduct", -> +productEditModule.directive "ofnTrackProduct", ['$parse', ($parse) -> require: "ngModel" link: (scope, element, attrs, ngModel) -> - property_name = attrs.ofnTrackProduct ngModel.$parsers.push (viewValue) -> if ngModel.$dirty - addDirtyProperty scope.dirtyProducts, scope.product.id, property_name, viewValue + parsedPropertyName = $parse(attrs.ofnTrackProduct) + addDirtyProperty scope.dirtyProducts, scope.product.id, parsedPropertyName, viewValue scope.displayDirtyProducts() viewValue + ] -productEditModule.directive "ofnTrackVariant", -> +productEditModule.directive "ofnTrackVariant", ['$parse', ($parse) -> require: "ngModel" link: (scope, element, attrs, ngModel) -> - property_name = attrs.ofnTrackVariant ngModel.$parsers.push (viewValue) -> dirtyVariants = {} dirtyVariants = scope.dirtyProducts[scope.product.id].variants if scope.dirtyProducts.hasOwnProperty(scope.product.id) and scope.dirtyProducts[scope.product.id].hasOwnProperty("variants") if ngModel.$dirty - addDirtyProperty dirtyVariants, scope.variant.id, property_name, viewValue - addDirtyProperty scope.dirtyProducts, scope.product.id, "variants", dirtyVariants + parsedPropertyName = $parse(attrs.ofnTrackVariant) + addDirtyProperty dirtyVariants, scope.variant.id, parsedPropertyName, viewValue + addDirtyProperty scope.dirtyProducts, scope.product.id, $parse("variants"), dirtyVariants scope.displayDirtyProducts() viewValue - + ] productEditModule.directive "ofnToggleVariants", -> link: (scope, element, attrs) -> @@ -515,13 +516,11 @@ filterSubmitProducts = (productsToFilter) -> filteredProducts -addDirtyProperty = (dirtyObjects, objectID, propertyName, propertyValue) -> - if dirtyObjects.hasOwnProperty(objectID) - dirtyObjects[objectID][propertyName] = propertyValue - else +addDirtyProperty = (dirtyObjects, objectID, parsedPropertyName, propertyValue) -> + if !dirtyObjects.hasOwnProperty(objectID) dirtyObjects[objectID] = {} dirtyObjects[objectID]["id"] = objectID - dirtyObjects[objectID][propertyName] = propertyValue + parsedPropertyName.assign(dirtyObjects[objectID], propertyValue) removeCleanProperty = (dirtyObjects, objectID, propertyName) -> diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 9fe79278d4..4381bec9e1 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -202,10 +202,17 @@ describe "filtering products for submission to database", -> describe "Maintaining a live record of dirty products and properties", -> + parse = null + beforeEach -> + module "ofn.bulk_product_edit" + beforeEach inject(($parse) -> + parse = $parse + ) + describe "adding product properties to the dirtyProducts object", -> # Applies to both products and variants it "adds the product and the property to the list if property is dirty", -> dirtyProducts = {} - addDirtyProperty dirtyProducts, 1, "name", "Product 1" + addDirtyProperty dirtyProducts, 1, parse("name"), "Product 1" expect(dirtyProducts).toEqual 1: id: 1 name: "Product 1" @@ -216,7 +223,7 @@ describe "Maintaining a live record of dirty products and properties", -> id: 1 notaname: "something" - addDirtyProperty dirtyProducts, 1, "name", "Product 3" + addDirtyProperty dirtyProducts, 1, parse("name"), "Product 3" expect(dirtyProducts).toEqual 1: id: 1 notaname: "something" @@ -228,7 +235,7 @@ describe "Maintaining a live record of dirty products and properties", -> id: 1 name: "Product 1" - addDirtyProperty dirtyProducts, 1, "name", "Product 2" + addDirtyProperty dirtyProducts, 1, parse("name"), "Product 2" expect(dirtyProducts).toEqual 1: id: 1 name: "Product 2" From 0b255ed1e954bba06ca55c2150022c74c067a51f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 11 Feb 2014 19:19:12 +1100 Subject: [PATCH 16/87] Pack the master variant before sending to the server --- .../admin/bulk_product_update.js.coffee | 10 ++++-- .../unit/bulk_product_update_spec.js.coffee | 31 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index ce198c4a74..3d13e0423c 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -216,8 +216,13 @@ productEditModule.controller "AdminProductEditCtrl", [ if product.variants for variant in product.variants - unit_value = $scope.variantUnitValue product, variant - variant.unit_value_with_description = "#{unit_value || ''} #{variant.unit_description || ''}".trim() + $scope.loadVariantVariantUnit product, variant + $scope.loadVariantVariantUnit product, product.master if product.master + + + $scope.loadVariantVariantUnit = (product, variant) -> + unit_value = $scope.variantUnitValue product, variant + variant.unit_value_with_description = "#{unit_value || ''} #{variant.unit_description || ''}".trim() $scope.variantUnitValue = (product, variant) -> @@ -369,6 +374,7 @@ productEditModule.controller "AdminProductEditCtrl", [ else product.variant_unit = product.variant_unit_with_scale product.variant_unit_scale = null + $scope.packVariant product, product.master if product.master if product.variants for id, variant of product.variants $scope.packVariant product, variant diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 4381bec9e1..769f6cf8c8 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -427,12 +427,24 @@ describe "AdminProductEditCtrl", -> scope.loadVariantUnit product expect(product.variant_unit_with_scale).toEqual "items" + it "loads data for variants (inc. master)", -> + spyOn scope, "loadVariantVariantUnit" + + product = + variant_unit_scale: 1.0 + master: {id: 1, unit_value: 1, unit_description: '(one)'} + variants: [{id: 2, unit_value: 2, unit_description: '(two)'}] + scope.loadVariantUnit product + + expect(scope.loadVariantVariantUnit).toHaveBeenCalledWith product, product.variants[0] + expect(scope.loadVariantVariantUnit).toHaveBeenCalledWith product, product.master + describe "setting variant unit_value_with_description", -> it "sets by combining unit_value and unit_description", -> product = variant_unit_scale: 1.0 variants: [{id: 1, unit_value: 1, unit_description: '(bottle)'}] - scope.loadVariantUnit product + scope.loadVariantVariantUnit product, product.variants[0] expect(product.variants[0]).toEqual id: 1 unit_value: 1 @@ -443,21 +455,21 @@ describe "AdminProductEditCtrl", -> product = variant_unit_scale: 1.0 variants: [{id: 1, unit_value: 1}] - scope.loadVariantUnit product + scope.loadVariantVariantUnit product, product.variants[0] expect(product.variants[0].unit_value_with_description).toEqual '1' it "uses unit_description when value is missing", -> product = variant_unit_scale: 1.0 variants: [{id: 1, unit_description: 'Small'}] - scope.loadVariantUnit product + scope.loadVariantVariantUnit product, product.variants[0] expect(product.variants[0].unit_value_with_description).toEqual 'Small' it "converts values from base value to chosen unit", -> product = variant_unit_scale: 1000.0 variants: [{id: 1, unit_value: 2500}] - scope.loadVariantUnit product + scope.loadVariantVariantUnit product, product.variants[0] expect(product.variants[0].unit_value_with_description).toEqual '2.5' @@ -617,6 +629,17 @@ describe "AdminProductEditCtrl", -> variant_unit_scale: null variant_unit_with_scale: 'items' + it "packs the master variant", -> + spyOn scope, "packVariant" + testVariant = {id: 1} + testProduct = + id: 1 + master: testVariant + + scope.packProduct(testProduct) + + expect(scope.packVariant).toHaveBeenCalledWith(testProduct, testVariant) + it "packs each variant", -> spyOn scope, "packVariant" testVariant = {id: 1} From e845c0dc0647517e122b5ccc7420b29998925d18 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 11 Feb 2014 19:20:46 +1100 Subject: [PATCH 17/87] Remove derived attributes from master variant --- .../javascripts/admin/bulk_product_update.js.coffee | 3 +++ spec/javascripts/unit/bulk_product_update_spec.js.coffee | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 3d13e0423c..734b3d0db0 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -401,6 +401,9 @@ productEditModule.controller "AdminProductEditCtrl", [ delete variant.unit_value_with_description # If we end up live-updating this field, we might want to reinstate its verification here delete variant.options_text + if product.master + delete product.master.unit_value_with_description + delete product.master.options_text products_filtered diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 769f6cf8c8..90d80dd295 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -850,6 +850,15 @@ describe "AdminProductEditCtrl", -> } ] + it "returns master variant without the unit_value_with_description field", -> + scope.products = [{id: 123, master: {id: 234, unit_value_with_description: 'foo'}}] + expect(scope.productsWithoutDerivedAttributes(scope.products)).toEqual [ + { + id: 123 + master: {id: 234} + } + ] + describe "deep copying products", -> it "copies products", -> From b2ad6c7d577fe55fc0fed9d52e02deede6500884 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 11 Feb 2014 19:23:27 +1100 Subject: [PATCH 18/87] Filter products for submit includes master variant --- .../admin/bulk_product_update.js.coffee | 48 ++++++++++++------- .../unit/bulk_product_update_spec.js.coffee | 8 ++++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 734b3d0db0..14ff9557b4 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -466,6 +466,7 @@ productEditModule.filter "rangeArray", -> input.push(i) for i in [start..end] input + filterSubmitProducts = (productsToFilter) -> filteredProducts = [] if productsToFilter instanceof Object @@ -475,23 +476,16 @@ filterSubmitProducts = (productsToFilter) -> filteredVariants = [] if product.hasOwnProperty("variants") angular.forEach product.variants, (variant) -> - if not variant.deleted_at? and variant.hasOwnProperty("id") - hasUpdateableProperty = false - filteredVariant = {} - filteredVariant.id = variant.id - if variant.hasOwnProperty("on_hand") - filteredVariant.on_hand = variant.on_hand - hasUpdatableProperty = true - if variant.hasOwnProperty("price") - filteredVariant.price = variant.price - hasUpdatableProperty = true - if variant.hasOwnProperty("unit_value") - filteredVariant.unit_value = variant.unit_value - hasUpdatableProperty = true - if variant.hasOwnProperty("unit_description") - filteredVariant.unit_description = variant.unit_description - hasUpdatableProperty = true - filteredVariants.push filteredVariant if hasUpdatableProperty + result = filterSubmitVariant variant + filteredVariant = result.filteredVariant + hasUpdatableProperty = result.hasUpdatableProperty + filteredVariants.push filteredVariant if hasUpdatableProperty + + if product.hasOwnProperty("master") + result = filterSubmitVariant product.master + filteredMaster = result.filteredVariant + hasUpdatableProperty = result.hasUpdatableProperty + filteredProduct.master = filteredMaster if hasUpdatableProperty hasUpdatableProperty = false filteredProduct.id = product.id @@ -525,6 +519,26 @@ filterSubmitProducts = (productsToFilter) -> filteredProducts +filterSubmitVariant = (variant) -> + hasUpdatableProperty = false + filteredVariant = {} + if not variant.deleted_at? and variant.hasOwnProperty("id") + filteredVariant.id = variant.id + if variant.hasOwnProperty("on_hand") + filteredVariant.on_hand = variant.on_hand + hasUpdatableProperty = true + if variant.hasOwnProperty("price") + filteredVariant.price = variant.price + hasUpdatableProperty = true + if variant.hasOwnProperty("unit_value") + filteredVariant.unit_value = variant.unit_value + hasUpdatableProperty = true + if variant.hasOwnProperty("unit_description") + filteredVariant.unit_description = variant.unit_description + hasUpdatableProperty = true + {filteredVariant: filteredVariant, hasUpdatableProperty: hasUpdatableProperty} + + addDirtyProperty = (dirtyObjects, objectID, parsedPropertyName, propertyValue) -> if !dirtyObjects.hasOwnProperty(objectID) dirtyObjects[objectID] = {} diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 90d80dd295..bec7e8b8d3 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -171,6 +171,10 @@ describe "filtering products for submission to database", -> group_buy: null group_buy_unit_size: null on_demand: false + master: + id: 2 + unit_value: 250 + unit_description: "foo" variants: [ id: 1 on_hand: 2 @@ -191,6 +195,10 @@ describe "filtering products for submission to database", -> variant_unit_scale: 1 variant_unit_name: 'loaf' available_on: available_on + master: + id: 2 + unit_value: 250 + unit_description: "foo" variants_attributes: [ id: 1 on_hand: 2 From 871637c4af9bb082b2578eedf1020fbcccf1fcf2 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Wed, 12 Feb 2014 12:31:37 +1100 Subject: [PATCH 19/87] NO CAN HAZ EMPTY CART PAGE --- app/controllers/shop/checkout_controller.rb | 9 +++++++++ .../features/consumer/shopping/checkout_spec.rb | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index 1281f75b6b..71b30848c2 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -3,6 +3,7 @@ class Shop::CheckoutController < BaseController before_filter :set_distributor before_filter :require_order_cycle + before_filter :require_line_items def new @@ -21,4 +22,12 @@ class Shop::CheckoutController < BaseController redirect_to shop_path end end + + # Y U LOOK AT CART? CART IS EMPTY! + # NO CAN HAZ! + def require_line_items + if current_order.line_items.empty? + redirect_to shop_path + end + end end diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index da0df4fb4f..8cf331db8e 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -5,10 +5,14 @@ include WebHelper feature "As a consumer I want to check out my cart", js: true do let(:distributor) { create(:distributor_enterprise) } + let(:supplier) { create(:supplier_enterprise) } let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } + let(:product) { create(:simple_product, supplier: supplier) } before do create_enterprise_group_for distributor + exchange = Exchange.find(order_cycle.exchanges.to_enterprises(distributor).outgoing.first.id) + exchange.variants << product.master end describe "Attempting to access checkout without meeting the preconditions" do @@ -23,10 +27,17 @@ feature "As a consumer I want to check out my cart", js: true do current_path.should == shop_path end + it "redirects to the shop page if the current order is empty" do + select_distributor + select_order_cycle + visit "/shop/checkout" + current_path.should == shop_path + end it "renders checkout if we have distributor and order cycle selected" do select_distributor select_order_cycle + add_product_to_cart visit "/shop/checkout" current_path.should == "/shop/checkout" end @@ -37,6 +48,7 @@ feature "As a consumer I want to check out my cart", js: true do before do select_distributor select_order_cycle + add_product_to_cart end it "renders the login form if user is logged out" do @@ -108,3 +120,8 @@ def select_order_cycle visit "/shop" select exchange.pickup_time, from: "order_cycle_id" end + +def add_product_to_cart + fill_in "variants[#{product.master.id}]", with: 5 + first("form.custom > input.button.right").click +end From 4aa43cfbe039d12b7af161b56d66bf1c49593cbd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 12 Feb 2014 13:57:49 +1100 Subject: [PATCH 20/87] Set the master unit value for a product without variants --- .../admin/bulk_product_update.js.coffee | 30 +++++++------ app/models/spree/product_decorator.rb | 3 +- .../spree/admin/products/bulk_edit.html.haml | 4 +- .../spree/api/products/bulk_show.v1.rabl | 7 ++- .../admin/bulk_product_update_spec.rb | 44 ++++++++++++++++++- .../unit/bulk_product_update_spec.js.coffee | 9 ++-- 6 files changed, 73 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 14ff9557b4..6ef5b2a267 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -348,6 +348,10 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.resetProducts data $timeout -> $scope.displaySuccess() else + # console.log angular.toJson($scope.productsWithoutDerivedAttributes($scope.products)) + # console.log "---" + # console.log angular.toJson($scope.productsWithoutDerivedAttributes(data)) + # console.log "---" $scope.displayFailure "Product lists do not match." ).error (data, status) -> $scope.displayFailure "Server returned with error status: " + status @@ -365,6 +369,7 @@ productEditModule.controller "AdminProductEditCtrl", [ else $scope.setMessage $scope.updateStatusMessage, "No changes to update.", color: "grey", 3000 + $scope.packProduct = (product) -> if product.variant_unit_with_scale match = product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/) @@ -401,9 +406,7 @@ productEditModule.controller "AdminProductEditCtrl", [ delete variant.unit_value_with_description # If we end up live-updating this field, we might want to reinstate its verification here delete variant.options_text - if product.master - delete product.master.unit_value_with_description - delete product.master.options_text + delete product.master products_filtered @@ -472,23 +475,24 @@ filterSubmitProducts = (productsToFilter) -> if productsToFilter instanceof Object angular.forEach productsToFilter, (product) -> if product.hasOwnProperty("id") - filteredProduct = {} + filteredProduct = {id: product.id} filteredVariants = [] + hasUpdatableProperty = false + if product.hasOwnProperty("variants") angular.forEach product.variants, (variant) -> result = filterSubmitVariant variant filteredVariant = result.filteredVariant - hasUpdatableProperty = result.hasUpdatableProperty - filteredVariants.push filteredVariant if hasUpdatableProperty + variantHasUpdatableProperty = result.hasUpdatableProperty + filteredVariants.push filteredVariant if variantHasUpdatableProperty - if product.hasOwnProperty("master") - result = filterSubmitVariant product.master - filteredMaster = result.filteredVariant - hasUpdatableProperty = result.hasUpdatableProperty - filteredProduct.master = filteredMaster if hasUpdatableProperty + if product.master?.hasOwnProperty("unit_value") + filteredProduct.unit_value = product.master.unit_value + hasUpdatableProperty = true + if product.master?.hasOwnProperty("unit_description") + filteredProduct.unit_description = product.master.unit_description + hasUpdatableProperty = true - hasUpdatableProperty = false - filteredProduct.id = product.id if product.hasOwnProperty("name") filteredProduct.name = product.name hasUpdatableProperty = true diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 3f570bdd2f..33ccf73a81 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -11,8 +11,9 @@ Spree::Product.class_eval do has_many :distributors, :through => :product_distributions accepts_nested_attributes_for :product_distributions, :allow_destroy => true + delegate_belongs_to :master, :unit_value, :unit_description - attr_accessible :supplier_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size, :variant_unit, :variant_unit_scale, :variant_unit_name, :notes + attr_accessible :supplier_id, :distributor_ids, :product_distributions_attributes, :group_buy, :group_buy_unit_size, :variant_unit, :variant_unit_scale, :variant_unit_name, :unit_value, :unit_description, :notes validates_presence_of :supplier diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index e2ae2dcee4..68e73e5da3 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -117,7 +117,9 @@ %input{ 'ng-model' => "product.name", :name => 'product_name', 'ofn-track-product' => 'name', :type => 'text' } %td.unit{ 'ng-show' => 'columns.unit.visible' } %select.select2{ 'ng-model' => 'product.variant_unit_with_scale', :name => 'variant_unit_with_scale', 'ofn-track-product' => 'variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options' } - %input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' } + %option{'value' => '', 'ng-hide' => "hasVariants(product)"} + %input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-product' => 'master.unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "product.variant_unit_with_scale != undefined && !hasVariants(product)" } + %input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', :placeholder => 'unit', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' } %td{ 'ng-show' => 'columns.price.visible' } %input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text' } %td{ 'ng-show' => 'columns.on_hand.visible' } diff --git a/app/views/spree/api/products/bulk_show.v1.rabl b/app/views/spree/api/products/bulk_show.v1.rabl index 1f07d3d91f..6219bc193e 100644 --- a/app/views/spree/api/products/bulk_show.v1.rabl +++ b/app/views/spree/api/products/bulk_show.v1.rabl @@ -7,8 +7,11 @@ node( :on_hand ) { |p| p.on_hand.to_f.finite? ? p.on_hand : "On demand" } node( :available_on ) { |p| p.available_on.blank? ? "" : p.available_on.strftime("%F %T") } node( :permalink_live ) { |p| p.permalink } node( :supplier ) do |p| - partial 'spree/api/enterprises/bulk_show', :object => p.supplier + partial 'spree/api/enterprises/bulk_show', :object => p.supplier end node( :variants ) do |p| - partial 'spree/api/variants/bulk_index', :object => p.variants.order('id ASC') + partial 'spree/api/variants/bulk_index', :object => p.variants.order('id ASC') +end +node( :master ) do |p| + partial 'spree/api/variants/bulk_show', :object => p.master end diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 352c3d14c7..fb57fa7254 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -295,7 +295,7 @@ feature %q{ page.should have_field "on_hand", with: "18" end - scenario "updating a product with an items variant unit" do + scenario "updating a product with a variant unit of 'items'" do p = FactoryGirl.create(:product, variant_unit: 'weight', variant_unit_scale: 1000) login_to_admin_section @@ -340,6 +340,48 @@ feature %q{ page.should have_field "variant_unit_value_with_description", with: "123 abc" end + describe "setting the master unit value for a product without variants" do + it "sets the master unit value" do + p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) + + login_to_admin_section + + visit '/admin/products/bulk_edit' + + page.should have_select "variant_unit_with_scale", selected: '' + page.should_not have_field "master_unit_value_with_description", visible: true + + select "Weight (kg)", from: "variant_unit_with_scale" + fill_in "master_unit_value_with_description", with: '123 abc' + + click_button 'Update' + page.find("span#update-status-message").should have_content "Update complete" + + visit '/admin/products/bulk_edit' + + page.should have_select "variant_unit_with_scale", selected: "Weight (kg)" + page.should have_field "master_unit_value_with_description", with: "123 abc" + + p.reload + p.variant_unit.should == 'weight' + p.variant_unit_scale.should == 1000 + p.master.unit_value.should == 123000 + p.master.unit_description.should == 'abc' + end + + it "does not show the field when the product has variants" do + p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) + v = FactoryGirl.create(:variant, product: p, unit_value: nil, unit_description: nil) + + login_to_admin_section + + visit '/admin/products/bulk_edit' + + select "Weight (kg)", from: "variant_unit_with_scale" + page.should_not have_field "master_unit_value_with_description", visible: true + end + end + scenario "updating a product with variants" do s1 = FactoryGirl.create(:supplier_enterprise) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index bec7e8b8d3..604fb4e9b4 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -194,11 +194,9 @@ describe "filtering products for submission to database", -> variant_unit: 'volume' variant_unit_scale: 1 variant_unit_name: 'loaf' + unit_value: 250 + unit_description: "foo" available_on: available_on - master: - id: 2 - unit_value: 250 - unit_description: "foo" variants_attributes: [ id: 1 on_hand: 2 @@ -858,12 +856,11 @@ describe "AdminProductEditCtrl", -> } ] - it "returns master variant without the unit_value_with_description field", -> + it "removes the master variant", -> scope.products = [{id: 123, master: {id: 234, unit_value_with_description: 'foo'}}] expect(scope.productsWithoutDerivedAttributes(scope.products)).toEqual [ { id: 123 - master: {id: 234} } ] From 4d24fec6fb32cf99367ac48c4459ca9485478306 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 13 Feb 2014 09:26:07 +1100 Subject: [PATCH 21/87] BPE: Add a variant. Do not show edit on unsaved variants. Delete unsaved variants. --- .../admin/bulk_product_update.js.coffee | 41 +++- .../spree/admin/products/bulk_edit.html.haml | 8 +- .../admin/bulk_product_update_spec.rb | 42 +++- .../unit/bulk_product_update_spec.js.coffee | 223 ++++++++++++------ 4 files changed, 230 insertions(+), 84 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 6ef5b2a267..eb809d084f 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -285,6 +285,17 @@ productEditModule.controller "AdminProductEditCtrl", [ window.location = "/admin/products/" + product.permalink_live + ((if variant then "/variants/" + variant.id else "")) + "/edit" + $scope.addVariant = (product) -> + product.variants.push {id: $scope.nextVariantId()} + $scope.displayProperties[product.id].showVariants = true + + + $scope.nextVariantId = -> + $scope.variantIdCounter = 0 unless $scope.variantIdCounter? + $scope.variantIdCounter -= 1 + $scope.variantIdCounter + + $scope.deleteProduct = (product) -> if confirm("Are you sure?") $http( @@ -297,14 +308,20 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.deleteVariant = (product, variant) -> - if confirm("Are you sure?") - $http( - method: "DELETE" - url: "/api/products/" + product.id + "/variants/" + variant.id - ).success (data) -> - product.variants.splice product.variants.indexOf(variant), 1 - delete $scope.dirtyProducts[product.id].variants[variant.id] if $scope.dirtyProducts.hasOwnProperty(product.id) and $scope.dirtyProducts[product.id].hasOwnProperty("variants") and $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id) - $scope.displayDirtyProducts() + if !$scope.variantSaved(variant) + $scope.removeVariant(product, variant) + else + if confirm("Are you sure?") + $http( + method: "DELETE" + url: "/api/products/" + product.id + "/variants/" + variant.id + ).success (data) -> + $scope.removeVariant(product, variant) + + $scope.removeVariant = (product, variant) -> + product.variants.splice product.variants.indexOf(variant), 1 + delete $scope.dirtyProducts[product.id].variants[variant.id] if $scope.dirtyProducts.hasOwnProperty(product.id) and $scope.dirtyProducts[product.id].hasOwnProperty("variants") and $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id) + $scope.displayDirtyProducts() $scope.cloneProduct = (product) -> @@ -325,6 +342,14 @@ productEditModule.controller "AdminProductEditCtrl", [ Object.keys(product.variants).length > 0 + $scope.hasUnit = (product) -> + product.variant_unit_with_scale? + + + $scope.variantSaved = (variant) -> + variant.hasOwnProperty('id') && variant.id > 0 + + $scope.hasOnDemandVariants = (product) -> (variant for id, variant of product.variants when variant.on_demand).length > 0 diff --git a/app/views/spree/admin/products/bulk_edit.html.haml b/app/views/spree/admin/products/bulk_edit.html.haml index 68e73e5da3..28cefe564f 100644 --- a/app/views/spree/admin/products/bulk_edit.html.haml +++ b/app/views/spree/admin/products/bulk_edit.html.haml @@ -111,6 +111,7 @@ %tr.product %td.left-actions %a{ 'ofn-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' } + %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "!hasVariants(product) && hasUnit(product)" } %td.supplier{ 'ng-show' => 'columns.supplier.visible' } %select.select2{ 'ng-model' => 'product.supplier', :name => 'supplier', 'ofn-track-product' => 'supplier', 'ng-options' => 's.name for s in suppliers' } %td{ 'ng-show' => 'columns.name.visible' } @@ -118,7 +119,7 @@ %td.unit{ 'ng-show' => 'columns.unit.visible' } %select.select2{ 'ng-model' => 'product.variant_unit_with_scale', :name => 'variant_unit_with_scale', 'ofn-track-product' => 'variant_unit_with_scale', 'ng-options' => 'unit[1] as unit[0] for unit in variant_unit_options' } %option{'value' => '', 'ng-hide' => "hasVariants(product)"} - %input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-product' => 'master.unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "product.variant_unit_with_scale != undefined && !hasVariants(product)" } + %input{ 'ng-model' => 'product.master.unit_value_with_description', :name => 'master_unit_value_with_description', 'ofn-track-product' => 'master.unit_value_with_description', :type => 'text', :placeholder => 'value', 'ng-show' => "!hasVariants(product) && hasUnit(product)" } %input{ 'ng-model' => 'product.variant_unit_name', :name => 'variant_unit_name', 'ofn-track-product' => 'variant_unit_name', :placeholder => 'unit', 'ng-show' => "product.variant_unit_with_scale == 'items'", :type => 'text' } %td{ 'ng-show' => 'columns.price.visible' } %input{ 'ng-model' => 'product.price', 'ofn-decimal' => :true, :name => 'price', 'ofn-track-product' => 'price', :type => 'text' } @@ -135,7 +136,8 @@ %a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text" } %tr.variant{ 'ng-repeat' => 'variant in product.variants', 'ng-show' => 'displayProperties[product.id].showVariants', 'ng-class-even' => "'even'", 'ng-class-odd' => "'odd'" } %td.left-actions - %a{ :class => "variant-item icon-caret-right" } + %a{ :class => "variant-item icon-caret-right", 'ng-hide' => "$last" } + %a{ :class => "add-variant icon-plus-sign", 'ng-click' => "addVariant(product)", 'ng-show' => "$last" } %td{ 'ng-show' => 'columns.supplier.visible' } %td{ 'ng-show' => 'columns.name.visible' } {{ variant.options_text }} @@ -148,7 +150,7 @@ %span{ 'ng-bind' => 'variant.on_hand', :name => 'variant_on_hand', 'ng-show' => 'variant.on_demand' } %td{ 'ng-show' => 'columns.available_on.visible' } %td.actions - %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text" } + %a{ 'ng-click' => 'editWarn(product,variant)', :class => "edit-variant icon-edit no-text", 'ng-show' => "variantSaved(variant)" } %td.actions %td.actions %a{ 'ng-click' => 'deleteVariant(product,variant)', :class => "delete-variant icon-trash no-text" } diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index fb57fa7254..0ff07ca0ab 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -229,7 +229,8 @@ feature %q{ end end - scenario "create a new product" do + + scenario "creating a new product" do s = FactoryGirl.create(:supplier_enterprise) d = FactoryGirl.create(:distributor_enterprise) @@ -253,6 +254,44 @@ feature %q{ page.should have_field "product_name", with: 'Big Bag Of Apples' end + + scenario "creating new variants" do + # Given a product without variants or a unit + p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) + login_to_admin_section + visit '/admin/products/bulk_edit' + + # I should not see an add variant button + page.should_not have_selector 'a.add-variant', visible: true + + # When I set the unit + select "Weight (kg)", from: "variant_unit_with_scale" + + # I should see an add variant button + page.should have_selector 'a.add-variant', visible: true + + # When I add three variants + page.find('a.add-variant', visible: true).click + page.find('a.add-variant', visible: true).click + page.find('a.add-variant', visible: true).click + + # They should be added, and should see no edit buttons + page.all("tr.variant").count.should == 3 + page.should_not have_selector "a.edit-variant", visible: true + + # When I remove one, it should be removed + page.all('a.delete-variant').first.click + page.all("tr.variant").count.should == 2 + + # When I fill out variant details and hit update + + # Then the variants should be saved + + # And I should see edit buttons for the new variants + + end + + scenario "updating a product with no variants (except master)" do s1 = FactoryGirl.create(:supplier_enterprise) s2 = FactoryGirl.create(:supplier_enterprise) @@ -340,6 +379,7 @@ feature %q{ page.should have_field "variant_unit_value_with_description", with: "123 abc" end + describe "setting the master unit value for a product without variants" do it "sets the master unit value" do p = FactoryGirl.create(:product, variant_unit: nil, variant_unit_scale: nil) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index 604fb4e9b4..f87c13a21a 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -603,6 +603,43 @@ describe "AdminProductEditCtrl", -> expect(scope.hasOnDemandVariants(product)).toBe(false) + describe "determining whether a product has variants", -> + it "returns true when it does", -> + product = + variants: [{id: 1}, {id: 2}] + expect(scope.hasVariants(product)).toBe(true) + + it "returns false when it does not", -> + product = + variants: [] + expect(scope.hasVariants(product)).toBe(false) + + + describe "determining whether a product has a unit", -> + it "returns true when it does", -> + product = + variant_unit_with_scale: 'weight_1000' + expect(scope.hasUnit(product)).toBe(true) + + it "returns false when its unit is undefined", -> + product = {} + expect(scope.hasUnit(product)).toBe(false) + + + describe "determining whether a variant has been saved", -> + it "returns true when it has a positive id", -> + variant = {id: 1} + expect(scope.variantSaved(variant)).toBe(true) + + it "returns false when it has no id", -> + variant = {} + expect(scope.variantSaved(variant)).toBe(false) + + it "returns false when it has a negative id", -> + variant = {id: -1} + expect(scope.variantSaved(variant)).toBe(false) + + describe "submitting products to be updated", -> describe "packing products", -> it "extracts variant_unit_with_scale into variant_unit and variant_unit_scale", -> @@ -889,6 +926,31 @@ describe "AdminProductEditCtrl", -> expect(scope.findProduct(123)).toBeNull() + describe "adding variants", -> + beforeEach -> + scope.displayProperties ||= {123: {}} + + it "adds the first variant", -> + product = {id: 123, variants: []} + scope.addVariant(product) + expect(product).toEqual + id: 123 + variants: [{id: -1}] + + it "adds subsequent variants", -> + product = {id: 123, variants: []} + scope.addVariant(product) + scope.addVariant(product) + expect(product).toEqual + id: 123 + variants: [{id: -1}, {id: -2}] + + it "shows the variant(s)", -> + product = {id: 123, variants: []} + scope.addVariant(product) + expect(scope.displayProperties[123].showVariants).toBe(true) + + describe "deleting products", -> it "deletes products with a http delete request to /api/products/id", -> spyOn(window, "confirm").andReturn true @@ -942,83 +1004,100 @@ describe "AdminProductEditCtrl", -> describe "deleting variants", -> - it "deletes variants with a http delete request to /api/products/product_id/variants/(variant_id)", -> - spyOn(window, "confirm").andReturn true - scope.products = [ - { - id: 9 - permalink_live: "apples" - variants: [ - id: 3 - price: 12 - ] - } - { - id: 13 - permalink_live: "oranges" - } - ] - scope.dirtyProducts = {} - httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" - scope.deleteVariant scope.products[0], scope.products[0].variants[0] - httpBackend.flush() + describe "when the variant has not been saved", -> + it "removes the variant from products and dirtyProducts", -> + spyOn(window, "confirm").andReturn true + scope.products = [ + {id: 1, variants: [{id: -1}]} + ] + scope.dirtyProducts = + 1: {id: 1, variants: {'-1': {id: -1}}} + scope.deleteVariant scope.products[0], scope.products[0].variants[0] + expect(scope.products).toEqual([ + {id: 1, variants: []} + ]) + expect(scope.dirtyProducts).toEqual + 1: {id: 1, variants: {}} - it "removes the specified variant from both the variants object and scope.dirtyProducts (if it exists there)", -> - spyOn(window, "confirm").andReturn true - scope.products = [ - { - id: 9 - permalink_live: "apples" - variants: [ - { + + describe "when the variant has been saved", -> + it "deletes variants with a http delete request to /api/products/product_id/variants/(variant_id)", -> + spyOn(window, "confirm").andReturn true + scope.products = [ + { + id: 9 + permalink_live: "apples" + variants: [ id: 3 - price: 12.0 - } - { - id: 4 - price: 6.0 - } - ] - } - { - id: 13 - permalink_live: "oranges" - } - ] - scope.dirtyProducts = - 9: - id: 9 - variants: - 3: - id: 3 - price: 12.0 + price: 12 + ] + } + { + id: 13 + permalink_live: "oranges" + } + ] + scope.dirtyProducts = {} + httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" + scope.deleteVariant scope.products[0], scope.products[0].variants[0] + httpBackend.flush() - 4: - id: 4 - price: 6.0 + it "removes the specified variant from both the variants object and scope.dirtyProducts (if it exists there)", -> + spyOn(window, "confirm").andReturn true + scope.products = [ + { + id: 9 + permalink_live: "apples" + variants: [ + { + id: 3 + price: 12.0 + } + { + id: 4 + price: 6.0 + } + ] + } + { + id: 13 + permalink_live: "oranges" + } + ] + scope.dirtyProducts = + 9: + id: 9 + variants: + 3: + id: 3 + price: 12.0 + + 4: + id: 4 + price: 6.0 + + 13: + id: 13 + name: "P1" - 13: - id: 13 - name: "P1" + httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" + scope.deleteVariant scope.products[0], scope.products[0].variants[0] + httpBackend.flush() + expect(scope.products[0].variants).toEqual [ + id: 4 + price: 6.0 + ] + expect(scope.dirtyProducts).toEqual + 9: + id: 9 + variants: + 4: + id: 4 + price: 6.0 - httpBackend.expectDELETE("/api/products/9/variants/3").respond 200, "data" - scope.deleteVariant scope.products[0], scope.products[0].variants[0] - httpBackend.flush() - expect(scope.products[0].variants).toEqual [ - id: 4 - price: 6.0 - ] - expect(scope.dirtyProducts).toEqual - 9: - id: 9 - variants: - 4: - id: 4 - price: 6.0 - - 13: - id: 13 - name: "P1" + 13: + id: 13 + name: "P1" From 2425de8c98542a849bbff2beb3a6fcfb9e699a7c Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 13 Feb 2014 11:40:52 +1100 Subject: [PATCH 22/87] Basic form implementation --- app/controllers/shop/checkout_controller.rb | 3 +- app/views/shop/checkout/_form.html.haml | 76 +++++++++++++++++++++ app/views/shop/checkout/new.html.haml | 4 ++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/views/shop/checkout/_form.html.haml diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index 71b30848c2..ab8c9fe9bb 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -6,7 +6,8 @@ class Shop::CheckoutController < BaseController before_filter :require_line_items def new - + @order = current_order + @order.bill_address = Spree::Address.new end private diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml new file mode 100644 index 0000000000..f175b1c84e --- /dev/null +++ b/app/views/shop/checkout/_form.html.haml @@ -0,0 +1,76 @@ += form_for @order, url: "test" do |f| + .large-8.columns + %fieldset#details + %legend Customer Details + .row + .large-6.columns + = f.label :email, "Email Address" + = f.text_field :email + = f.fields_for @order.bill_address do |ba| + .large-6.columns + = ba.label :phone + = ba.text_field :phone + = f.fields_for @order.bill_address do |ba| + .row + .large-6.columns + = ba.label :firstname + = ba.text_field :firstname + .large-6.columns + = ba.label :lastname + = ba.text_field :lastname + + %fieldset + %legend Billing Address + = f.fields_for @order.bill_address do |ba| + .row + .large-12.columns + = ba.label :address1 + = ba.text_field :address1 + .row + .large-12.columns + = ba.label :address2 + = ba.text_field :address2 + .row + .large-6.columns + = ba.label :city + = ba.text_field :city + .large-6.columns + = ba.label :country + = ba.text_field :country + .row + .large-6.columns.right + = ba.label :zipcode + = ba.text_field :zipcode + %fieldset#shipping + %legend Shipping + = fields_for @order.ship_address do |sa| + .row + .large-12.columns + = sa.label :address1 + = sa.text_field :address1 + .row + .large-12.columns + = sa.label :address2 + = sa.text_field :address2 + + .row + .large-6.columns + = sa.label :city + = sa.text_field :city + .large-6.columns + = sa.label :country + = sa.text_field :country + .row + .large-6.columns.right + = sa.label :zipcode + = sa.text_field :zipcode + .row + .large-6.columns + = sa.label :firstname + = sa.text_field :firstname + .large-6.columns + = sa.label :lastname + = sa.text_field :lastname + %fieldset#payment + %legend Payment Details + This looks complicated :/ diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/new.html.haml index 0ea78fdc71..b3f9e0a264 100644 --- a/app/views/shop/checkout/new.html.haml +++ b/app/views/shop/checkout/new.html.haml @@ -3,3 +3,7 @@ = render partial: "shop/checkout/login" %section#checkout_signup = render partial: "shop/checkout/signup" + + += render partial: "shop/checkout/form" + From 701896be95fcf4c48b1e1dc16845b6ef20c39552 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 13 Feb 2014 11:52:44 +1100 Subject: [PATCH 23/87] BPE: Add variant and save it to server. Edit the saved variant. --- .../admin/bulk_product_update.js.coffee | 56 +++++++++++++------ app/models/spree/product_set.rb | 6 +- .../admin/bulk_product_update_spec.rb | 21 +++++-- .../unit/bulk_product_update_spec.js.coffee | 53 +++++++++++++++--- 4 files changed, 106 insertions(+), 30 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index eb809d084f..a9735aca4e 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -286,7 +286,13 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.addVariant = (product) -> - product.variants.push {id: $scope.nextVariantId()} + product.variants.push + id: $scope.nextVariantId() + price: null + unit_value: null + unit_description: null + on_demand: false + on_hand: null $scope.displayProperties[product.id].showVariants = true @@ -354,6 +360,19 @@ productEditModule.controller "AdminProductEditCtrl", [ (variant for id, variant of product.variants when variant.on_demand).length > 0 + $scope.submitProducts = -> + # Pack pack $scope.products, so they will match the list returned from the server, + # then pack $scope.dirtyProducts, ensuring that the correct product info is sent to the server. + $scope.packProduct product for id, product of $scope.products + $scope.packProduct product for id, product of $scope.dirtyProducts + + productsToSubmit = filterSubmitProducts($scope.dirtyProducts) + if productsToSubmit.length > 0 + $scope.updateProducts productsToSubmit # Don't submit an empty list + else + $scope.setMessage $scope.updateStatusMessage, "No changes to update.", color: "grey", 3000 + + $scope.updateProducts = (productsToSubmit) -> $scope.displayUpdating() $http( @@ -369,7 +388,7 @@ productEditModule.controller "AdminProductEditCtrl", [ # doing things. TODO: Review together and decide on strategy here. -- Rohan, 14-1-2014 #if subset($scope.productsWithoutDerivedAttributes(), data) - if angular.toJson($scope.productsWithoutDerivedAttributes($scope.products)) == angular.toJson($scope.productsWithoutDerivedAttributes(data)) + if $scope.productListsMatch $scope.products, data $scope.resetProducts data $timeout -> $scope.displaySuccess() else @@ -382,19 +401,6 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.displayFailure "Server returned with error status: " + status - $scope.submitProducts = -> - # Pack pack $scope.products, so they will match the list returned from the server, - # then pack $scope.dirtyProducts, ensuring that the correct product info is sent to the server. - $scope.packProduct product for id, product of $scope.products - $scope.packProduct product for id, product of $scope.dirtyProducts - - productsToSubmit = filterSubmitProducts($scope.dirtyProducts) - if productsToSubmit.length > 0 - $scope.updateProducts productsToSubmit # Don't submit an empty list - else - $scope.setMessage $scope.updateStatusMessage, "No changes to update.", color: "grey", 3000 - - $scope.packProduct = (product) -> if product.variant_unit_with_scale match = product.variant_unit_with_scale.match(/^([^_]+)_([\d\.]+)$/) @@ -420,6 +426,24 @@ productEditModule.controller "AdminProductEditCtrl", [ variant.unit_description = match[3] + $scope.productListsMatch = (clientProducts, serverProducts) -> + $scope.copyNewVariantIds clientProducts, serverProducts + angular.toJson($scope.productsWithoutDerivedAttributes(clientProducts)) == angular.toJson($scope.productsWithoutDerivedAttributes(serverProducts)) + + + # When variants are created clientside, they are given a negative id. The server + # responds with a real id, which would cause the productListsMatch() check to fail. + # To avoid that false negative, we copy the server variant id to the client for any + # negative ids. + $scope.copyNewVariantIds = (clientProducts, serverProducts) -> + if clientProducts? + for product, i in clientProducts + if product.variants? + for variant, j in product.variants + if variant.id < 0 + variant.id = serverProducts[i].variants[j].id + + $scope.productsWithoutDerivedAttributes = (products) -> products_filtered = [] if products @@ -552,7 +576,7 @@ filterSubmitVariant = (variant) -> hasUpdatableProperty = false filteredVariant = {} if not variant.deleted_at? and variant.hasOwnProperty("id") - filteredVariant.id = variant.id + filteredVariant.id = variant.id unless variant.id <= 0 if variant.hasOwnProperty("on_hand") filteredVariant.on_hand = variant.on_hand hasUpdatableProperty = true diff --git a/app/models/spree/product_set.rb b/app/models/spree/product_set.rb index db3095187e..e79408d8c5 100644 --- a/app/models/spree/product_set.rb +++ b/app/models/spree/product_set.rb @@ -20,7 +20,11 @@ class Spree::ProductSet < ModelSet def update_variants_attributes(product, variants_attributes) variants_attributes.each do |attributes| e = product.variants.detect { |e| e.id.to_s == attributes[:id].to_s && !e.id.nil? } - e.update_attributes(attributes.except(:id)) if e.present? + if e.present? + e.update_attributes(attributes.except(:id)) + else + product.variants.create attributes + end end end diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 0ff07ca0ab..4644aae8c1 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -279,16 +279,29 @@ feature %q{ page.all("tr.variant").count.should == 3 page.should_not have_selector "a.edit-variant", visible: true - # When I remove one, it should be removed + # When I remove two, they should be removed page.all('a.delete-variant').first.click - page.all("tr.variant").count.should == 2 + page.all('a.delete-variant').first.click + page.all("tr.variant").count.should == 1 # When I fill out variant details and hit update + fill_in "variant_unit_value_with_description", with: "4000 (12x250 mL bottles)" + fill_in "variant_price", with: "4.0" + fill_in "variant_on_hand", with: "10" + click_button 'Update' + page.find("span#update-status-message").should have_content "Update complete" - # Then the variants should be saved + # Then I should see edit buttons for the new variant + page.should have_selector "a.edit-variant", visible: true - # And I should see edit buttons for the new variants + # And the variants should be saved + visit '/admin/products/bulk_edit' + page.should have_selector "a.view-variants" + first("a.view-variants").click + page.should have_field "variant_unit_value_with_description", with: "4000 (12x250 mL bottles)" + page.should have_field "variant_price", with: "4.0" + page.should have_field "variant_on_hand", with: "10" end diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index f87c13a21a..e0d3f33660 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -121,6 +121,27 @@ describe "filtering products for submission to database", -> ] ] + it "returns variants with a negative id without that id", -> + testProduct = + id: 1 + variants: [ + id: -1 + on_hand: 5 + price: 12.0 + unit_value: 250 + unit_description: "(bottle)" + ] + + expect(filterSubmitProducts([testProduct])).toEqual [ + id: 1 + variants_attributes: [ + on_hand: 5 + price: 12.0 + unit_value: 250 + unit_description: "(bottle)" + ] + ] + it "does not return variants_attributes property if variants is an empty array", -> testProduct = id: 1 @@ -862,6 +883,24 @@ describe "AdminProductEditCtrl", -> expect(scope.displayFailure).toHaveBeenCalled() + describe "copying new variant ids from server to client", -> + it "copies server ids to the client where the client id is negative", -> + clientProducts = [ + { + id: 123 + variants: [{id: 1}, {id: -2}, {id: -3}] + } + ] + serverProducts = [ + { + id: 123 + variants: [{id: 1}, {id: 4534}, {id: 3453}] + } + ] + scope.copyNewVariantIds(clientProducts, serverProducts) + expect(clientProducts).toEqual(serverProducts) + + describe "fetching products without derived attributes", -> it "returns products without the variant_unit_with_scale field", -> scope.products = [{id: 123, variant_unit_with_scale: 'weight_1000'}] @@ -930,20 +969,16 @@ describe "AdminProductEditCtrl", -> beforeEach -> scope.displayProperties ||= {123: {}} - it "adds the first variant", -> - product = {id: 123, variants: []} - scope.addVariant(product) - expect(product).toEqual - id: 123 - variants: [{id: -1}] - - it "adds subsequent variants", -> + it "adds first and subsequent variants", -> product = {id: 123, variants: []} scope.addVariant(product) scope.addVariant(product) expect(product).toEqual id: 123 - variants: [{id: -1}, {id: -2}] + variants: [ + {id: -1, price: null, unit_value: null, unit_description: null, on_demand: false, on_hand: null} + {id: -2, price: null, unit_value: null, unit_description: null, on_demand: false, on_hand: null} + ] it "shows the variant(s)", -> product = {id: 123, variants: []} From 7f58007c457267d6643d5f935f7a97d6a4715b56 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 13 Feb 2014 16:12:26 +1100 Subject: [PATCH 24/87] Adding some fields to Shipping Methods --- ...13003443_add_require_ship_address_to_shipping_methods.rb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 db/migrate/20140213003443_add_require_ship_address_to_shipping_methods.rb diff --git a/db/migrate/20140213003443_add_require_ship_address_to_shipping_methods.rb b/db/migrate/20140213003443_add_require_ship_address_to_shipping_methods.rb new file mode 100644 index 0000000000..a38ad29242 --- /dev/null +++ b/db/migrate/20140213003443_add_require_ship_address_to_shipping_methods.rb @@ -0,0 +1,6 @@ +class AddRequireShipAddressToShippingMethods < ActiveRecord::Migration + def change + add_column :spree_shipping_methods, :require_ship_address, :boolean, :default => true + add_column :spree_shipping_methods, :description, :text + end +end From 25916caeb4e4309d048f0cbb175190ea15e3cfa4 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 13 Feb 2014 16:13:50 +1100 Subject: [PATCH 25/87] Starting on Angular models for Checkout, pretties --- .../javascripts/darkswarm/checkout.js.coffee | 2 + .../controllers/checkout_controller.js.coffee | 2 + .../stylesheets/darkswarm/forms.css.sass | 16 ++ .../stylesheets/darkswarm/variables.css.sass | 1 + app/views/shop/checkout/_form.html.haml | 151 ++++++++++-------- app/views/shop/checkout/_login.html.haml | 25 +-- app/views/shop/checkout/_signup.html.haml | 23 +-- app/views/shop/checkout/new.html.haml | 17 +- db/schema.rb | 8 +- .../consumer/shopping/checkout_spec.rb | 35 ++++ 10 files changed, 179 insertions(+), 101 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/checkout.js.coffee create mode 100644 app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee create mode 100644 app/assets/stylesheets/darkswarm/forms.css.sass diff --git a/app/assets/javascripts/darkswarm/checkout.js.coffee b/app/assets/javascripts/darkswarm/checkout.js.coffee new file mode 100644 index 0000000000..65bd062d5d --- /dev/null +++ b/app/assets/javascripts/darkswarm/checkout.js.coffee @@ -0,0 +1,2 @@ +window.Checkout = angular.module("Checkout", ["ngResource", "filters"]).config ($httpProvider) -> + $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content') diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee new file mode 100644 index 0000000000..b2df025a13 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -0,0 +1,2 @@ +angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> + $scope.require_ship_method = false diff --git a/app/assets/stylesheets/darkswarm/forms.css.sass b/app/assets/stylesheets/darkswarm/forms.css.sass new file mode 100644 index 0000000000..bada1fb68d --- /dev/null +++ b/app/assets/stylesheets/darkswarm/forms.css.sass @@ -0,0 +1,16 @@ +@import variables + +form + fieldset + padding: 0px + border: none + legend + border: 1px solid $dark-grey + border-left: 0px + border-right: 0px + padding: 16px 24px + display: block + width: 100% + margin-bottom: 1em + text-transform: uppercase + color: #999999 diff --git a/app/assets/stylesheets/darkswarm/variables.css.sass b/app/assets/stylesheets/darkswarm/variables.css.sass index a13eb9d014..c8edbc4a46 100644 --- a/app/assets/stylesheets/darkswarm/variables.css.sass +++ b/app/assets/stylesheets/darkswarm/variables.css.sass @@ -1 +1,2 @@ $fawn: #f6efe5 +$dark-grey: #c7c7c7 diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index f175b1c84e..e3b998d530 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,76 +1,89 @@ -= form_for @order, url: "test" do |f| - .large-8.columns - %fieldset#details - %legend Customer Details - .row - .large-6.columns - = f.label :email, "Email Address" - = f.text_field :email - = f.fields_for @order.bill_address do |ba| - .large-6.columns - = ba.label :phone - = ba.text_field :phone - = f.fields_for @order.bill_address do |ba| +%checkout{"ng-app" => "Checkout", "ng-controller" => "CheckoutCtrl"} + = form_for @order, url: "test", html: { class: "custom" } do |f| + .large-8.columns + %fieldset#details + %legend Customer Details .row .large-6.columns - = ba.label :firstname - = ba.text_field :firstname - .large-6.columns - = ba.label :lastname - = ba.text_field :lastname - - %fieldset - %legend Billing Address + = f.label :email, "Email Address" + = f.text_field :email + = f.fields_for @order.bill_address do |ba| + .large-6.columns + = ba.label :phone + = ba.text_field :phone = f.fields_for @order.bill_address do |ba| - .row - .large-12.columns - = ba.label :address1 - = ba.text_field :address1 - .row - .large-12.columns - = ba.label :address2 - = ba.text_field :address2 .row .large-6.columns - = ba.label :city - = ba.text_field :city + = ba.label :firstname + = ba.text_field :firstname .large-6.columns - = ba.label :country - = ba.text_field :country - .row - .large-6.columns.right - = ba.label :zipcode - = ba.text_field :zipcode - %fieldset#shipping - %legend Shipping - = fields_for @order.ship_address do |sa| - .row - .large-12.columns - = sa.label :address1 - = sa.text_field :address1 - .row - .large-12.columns - = sa.label :address2 - = sa.text_field :address2 + = ba.label :lastname + = ba.text_field :lastname - .row - .large-6.columns - = sa.label :city - = sa.text_field :city - .large-6.columns - = sa.label :country - = sa.text_field :country - .row - .large-6.columns.right - = sa.label :zipcode - = sa.text_field :zipcode - .row - .large-6.columns - = sa.label :firstname - = sa.text_field :firstname - .large-6.columns - = sa.label :lastname - = sa.text_field :lastname - %fieldset#payment - %legend Payment Details - This looks complicated :/ + %fieldset + %legend Billing Address + = f.fields_for @order.bill_address do |ba| + .row + .large-12.columns + = ba.label :address1 + = ba.text_field :address1 + .row + .large-12.columns + = ba.label :address2 + = ba.text_field :address2 + .row + .large-6.columns + = ba.label :city + = ba.text_field :city + .large-6.columns + = ba.label :country + = ba.text_field :country + .row + .large-6.columns.right + = ba.label :zipcode + = ba.text_field :zipcode + + %fieldset#shipping + %legend Shipping + + - for ship_method in current_distributor.shipping_methods.uniq + .row + .large-12.columns + %label + = f.radio_button :shipping_method, ship_method.id, + "data-require-ship-address" => ship_method.require_ship_address + %span.custom.radio + = ship_method.name + + = fields_for @order.ship_address do |sa| + #ship_address{"ng-show" => "require_ship_method"} + .row + .large-12.columns + = sa.label :address1 + = sa.text_field :address1 + .row + .large-12.columns + = sa.label :address2 + = sa.text_field :address2 + + .row + .large-6.columns + = sa.label :city + = sa.text_field :city + .large-6.columns + = sa.label :country + = sa.text_field :country + .row + .large-6.columns.right + = sa.label :zipcode + = sa.text_field :zipcode + .row + .large-6.columns + = sa.label :firstname + = sa.text_field :firstname + .large-6.columns + = sa.label :lastname + = sa.text_field :lastname + %fieldset#payment + %legend Payment Details + This looks complicated :/ diff --git a/app/views/shop/checkout/_login.html.haml b/app/views/shop/checkout/_login.html.haml index 4509a9dea7..33bf41fed5 100644 --- a/app/views/shop/checkout/_login.html.haml +++ b/app/views/shop/checkout/_login.html.haml @@ -1,13 +1,14 @@ -%h2 Login = form_for Spree::User.new, :html => {'data-type' => :json}, :as => :spree_user, :url => spree.spree_user_session_path do |f| - %p - = f.label :email, t(:email) - = f.email_field :email, :class => 'title', :tabindex => 1, :id => "login_spree_user_email" - %p - = f.label :password, t(:password) - = f.password_field :password, :class => 'title', :tabindex => 2, :id => "login_spree_user_password" - %p - %label - = f.check_box :remember_me - = f.label :remember_me, t(:remember_me) - %p= f.submit t(:login), :class => 'button primary', :tabindex => 3, :id => "login_spree_user_remember_me" + %fieldset + %legend I have an OFN Account + %p + = f.label :email, t(:email) + = f.email_field :email, :class => 'title', :tabindex => 1, :id => "login_spree_user_email" + %p + = f.label :password, t(:password) + = f.password_field :password, :class => 'title', :tabindex => 2, :id => "login_spree_user_password" + %p + %label + = f.check_box :remember_me + = f.label :remember_me, t(:remember_me) + %p= f.submit t(:login), :class => 'button primary', :tabindex => 3, :id => "login_spree_user_remember_me" diff --git a/app/views/shop/checkout/_signup.html.haml b/app/views/shop/checkout/_signup.html.haml index 51b8265f19..81ed7744d0 100644 --- a/app/views/shop/checkout/_signup.html.haml +++ b/app/views/shop/checkout/_signup.html.haml @@ -1,13 +1,14 @@ -%h2 Sign Up = form_for Spree::User.new, :as => :spree_user, :url => spree.spree_user_registration_path(@spree_user) do |f| - %p - = f.label :email, t(:email) - = f.email_field :email, :class => 'title', :id => "signup_spree_user_email" - %p - = f.label :password, t(:password) - = f.password_field :password, :class => 'title', :id => "signup_spree_user_password" - %p - = f.label :password_confirmation, t(:confirm_password) - = f.password_field :password_confirmation, :class => 'title', :id => "signup_spree_user_password_confirmation" + %fieldset + %legend New to OFN? + %p + = f.label :email, t(:email) + = f.email_field :email, :class => 'title', :id => "signup_spree_user_email" + %p + = f.label :password, t(:password) + = f.password_field :password, :class => 'title', :id => "signup_spree_user_password" + %p + = f.label :password_confirmation, t(:confirm_password) + = f.password_field :password_confirmation, :class => 'title', :id => "signup_spree_user_password_confirmation" - = f.submit "Sign Up", :class => 'button' + = f.submit "Sign Up", :class => 'button' diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/new.html.haml index b3f9e0a264..97a582309d 100644 --- a/app/views/shop/checkout/new.html.haml +++ b/app/views/shop/checkout/new.html.haml @@ -1,9 +1,14 @@ - unless spree_current_user - %section#checkout_login - = render partial: "shop/checkout/login" - %section#checkout_signup - = render partial: "shop/checkout/signup" + .row + %section#checkout_login + .large-4.columns + = render partial: "shop/checkout/login" + %section#checkout_signup + .large-4.columns + = render partial: "shop/checkout/signup" + .large-2.columns + Cart bitches! -= render partial: "shop/checkout/form" - +.row + = render partial: "shop/checkout/form" diff --git a/db/schema.rb b/db/schema.rb index 313c7ddf48..b9f151fedf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140204011203) do +ActiveRecord::Schema.define(:version => 20140213003443) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -744,14 +744,16 @@ ActiveRecord::Schema.define(:version => 20140204011203) do create_table "spree_shipping_methods", :force => true do |t| t.string "name" t.integer "zone_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "display_on" t.integer "shipping_category_id" t.boolean "match_none" t.boolean "match_all" t.boolean "match_one" t.datetime "deleted_at" + t.boolean "require_ship_address", :default => true + t.text "description" end create_table "spree_skrill_transactions", :force => true do |t| diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 8cf331db8e..336a35b790 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -108,6 +108,40 @@ feature "As a consumer I want to check out my cart", js: true do end end end + + describe "logged in, distributor selected, order cycle selected, product in cart" do + let(:user) { create_enterprise_user } + before do + login_to_consumer_section + select_distributor + select_order_cycle + add_product_to_cart + end + + describe "with shipping methods" do + let(:sm1) { create(:shipping_method, require_ship_address: true, name: "Frogs", description: "yellow") } + let(:sm2) { create(:shipping_method, require_ship_address: true, name: "Donkeys", description: "blue") } + before do + distributor.shipping_methods << sm1 + distributor.shipping_methods << sm2 + visit "/shop/checkout" + end + it "shows all shipping methods" do + page.should have_content "Frogs" + page.should have_content "Donkeys" + end + + it "doesn't show ship address forms by default" do + find("#ship_address").visible?.should be_false + end + + it "shows ship address forms when selected shipping method requires one" do + # Fancy Foundation Forms are weird + find("#order_shipping_method_#{sm1.id} + span").click + find("#ship_address").visible?.should be_true + end + end + end end def select_distributor @@ -122,6 +156,7 @@ def select_order_cycle end def add_product_to_cart + fill_in "variants[#{product.master.id}]", with: 5 first("form.custom > input.button.right").click end From 4704e927e3be68db4c8b3b79a6844a74d4c1f2c6 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 11:31:27 +1100 Subject: [PATCH 26/87] Form toggling! Fuck yeah --- .../darkswarm/controllers/checkout_controller.js.coffee | 5 ++++- app/views/shop/checkout/_form.html.haml | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index b2df025a13..7bfef9d1e2 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -1,2 +1,5 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> - $scope.require_ship_method = false + $scope.require_ship_address = false + $scope.shipping_method = -1 + $scope.shippingMethodChanged = -> + $scope.require_ship_address = $("#order_shipping_method_" + $scope.shipping_method).attr("data-require-ship-address") diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index e3b998d530..8a987be285 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,5 +1,5 @@ %checkout{"ng-app" => "Checkout", "ng-controller" => "CheckoutCtrl"} - = form_for @order, url: "test", html: { class: "custom" } do |f| + = form_for @order, url: "test" do |f| .large-8.columns %fieldset#details %legend Customer Details @@ -50,13 +50,14 @@ .row .large-12.columns %label - = f.radio_button :shipping_method, ship_method.id, + = f.radio_button :shipping_method, ship_method.id, + "ng-model" => "shipping_method", + "ng-change" => "shippingMethodChanged()", "data-require-ship-address" => ship_method.require_ship_address - %span.custom.radio = ship_method.name = fields_for @order.ship_address do |sa| - #ship_address{"ng-show" => "require_ship_method"} + #ship_address{"ng-show" => "require_ship_address"} .row .large-12.columns = sa.label :address1 From 3c696b676241b179a829d9b68eaac70360c107b7 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 13:30:28 +1100 Subject: [PATCH 27/87] Adding payment method switching --- .../controllers/checkout_controller.js.coffee | 1 + app/views/shop/checkout/_form.html.haml | 11 ++++++++++- spec/features/consumer/shopping/checkout_spec.rb | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index 7bfef9d1e2..935c476c8c 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -1,5 +1,6 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> $scope.require_ship_address = false $scope.shipping_method = -1 + $scope.payment_method = -1 $scope.shippingMethodChanged = -> $scope.require_ship_address = $("#order_shipping_method_" + $scope.shipping_method).attr("data-require-ship-address") diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 8a987be285..8787ba141a 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -87,4 +87,13 @@ = sa.text_field :lastname %fieldset#payment %legend Payment Details - This looks complicated :/ + - current_order.available_payment_methods.each do |method| + .row + .large-12.columns + %label + = radio_button_tag "order[payments_attributes][][payment_method_id]", method.id, false, + "ng-model" => "payment_method" + = method.name + .row{"ng-show" => "payment_method == #{method.id}"} + .large-12.columns + = render partial: "spree/checkout/payment/#{method.method_type}", :locals => { :payment_method => method } diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 336a35b790..23071e730c 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -137,10 +137,23 @@ feature "As a consumer I want to check out my cart", js: true do it "shows ship address forms when selected shipping method requires one" do # Fancy Foundation Forms are weird - find("#order_shipping_method_#{sm1.id} + span").click + choose(sm1.name) find("#ship_address").visible?.should be_true end end + + describe "with payment methods" do + let(:pm1) { create(:payment_method, distributors: [distributor]) } + let(:pm2) { create(:payment_method, distributors: [distributor]) } + + it "shows all available payment methods" do + pm1 # Lazy evaluation of ze create()s + pm2 + visit "/shop/checkout" + page.should have_content pm1.name + page.should have_content pm2.name + end + end end end From a722320ece765b84b786e5f485cd9c483a4934e2 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 13:43:23 +1100 Subject: [PATCH 28/87] Patching up the tests for new content --- spec/features/consumer/shopping/checkout_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 23071e730c..7486ff3575 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -54,7 +54,7 @@ feature "As a consumer I want to check out my cart", js: true do it "renders the login form if user is logged out" do visit "/shop/checkout" within "section[role='main']" do - page.should have_content "Login" + page.should have_content "I HAVE AN OFN ACCOUNT" end end @@ -62,14 +62,14 @@ feature "As a consumer I want to check out my cart", js: true do login_to_consumer_section visit "/shop/checkout" within "section[role='main']" do - page.should_not have_content "Login" + page.should_not have_content "I HAVE AN OFN ACCOUNT" end end it "renders the signup link if user is logged out" do visit "/shop/checkout" within "section[role='main']" do - page.should have_content "Sign Up" + page.should have_content "NEW TO OFN" end end @@ -77,7 +77,7 @@ feature "As a consumer I want to check out my cart", js: true do login_to_consumer_section visit "/shop/checkout" within "section[role='main']" do - page.should_not have_content "Sign Up" + page.should_not have_content "NEW TO OFN" end end @@ -88,9 +88,10 @@ feature "As a consumer I want to check out my cart", js: true do fill_in "spree_user[password]", with: user.password click_button "Login" end + current_path.should == "/shop/checkout" within "section[role='main']" do - page.should_not have_content "Login" + page.should_not have_content "I have an OFN Account" end end From 5c498735b0e26f976069f31703bbfc6c8fc93f2b Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 14:26:31 +1100 Subject: [PATCH 29/87] Moving from new to edit --- app/controllers/shop/checkout_controller.rb | 4 +-- .../{new.html.haml => edit.html.haml} | 1 + config/routes.rb | 8 +++--- .../shop/checkout_controller_spec.rb | 27 ++++++++++++++----- 4 files changed, 29 insertions(+), 11 deletions(-) rename app/views/shop/checkout/{new.html.haml => edit.html.haml} (99%) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index ab8c9fe9bb..d7b18c9324 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -5,9 +5,9 @@ class Shop::CheckoutController < BaseController before_filter :require_order_cycle before_filter :require_line_items - def new + def edit @order = current_order - @order.bill_address = Spree::Address.new + @order.bill_address ||= Spree::Address.new end private diff --git a/app/views/shop/checkout/new.html.haml b/app/views/shop/checkout/edit.html.haml similarity index 99% rename from app/views/shop/checkout/new.html.haml rename to app/views/shop/checkout/edit.html.haml index 97a582309d..4d4d7dddd8 100644 --- a/app/views/shop/checkout/new.html.haml +++ b/app/views/shop/checkout/edit.html.haml @@ -1,4 +1,5 @@ - unless spree_current_user + .row %section#checkout_login .large-4.columns diff --git a/config/routes.rb b/config/routes.rb index 479970737d..adadef7cf0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,9 +8,11 @@ Openfoodnetwork::Application.routes.draw do end namespace :shop do - resource :checkout, controller: :checkout do - get :new - end + #resource :checkout, only: :edit, controller: :checkout do + #get '', to: "" + #end + get '/checkout', :to => 'checkout#edit' , :as => :checkout + post '/checkout', :to => 'checkout#update' , :as => :update_checkout end resources :enterprises do diff --git a/spec/controllers/shop/checkout_controller_spec.rb b/spec/controllers/shop/checkout_controller_spec.rb index 59b58b49b0..89cf8288ec 100644 --- a/spec/controllers/shop/checkout_controller_spec.rb +++ b/spec/controllers/shop/checkout_controller_spec.rb @@ -1,21 +1,36 @@ require 'spec_helper' describe Shop::CheckoutController do + let(:distributor) { double(:distributor) } + let(:order_cycle) { create(:order_cycle) } + let(:order) { create(:order) } it "redirects home when no distributor is selected" do - get :new + get :edit response.should redirect_to root_path end it "redirects to the shop when no order cycle is selected" do - controller.stub(:current_distributor).and_return(double(:distributor)) - get :new + controller.stub(:current_distributor).and_return(distributor) + get :edit + response.should redirect_to shop_path + end + + it "redirects to the shop when no line items are present" do + controller.stub(:current_distributor).and_return(distributor) + controller.stub(:current_order_cycle).and_return(order_cycle) + controller.stub(:current_order).and_return(order) + order.stub_chain(:line_items, :empty?).and_return true + get :edit response.should redirect_to shop_path end it "renders when both distributor and order cycle is selected" do - controller.stub(:current_distributor).and_return(double(:distributor)) - controller.stub(:current_order_cycle).and_return(create(:order_cycle)) - get :new + controller.stub(:current_distributor).and_return(distributor) + controller.stub(:current_order_cycle).and_return(order_cycle) + controller.stub(:current_order).and_return(order) + order.stub_chain(:line_items, :empty?).and_return false + + get :edit response.should be_success end end From a6d58aa498aa3e03d8ab6540109c3be29875d7ed Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 14:40:58 +1100 Subject: [PATCH 30/87] Partializing and adding yield blocks --- .../stylesheets/darkswarm/shop.css.sass | 2 +- app/views/shop/_details.html.haml | 11 +++++++ app/views/shop/checkout/edit.html.haml | 33 +++++++++++-------- app/views/shop/shop/_order_cycles.html.haml | 14 +------- app/views/shop/shop/show.html.haml | 25 +++++++------- 5 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 app/views/shop/_details.html.haml diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 3781b380f8..6127a84faf 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -4,7 +4,7 @@ product display: block -shop +.darkswarm #search font-size: 2em @include big-input diff --git a/app/views/shop/_details.html.haml b/app/views/shop/_details.html.haml new file mode 100644 index 0000000000..6b77e45a27 --- /dev/null +++ b/app/views/shop/_details.html.haml @@ -0,0 +1,11 @@ +%navigation + %distributor.details.row + #distributor_title + %img.left{src: current_distributor.logo.url(:thumb)} + %h4 + = current_distributor.name + %location= current_distributor.address.city + %small + %a{href: "/"} Change location + + = render partial: "shop/shop/order_cycles" diff --git a/app/views/shop/checkout/edit.html.haml b/app/views/shop/checkout/edit.html.haml index 4d4d7dddd8..6ef3039d85 100644 --- a/app/views/shop/checkout/edit.html.haml +++ b/app/views/shop/checkout/edit.html.haml @@ -1,15 +1,22 @@ -- unless spree_current_user +%checkout.darkswarm + - content_for :order_cycle_form do + %form.custom + %strong.avenir + Order ready on + = pickup_time current_order_cycle + = render partial: "shop/details" + + - unless spree_current_user + .row + %section#checkout_login + .large-4.columns + = render partial: "shop/checkout/login" + %section#checkout_signup + .large-4.columns + = render partial: "shop/checkout/signup" + .large-2.columns + Cart bitches! + .row - %section#checkout_login - .large-4.columns - = render partial: "shop/checkout/login" - %section#checkout_signup - .large-4.columns - = render partial: "shop/checkout/signup" - .large-2.columns - Cart bitches! - - -.row - = render partial: "shop/checkout/form" + = render partial: "shop/checkout/form" diff --git a/app/views/shop/shop/_order_cycles.html.haml b/app/views/shop/shop/_order_cycles.html.haml index 22463e677b..52cc7f04c6 100644 --- a/app/views/shop/shop/_order_cycles.html.haml +++ b/app/views/shop/shop/_order_cycles.html.haml @@ -1,9 +1,7 @@ %ordercycle{"ng-controller" => "OrderCycleCtrl"} - :javascript angular.module('Shop').value('orderCycleData', #{render "shop/shop/order_cycle"}) - - if @order_cycles.empty? Orders are currently closed for this hub %p @@ -14,14 +12,4 @@ = render partial: "shop/shop/last_order_cycle" - else - %form.custom - %strong.avenir Ready for - %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", - "ng-change" => "changeOrderCycle()", - "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}"} - - %closing - -#%img{src: "/icon/goes/here"} - Orders close - %strong {{ order_cycle.orders_close_at | date_in_words }} - + = yield :order_cycle_form diff --git a/app/views/shop/shop/show.html.haml b/app/views/shop/shop/show.html.haml index 5063841152..924c68da91 100644 --- a/app/views/shop/shop/show.html.haml +++ b/app/views/shop/shop/show.html.haml @@ -1,17 +1,18 @@ -%shop{"ng-app" => "Shop"} - %navigation - %distributor.details.row - #distributor_title - %img.left{src: @distributor.logo.url(:thumb)} - %h4 - = @distributor.name - %location= @distributor.address.city - %small - %a{href: "/"} Change location +%shop.darkswarm{"ng-app" => "Shop"} - = render partial: "shop/shop/order_cycles" + - content_for :order_cycle_form do + %form.custom + %strong.avenir Ready for + %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", + "ng-change" => "changeOrderCycle()", + "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}"} + + %closing + -#%img{src: "/icon/goes/here"} + Orders close + %strong {{ order_cycle.orders_close_at | date_in_words }} + = render partial: "shop/details" - -#%description %tabs .row From 09d8e19c3511cdd8d885401db4ae9122a5426ba0 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 14:43:50 +1100 Subject: [PATCH 31/87] DRYING UP MY VIEWS --- app/views/shop/checkout/edit.html.haml | 1 + app/views/shop/shop/_order_cycles.html.haml | 3 ++- app/views/shop/shop/show.html.haml | 17 ++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/views/shop/checkout/edit.html.haml b/app/views/shop/checkout/edit.html.haml index 6ef3039d85..5df7861d9e 100644 --- a/app/views/shop/checkout/edit.html.haml +++ b/app/views/shop/checkout/edit.html.haml @@ -4,6 +4,7 @@ %strong.avenir Order ready on = pickup_time current_order_cycle + = render partial: "shop/details" - unless spree_current_user diff --git a/app/views/shop/shop/_order_cycles.html.haml b/app/views/shop/shop/_order_cycles.html.haml index 52cc7f04c6..aff1a3259d 100644 --- a/app/views/shop/shop/_order_cycles.html.haml +++ b/app/views/shop/shop/_order_cycles.html.haml @@ -12,4 +12,5 @@ = render partial: "shop/shop/last_order_cycle" - else - = yield :order_cycle_form + %form.custom + = yield :order_cycle_form diff --git a/app/views/shop/shop/show.html.haml b/app/views/shop/shop/show.html.haml index 924c68da91..5072576f6f 100644 --- a/app/views/shop/shop/show.html.haml +++ b/app/views/shop/shop/show.html.haml @@ -1,16 +1,15 @@ %shop.darkswarm{"ng-app" => "Shop"} - content_for :order_cycle_form do - %form.custom - %strong.avenir Ready for - %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", - "ng-change" => "changeOrderCycle()", - "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}"} + %strong.avenir Ready for + %select.avenir#order_cycle_id{"ng-model" => "order_cycle.order_cycle_id", + "ng-change" => "changeOrderCycle()", + "ng-options" => "oc.id as oc.time for oc in #{@order_cycles.map {|oc| {time: pickup_time(oc), id: oc.id}}.to_json}"} - %closing - -#%img{src: "/icon/goes/here"} - Orders close - %strong {{ order_cycle.orders_close_at | date_in_words }} + %closing + -#%img{src: "/icon/goes/here"} + Orders close + %strong {{ order_cycle.orders_close_at | date_in_words }} = render partial: "shop/details" From e9178570e91fe2c4c0de56b19a1f2c2fbd57664f Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 15:18:03 +1100 Subject: [PATCH 32/87] Committing order summary. NO TESTS YET --- .../stylesheets/darkswarm/checkout.css.sass | 2 + app/views/shop/checkout/_form.html.haml | 2 +- app/views/shop/checkout/_summary.html.haml | 27 +++++++++++++ app/views/shop/checkout/edit.html.haml | 38 ++++++++++--------- 4 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 app/assets/stylesheets/darkswarm/checkout.css.sass create mode 100644 app/views/shop/checkout/_summary.html.haml diff --git a/app/assets/stylesheets/darkswarm/checkout.css.sass b/app/assets/stylesheets/darkswarm/checkout.css.sass new file mode 100644 index 0000000000..c864d4928a --- /dev/null +++ b/app/assets/stylesheets/darkswarm/checkout.css.sass @@ -0,0 +1,2 @@ +checkout + display: block diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 8787ba141a..7f344ef749 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,6 +1,6 @@ %checkout{"ng-app" => "Checkout", "ng-controller" => "CheckoutCtrl"} = form_for @order, url: "test" do |f| - .large-8.columns + .large-12.columns %fieldset#details %legend Customer Details .row diff --git a/app/views/shop/checkout/_summary.html.haml b/app/views/shop/checkout/_summary.html.haml new file mode 100644 index 0000000000..6e2689b1a6 --- /dev/null +++ b/app/views/shop/checkout/_summary.html.haml @@ -0,0 +1,27 @@ +%form + %fieldset + %legend Your Order + + %table + %tr + %th Cart subtotal + %td= @order.display_item_total + + - checkout_adjustments_for_summary(@order).each do |adjustment| + %tr + %th= adjustment.label + %td= adjustment.display_amount.to_html + + %tr + %th Cart total + %td= @order.display_total.to_html + + + - if @order.price_adjustment_totals.present? + - @order.price_adjustment_totals.each do |label, total| + %tr + %th= label + %td= total + + %a.button Purchase + diff --git a/app/views/shop/checkout/edit.html.haml b/app/views/shop/checkout/edit.html.haml index 5df7861d9e..21865a4822 100644 --- a/app/views/shop/checkout/edit.html.haml +++ b/app/views/shop/checkout/edit.html.haml @@ -1,23 +1,27 @@ %checkout.darkswarm - content_for :order_cycle_form do - %form.custom - %strong.avenir - Order ready on - = pickup_time current_order_cycle + %strong.avenir + Order ready on + = pickup_time current_order_cycle = render partial: "shop/details" - - unless spree_current_user - .row - %section#checkout_login - .large-4.columns - = render partial: "shop/checkout/login" - %section#checkout_signup - .large-4.columns - = render partial: "shop/checkout/signup" - .large-2.columns - Cart bitches! - - .row - = render partial: "shop/checkout/form" + .large-9.columns + - unless spree_current_user + .row + %section#checkout_login + .large-6.columns + = render partial: "shop/checkout/login" + %section#checkout_signup + .large-6.columns + = render partial: "shop/checkout/signup" + + + .row + = render partial: "shop/checkout/form" + + + .large-3.columns + .row + = render partial: "shop/checkout/summary" From 134d9831fe6df1d805e0da0e863b8efda2354712 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 15:34:55 +1100 Subject: [PATCH 33/87] Fixing bug #271 --- app/views/shop/_variant.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/shop/_variant.html.haml b/app/views/shop/_variant.html.haml index 0adb5a2421..9a71ac8b39 100644 --- a/app/views/shop/_variant.html.haml +++ b/app/views/shop/_variant.html.haml @@ -1,5 +1,6 @@ -%td{colspan: 2} +%td +%td.notes %td {{variant.options_text}} %td %input{type: :number, From 502dba1b3f7e1718d53289b693d433d08fd7142c Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 15:35:40 +1100 Subject: [PATCH 34/87] Fixing bug #281 --- app/assets/stylesheets/darkswarm/shop.css.sass | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 3781b380f8..825d720cbf 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -122,7 +122,7 @@ shop //&.notes //width: 140px &.variant - width: 100px + width: 180px &.quantity, &.bulk, &.price width: 90px .notes From 19e31a264d8da2ee2e3b6080caedae2d6d8157c8 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 14 Feb 2014 15:49:16 +1100 Subject: [PATCH 35/87] Tidying footer contact details --- app/assets/stylesheets/darkswarm/footer.sass | 4 ++++ app/views/shop/_contact_us.html.haml | 22 ++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/footer.sass b/app/assets/stylesheets/darkswarm/footer.sass index ba2424e94b..df649a8d59 100644 --- a/app/assets/stylesheets/darkswarm/footer.sass +++ b/app/assets/stylesheets/darkswarm/footer.sass @@ -10,3 +10,7 @@ img display: block margin: 0px auto 8px + + .contact + strong + padding-right: 1em diff --git a/app/views/shop/_contact_us.html.haml b/app/views/shop/_contact_us.html.haml index 2cb4d65984..58a69ed38f 100644 --- a/app/views/shop/_contact_us.html.haml +++ b/app/views/shop/_contact_us.html.haml @@ -1,8 +1,18 @@ .contact.small-2.large-3.columns %h3 Contact - %ul - %li= @distributor.email - %li= @distributor.website - = @distributor.address.address1 - = @distributor.address.address2 - = @distributor.address.city + + %p + %strong E + %a{href: "mailto:#{@distributor.email}"}= @distributor.email + + - unless @distributor.website.blank? + %p + %strong W + %a{href: @distributor.website}= @distributor.website + + %p + = @distributor.address.address1 + %br + = @distributor.address.address2 + %br + = @distributor.address.city From 547f46fbc9d73ba1985b0f17bea698e7f67efab8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 18 Feb 2014 10:32:36 +1100 Subject: [PATCH 36/87] Deal with unit_value of zero correctly - do not treat as nil --- .../admin/bulk_product_update.js.coffee | 8 ++++--- .../unit/bulk_product_update_spec.js.coffee | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index a9735aca4e..83590ba45d 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -222,11 +222,12 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.loadVariantVariantUnit = (product, variant) -> unit_value = $scope.variantUnitValue product, variant - variant.unit_value_with_description = "#{unit_value || ''} #{variant.unit_description || ''}".trim() + unit_value = if unit_value? then unit_value else '' + variant.unit_value_with_description = "#{unit_value} #{variant.unit_description || ''}".trim() $scope.variantUnitValue = (product, variant) -> - if variant.unit_value + if variant.unit_value? if product.variant_unit_scale variant.unit_value / product.variant_unit_scale else @@ -421,7 +422,8 @@ productEditModule.controller "AdminProductEditCtrl", [ match = variant.unit_value_with_description.match(/^([\d\.]+|)( |)(.*)$/) if match product = $scope.findProduct(product.id) - variant.unit_value = parseFloat(match[1]) || null + variant.unit_value = parseFloat(match[1]) + variant.unit_value = null if isNaN(variant.unit_value) variant.unit_value *= product.variant_unit_scale if variant.unit_value && product.variant_unit_scale variant.unit_description = match[3] diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index e0d3f33660..d88bb1e34e 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -499,6 +499,13 @@ describe "AdminProductEditCtrl", -> scope.loadVariantVariantUnit product, product.variants[0] expect(product.variants[0].unit_value_with_description).toEqual '2.5' + it "displays a unit_value of zero", -> + product = + variant_unit_scale: 1.0 + variants: [{id: 1, unit_value: 0}] + scope.loadVariantVariantUnit product, product.variants[0] + expect(product.variants[0].unit_value_with_description).toEqual '0' + describe "calculating the scaled unit value for a variant", -> it "returns the scaled value when variant has a unit_value", -> @@ -511,6 +518,11 @@ describe "AdminProductEditCtrl", -> variant = {unit_value: 5} expect(scope.variantUnitValue(product, variant)).toEqual 5 + it "returns zero when the value is zero", -> + product = {} + variant = {unit_value: 0} + expect(scope.variantUnitValue(product, variant)).toEqual 0 + it "returns null when the variant has no unit_value", -> product = {} variant = {} @@ -761,6 +773,15 @@ describe "AdminProductEditCtrl", -> scope.packVariant(testProduct, testVariant) expect(testVariant).toEqual {} + it "sets zero when the field is zero", -> + testProduct = {id: 123, variant_unit_scale: 1.0} + testVariant = {unit_value_with_description: "0"} + scope.packVariant(testProduct, testVariant) + expect(testVariant).toEqual + unit_value: 0 + unit_description: '' + unit_value_with_description: "0" + it "converts value from chosen unit to base unit", -> testProduct = {id: 123, variant_unit_scale: 1000} testVariant = {unit_value_with_description: "250.5"} From 3024007bd35e3c1ebc509f28da5405a242eaab13 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Tue, 18 Feb 2014 13:51:20 +1100 Subject: [PATCH 37/87] Reworking our page structure to work better with Angular controllers --- .../controllers/checkout_controller.js.coffee | 2 + .../controllers/summary_controller.js.coffee | 4 ++ app/views/shop/checkout/_form.html.haml | 2 +- app/views/shop/checkout/_summary.html.haml | 42 +++++++++---------- app/views/shop/checkout/edit.html.haml | 34 +++++++-------- .../consumer/shopping/checkout_spec.rb | 14 ++++++- 6 files changed, 55 insertions(+), 43 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index 935c476c8c..679c42fa6a 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -2,5 +2,7 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> $scope.require_ship_address = false $scope.shipping_method = -1 $scope.payment_method = -1 + $scope.shippingMethodChanged = -> $scope.require_ship_address = $("#order_shipping_method_" + $scope.shipping_method).attr("data-require-ship-address") + diff --git a/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee new file mode 100644 index 0000000000..9819f85977 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee @@ -0,0 +1,4 @@ +angular.module("Checkout").controller "SummaryCtrl", ($scope) -> + $scope.purchase = (event)-> + event.preventDefault() + console.log "test" diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 7f344ef749..0d0f7d05a6 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,4 +1,4 @@ -%checkout{"ng-app" => "Checkout", "ng-controller" => "CheckoutCtrl"} +%checkout{"ng-controller" => "CheckoutCtrl"} = form_for @order, url: "test" do |f| .large-12.columns %fieldset#details diff --git a/app/views/shop/checkout/_summary.html.haml b/app/views/shop/checkout/_summary.html.haml index 6e2689b1a6..ea72c2e4e0 100644 --- a/app/views/shop/checkout/_summary.html.haml +++ b/app/views/shop/checkout/_summary.html.haml @@ -1,27 +1,23 @@ -%form - %fieldset - %legend Your Order - - %table - %tr - %th Cart subtotal - %td= @order.display_item_total - - - checkout_adjustments_for_summary(@order).each do |adjustment| +%orderdetails{"ng-controller" => "SummaryCtrl"} + = form_for @order, url: "#", html: {"ng-submit" => "purchase($event)"} do |f| + %fieldset + %legend Your Order + %table %tr - %th= adjustment.label - %td= adjustment.display_amount.to_html + %th Cart subtotal + %td= @order.display_item_total - %tr - %th Cart total - %td= @order.display_total.to_html - - - - if @order.price_adjustment_totals.present? - - @order.price_adjustment_totals.each do |label, total| + - checkout_adjustments_for_summary(@order).each do |adjustment| %tr - %th= label - %td= total - - %a.button Purchase + %th= adjustment.label + %td= adjustment.display_amount.to_html + %tr + %th Cart total + %td= @order.display_total.to_html + - if @order.price_adjustment_totals.present? + - @order.price_adjustment_totals.each do |label, total| + %tr + %th= label + %td= total + = f.submit "Purchase", class: "button" diff --git a/app/views/shop/checkout/edit.html.haml b/app/views/shop/checkout/edit.html.haml index 21865a4822..5eb6c2bcd4 100644 --- a/app/views/shop/checkout/edit.html.haml +++ b/app/views/shop/checkout/edit.html.haml @@ -1,4 +1,4 @@ -%checkout.darkswarm +.darkswarm - content_for :order_cycle_form do %strong.avenir Order ready on @@ -6,22 +6,20 @@ = render partial: "shop/details" - .row - .large-9.columns - - unless spree_current_user + %checkout{"ng-app" => "Checkout"} + .row + .large-9.columns + - unless spree_current_user + .row + %section#checkout_login + .large-6.columns + = render partial: "shop/checkout/login" + %section#checkout_signup + .large-6.columns + = render partial: "shop/checkout/signup" .row - %section#checkout_login - .large-6.columns - = render partial: "shop/checkout/login" - %section#checkout_signup - .large-6.columns - = render partial: "shop/checkout/signup" + = render partial: "shop/checkout/form" - - .row - = render partial: "shop/checkout/form" - - - .large-3.columns - .row - = render partial: "shop/checkout/summary" + .large-3.columns + .row + = render partial: "shop/checkout/summary" diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 7486ff3575..1d882be092 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -147,17 +147,29 @@ feature "As a consumer I want to check out my cart", js: true do let(:pm1) { create(:payment_method, distributors: [distributor]) } let(:pm2) { create(:payment_method, distributors: [distributor]) } - it "shows all available payment methods" do + before do pm1 # Lazy evaluation of ze create()s pm2 visit "/shop/checkout" + end + + it "shows all available payment methods" do page.should have_content pm1.name page.should have_content pm2.name end + + describe "Purchase" do + it "re-renders with errors when we submit the incomplete form" do + click_button "Purchase" + current_path.should == "/shop/checkout" + page.should have_content "We could not process your order" + end + end end end end + def select_distributor visit "/" click_link distributor.name From ece7caa14a0e0276993a34d5a7a60c17d6743e5c Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Tue, 18 Feb 2014 13:59:52 +1100 Subject: [PATCH 38/87] Moving Darkswarm to new Angular version --- Gemfile | 1 + Gemfile.lock | 2 ++ app/assets/javascripts/darkswarm/all.js.coffee | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 3271c30756..444c61e884 100644 --- a/Gemfile +++ b/Gemfile @@ -16,6 +16,7 @@ gem 'comfortable_mexican_sofa' gem 'simple_form', :github => 'RohanM/simple_form' gem 'unicorn' +gem 'angularjs-rails' gem 'bugsnag' gem 'newrelic_rpm' gem 'haml' diff --git a/Gemfile.lock b/Gemfile.lock index 78b60d1977..4ac81171c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -159,6 +159,7 @@ GEM acts_as_list (0.1.4) addressable (2.3.3) andand (1.3.3) + angularjs-rails (1.2.13) ansi (1.4.2) arel (3.0.2) awesome_nested_set (2.1.5) @@ -495,6 +496,7 @@ PLATFORMS DEPENDENCIES andand + angularjs-rails awesome_print aws-sdk bugsnag diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index 32580e15ee..1f2ecf155d 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -2,8 +2,10 @@ #= require jquery_ujs #= require jquery-ui #= require spin -#= require ../shared/angular -#= require ../shared/angular-resource +# +#= require angular +#= require angular-resource +# #= require ../shared/jquery.timeago #= require foundation #= require ./shop @@ -11,5 +13,3 @@ $ -> $(document).foundation() - - From 4ee4ea7c605a92b8b3c25e4387a5d9e5fdd17da2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 18 Feb 2014 14:02:17 +1100 Subject: [PATCH 39/87] Fix mismatch between client and server variants (ordering issue) --- app/views/spree/api/products/bulk_show.v1.rabl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/api/products/bulk_show.v1.rabl b/app/views/spree/api/products/bulk_show.v1.rabl index 6219bc193e..a220737007 100644 --- a/app/views/spree/api/products/bulk_show.v1.rabl +++ b/app/views/spree/api/products/bulk_show.v1.rabl @@ -10,7 +10,7 @@ node( :supplier ) do |p| partial 'spree/api/enterprises/bulk_show', :object => p.supplier end node( :variants ) do |p| - partial 'spree/api/variants/bulk_index', :object => p.variants.order('id ASC') + partial 'spree/api/variants/bulk_index', :object => p.variants.reorder('spree_variants.id ASC') end node( :master ) do |p| partial 'spree/api/variants/bulk_show', :object => p.master From 6a861de1e169d206f5af7b7b25056b076a2605a0 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Tue, 18 Feb 2014 14:50:50 +1100 Subject: [PATCH 40/87] Reworking the checkout form some more --- .../darkswarm/controllers/checkout_controller.js.coffee | 3 +++ .../darkswarm/controllers/summary_controller.js.coffee | 4 ---- app/views/shop/checkout/_form.html.haml | 3 ++- app/views/shop/checkout/_summary.html.haml | 2 +- config/routes.rb | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index 679c42fa6a..ed4aee83a3 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -6,3 +6,6 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> $scope.shippingMethodChanged = -> $scope.require_ship_address = $("#order_shipping_method_" + $scope.shipping_method).attr("data-require-ship-address") + $scope.purchase = (event)-> + event.preventDefault() + checkout.submit() diff --git a/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee deleted file mode 100644 index 9819f85977..0000000000 --- a/app/assets/javascripts/darkswarm/controllers/summary_controller.js.coffee +++ /dev/null @@ -1,4 +0,0 @@ -angular.module("Checkout").controller "SummaryCtrl", ($scope) -> - $scope.purchase = (event)-> - event.preventDefault() - console.log "test" diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 0d0f7d05a6..afae84fb99 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,5 +1,6 @@ %checkout{"ng-controller" => "CheckoutCtrl"} - = form_for @order, url: "test" do |f| + = form_for @order, url: shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| + {{ checkout.$valid }} .large-12.columns %fieldset#details %legend Customer Details diff --git a/app/views/shop/checkout/_summary.html.haml b/app/views/shop/checkout/_summary.html.haml index ea72c2e4e0..0f1ca388e0 100644 --- a/app/views/shop/checkout/_summary.html.haml +++ b/app/views/shop/checkout/_summary.html.haml @@ -1,4 +1,4 @@ -%orderdetails{"ng-controller" => "SummaryCtrl"} +%orderdetails{"ng-controller" => "CheckoutCtrl"} = form_for @order, url: "#", html: {"ng-submit" => "purchase($event)"} do |f| %fieldset %legend Your Order diff --git a/config/routes.rb b/config/routes.rb index adadef7cf0..76c192dc67 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,7 @@ Openfoodnetwork::Application.routes.draw do #get '', to: "" #end get '/checkout', :to => 'checkout#edit' , :as => :checkout - post '/checkout', :to => 'checkout#update' , :as => :update_checkout + put '/checkout', :to => 'checkout#update' , :as => :update_checkout end resources :enterprises do From adb58bc059b5fdb8b80a1f5d2bf432739bf4b7d6 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Tue, 18 Feb 2014 15:07:00 +1100 Subject: [PATCH 41/87] Moving from @order to current_order to keep DRY --- app/controllers/shop/checkout_controller.rb | 7 +++++-- app/views/shop/checkout/_form.html.haml | 10 +++++----- app/views/shop/checkout/_summary.html.haml | 12 ++++++------ spec/features/consumer/shopping/checkout_spec.rb | 1 - 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index d7b18c9324..e433e1acd4 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -6,8 +6,11 @@ class Shop::CheckoutController < BaseController before_filter :require_line_items def edit - @order = current_order - @order.bill_address ||= Spree::Address.new + current_order.bill_address ||= Spree::Address.new + end + + def update + end private diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index afae84fb99..6ac5a980b0 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,5 +1,5 @@ %checkout{"ng-controller" => "CheckoutCtrl"} - = form_for @order, url: shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| + = form_for current_order, url: shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| {{ checkout.$valid }} .large-12.columns %fieldset#details @@ -8,11 +8,11 @@ .large-6.columns = f.label :email, "Email Address" = f.text_field :email - = f.fields_for @order.bill_address do |ba| + = f.fields_for current_order.bill_address do |ba| .large-6.columns = ba.label :phone = ba.text_field :phone - = f.fields_for @order.bill_address do |ba| + = f.fields_for current_order.bill_address do |ba| .row .large-6.columns = ba.label :firstname @@ -23,7 +23,7 @@ %fieldset %legend Billing Address - = f.fields_for @order.bill_address do |ba| + = f.fields_for current_order.bill_address do |ba| .row .large-12.columns = ba.label :address1 @@ -57,7 +57,7 @@ "data-require-ship-address" => ship_method.require_ship_address = ship_method.name - = fields_for @order.ship_address do |sa| + = fields_for current_order.ship_address do |sa| #ship_address{"ng-show" => "require_ship_address"} .row .large-12.columns diff --git a/app/views/shop/checkout/_summary.html.haml b/app/views/shop/checkout/_summary.html.haml index 0f1ca388e0..edda8bc43a 100644 --- a/app/views/shop/checkout/_summary.html.haml +++ b/app/views/shop/checkout/_summary.html.haml @@ -1,21 +1,21 @@ %orderdetails{"ng-controller" => "CheckoutCtrl"} - = form_for @order, url: "#", html: {"ng-submit" => "purchase($event)"} do |f| + = form_for current_order, url: "#", html: {"ng-submit" => "purchase($event)"} do |f| %fieldset %legend Your Order %table %tr %th Cart subtotal - %td= @order.display_item_total + %td= current_order.display_item_total - - checkout_adjustments_for_summary(@order).each do |adjustment| + - checkout_adjustments_for_summary(current_order).each do |adjustment| %tr %th= adjustment.label %td= adjustment.display_amount.to_html %tr %th Cart total - %td= @order.display_total.to_html - - if @order.price_adjustment_totals.present? - - @order.price_adjustment_totals.each do |label, total| + %td= current_order.display_total.to_html + - if current_order.price_adjustment_totals.present? + - current_order.price_adjustment_totals.each do |label, total| %tr %th= label %td= total diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 1d882be092..91fc4f2d7e 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -169,7 +169,6 @@ feature "As a consumer I want to check out my cart", js: true do end end - def select_distributor visit "/" click_link distributor.name From b0cb19e3702894c5b2eee96f05632009d77faff3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Tue, 18 Feb 2014 16:45:57 +1100 Subject: [PATCH 42/87] Do not show master options text (eg. '1kg') when product has variants --- app/views/shop/_products.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shop/_products.html.haml b/app/views/shop/_products.html.haml index 98b6a7ecf1..4a29a23170 100644 --- a/app/views/shop/_products.html.haml +++ b/app/views/shop/_products.html.haml @@ -21,7 +21,7 @@ {{ product.supplier.name }} %td.notes {{ product.notes | truncate:80 }} %td - {{ product.master.options_text }} + %span{"ng-hide" => "product.variants.length > 0"} {{ product.master.options_text }} %span{"ng-show" => "product.variants.length > 0"} %img.collapse{src: "/assets/collapse.png", "ng-show" => "product.show_variants", From 7b8051862114bfdce65c1898c51b6f983e8a3981 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 11:29:26 +1100 Subject: [PATCH 43/87] On admin variant edit page, do not show option values for unit-related option types --- .../admin/variants_controller_decorator.rb | 3 ++ .../spree/products_helper_decorator.rb | 5 +++ app/models/spree/variant_decorator.rb | 3 -- .../hide_unit_option_types.html.haml.deface | 9 +++++ spec/features/admin/variants_spec.rb | 34 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 app/controllers/spree/admin/variants_controller_decorator.rb create mode 100644 app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface create mode 100644 spec/features/admin/variants_spec.rb diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb new file mode 100644 index 0000000000..a567f0a8c9 --- /dev/null +++ b/app/controllers/spree/admin/variants_controller_decorator.rb @@ -0,0 +1,3 @@ +Spree::Admin::VariantsController.class_eval do + helper 'spree/products' +end diff --git a/app/helpers/spree/products_helper_decorator.rb b/app/helpers/spree/products_helper_decorator.rb index 8c7b7fbe54..d83b2ad95b 100644 --- a/app/helpers/spree/products_helper_decorator.rb +++ b/app/helpers/spree/products_helper_decorator.rb @@ -4,5 +4,10 @@ module Spree def variant_price_diff(variant) "(#{number_to_currency variant.price})" end + + + def variant_unit_option_type?(option_type) + Spree::Product.all_variant_unit_option_types.include? option_type + end end end diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb index de9a9fa9d0..12a4ca1a0e 100644 --- a/app/models/spree/variant_decorator.rb +++ b/app/models/spree/variant_decorator.rb @@ -16,14 +16,11 @@ Spree::Variant.class_eval do price + fees_for(distributor, order_cycle) end - # TODO: This method seems a little redundant. Though perhaps a useful interface. - # Consider removing. def fees_for(distributor, order_cycle) order_cycle.fees_for(self, distributor) end - # Copied and modified from Spree::Variant def options_text values = self.option_values.joins(:option_type).order("#{Spree::OptionType.table_name}.position asc") diff --git a/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface b/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface new file mode 100644 index 0000000000..8facf38294 --- /dev/null +++ b/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface @@ -0,0 +1,9 @@ +/ replace_contents "[data-hook='presentation']" + += label :new_variant, option.option_type.presentation +- if @variant.new_record? + = select(:new_variant, option.option_type.presentation, option.option_type.option_values.collect {|ov| [ ov.presentation, ov.id ] }, {}, {:class => 'select2 fullwidth'}) +- else + - opt = @variant.option_values.detect {|o| o.option_type == option.option_type }.try(:presentation) + - if opt && !variant_unit_option_type?(option.option_type) + = text_field(:new_variant, option.option_type.presentation, :value => opt, :disabled => 'disabled', :class => 'fullwidth') diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb new file mode 100644 index 0000000000..5380333c61 --- /dev/null +++ b/spec/features/admin/variants_spec.rb @@ -0,0 +1,34 @@ +require "spec_helper" + +feature %q{ + As an admin + I want to manage product variants +} do + include AuthenticationWorkflow + include WebHelper + + describe "units and values" do + it "does not show traditional option value fields for unit-related option types" do + # Given a product with unit-related option types, with a variant + p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") + v = create(:variant, product: p, unit_value: 1, unit_description: 'foo') + + # And the product has option types for the unit-related and non-unit-related option values + p.option_types << v.option_values.first.option_type + + # When I view the variant + login_to_admin_section + click_link 'Products' + within('#sub_nav') { click_link 'Products' } + click_link p.name + click_link 'Variants' + + page.find('table.index .icon-edit').click + + # Then I should not see a traditional option value field for the unit-related option value + page.all("div[data-hook='presentation'] input").count.should == 1 + end + + it "shows unit value and description fields when the variant's product has associated option types set" + end +end From fb427244300747b3a7bfea81220ee43ca733b67a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 11:56:10 +1100 Subject: [PATCH 44/87] Remove entire field div instead of just the text field --- .../hide_unit_option_types.html.haml.deface | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface b/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface index 8facf38294..16a0e572b4 100644 --- a/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface +++ b/app/overrides/spree/admin/variants/_form/hide_unit_option_types.html.haml.deface @@ -1,9 +1,10 @@ -/ replace_contents "[data-hook='presentation']" +/ replace "[data-hook='presentation']" -= label :new_variant, option.option_type.presentation -- if @variant.new_record? - = select(:new_variant, option.option_type.presentation, option.option_type.option_values.collect {|ov| [ ov.presentation, ov.id ] }, {}, {:class => 'select2 fullwidth'}) -- else - - opt = @variant.option_values.detect {|o| o.option_type == option.option_type }.try(:presentation) - - if opt && !variant_unit_option_type?(option.option_type) - = text_field(:new_variant, option.option_type.presentation, :value => opt, :disabled => 'disabled', :class => 'fullwidth') +- unless variant_unit_option_type?(option.option_type) + .field{"data-hook" => "presentation"} + = label :new_variant, option.option_type.presentation + - if @variant.new_record? + = select(:new_variant, option.option_type.presentation, option.option_type.option_values.collect {|ov| [ ov.presentation, ov.id ] }, {}, {:class => 'select2 fullwidth'}) + - else + - if opt = @variant.option_values.detect {|o| o.option_type == option.option_type }.try(:presentation) + = text_field(:new_variant, option.option_type.presentation, :value => opt, :disabled => 'disabled', :class => 'fullwidth') From 3024bbbeb59404195ec8c075721625f8f6c788a5 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 11:56:37 +1100 Subject: [PATCH 45/87] Edit unit value and description of variant --- ...nit_value_and_description.html.haml.deface | 9 ++++ spec/features/admin/variants_spec.rb | 48 ++++++++++++------- 2 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface diff --git a/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface b/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface new file mode 100644 index 0000000000..8fbd99d4e3 --- /dev/null +++ b/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface @@ -0,0 +1,9 @@ +/ insert_top "[data-hook='admin_variant_form_fields']" + +.field{"data-hook" => "unit_value"} + = f.label :unit_value, "Unit Value" + = f.text_field :unit_value, class: "fullwidth" + +.field{"data-hook" => "unit_description"} + = f.label :unit_description, "Unit Description" + = f.text_field :unit_description, class: "fullwidth" diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 5380333c61..c0bd305f15 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -7,28 +7,40 @@ feature %q{ include AuthenticationWorkflow include WebHelper - describe "units and values" do - it "does not show traditional option value fields for unit-related option types" do - # Given a product with unit-related option types, with a variant - p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") - v = create(:variant, product: p, unit_value: 1, unit_description: 'foo') + scenario "editing unit value and description for a variant" do + # Given a product with unit-related option types, with a variant + p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") + v = create(:variant, product: p, unit_value: 1, unit_description: 'foo') - # And the product has option types for the unit-related and non-unit-related option values - p.option_types << v.option_values.first.option_type + # And the product has option types for the unit-related and non-unit-related option values + p.option_types << v.option_values.first.option_type - # When I view the variant - login_to_admin_section - click_link 'Products' - within('#sub_nav') { click_link 'Products' } - click_link p.name - click_link 'Variants' + # When I view the variant + login_to_admin_section + click_link 'Products' + within('#sub_nav') { click_link 'Products' } + click_link p.name + click_link 'Variants' + page.find('table.index .icon-edit').click - page.find('table.index .icon-edit').click + # Then I should not see a traditional option value field for the unit-related option value + page.all("div[data-hook='presentation'] input").count.should == 1 - # Then I should not see a traditional option value field for the unit-related option value - page.all("div[data-hook='presentation'] input").count.should == 1 - end + # And I should see unit value and description fields for the unit-related option value + page.should have_field "variant_unit_value", with: "1" + page.should have_field "variant_unit_description", with: "foo" - it "shows unit value and description fields when the variant's product has associated option types set" + # When I update the fields and save the variant + fill_in "variant_unit_value", with: "123" + fill_in "variant_unit_description", with: "bar" + click_button 'Update' + page.should have_content %Q(Variant "#{p.name}" has been successfully updated!) + + # Then the unit value and description should have been saved + v.reload + v.unit_value.should == 123 + v.unit_description.should == 'bar' end + + it "should not show unit value or description fields when the product does not have a unit-related option type" end From 3f9f24157c927720c97708ef8040423e858e9a1a Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 12:07:53 +1100 Subject: [PATCH 46/87] Do not show unit value or description when product does not have a unit-related option type --- .../spree/products_helper_decorator.rb | 5 +++++ ...nit_value_and_description.html.haml.deface | 13 ++++++------ spec/features/admin/variants_spec.rb | 21 ++++++++++++++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/app/helpers/spree/products_helper_decorator.rb b/app/helpers/spree/products_helper_decorator.rb index d83b2ad95b..99596a73e9 100644 --- a/app/helpers/spree/products_helper_decorator.rb +++ b/app/helpers/spree/products_helper_decorator.rb @@ -6,6 +6,11 @@ module Spree end + def product_has_variant_unit_option_type?(product) + product.option_types.any? { |option_type| variant_unit_option_type? option_type } + end + + def variant_unit_option_type?(option_type) Spree::Product.all_variant_unit_option_types.include? option_type end diff --git a/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface b/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface index 8fbd99d4e3..8696134177 100644 --- a/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface +++ b/app/overrides/spree/admin/variants/_form/add_unit_value_and_description.html.haml.deface @@ -1,9 +1,10 @@ / insert_top "[data-hook='admin_variant_form_fields']" -.field{"data-hook" => "unit_value"} - = f.label :unit_value, "Unit Value" - = f.text_field :unit_value, class: "fullwidth" +- if product_has_variant_unit_option_type?(@product) + .field{"data-hook" => "unit_value"} + = f.label :unit_value, "Unit Value" + = f.text_field :unit_value, class: "fullwidth" -.field{"data-hook" => "unit_description"} - = f.label :unit_description, "Unit Description" - = f.text_field :unit_description, class: "fullwidth" + .field{"data-hook" => "unit_description"} + = f.label :unit_description, "Unit Description" + = f.text_field :unit_description, class: "fullwidth" diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index c0bd305f15..7e178ab443 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -42,5 +42,24 @@ feature %q{ v.unit_description.should == 'bar' end - it "should not show unit value or description fields when the product does not have a unit-related option type" + it "does not show unit value or description fields when the product does not have a unit-related option type" do + # Given a product without unit-related option types, with a variant + p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil) + v = create(:variant, product: p, unit_value: nil, unit_description: nil) + + # And the product has option types for the variant's option values + p.option_types << v.option_values.first.option_type + + # When I view the variant + login_to_admin_section + click_link 'Products' + within('#sub_nav') { click_link 'Products' } + click_link p.name + click_link 'Variants' + page.find('table.index .icon-edit').click + + # Then I should not see unit value and description fields + page.should_not have_field "variant_unit_value" + page.should_not have_field "variant_unit_description" + end end From acbf1ed680245710d527f4a972ce9d3fb5aa99f3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 13:12:52 +1100 Subject: [PATCH 47/87] Refactor fragile spec to use has_field? and has_select? in an attempt to make it more reliable in CI --- spec/features/admin/order_cycles_spec.rb | 32 +++++++++--------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 8f2c1b2a34..5cadd11a29 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -189,24 +189,20 @@ feature %q{ end # And the suppliers should have fees - page.find('#order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_id option[selected=selected]'). - text.should == oc.suppliers.first.name - page.find('#order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). - text.should == oc.suppliers.first.enterprise_fees.first.name + page.should have_select 'order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_id', selected: oc.suppliers.first.name + page.should have_select 'order_cycle_incoming_exchange_0_enterprise_fees_0_enterprise_fee_id', selected: oc.suppliers.first.enterprise_fees.first.name - page.find('#order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_id option[selected=selected]'). - text.should == oc.suppliers.last.name - page.find('#order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). - text.should == oc.suppliers.last.enterprise_fees.first.name + page.should have_select 'order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_id', selected: oc.suppliers.last.name + page.should have_select 'order_cycle_incoming_exchange_1_enterprise_fees_0_enterprise_fee_id', selected: oc.suppliers.last.enterprise_fees.first.name # And I should see the distributors page.should have_selector 'td.distributor_name', :text => oc.distributors.first.name page.should have_selector 'td.distributor_name', :text => oc.distributors.last.name - page.find('#order_cycle_outgoing_exchange_0_pickup_time').value.should == 'time 0' - page.find('#order_cycle_outgoing_exchange_0_pickup_instructions').value.should == 'instructions 0' - page.find('#order_cycle_outgoing_exchange_1_pickup_time').value.should == 'time 1' - page.find('#order_cycle_outgoing_exchange_1_pickup_instructions').value.should == 'instructions 1' + page.should have_field 'order_cycle_outgoing_exchange_0_pickup_time', with: 'time 0' + page.should have_field 'order_cycle_outgoing_exchange_0_pickup_instructions', with: 'instructions 0' + page.should have_field 'order_cycle_outgoing_exchange_1_pickup_time', with: 'time 1' + page.should have_field 'order_cycle_outgoing_exchange_1_pickup_instructions', with: 'instructions 1' # And the distributors should have products page.all('table.exchanges tbody tr.distributor').each do |row| @@ -219,15 +215,11 @@ feature %q{ end # And the distributors should have fees - page.find('#order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_id option[selected=selected]'). - text.should == oc.distributors.first.name - page.find('#order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). - text.should == oc.distributors.first.enterprise_fees.first.name + page.should have_select 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_id', selected: oc.distributors.first.name + page.should have_select 'order_cycle_outgoing_exchange_0_enterprise_fees_0_enterprise_fee_id', selected: oc.distributors.first.enterprise_fees.first.name - page.find('#order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_id option[selected=selected]'). - text.should == oc.distributors.last.name - page.find('#order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_fee_id option[selected=selected]'). - text.should == oc.distributors.last.enterprise_fees.first.name + page.should have_select 'order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_id', selected: oc.distributors.last.name + page.should have_select 'order_cycle_outgoing_exchange_1_enterprise_fees_0_enterprise_fee_id', selected: oc.distributors.last.enterprise_fees.first.name end From f88b930137a7c5609bd5f09a5fa13e5ecc050c8d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 13:32:17 +1100 Subject: [PATCH 48/87] Extract unit_value_with_description into unit_description only when a string starting with a number is provided --- .../javascripts/admin/bulk_product_update.js.coffee | 2 +- spec/javascripts/unit/bulk_product_update_spec.js.coffee | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 83590ba45d..42e727bb6f 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -419,7 +419,7 @@ productEditModule.controller "AdminProductEditCtrl", [ $scope.packVariant = (product, variant) -> if variant.hasOwnProperty("unit_value_with_description") - match = variant.unit_value_with_description.match(/^([\d\.]+|)( |)(.*)$/) + match = variant.unit_value_with_description.match(/^([\d\.]+(?= |$)|)( |)(.*)$/) if match product = $scope.findProduct(product.id) variant.unit_value = parseFloat(match[1]) diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee index d88bb1e34e..bd85419ac9 100644 --- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee @@ -760,6 +760,14 @@ describe "AdminProductEditCtrl", -> unit_description: 'Medium' unit_value_with_description: "Medium" + it "extracts into unit_description when a string starting with a number is provided", -> + testVariant = {unit_value_with_description: "1kg"} + scope.packVariant(testProduct, testVariant) + expect(testVariant).toEqual + unit_value: null + unit_description: '1kg' + unit_value_with_description: "1kg" + it "sets blank values when no value provided", -> testVariant = {unit_value_with_description: ""} scope.packVariant(testProduct, testVariant) From 6d1a20280097a4d7f4019c93ac30edb3d22962d4 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 13:32:57 +1100 Subject: [PATCH 49/87] Fix grammar --- spec/features/admin/products_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 3399785a3c..58d9d57775 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -1,8 +1,8 @@ require "spec_helper" feature %q{ - As a supplier - I want set a supplier and distributor(s) for a product + As an admin + I want to set a supplier and distributor(s) for a product } do include AuthenticationWorkflow include WebHelper From 97a6a812b833c0204ded88f650970cbefef22420 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 19 Feb 2014 13:44:31 +1100 Subject: [PATCH 50/87] Fix product listing appearing on RHS of page on Firefox --- app/assets/stylesheets/admin/products.css.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/admin/products.css.scss b/app/assets/stylesheets/admin/products.css.scss index 026e4cf83b..20f9c49a84 100644 --- a/app/assets/stylesheets/admin/products.css.scss +++ b/app/assets/stylesheets/admin/products.css.scss @@ -106,6 +106,8 @@ ul.column-list { } table#listing_products.bulk { + clear: both; + td.supplier { select { width: 125px; From b6b5c2889eb55e8e70ee920a8586c62d392cbf16 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 21 Feb 2014 14:31:03 +1100 Subject: [PATCH 51/87] Reworking checkout to use f_form_for, show validation, better controller layout --- Gemfile | 1 + Gemfile.lock | 17 +++++- app/controllers/shop/checkout_controller.rb | 61 ++++++++++++++----- app/helpers/application_helper.rb | 2 + app/views/layouts/darkswarm.html.haml | 1 + app/views/shop/checkout/_form.html.haml | 18 +----- .../shop/checkout_controller_spec.rb | 9 ++- .../consumer/shopping/checkout_spec.rb | 5 +- 8 files changed, 75 insertions(+), 39 deletions(-) diff --git a/Gemfile b/Gemfile index 444c61e884..a8557a7330 100644 --- a/Gemfile +++ b/Gemfile @@ -51,6 +51,7 @@ group :assets do gem 'turbo-sprockets-rails3' gem 'zurb-foundation', :github => 'zurb/foundation' end +gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3" gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 4ac81171c3..d373f97849 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -93,6 +93,16 @@ GIT i18n (~> 0.5) spree (~> 1.1) +GIT + remote: git://github.com/willrjmarshall/foundation_rails_helper.git + revision: 61ca053cea97c611dfd546bf7fcd846b98abd401 + branch: rails3 + specs: + foundation_rails_helper (0.4) + actionpack (>= 3.0) + activemodel (>= 3.0) + railties (>= 3.0) + GIT remote: git://github.com/zurb/foundation.git revision: a81d639847ba5fc2e324b749b8e409e31e8d83c9 @@ -292,7 +302,7 @@ GEM httparty (0.11.0) multi_json (~> 1.0) multi_xml (>= 0.5.2) - i18n (0.6.5) + i18n (0.6.9) journey (1.0.4) jquery-rails (2.2.2) railties (>= 3.0, < 5.0) @@ -325,7 +335,7 @@ GEM money (5.0.0) i18n (~> 0.4) json - multi_json (1.8.2) + multi_json (1.8.4) multi_xml (0.5.5) net-scp (1.1.2) net-ssh (>= 2.6.5) @@ -383,7 +393,7 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) raindrops (0.9.0) - rake (10.1.0) + rake (10.1.1) ransack (0.7.2) actionpack (~> 3.0) activerecord (~> 3.0) @@ -512,6 +522,7 @@ DEPENDENCIES eaterprises_feature! factory_girl_rails faker + foundation_rails_helper! geocoder gmaps4rails guard diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index e433e1acd4..b81e07a762 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -1,37 +1,70 @@ -class Shop::CheckoutController < BaseController +class Shop::CheckoutController < Spree::CheckoutController layout 'darkswarm' - before_filter :set_distributor - before_filter :require_order_cycle - before_filter :require_line_items - + prepend_before_filter :require_order_cycle + prepend_before_filter :require_distributor_chosen + skip_before_filter :check_registration + + include EnterprisesHelper + def edit - current_order.bill_address ||= Spree::Address.new end def update + if @order.update_attributes(params[:order]) + fire_event('spree.checkout.update') + while @order.state != "complete" + if @order.next + state_callback(:after) + else + flash[:error] = t(:payment_processing_failed) + respond_with @order, location: shop_checkout_path + break + end + end + if @order.state == "complete" || @order.completed? + flash.notice = t(:order_processed_successfully) + flash[:commerce_tracking] = "nothing special" + respond_with(@order, :location => order_conf) + else + respond_with @order, location: shop_checkout_path + end + else + respond_with @order, location: shop_checkout_path + end end private + def skip_state_validation? + true + end + def set_distributor unless @distributor = current_distributor - redirect_to root_path + redirect_to main_app.root_path end end def require_order_cycle unless current_order_cycle - redirect_to shop_path + redirect_to main_app.shop_path end end - # Y U LOOK AT CART? CART IS EMPTY! - # NO CAN HAZ! - def require_line_items - if current_order.line_items.empty? - redirect_to shop_path - end + def load_order + @order = current_order + redirect_to main_app.shop_path and return unless @order and @order.checkout_allowed? + raise_insufficient_quantity and return if @order.insufficient_stock_lines.present? + redirect_to main_app.shop_path and return if @order.completed? + before_address + state_callback(:before) + end + + # Overriding Spree's methods + def raise_insufficient_quantity + flash[:error] = t(:spree_inventory_error_flash_for_insufficient_quantity) + redirect_to main_app.shop_path end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 794aee744b..7afe5e180b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,6 @@ module ApplicationHelper + include FoundationRailsHelper::FlashHelper + def home_page_cms_content if controller.controller_name == 'home' && controller.action_name == 'index' cms_page_content(:content, Cms::Page.find_by_full_path('/')) diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 823d2d7350..150ffaac52 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -14,6 +14,7 @@ %body.off-canvas = render partial: "shared/menu" + = display_flash_messages %section{ role: "main" } = yield diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 6ac5a980b0..192ab67aba 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,24 +1,20 @@ %checkout{"ng-controller" => "CheckoutCtrl"} - = form_for current_order, url: shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| + = f_form_for current_order, url: main_app.shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| {{ checkout.$valid }} .large-12.columns %fieldset#details %legend Customer Details .row .large-6.columns - = f.label :email, "Email Address" = f.text_field :email = f.fields_for current_order.bill_address do |ba| .large-6.columns - = ba.label :phone = ba.text_field :phone = f.fields_for current_order.bill_address do |ba| .row .large-6.columns - = ba.label :firstname = ba.text_field :firstname .large-6.columns - = ba.label :lastname = ba.text_field :lastname %fieldset @@ -26,22 +22,17 @@ = f.fields_for current_order.bill_address do |ba| .row .large-12.columns - = ba.label :address1 = ba.text_field :address1 .row .large-12.columns - = ba.label :address2 = ba.text_field :address2 .row .large-6.columns - = ba.label :city = ba.text_field :city .large-6.columns - = ba.label :country = ba.text_field :country .row .large-6.columns.right - = ba.label :zipcode = ba.text_field :zipcode %fieldset#shipping @@ -61,30 +52,23 @@ #ship_address{"ng-show" => "require_ship_address"} .row .large-12.columns - = sa.label :address1 = sa.text_field :address1 .row .large-12.columns - = sa.label :address2 = sa.text_field :address2 .row .large-6.columns - = sa.label :city = sa.text_field :city .large-6.columns - = sa.label :country = sa.text_field :country .row .large-6.columns.right - = sa.label :zipcode = sa.text_field :zipcode .row .large-6.columns - = sa.label :firstname = sa.text_field :firstname .large-6.columns - = sa.label :lastname = sa.text_field :lastname %fieldset#payment %legend Payment Details diff --git a/spec/controllers/shop/checkout_controller_spec.rb b/spec/controllers/shop/checkout_controller_spec.rb index 89cf8288ec..ae560a26b8 100644 --- a/spec/controllers/shop/checkout_controller_spec.rb +++ b/spec/controllers/shop/checkout_controller_spec.rb @@ -4,6 +4,10 @@ describe Shop::CheckoutController do let(:distributor) { double(:distributor) } let(:order_cycle) { create(:order_cycle) } let(:order) { create(:order) } + before do + order.stub(:checkout_allowed?).and_return true + controller.stub(:check_authorization).and_return true + end it "redirects home when no distributor is selected" do get :edit response.should redirect_to root_path @@ -19,7 +23,7 @@ describe Shop::CheckoutController do controller.stub(:current_distributor).and_return(distributor) controller.stub(:current_order_cycle).and_return(order_cycle) controller.stub(:current_order).and_return(order) - order.stub_chain(:line_items, :empty?).and_return true + order.stub_chain(:insufficient_stock_lines, :present?).and_return true get :edit response.should redirect_to shop_path end @@ -28,8 +32,7 @@ describe Shop::CheckoutController do controller.stub(:current_distributor).and_return(distributor) controller.stub(:current_order_cycle).and_return(order_cycle) controller.stub(:current_order).and_return(order) - order.stub_chain(:line_items, :empty?).and_return false - + order.stub_chain(:insufficient_stock_lines, :present?).and_return false get :edit response.should be_success end diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 91fc4f2d7e..2e287e7c71 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -158,11 +158,12 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_content pm2.name end - describe "Purchase" do + describe "Purchasing" do it "re-renders with errors when we submit the incomplete form" do click_button "Purchase" current_path.should == "/shop/checkout" - page.should have_content "We could not process your order" + save_and_open_page + page.should have_content "can't be blank" end end end From 04ac6f466c2978322007860a1da54c8786c25003 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 21 Feb 2014 15:15:06 +1100 Subject: [PATCH 52/87] Forcing the class of Country --- app/models/spree/address_decorator.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/spree/address_decorator.rb b/app/models/spree/address_decorator.rb index 7cfd6cca0b..28f292fbd3 100644 --- a/app/models/spree/address_decorator.rb +++ b/app/models/spree/address_decorator.rb @@ -1,5 +1,6 @@ Spree::Address.class_eval do has_one :enterprise + belongs_to :country, class_name: "Spree::Country" geocoded_by :full_address From a162f4a108589fc4a66f7d06e43a380b64aedc0a Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 21 Feb 2014 15:17:18 +1100 Subject: [PATCH 53/87] Adding some spec tweaks --- spec/features/consumer/shopping/checkout_spec.rb | 1 - spec/models/spree/addresses_spec.rb | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 2e287e7c71..fcb06bb11f 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -162,7 +162,6 @@ feature "As a consumer I want to check out my cart", js: true do it "re-renders with errors when we submit the incomplete form" do click_button "Purchase" current_path.should == "/shop/checkout" - save_and_open_page page.should have_content "can't be blank" end end diff --git a/spec/models/spree/addresses_spec.rb b/spec/models/spree/addresses_spec.rb index 30497c8c26..6c6a88ba11 100644 --- a/spec/models/spree/addresses_spec.rb +++ b/spec/models/spree/addresses_spec.rb @@ -29,6 +29,12 @@ describe Spree::Address do end end + describe "setters" do + it "lets us set a country" do + expect { Spree::Address.new.country = "A country" }.to raise_error ActiveRecord::AssociationTypeMismatch + end + end + describe "notifying bugsnag when saved with missing data" do it "notifies on create" do Bugsnag.should_receive(:notify) From 6b75e8c9ef10280195ded14f89bb4537ec69f128 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 21 Feb 2014 15:19:12 +1100 Subject: [PATCH 54/87] Switching to f_form_for and :bill_address --- app/views/shop/checkout/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 192ab67aba..5ae7631b7b 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -19,7 +19,7 @@ %fieldset %legend Billing Address - = f.fields_for current_order.bill_address do |ba| + = f.fields_for :bill_address do |ba| .row .large-12.columns = ba.text_field :address1 From 08009d40205f454b1de7da10d892527f69111cb3 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 21 Feb 2014 15:40:54 +1100 Subject: [PATCH 55/87] Extract application of enterprise fees as adjustments into its own class --- app/models/exchange.rb | 4 ++ app/models/order_cycle.rb | 26 +++-------- .../enterprise_fee_applicator.rb | 16 +++++++ .../enterprise_fee_applicator_spec.rb | 37 ++++++++++++++++ spec/models/exchange_spec.rb | 14 ++++++ spec/models/order_cycle_spec.rb | 44 ++++--------------- 6 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 lib/open_food_network/enterprise_fee_applicator.rb create mode 100644 spec/lib/open_food_network/enterprise_fee_applicator_spec.rb diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 0f8b472443..ce698c1cd0 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -35,6 +35,10 @@ class Exchange < ActiveRecord::Base receiver == order_cycle.coordinator end + def role + incoming? ? 'supplier' : 'distributor' + end + def to_h(core=false) h = attributes.merge({ 'variant_ids' => variant_ids.sort, 'enterprise_fee_ids' => enterprise_fee_ids.sort }) h.reject! { |k| %w(id order_cycle_id created_at updated_at).include? k } if core diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index d710befc10..1dbdbbed18 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -1,3 +1,5 @@ +require 'open_food_network/enterprise_fee_applicator' + class OrderCycle < ActiveRecord::Base belongs_to :coordinator, :class_name => 'Enterprise' has_and_belongs_to_many :coordinator_fees, :class_name => 'EnterpriseFee', :join_table => 'coordinator_fees' @@ -144,12 +146,12 @@ class OrderCycle < ActiveRecord::Base # -- Fees def fees_for(variant, distributor) - enterprise_fees_for(variant, distributor).sum do |fee| + enterprise_fees_for(variant, distributor).sum do |applicator| # Spree's Calculator interface accepts Orders or LineItems, # so we meet that interface with a struct. # Amount is faked, this is a method on LineItem line_item = OpenStruct.new variant: variant, quantity: 1, amount: (variant.price) - fee[:enterprise_fee].compute_amount(line_item) + applicator.enterprise_fee.compute_amount(line_item) end end @@ -157,7 +159,7 @@ class OrderCycle < ActiveRecord::Base variant = line_item.variant distributor = line_item.order.distributor - enterprise_fees_for(variant, distributor).each { |fee| create_adjustment_for_fee line_item, fee[:enterprise_fee], fee[:label], fee[:role] } + enterprise_fees_for(variant, distributor).each { |applicator| applicator.create_line_item_adjustment(line_item) } end private @@ -168,31 +170,17 @@ class OrderCycle < ActiveRecord::Base exchanges_carrying(variant, distributor).each do |exchange| exchange.enterprise_fees.each do |enterprise_fee| - role = exchange.incoming? ? 'supplier' : 'distributor' - fees << {enterprise_fee: enterprise_fee, - label: adjustment_label_for(variant, enterprise_fee, role), - role: role} + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, exchange.role) end end coordinator_fees.each do |enterprise_fee| - fees << {enterprise_fee: enterprise_fee, - label: adjustment_label_for(variant, enterprise_fee, 'coordinator'), - role: 'coordinator'} + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, 'coordinator') end fees end - def create_adjustment_for_fee(line_item, enterprise_fee, label, role) - a = enterprise_fee.create_locked_adjustment(label, line_item.order, line_item, true) - AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role - end - - def adjustment_label_for(variant, enterprise_fee, role) - "#{variant.product.name} - #{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" - end - def exchanges_carrying(variant, distributor) exchanges.to_enterprises([coordinator, distributor]).with_variant(variant) end diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb new file mode 100644 index 0000000000..c2b3001bca --- /dev/null +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -0,0 +1,16 @@ +module OpenFoodNetwork + class EnterpriseFeeApplicator < Struct.new(:enterprise_fee, :variant, :role) + def create_line_item_adjustment(line_item) + a = enterprise_fee.create_locked_adjustment(adjustment_label, line_item.order, line_item, true) + AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role + end + + + private + + def adjustment_label + "#{variant.product.name} - #{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" + end + end +end + diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb new file mode 100644 index 0000000000..345eae845c --- /dev/null +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -0,0 +1,37 @@ +require 'open_food_network/enterprise_fee_applicator' + +module OpenFoodNetwork + describe EnterpriseFeeApplicator do + it "creates an adjustment for a line item" do + line_item = create(:line_item) + enterprise_fee = create(:enterprise_fee) + product = create(:simple_product) + + efa = EnterpriseFeeApplicator.new enterprise_fee, product.master, 'role' + efa.stub(:adjustment_label) { 'label' } + efa.create_line_item_adjustment line_item + + adjustment = Spree::Adjustment.last + adjustment.label.should == 'label' + adjustment.adjustable.should == line_item.order + adjustment.source.should == line_item + adjustment.originator.should == enterprise_fee + adjustment.should be_mandatory + + md = adjustment.metadata + md.enterprise.should == enterprise_fee.enterprise + md.fee_name.should == enterprise_fee.name + md.fee_type.should == enterprise_fee.fee_type + md.enterprise_role.should == 'role' + end + + it "makes adjustment labels" do + variant = double(:variant, product: double(:product, name: 'Bananas')) + enterprise_fee = double(:enterprise_fee, fee_type: 'packing', enterprise: double(:enterprise, name: 'Ballantyne')) + + efa = EnterpriseFeeApplicator.new enterprise_fee, variant, 'distributor' + + efa.send(:adjustment_label).should == "Bananas - packing fee by distributor Ballantyne" + end + end +end diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index f4ebfc2d8a..df26d20ac0 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -62,6 +62,20 @@ describe Exchange do end end + describe "reporting its role" do + it "returns 'supplier' when it is an incoming exchange" do + e = Exchange.new + e.stub(:incoming?) { true } + e.role.should == 'supplier' + end + + it "returns 'distributor' when it is an outgoing exchange" do + e = Exchange.new + e.stub(:incoming?) { false } + e.role.should == 'distributor' + end + end + describe "scopes" do let(:supplier) { create(:supplier_enterprise) } let(:coordinator) { create(:distributor_enterprise) } diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index bd8d835d91..36480ca74b 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -348,9 +348,9 @@ describe OrderCycle do let(:line_item) { double(:line_item, variant: variant, order: order) } it "creates adjustment for each fee" do - fee = {enterprise_fee: 'ef', label: 'label', role: 'role'} - oc.should_receive(:enterprise_fees_for).with(variant, distributor) { [fee] } - oc.should_receive(:create_adjustment_for_fee).with(line_item, 'ef', 'label', 'role') + applicator = double(:enterprise_fee_applicator) + applicator.should_receive(:create_line_item_adjustment).with(line_item) + oc.should_receive(:enterprise_fees_for).with(variant, distributor) { [applicator] } oc.send(:create_adjustments_for, line_item) end @@ -360,44 +360,16 @@ describe OrderCycle do ef1 = double(:enterprise_fee) ef2 = double(:enterprise_fee) ef3 = double(:enterprise_fee) - incoming_exchange = double(:exchange, enterprise_fees: [ef1], incoming?: true) - outgoing_exchange = double(:exchange, enterprise_fees: [ef2], incoming?: false) + incoming_exchange = double(:exchange, enterprise_fees: [ef1], role: 'supplier') + outgoing_exchange = double(:exchange, enterprise_fees: [ef2], role: 'distributor') oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } oc.stub(:coordinator_fees) { [ef3] } - oc.stub(:adjustment_label_for) { 'label' } oc.send(:enterprise_fees_for, line_item.variant, distributor).should == - [{enterprise_fee: ef1, label: 'label', role: 'supplier'}, - {enterprise_fee: ef2, label: 'label', role: 'distributor'}, - {enterprise_fee: ef3, label: 'label', role: 'coordinator'}] - end - - it "creates an adjustment for a fee" do - line_item = create(:line_item) - enterprise_fee = create(:enterprise_fee) - - oc.send(:create_adjustment_for_fee, line_item, enterprise_fee, 'label', 'role') - - adjustment = Spree::Adjustment.last - adjustment.label.should == 'label' - adjustment.adjustable.should == line_item.order - adjustment.source.should == line_item - adjustment.originator.should == enterprise_fee - adjustment.should be_mandatory - - md = adjustment.metadata - md.enterprise.should == enterprise_fee.enterprise - md.fee_name.should == enterprise_fee.name - md.fee_type.should == enterprise_fee.fee_type - md.enterprise_role.should == 'role' - end - - it "makes adjustment labels" do - variant = double(:variant, product: double(:product, name: 'Bananas')) - enterprise_fee = double(:enterprise_fee, fee_type: 'packing', enterprise: double(:enterprise, name: 'Ballantyne')) - - oc.send(:adjustment_label_for, variant, enterprise_fee, 'distributor').should == "Bananas - packing fee by distributor Ballantyne" + [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, line_item.variant, 'supplier'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, line_item.variant, 'distributor'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, line_item.variant, 'coordinator')] end end From da8a8e8a1af7bce1acd2cd455620790332d06010 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 21 Feb 2014 15:49:10 +1100 Subject: [PATCH 56/87] Rename method --- app/models/order_cycle.rb | 6 +++--- spec/models/order_cycle_spec.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 1dbdbbed18..057f1c4211 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -146,7 +146,7 @@ class OrderCycle < ActiveRecord::Base # -- Fees def fees_for(variant, distributor) - enterprise_fees_for(variant, distributor).sum do |applicator| + per_item_enterprise_fee_applicators_for(variant, distributor).sum do |applicator| # Spree's Calculator interface accepts Orders or LineItems, # so we meet that interface with a struct. # Amount is faked, this is a method on LineItem @@ -159,13 +159,13 @@ class OrderCycle < ActiveRecord::Base variant = line_item.variant distributor = line_item.order.distributor - enterprise_fees_for(variant, distributor).each { |applicator| applicator.create_line_item_adjustment(line_item) } + per_item_enterprise_fee_applicators_for(variant, distributor).each { |applicator| applicator.create_line_item_adjustment(line_item) } end private # -- Fees - def enterprise_fees_for(variant, distributor) + def per_item_enterprise_fee_applicators_for(variant, distributor) fees = [] exchanges_carrying(variant, distributor).each do |exchange| diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 36480ca74b..7ab229d0cc 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -350,7 +350,7 @@ describe OrderCycle do it "creates adjustment for each fee" do applicator = double(:enterprise_fee_applicator) applicator.should_receive(:create_line_item_adjustment).with(line_item) - oc.should_receive(:enterprise_fees_for).with(variant, distributor) { [applicator] } + oc.should_receive(:per_item_enterprise_fee_applicators_for).with(variant, distributor) { [applicator] } oc.send(:create_adjustments_for, line_item) end @@ -366,7 +366,7 @@ describe OrderCycle do oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } oc.stub(:coordinator_fees) { [ef3] } - oc.send(:enterprise_fees_for, line_item.variant, distributor).should == + oc.send(:per_item_enterprise_fee_applicators_for, line_item.variant, distributor).should == [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, line_item.variant, 'supplier'), OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, line_item.variant, 'distributor'), OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, line_item.variant, 'coordinator')] From febbe087e9d06bb52045c99632a9d40a468e7bcd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 10:39:39 +1100 Subject: [PATCH 57/87] Find EnterpriseFees with per-item calculators --- app/models/enterprise_fee.rb | 4 ++++ spec/models/enterprise_fee_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 8f3f038ded..2d8ecbd828 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -21,6 +21,10 @@ class EnterpriseFee < ActiveRecord::Base end } + scope :per_item, lambda { + joins(:calculator).where('spree_calculators.type NOT IN (?)', ['Spree::Calculator::FlatRate', 'Spree::Calculator::FlexiRate']) + } + def self.clear_all_adjustments_for(line_item) line_item.order.adjustments.where(originator_type: 'EnterpriseFee', source_id: line_item, source_type: 'Spree::LineItem').destroy_all end diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 98484d6545..45ba74caac 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -9,6 +9,26 @@ describe EnterpriseFee do it { should validate_presence_of(:name) } end + describe "scopes" do + describe "finding per-item enterprise fees" do + it "does not return fees with FlatRate and FlexiRate calculators" do + create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new) + create(:enterprise_fee, calculator: Spree::Calculator::FlexiRate.new) + + EnterpriseFee.per_item.should be_empty + end + + it "returns fees with any other calculator" do + ef1 = create(:enterprise_fee, calculator: Spree::Calculator::DefaultTax.new) + ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlatPercentItemTotal.new) + ef3 = create(:enterprise_fee, calculator: Spree::Calculator::PerItem.new) + ef4 = create(:enterprise_fee, calculator: Spree::Calculator::PriceSack.new) + + EnterpriseFee.per_item.sort.should == [ef1, ef2, ef3, ef4].sort + end + end + end + describe "clearing all enterprise fee adjustments for a line item" do it "clears adjustments originating from many different enterprise fees" do p = create(:simple_product) From 75c8da1774599902d5713cfba96ad5eb62110e36 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 12:07:38 +1100 Subject: [PATCH 58/87] Rename create_adjustments_for to create_line_item_adjustments_for, only show per-item fees --- app/models/order_cycle.rb | 8 ++++---- app/models/spree/order_decorator.rb | 2 +- spec/models/order_cycle_spec.rb | 10 ++++++---- spec/models/spree/order_spec.rb | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 057f1c4211..7e85258b19 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -150,12 +150,12 @@ class OrderCycle < ActiveRecord::Base # Spree's Calculator interface accepts Orders or LineItems, # so we meet that interface with a struct. # Amount is faked, this is a method on LineItem - line_item = OpenStruct.new variant: variant, quantity: 1, amount: (variant.price) + line_item = OpenStruct.new variant: variant, quantity: 1, amount: variant.price applicator.enterprise_fee.compute_amount(line_item) end end - def create_adjustments_for(line_item) + def create_line_item_adjustments_for(line_item) variant = line_item.variant distributor = line_item.order.distributor @@ -169,12 +169,12 @@ class OrderCycle < ActiveRecord::Base fees = [] exchanges_carrying(variant, distributor).each do |exchange| - exchange.enterprise_fees.each do |enterprise_fee| + exchange.enterprise_fees.per_item.each do |enterprise_fee| fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, exchange.role) end end - coordinator_fees.each do |enterprise_fee| + coordinator_fees.per_item.each do |enterprise_fee| fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, variant, 'coordinator') end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 45caf95df9..20c90fe133 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -80,7 +80,7 @@ Spree::Order.class_eval do line_items.each do |line_item| if provided_by_order_cycle? line_item - order_cycle.create_adjustments_for line_item + order_cycle.create_line_item_adjustments_for line_item else pd = product_distribution_for line_item diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 7ab229d0cc..ca11a24ded 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -352,7 +352,7 @@ describe OrderCycle do applicator.should_receive(:create_line_item_adjustment).with(line_item) oc.should_receive(:per_item_enterprise_fee_applicators_for).with(variant, distributor) { [applicator] } - oc.send(:create_adjustments_for, line_item) + oc.send(:create_line_item_adjustments_for, line_item) end it "finds fees for a line item" do @@ -360,11 +360,13 @@ describe OrderCycle do ef1 = double(:enterprise_fee) ef2 = double(:enterprise_fee) ef3 = double(:enterprise_fee) - incoming_exchange = double(:exchange, enterprise_fees: [ef1], role: 'supplier') - outgoing_exchange = double(:exchange, enterprise_fees: [ef2], role: 'distributor') + incoming_exchange = double(:exchange, role: 'supplier') + outgoing_exchange = double(:exchange, role: 'distributor') + incoming_exchange.stub_chain(:enterprise_fees, :per_item) { [ef1] } + outgoing_exchange.stub_chain(:enterprise_fees, :per_item) { [ef2] } oc.stub(:exchanges_carrying) { [incoming_exchange, outgoing_exchange] } - oc.stub(:coordinator_fees) { [ef3] } + oc.stub_chain(:coordinator_fees, :per_item) { [ef3] } oc.send(:per_item_enterprise_fee_applicators_for, line_item.variant, distributor).should == [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, line_item.variant, 'supplier'), diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 06f82dacac..5206aae0b2 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -61,7 +61,7 @@ describe Spree::Order do subject.stub(:provided_by_order_cycle?) { true } order_cycle = double(:order_cycle) - order_cycle.should_receive(:create_adjustments_for).with(line_item) + order_cycle.should_receive(:create_line_item_adjustments_for).with(line_item) subject.stub(:order_cycle) { order_cycle } subject.update_distribution_charge! From 890af85d307a6e0bc3a30fa55238d5f097986ab8 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 13:28:05 +1100 Subject: [PATCH 59/87] Create per-order adjustments with EnterpriseFeeApplicator --- .../enterprise_fee_applicator.rb | 19 ++++++++-- .../enterprise_fee_applicator_spec.rb | 38 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/lib/open_food_network/enterprise_fee_applicator.rb b/lib/open_food_network/enterprise_fee_applicator.rb index c2b3001bca..45905bfaca 100644 --- a/lib/open_food_network/enterprise_fee_applicator.rb +++ b/lib/open_food_network/enterprise_fee_applicator.rb @@ -1,15 +1,28 @@ module OpenFoodNetwork class EnterpriseFeeApplicator < Struct.new(:enterprise_fee, :variant, :role) def create_line_item_adjustment(line_item) - a = enterprise_fee.create_locked_adjustment(adjustment_label, line_item.order, line_item, true) + a = enterprise_fee.create_locked_adjustment(line_item_adjustment_label, line_item.order, line_item, true) + AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role + end + + def create_order_adjustment(order) + a = enterprise_fee.create_locked_adjustment(order_adjustment_label, order, order, true) AdjustmentMetadata.create! adjustment: a, enterprise: enterprise_fee.enterprise, fee_name: enterprise_fee.name, fee_type: enterprise_fee.fee_type, enterprise_role: role end private - def adjustment_label - "#{variant.product.name} - #{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" + def line_item_adjustment_label + "#{variant.product.name} - #{base_adjustment_label}" + end + + def order_adjustment_label + "Whole order - #{base_adjustment_label}" + end + + def base_adjustment_label + "#{enterprise_fee.fee_type} fee by #{role} #{enterprise_fee.enterprise.name}" end end end diff --git a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb index 345eae845c..d10a5e9b72 100644 --- a/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb +++ b/spec/lib/open_food_network/enterprise_fee_applicator_spec.rb @@ -8,7 +8,7 @@ module OpenFoodNetwork product = create(:simple_product) efa = EnterpriseFeeApplicator.new enterprise_fee, product.master, 'role' - efa.stub(:adjustment_label) { 'label' } + efa.stub(:line_item_adjustment_label) { 'label' } efa.create_line_item_adjustment line_item adjustment = Spree::Adjustment.last @@ -25,13 +25,45 @@ module OpenFoodNetwork md.enterprise_role.should == 'role' end - it "makes adjustment labels" do + it "creates an adjustment for an order" do + order = create(:order) + #line_item = create(:line_item) + enterprise_fee = create(:enterprise_fee) + product = create(:simple_product) + + efa = EnterpriseFeeApplicator.new enterprise_fee, nil, 'role' + efa.stub(:order_adjustment_label) { 'label' } + efa.create_order_adjustment order + + adjustment = Spree::Adjustment.last + adjustment.label.should == 'label' + adjustment.adjustable.should == order + adjustment.source.should == order + adjustment.originator.should == enterprise_fee + adjustment.should be_mandatory + + md = adjustment.metadata + md.enterprise.should == enterprise_fee.enterprise + md.fee_name.should == enterprise_fee.name + md.fee_type.should == enterprise_fee.fee_type + md.enterprise_role.should == 'role' + end + + it "makes an adjustment label for a line item" do variant = double(:variant, product: double(:product, name: 'Bananas')) enterprise_fee = double(:enterprise_fee, fee_type: 'packing', enterprise: double(:enterprise, name: 'Ballantyne')) efa = EnterpriseFeeApplicator.new enterprise_fee, variant, 'distributor' - efa.send(:adjustment_label).should == "Bananas - packing fee by distributor Ballantyne" + efa.send(:line_item_adjustment_label).should == "Bananas - packing fee by distributor Ballantyne" + end + + it "makes an adjustment label for an order" do + enterprise_fee = double(:enterprise_fee, fee_type: 'packing', enterprise: double(:enterprise, name: 'Ballantyne')) + + efa = EnterpriseFeeApplicator.new enterprise_fee, nil, 'distributor' + + efa.send(:order_adjustment_label).should == "Whole order - packing fee by distributor Ballantyne" end end end From 5057e236a96110c6d690d874177ad28ebbc7220f Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 13:48:51 +1100 Subject: [PATCH 60/87] Find enterprise fees with per-order calculators --- app/models/enterprise_fee.rb | 7 ++++++- spec/models/enterprise_fee_spec.rb | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 2d8ecbd828..33f41b5fec 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -6,6 +6,8 @@ class EnterpriseFee < ActiveRecord::Base attr_accessible :enterprise_id, :fee_type, :name, :calculator_type FEE_TYPES = %w(packing transport admin sales) + PER_ORDER_CALCULATORS = ['Spree::Calculator::FlatRate', 'Spree::Calculator::FlexiRate'] + validates_inclusion_of :fee_type, :in => FEE_TYPES validates_presence_of :name @@ -22,7 +24,10 @@ class EnterpriseFee < ActiveRecord::Base } scope :per_item, lambda { - joins(:calculator).where('spree_calculators.type NOT IN (?)', ['Spree::Calculator::FlatRate', 'Spree::Calculator::FlexiRate']) + joins(:calculator).where('spree_calculators.type NOT IN (?)', PER_ORDER_CALCULATORS) + } + scope :per_order, lambda { + joins(:calculator).where('spree_calculators.type IN (?)', PER_ORDER_CALCULATORS) } def self.clear_all_adjustments_for(line_item) diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 45ba74caac..4408c069bc 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -27,6 +27,24 @@ describe EnterpriseFee do EnterpriseFee.per_item.sort.should == [ef1, ef2, ef3, ef4].sort end end + + describe "finding per-order enterprise fees" do + it "returns fees with FlatRate and FlexiRate calculators" do + ef1 = create(:enterprise_fee, calculator: Spree::Calculator::FlatRate.new) + ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlexiRate.new) + + EnterpriseFee.per_order.sort.should == [ef1, ef2].sort + end + + it "does not return fees with any other calculator" do + ef1 = create(:enterprise_fee, calculator: Spree::Calculator::DefaultTax.new) + ef2 = create(:enterprise_fee, calculator: Spree::Calculator::FlatPercentItemTotal.new) + ef3 = create(:enterprise_fee, calculator: Spree::Calculator::PerItem.new) + ef4 = create(:enterprise_fee, calculator: Spree::Calculator::PriceSack.new) + + EnterpriseFee.per_order.should be_empty + end + end end describe "clearing all enterprise fee adjustments for a line item" do From 9dec40703a5e16c2484eed2b4d4cfa8be534a6b2 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 13:49:10 +1100 Subject: [PATCH 61/87] Find exchanges with any of a number of variants --- app/models/exchange.rb | 1 + spec/models/exchange_spec.rb | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/app/models/exchange.rb b/app/models/exchange.rb index ce698c1cd0..90db51c36f 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -20,6 +20,7 @@ class Exchange < ActiveRecord::Base scope :from_enterprises, lambda { |enterprises| where('exchanges.sender_id IN (?)', enterprises) } scope :to_enterprises, lambda { |enterprises| where('exchanges.receiver_id IN (?)', enterprises) } scope :with_variant, lambda { |variant| joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant) } + scope :any_variant, lambda { |variants| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', variants) } scope :with_product, lambda { |product| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', product.variants_including_master) } def clone!(new_order_cycle) diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index df26d20ac0..7095dd03f9 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -111,6 +111,15 @@ describe Exchange do Exchange.with_variant(v).should == [ex] end + it "finds exchanges with any of a number of variants" do + v1 = create(:variant) + v2 = create(:variant) + ex = create(:exchange) + ex.variants << v1 + + Exchange.any_variant([v1, v2]).should == [ex] + end + it "finds exchanges with a particular product's master variant" do p = create(:simple_product) ex = create(:exchange) From b720a1d8f2f11f2940b188c2f21fd1857a17da57 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 14:33:28 +1100 Subject: [PATCH 62/87] EnterpriseFee.clear_all_adjustments_on_order clears adjustments from per-order fees --- app/models/enterprise_fee.rb | 2 +- spec/models/enterprise_fee_spec.rb | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/models/enterprise_fee.rb b/app/models/enterprise_fee.rb index 33f41b5fec..148c18d367 100644 --- a/app/models/enterprise_fee.rb +++ b/app/models/enterprise_fee.rb @@ -35,7 +35,7 @@ class EnterpriseFee < ActiveRecord::Base end def self.clear_all_adjustments_on_order(order) - order.adjustments.where(originator_type: 'EnterpriseFee', source_type: 'Spree::LineItem').destroy_all + order.adjustments.where(originator_type: 'EnterpriseFee').destroy_all end # Create an adjustment that starts as locked. Preferable to making an adjustment and locking it since diff --git a/spec/models/enterprise_fee_spec.rb b/spec/models/enterprise_fee_spec.rb index 4408c069bc..806eb7759c 100644 --- a/spec/models/enterprise_fee_spec.rb +++ b/spec/models/enterprise_fee_spec.rb @@ -76,7 +76,7 @@ describe EnterpriseFee do end describe "clearing all enterprise fee adjustments on an order" do - it "clears adjustments from many fees and one all line items" do + it "clears adjustments from many fees and on all line items" do order = create(:order) p1 = create(:simple_product) @@ -98,6 +98,17 @@ describe EnterpriseFee do end.to change(order.adjustments, :count).by(-4) end + it "clears adjustments from per-order fees" do + order = create(:order) + ef = create(:enterprise_fee) + efa = OpenFoodNetwork::EnterpriseFeeApplicator.new(ef, nil, 'coordinator') + efa.create_order_adjustment(order) + + expect do + EnterpriseFee.clear_all_adjustments_on_order order + end.to change(order.adjustments, :count).by(-1) + end + it "does not clear adjustments from another originator" do order = create(:order) tax_rate = create(:tax_rate, calculator: stub_model(Spree::Calculator)) From 11fb6c96a1940af30099ef51018c377b6d12ac36 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 14:34:30 +1100 Subject: [PATCH 63/87] Charge per-order fees on orders exactly once --- app/models/order_cycle.rb | 32 +++++++++++++++++++- app/models/spree/order_decorator.rb | 2 ++ spec/features/consumer/checkout_spec.rb | 24 +++++++-------- spec/models/order_cycle_spec.rb | 39 +++++++++++++++++++++++-- spec/models/spree/order_spec.rb | 21 +++++++++++++ 5 files changed, 101 insertions(+), 17 deletions(-) diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index 7e85258b19..519e693b1e 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -159,9 +159,18 @@ class OrderCycle < ActiveRecord::Base variant = line_item.variant distributor = line_item.order.distributor - per_item_enterprise_fee_applicators_for(variant, distributor).each { |applicator| applicator.create_line_item_adjustment(line_item) } + per_item_enterprise_fee_applicators_for(variant, distributor).each do |applicator| + applicator.create_line_item_adjustment(line_item) + end end + def create_order_adjustments_for(order) + per_order_enterprise_fee_applicators_for(order).each do |applicator| + applicator.create_order_adjustment(order) + end + end + + private # -- Fees @@ -181,7 +190,28 @@ class OrderCycle < ActiveRecord::Base fees end + def per_order_enterprise_fee_applicators_for(order) + fees = [] + + exchanges_supplying(order).each do |exchange| + exchange.enterprise_fees.per_order.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, exchange.role) + end + end + + coordinator_fees.per_order.each do |enterprise_fee| + fees << OpenFoodNetwork::EnterpriseFeeApplicator.new(enterprise_fee, nil, 'coordinator') + end + + fees + end + def exchanges_carrying(variant, distributor) exchanges.to_enterprises([coordinator, distributor]).with_variant(variant) end + + def exchanges_supplying(order) + variants = order.line_items.map(&:variant) + exchanges.to_enterprises([coordinator, order.distributor]).any_variant(variants) + end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 20c90fe133..35688259dc 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -87,6 +87,8 @@ Spree::Order.class_eval do pd.create_adjustment_for line_item if pd end end + + order_cycle.create_order_adjustments_for self if order_cycle end def set_variant_attributes(variant, attributes) diff --git a/spec/features/consumer/checkout_spec.rb b/spec/features/consumer/checkout_spec.rb index 0c21352400..a81b7e10f2 100644 --- a/spec/features/consumer/checkout_spec.rb +++ b/spec/features/consumer/checkout_spec.rb @@ -128,16 +128,14 @@ feature %q{ ["Bananas - transport fee by supplier Supplier 1", "$4.00", ""], ["Bananas - packing fee by distributor FruitAndVeg", "$7.00", ""], ["Bananas - transport fee by distributor FruitAndVeg", "$8.00", ""], - ["Bananas - admin fee by coordinator My coordinator", "$1.00", ""], - ["Bananas - sales fee by coordinator My coordinator", "$2.00", ""], ["Zucchini - admin fee by supplier Supplier 2", "$5.00", ""], ["Zucchini - sales fee by supplier Supplier 2", "$6.00", ""], ["Zucchini - packing fee by distributor FruitAndVeg", "$7.00", ""], ["Zucchini - transport fee by distributor FruitAndVeg", "$8.00", ""], - ["Zucchini - admin fee by coordinator My coordinator", "$1.00", ""], - ["Zucchini - sales fee by coordinator My coordinator", "$2.00", ""]] + ["Whole order - admin fee by coordinator My coordinator", "$1.00", ""], + ["Whole order - sales fee by coordinator My coordinator", "$2.00", ""]] - page.should have_selector 'span.distribution-total', :text => '$54.00' + page.should have_selector 'span.distribution-total', :text => '$51.00' end scenario "attempting to purchase products that mix product and order cycle distribution", future: true do @@ -396,7 +394,7 @@ feature %q{ # -- Checkout: Delivery order_charges = page.all("tbody#summary-order-charges tr").map {|row| row.all('td').map(&:text)}.take(2) order_charges.should == [["Delivery:", "$0.00"], - ["Distribution:", "$54.00"]] + ["Distribution:", "$51.00"]] click_checkout_continue_button # -- Checkout: Payment @@ -411,12 +409,12 @@ feature %q{ page.should have_selector 'figure#logo h1', text: @distributor_oc.name page.should have_selector 'tfoot#order-charges tr.total td', text: 'Distribution' - page.should have_selector 'tfoot#order-charges tr.total td', text: '54.00' + page.should have_selector 'tfoot#order-charges tr.total td', text: '51.00' # -- Checkout: Email email = ActionMailer::Base.deliveries.last email.reply_to.include?(@distributor_oc.email).should == true - email.body.should =~ /Distribution[\s+]\$54.00/ + email.body.should =~ /Distribution[\s+]\$51.00/ end scenario "when I have past orders, it fills in my address", :js => true do @@ -481,7 +479,7 @@ feature %q{ # -- Checkout: Delivery order_charges = page.all("tbody#summary-order-charges tr").map {|row| row.all('td').map(&:text)}.take(2) order_charges.should == [["Delivery:", "$0.00"], - ["Distribution:", "$54.00"]] + ["Distribution:", "$51.00"]] click_checkout_continue_button # -- Checkout: Payment @@ -495,11 +493,11 @@ feature %q{ page.should have_content @payment_method_distributor_oc.description page.should have_selector 'tfoot#order-charges tr.total td', text: 'Distribution' - page.should have_selector 'tfoot#order-charges tr.total td', text: '54.00' + page.should have_selector 'tfoot#order-charges tr.total td', text: '51.00' # -- Checkout: Email email = ActionMailer::Base.deliveries.last - email.body.should =~ /Distribution[\s+]\$54.00/ + email.body.should =~ /Distribution[\s+]\$51.00/ end @@ -509,8 +507,8 @@ feature %q{ @order_cycle = oc = create(:simple_order_cycle, coordinator: create(:distributor_enterprise, name: 'My coordinator')) # Coordinator - coordinator_fee1 = create(:enterprise_fee, enterprise: oc.coordinator, fee_type: 'admin', amount: 1) - coordinator_fee2 = create(:enterprise_fee, enterprise: oc.coordinator, fee_type: 'sales', amount: 2) + coordinator_fee1 = create(:enterprise_fee, enterprise: oc.coordinator, fee_type: 'admin', calculator: Spree::Calculator::FlatRate.new(preferred_amount: 1)) + coordinator_fee2 = create(:enterprise_fee, enterprise: oc.coordinator, fee_type: 'sales', calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) oc.coordinator_fees << coordinator_fee1 oc.coordinator_fees << coordinator_fee2 diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index ca11a24ded..39c9a87412 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -347,7 +347,7 @@ describe OrderCycle do let(:order) { double(:order, distributor: distributor) } let(:line_item) { double(:line_item, variant: variant, order: order) } - it "creates adjustment for each fee" do + it "creates an adjustment for each fee" do applicator = double(:enterprise_fee_applicator) applicator.should_receive(:create_line_item_adjustment).with(line_item) oc.should_receive(:per_item_enterprise_fee_applicators_for).with(variant, distributor) { [applicator] } @@ -355,7 +355,7 @@ describe OrderCycle do oc.send(:create_line_item_adjustments_for, line_item) end - it "finds fees for a line item" do + it "makes fee applicators for a line item" do distributor = double(:distributor) ef1 = double(:enterprise_fee) ef2 = double(:enterprise_fee) @@ -374,7 +374,40 @@ describe OrderCycle do OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, line_item.variant, 'coordinator')] end end - + + describe "creating adjustments for an order" do + let(:oc) { OrderCycle.new } + let(:distributor) { double(:distributor) } + let(:order) { double(:order, distributor: distributor) } + + it "creates an adjustment for each fee" do + applicator = double(:enterprise_fee_applicator) + applicator.should_receive(:create_order_adjustment).with(order) + oc.should_receive(:per_order_enterprise_fee_applicators_for).with(order) { [applicator] } + + oc.send(:create_order_adjustments_for, order) + end + + it "makes fee applicators for an order" do + distributor = double(:distributor) + ef1 = double(:enterprise_fee) + ef2 = double(:enterprise_fee) + ef3 = double(:enterprise_fee) + incoming_exchange = double(:exchange, role: 'supplier') + outgoing_exchange = double(:exchange, role: 'distributor') + incoming_exchange.stub_chain(:enterprise_fees, :per_order) { [ef1] } + outgoing_exchange.stub_chain(:enterprise_fees, :per_order) { [ef2] } + + oc.stub(:exchanges_supplying) { [incoming_exchange, outgoing_exchange] } + oc.stub_chain(:coordinator_fees, :per_order) { [ef3] } + + oc.send(:per_order_enterprise_fee_applicators_for, order).should == + [OpenFoodNetwork::EnterpriseFeeApplicator.new(ef1, nil, 'supplier'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef2, nil, 'distributor'), + OpenFoodNetwork::EnterpriseFeeApplicator.new(ef3, nil, 'coordinator')] + end + end + describe "finding recently closed order cycles" do it "should give the most recently closed order cycle for a distributor" do distributor = create(:distributor_enterprise) diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 5206aae0b2..69ff50bd3b 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -54,6 +54,15 @@ describe Spree::Order do subject.update_distribution_charge! end + it "skips order cycle per-order adjustments for orders that don't have an order cycle" do + EnterpriseFee.stub(:clear_all_adjustments_on_order) + subject.stub(:line_items) { [] } + + subject.stub(:order_cycle) { nil } + + subject.update_distribution_charge! + end + it "ensures the correct adjustment(s) are created for order cycles" do EnterpriseFee.stub(:clear_all_adjustments_on_order) line_item = double(:line_item) @@ -62,6 +71,18 @@ describe Spree::Order do order_cycle = double(:order_cycle) order_cycle.should_receive(:create_line_item_adjustments_for).with(line_item) + order_cycle.stub(:create_order_adjustments_for) + subject.stub(:order_cycle) { order_cycle } + + subject.update_distribution_charge! + end + + it "ensures the correct per-order adjustment(s) are created for order cycles" do + EnterpriseFee.stub(:clear_all_adjustments_on_order) + subject.stub(:line_items) { [] } + + order_cycle = double(:order_cycle) + order_cycle.should_receive(:create_order_adjustments_for).with(subject) subject.stub(:order_cycle) { order_cycle } subject.update_distribution_charge! From f5a3167facbf65f8c03a51b41b8123bc9b520a9b Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Wed, 26 Feb 2014 15:12:35 +1100 Subject: [PATCH 64/87] Patching up the form and update to get form submission working --- Gemfile.lock | 2 +- app/controllers/shop/checkout_controller.rb | 11 ++-- app/views/shop/checkout/_form.html.haml | 14 ++--- .../consumer/shopping/checkout_spec.rb | 52 ++++++++++++------- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d373f97849..0cfd3f2aaa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -95,7 +95,7 @@ GIT GIT remote: git://github.com/willrjmarshall/foundation_rails_helper.git - revision: 61ca053cea97c611dfd546bf7fcd846b98abd401 + revision: 4d5d53fdc4b1fb71e66524d298c5c635de82cfbb branch: rails3 specs: foundation_rails_helper (0.4) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index b81e07a762..b253128d3e 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -13,25 +13,26 @@ class Shop::CheckoutController < Spree::CheckoutController def update if @order.update_attributes(params[:order]) fire_event('spree.checkout.update') + while @order.state != "complete" if @order.next state_callback(:after) else flash[:error] = t(:payment_processing_failed) - respond_with @order, location: shop_checkout_path - break + respond_with @order, location: main_app.shop_checkout_path + return end end if @order.state == "complete" || @order.completed? flash.notice = t(:order_processed_successfully) flash[:commerce_tracking] = "nothing special" - respond_with(@order, :location => order_conf) + respond_with(@order, :location => order_path(@order)) else - respond_with @order, location: shop_checkout_path + respond_with @order, location: main_app.shop_checkout_path end else - respond_with @order, location: shop_checkout_path + respond_with @order, location: main_app.shop_checkout_path end end diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 5ae7631b7b..44374f49aa 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -7,10 +7,10 @@ .row .large-6.columns = f.text_field :email - = f.fields_for current_order.bill_address do |ba| + = f.fields_for :bill_address do |ba| .large-6.columns = ba.text_field :phone - = f.fields_for current_order.bill_address do |ba| + = f.fields_for :bill_address do |ba| .row .large-6.columns = ba.text_field :firstname @@ -22,7 +22,7 @@ = f.fields_for :bill_address do |ba| .row .large-12.columns - = ba.text_field :address1 + = ba.text_field :address1, label: "Billing Address" .row .large-12.columns = ba.text_field :address2 @@ -30,8 +30,10 @@ .large-6.columns = ba.text_field :city .large-6.columns - = ba.text_field :country + = ba.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]} .row + .large-6.columns + = ba.select :state_id, Spree::State.order(:name).select([:id, :name]).map{|c| [c.name, c.id]} .large-6.columns.right = ba.text_field :zipcode @@ -42,11 +44,11 @@ .row .large-12.columns %label - = f.radio_button :shipping_method, ship_method.id, + = f.radio_button :shipping_method_id, ship_method.id, + text: ship_method.name, "ng-model" => "shipping_method", "ng-change" => "shippingMethodChanged()", "data-require-ship-address" => ship_method.require_ship_address - = ship_method.name = fields_for current_order.ship_address do |sa| #ship_address{"ng-show" => "require_ship_address"} diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index fcb06bb11f..fe12ef3611 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -141,28 +141,44 @@ feature "As a consumer I want to check out my cart", js: true do choose(sm1.name) find("#ship_address").visible?.should be_true end - end - describe "with payment methods" do - let(:pm1) { create(:payment_method, distributors: [distributor]) } - let(:pm2) { create(:payment_method, distributors: [distributor]) } + describe "with payment methods" do + let(:pm1) { create(:payment_method, distributors: [distributor], name: "Roger rabbit", type: "Spree::PaymentMethod::Check") } + let(:pm2) { create(:payment_method, distributors: [distributor]) } - before do - pm1 # Lazy evaluation of ze create()s - pm2 - visit "/shop/checkout" - end + before do + pm1 # Lazy evaluation of ze create()s + pm2 + visit "/shop/checkout" + end - it "shows all available payment methods" do - page.should have_content pm1.name - page.should have_content pm2.name - end + it "shows all available payment methods" do + page.should have_content pm1.name + page.should have_content pm2.name + end - describe "Purchasing" do - it "re-renders with errors when we submit the incomplete form" do - click_button "Purchase" - current_path.should == "/shop/checkout" - page.should have_content "can't be blank" + describe "Purchasing" do + it "re-renders with errors when we submit the incomplete form" do + click_button "Purchase" + current_path.should == "/shop/checkout" + page.should have_content "can't be blank" + end + + it "takes us to the order confirmation page when we submit a complete form" do + fill_in "Customer E-Mail", with: "test@test.com" + fill_in "Phone", with: "0468363090" + fill_in "First Name", with: "Will" + fill_in "Last Name", with: "Marshall" + fill_in "Billing Address", with: "123 Your Face" + select "Australia", from: "Country" + select "Victoria", from: "State" + fill_in "City", with: "Melbourne" + fill_in "Zip Code", with: "3066" + choose sm1.name + choose pm1.name + click_button "Purchase" + page.should have_content "Your order has been processed successfully" + end end end end From f7e1befc75e2b91cff96a3a8de43daeccc731148 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 15:16:30 +1100 Subject: [PATCH 65/87] Spec fees_for only sums per-item fees --- spec/models/order_cycle_spec.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 39c9a87412..47e49a73e3 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -310,16 +310,18 @@ describe OrderCycle do end describe "calculating fees for a variant via a particular distributor" do - it "sums all the fees for the variant in the specified hub + order cycle" do + it "sums all the per-item fees for the variant in the specified hub + order cycle" do coordinator = create(:distributor_enterprise) distributor = create(:distributor_enterprise) order_cycle = create(:simple_order_cycle) enterprise_fee1 = create(:enterprise_fee, amount: 20) enterprise_fee2 = create(:enterprise_fee, amount: 3) + enterprise_fee3 = create(:enterprise_fee, + calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) product = create(:simple_product) create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, - enterprise_fees: [enterprise_fee1, enterprise_fee2], variants: [product.master]) + enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product.master]) order_cycle.fees_for(product.master, distributor).should == 23 end From adf4c0e387292ea6395f39b6389fa28900eacd92 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Wed, 26 Feb 2014 15:38:41 +1100 Subject: [PATCH 66/87] Sort enterprises by name on enterprise fees admin page --- app/controllers/admin/enterprise_fees_controller.rb | 1 + app/views/admin/enterprise_fees/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/enterprise_fees_controller.rb b/app/controllers/admin/enterprise_fees_controller.rb index 6e1a7da113..085828d7b3 100644 --- a/app/controllers/admin/enterprise_fees_controller.rb +++ b/app/controllers/admin/enterprise_fees_controller.rb @@ -8,6 +8,7 @@ module Admin def index @include_calculators = params[:include_calculators].present? @enterprise = current_enterprise + @enterprises = Enterprise.managed_by(spree_current_user).by_name blank_enterprise_fee = EnterpriseFee.new blank_enterprise_fee.enterprise = current_enterprise diff --git a/app/views/admin/enterprise_fees/index.html.haml b/app/views/admin/enterprise_fees/index.html.haml index a59ffc99e0..d3e9a0b88c 100644 --- a/app/views/admin/enterprise_fees/index.html.haml +++ b/app/views/admin/enterprise_fees/index.html.haml @@ -25,7 +25,7 @@ %tr{'ng-repeat' => 'enterprise_fee in enterprise_fees | filter:query'} %td = f.ng_hidden_field :id - = f.ng_collection_select :enterprise_id, Enterprise.managed_by(spree_current_user), :id, :name, 'enterprise_fee.enterprise_id', :include_blank => true + = f.ng_collection_select :enterprise_id, @enterprises, :id, :name, 'enterprise_fee.enterprise_id', :include_blank => true %td= f.ng_select :fee_type, enterprise_fee_type_options, 'enterprise_fee.fee_type' %td= f.ng_text_field :name %td= f.ng_collection_select :calculator_type, @calculators, :name, :description, 'enterprise_fee.calculator_type', {'class' => 'calculator_type', 'ng-model' => 'calculatorType', 'spree-ensure-calculator-preferences-match-type' => "1"} From 7708bc9f9952aa6c49d6cbdb2016a499a354c110 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Wed, 26 Feb 2014 15:48:23 +1100 Subject: [PATCH 67/87] Patching up our shipping selection so there's always a default --- app/views/shop/checkout/_form.html.haml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 44374f49aa..68f4cf0a90 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -38,17 +38,17 @@ = ba.text_field :zipcode %fieldset#shipping - %legend Shipping - - - for ship_method in current_distributor.shipping_methods.uniq + %legend Shipping + - checked_id = @order.shipping_method_id || current_distributor.shipping_methods.first.id + - for ship_method, i in current_distributor.shipping_methods.uniq .row .large-12.columns - %label - = f.radio_button :shipping_method_id, ship_method.id, - text: ship_method.name, - "ng-model" => "shipping_method", - "ng-change" => "shippingMethodChanged()", - "data-require-ship-address" => ship_method.require_ship_address + = f.radio_button :shipping_method_id, ship_method.id, + text: ship_method.name, + "ng-init" => "shipping_method = #{checked_id}", + "ng-model" => "shipping_method", + "ng-change" => "shippingMethodChanged()", + "data-require-ship-address" => ship_method.require_ship_address = fields_for current_order.ship_address do |sa| #ship_address{"ng-show" => "require_ship_address"} From 0bfa36056ecd8f1ab24cd86ba9a32738b2f3919a Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Wed, 26 Feb 2014 16:15:53 +1100 Subject: [PATCH 68/87] removing some dud Angular crud --- app/views/shop/checkout/_form.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 68f4cf0a90..fa1d16867c 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -1,6 +1,5 @@ %checkout{"ng-controller" => "CheckoutCtrl"} = f_form_for current_order, url: main_app.shop_update_checkout_path, html: {name: "checkout", id: "checkout_form"} do |f| - {{ checkout.$valid }} .large-12.columns %fieldset#details %legend Customer Details From 353d2a4d9cc732714c126ca1b189ce2c08d1c3cd Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 27 Feb 2014 11:28:07 +1100 Subject: [PATCH 69/87] Do not error when creating variant for product without non-unit option type --- .../admin/variants_controller_decorator.rb | 10 +++++++++ spec/features/admin/variants_spec.rb | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb index a567f0a8c9..16a37d5286 100644 --- a/app/controllers/spree/admin/variants_controller_decorator.rb +++ b/app/controllers/spree/admin/variants_controller_decorator.rb @@ -1,3 +1,13 @@ Spree::Admin::VariantsController.class_eval do helper 'spree/products' + + + protected + + def create_before + option_values = params[:new_variant] + option_values.andand.each_value {|id| @object.option_values << OptionValue.find(id)} + @object.save + end + end diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 7e178ab443..d05aa75ef8 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -7,6 +7,27 @@ feature %q{ include AuthenticationWorkflow include WebHelper + scenario "creating a new variant" do + # Given a product with a unit-related option type + p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") + + # When I create a variant on the product + login_to_admin_section + click_link 'Products' + within('#sub_nav') { click_link 'Products' } + click_link p.name + click_link 'Variants' + click_link 'New Variant' + + fill_in 'variant_unit_value', with: '1' + fill_in 'variant_unit_description', with: 'foo' + click_button 'Create' + + # Then the variant should have been created + page.should have_content "Variant \"#{p.name}\" has been successfully created!" + end + + scenario "editing unit value and description for a variant" do # Given a product with unit-related option types, with a variant p = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1") From 3196e28d889ab2e174b7ede3e525fb452a536265 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 27 Feb 2014 11:29:39 +1100 Subject: [PATCH 70/87] Admin can assign units to a product --- .../spree/products_helper_decorator.rb | 7 +++++++ .../_form/add_units_form.html.haml.deface | 16 ++++++++++++++++ .../new/add_unit_form.html.haml.deface | 17 +++++++++++++++++ .../admin/products/_supplier_form.html.haml | 2 +- spec/features/admin/products_spec.rb | 19 ++++++++++++++----- 5 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 app/overrides/spree/admin/products/_form/add_units_form.html.haml.deface create mode 100644 app/overrides/spree/admin/products/new/add_unit_form.html.haml.deface diff --git a/app/helpers/spree/products_helper_decorator.rb b/app/helpers/spree/products_helper_decorator.rb index 99596a73e9..f918a47649 100644 --- a/app/helpers/spree/products_helper_decorator.rb +++ b/app/helpers/spree/products_helper_decorator.rb @@ -14,5 +14,12 @@ module Spree def variant_unit_option_type?(option_type) Spree::Product.all_variant_unit_option_types.include? option_type end + + + def product_variant_unit_options + [['Weight', 'weight'], + ['Volume', 'volume'], + ['Items', 'items']] + end end end diff --git a/app/overrides/spree/admin/products/_form/add_units_form.html.haml.deface b/app/overrides/spree/admin/products/_form/add_units_form.html.haml.deface new file mode 100644 index 0000000000..9a5b6a87d6 --- /dev/null +++ b/app/overrides/spree/admin/products/_form/add_units_form.html.haml.deface @@ -0,0 +1,16 @@ +/ insert_top "[data-hook='admin_product_form_right']" + += f.field_container :variant_unit do + = f.label :variant_unit, 'Variant unit' + = f.select :variant_unit, product_variant_unit_options, {:include_blank => true}, {:class => "select2 fullwidth"} + = f.error_message_on :variant_unit + += f.field_container :variant_unit_scale do + = f.label :variant_unit_scale, 'Variant unit scale' + = f.text_field :variant_unit_scale + = f.error_message_on :variant_unit_scale + += f.field_container :variant_unit_name do + = f.label :variant_unit_name, 'Variant unit name' + = f.text_field :variant_unit_name + = f.error_message_on :variant_unit_name diff --git a/app/overrides/spree/admin/products/new/add_unit_form.html.haml.deface b/app/overrides/spree/admin/products/new/add_unit_form.html.haml.deface new file mode 100644 index 0000000000..03021b41d9 --- /dev/null +++ b/app/overrides/spree/admin/products/new/add_unit_form.html.haml.deface @@ -0,0 +1,17 @@ +/ insert_before "[data-hook='new_product_attrs']" + +.row + .alpha.six.columns + = f.label :variant_unit, 'Variant unit' + = f.select :variant_unit, product_variant_unit_options, {:include_blank => true}, {:class => "select2 fullwidth"} + = f.error_message_on :variant_unit + + .four.columns + = f.label :variant_unit_scale, 'Variant unit scale' + = f.text_field :variant_unit_scale, {class: "fullwidth"} + = f.error_message_on :variant_unit_scale + + .omega.six.columns + = f.label :variant_unit_name, 'Variant unit name' + = f.text_field :variant_unit_name, {class: "fullwidth"} + = f.error_message_on :variant_unit_name diff --git a/app/views/spree/admin/products/_supplier_form.html.haml b/app/views/spree/admin/products/_supplier_form.html.haml index b73a9eb462..3e5c01c2a3 100644 --- a/app/views/spree/admin/products/_supplier_form.html.haml +++ b/app/views/spree/admin/products/_supplier_form.html.haml @@ -1,5 +1,5 @@ = f.field_container :supplier do = f.label :supplier %br - = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user), :id, :name, {:include_blank => true}, {:class => "select2"}) + = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2"}) = f.error_message_on :supplier diff --git a/spec/features/admin/products_spec.rb b/spec/features/admin/products_spec.rb index 58d9d57775..092d5239d4 100644 --- a/spec/features/admin/products_spec.rb +++ b/spec/features/admin/products_spec.rb @@ -14,15 +14,18 @@ feature %q{ end context "creating a product" do - scenario "assigning a supplier and distributors to the product" do + scenario "assigning a supplier, distributors and units to the product" do login_to_admin_section click_link 'Products' click_link 'New Product' - fill_in 'product_name', :with => 'A new product !!!' - fill_in 'product_price', :with => '19.99' - select 'New supplier', :from => 'product_supplier_id' + fill_in 'product_name', with: 'A new product !!!' + fill_in 'product_price', with: '19.99' + select 'New supplier', from: 'product_supplier_id' + select 'Weight', from: 'product_variant_unit' + fill_in 'product_variant_unit_scale', with: 1000 + fill_in 'product_variant_unit_name', with: '' click_button 'Create' @@ -31,6 +34,11 @@ feature %q{ product.supplier.should == @supplier product.group_buy.should be_false + product.variant_unit.should == 'weight' + product.variant_unit_scale.should == 1000 + product.variant_unit_name.should == '' + product.option_types.first.name.should == 'unit_weight' + # Distributors within('#sidebar') { click_link 'Product Distributions' } @@ -46,7 +54,8 @@ feature %q{ product.product_distributions.map { |pd| pd.enterprise_fee }.should == [@enterprise_fees[0], @enterprise_fees[2]] end - scenario "making a group buy product" do + + scenario "creating a group buy product" do login_to_admin_section click_link 'Products' From 8bb742b3f2bef5106511a8c296f162e5f08b9b98 Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Thu, 27 Feb 2014 11:33:02 +1100 Subject: [PATCH 71/87] Alphabetise suppliers when adding new product --- .../admin/products/_supplier_and_group_buy_for_new.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml b/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml index c0615a753b..d17a45fa6c 100644 --- a/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml +++ b/app/views/spree/admin/products/_supplier_and_group_buy_for_new.html.haml @@ -2,7 +2,7 @@ .alpha.six.columns = f.field_container :supplier do = f.label :supplier - = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user), :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) + = f.collection_select(:supplier_id, Enterprise.is_primary_producer.managed_by(spree_current_user).by_name, :id, :name, {:include_blank => true}, {:class => "select2 fullwidth"}) = f.error_message_on :supplier .four.columns = f.field_container :group_buy do @@ -17,4 +17,4 @@ .omega.six.columns = f.field_container :group_buy_unit_size do = f.label :group_buy_unit_size - = f.text_field :group_buy_unit_size, :class => "fullwidth" \ No newline at end of file + = f.text_field :group_buy_unit_size, :class => "fullwidth" From e92d21ec4e8cc78329ebcdc574fa63b44b07ca61 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 11:44:36 +1100 Subject: [PATCH 72/87] Patching a scoping bug that broke ALL the tests --- spec/features/consumer/shopping/checkout_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index fe12ef3611..1665483cc3 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -1,9 +1,10 @@ require 'spec_helper' -include AuthenticationWorkflow -include WebHelper feature "As a consumer I want to check out my cart", js: true do + include AuthenticationWorkflow + include WebHelper + let(:distributor) { create(:distributor_enterprise) } let(:supplier) { create(:supplier_enterprise) } let(:order_cycle) { create(:order_cycle, distributors: [distributor], coordinator: create(:distributor_enterprise)) } From 18f92fbff5364ee742f16efc4cb8e4ec9f4b1ae7 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 14:04:04 +1100 Subject: [PATCH 73/87] Fixing a nil.id bug --- app/views/shop/checkout/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index fa1d16867c..e21c86a94e 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -38,7 +38,7 @@ %fieldset#shipping %legend Shipping - - checked_id = @order.shipping_method_id || current_distributor.shipping_methods.first.id + - checked_id = @order.shipping_method_id || current_distributor.shipping_methods.first.andand.id - for ship_method, i in current_distributor.shipping_methods.uniq .row .large-12.columns From 91a8e1c0710bfa2ef3ca5639c4008d39e83e3e5f Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 14:47:33 +1100 Subject: [PATCH 74/87] Fixing a Javascript bug --- .../darkswarm/controllers/checkout_controller.js.coffee | 3 ++- app/views/shop/checkout/_form.html.haml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index ed4aee83a3..40f8d221f8 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -4,7 +4,8 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> $scope.payment_method = -1 $scope.shippingMethodChanged = -> - $scope.require_ship_address = $("#order_shipping_method_" + $scope.shipping_method).attr("data-require-ship-address") + console.log $("#order_shipping_method_id_" + $scope.shipping_method).attr("data-require-ship-address") + $scope.require_ship_address = $("#order_shipping_method_id_" + $scope.shipping_method).attr("data-require-ship-address") $scope.purchase = (event)-> event.preventDefault() diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index e21c86a94e..2688a4dfb7 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -36,7 +36,9 @@ .large-6.columns.right = ba.text_field :zipcode + {{ require_ship_address }} %fieldset#shipping + %legend Shipping - checked_id = @order.shipping_method_id || current_distributor.shipping_methods.first.andand.id - for ship_method, i in current_distributor.shipping_methods.uniq @@ -44,7 +46,7 @@ .large-12.columns = f.radio_button :shipping_method_id, ship_method.id, text: ship_method.name, - "ng-init" => "shipping_method = #{checked_id}", + "ng-init" => "shipping_method = #{checked_id}; shippingMethodChanged()", "ng-model" => "shipping_method", "ng-change" => "shippingMethodChanged()", "data-require-ship-address" => ship_method.require_ship_address From 19fa7200f3b45687a8ff03af9eb4c35d6fb29de6 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 14:47:56 +1100 Subject: [PATCH 75/87] Fixing the login redirection issue --- app/controllers/application_controller.rb | 3 ++- spec/features/consumer/authentication_spec.rb | 23 +++++++++++++++++++ .../consumer/shopping/checkout_spec.rb | 1 - 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 spec/features/consumer/authentication_spec.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eb90094d48..affab70c16 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,7 +7,8 @@ class ApplicationController < ActionController::Base def after_sign_in_path_for(resource) - request.referer || root_path + referer_path = URI(request.referer).path + [main_app.shop_checkout_path].include?(referer_path) ? referer_path : root_path end private diff --git a/spec/features/consumer/authentication_spec.rb b/spec/features/consumer/authentication_spec.rb new file mode 100644 index 0000000000..e1dd681af6 --- /dev/null +++ b/spec/features/consumer/authentication_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +feature "Authentication", js: true do + describe "login" do + let(:user) { create(:user, password: "password", password_confirmation: "password") } + scenario "with valid credentials" do + visit "/login" + fill_in "Email", with: user.email + fill_in "Password", with: "password" + click_button "Login" + current_path.should == "/" + end + + scenario "with invalid credentials" do + visit "/login" + fill_in "Email", with: user.email + fill_in "Password", with: "this isn't my password" + click_button "Login" + page.should have_content "Invalid email or password" + end + end +end + diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 1665483cc3..2859a91697 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -138,7 +138,6 @@ feature "As a consumer I want to check out my cart", js: true do end it "shows ship address forms when selected shipping method requires one" do - # Fancy Foundation Forms are weird choose(sm1.name) find("#ship_address").visible?.should be_true end From c1cdf3e331fcc5043fd1453c23d2d7ab7f325f8a Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:11:17 +1100 Subject: [PATCH 76/87] Removing some dud debugging stuff, fiddling with the checkout spec --- .../darkswarm/controllers/checkout_controller.js.coffee | 1 - app/views/shop/checkout/_form.html.haml | 1 - spec/features/consumer/shopping/checkout_spec.rb | 7 +++++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee index 40f8d221f8..6c4ec5d2cb 100644 --- a/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee @@ -4,7 +4,6 @@ angular.module("Checkout").controller "CheckoutCtrl", ($scope, $rootScope) -> $scope.payment_method = -1 $scope.shippingMethodChanged = -> - console.log $("#order_shipping_method_id_" + $scope.shipping_method).attr("data-require-ship-address") $scope.require_ship_address = $("#order_shipping_method_id_" + $scope.shipping_method).attr("data-require-ship-address") $scope.purchase = (event)-> diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index 2688a4dfb7..e84a068424 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -36,7 +36,6 @@ .large-6.columns.right = ba.text_field :zipcode - {{ require_ship_address }} %fieldset#shipping %legend Shipping diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 2859a91697..6f4dc1c740 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -122,7 +122,7 @@ feature "As a consumer I want to check out my cart", js: true do describe "with shipping methods" do let(:sm1) { create(:shipping_method, require_ship_address: true, name: "Frogs", description: "yellow") } - let(:sm2) { create(:shipping_method, require_ship_address: true, name: "Donkeys", description: "blue") } + let(:sm2) { create(:shipping_method, require_ship_address: false, name: "Donkeys", description: "blue") } before do distributor.shipping_methods << sm1 distributor.shipping_methods << sm2 @@ -133,12 +133,14 @@ feature "As a consumer I want to check out my cart", js: true do page.should have_content "Donkeys" end - it "doesn't show ship address forms by default" do + it "doesn't show ship address forms " do + choose(sm2.name) find("#ship_address").visible?.should be_false end it "shows ship address forms when selected shipping method requires one" do choose(sm1.name) + save_and_open_page find("#ship_address").visible?.should be_true end @@ -159,6 +161,7 @@ feature "As a consumer I want to check out my cart", js: true do describe "Purchasing" do it "re-renders with errors when we submit the incomplete form" do + save_and_open_page click_button "Purchase" current_path.should == "/shop/checkout" page.should have_content "can't be blank" From 4daeac0b1495fb8c9d6645ceb329213c833d68bf Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:15:06 +1100 Subject: [PATCH 77/87] Removing JMigrate warnings --- .../javascripts/jquery-migrate-1.0.0.js | 498 ++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 app/assets/javascripts/jquery-migrate-1.0.0.js diff --git a/app/assets/javascripts/jquery-migrate-1.0.0.js b/app/assets/javascripts/jquery-migrate-1.0.0.js new file mode 100644 index 0000000000..c374e6ba16 --- /dev/null +++ b/app/assets/javascripts/jquery-migrate-1.0.0.js @@ -0,0 +1,498 @@ +/*! + * jQuery Migrate - v1.0.0 - 2013-01-14 + * https://github.com/jquery/jquery-migrate + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT + */ +(function( jQuery, window, undefined ) { +"use strict"; + + +var warnedAbout = {}; + +// List of warnings already given; public read only +jQuery.migrateWarnings = []; + +// Set to true to prevent console output; migrateWarnings still maintained +jQuery.migrateMute = true; + +// Forget any warnings we've already given; public +jQuery.migrateReset = function() { + warnedAbout = {}; + jQuery.migrateWarnings.length = 0; +}; + +function migrateWarn( msg) { + if ( !warnedAbout[ msg ] ) { + warnedAbout[ msg ] = true; + jQuery.migrateWarnings.push( msg ); + if ( window.console && console.warn && !jQuery.migrateMute ) { + console.warn( "JQMIGRATE: " + msg ); + } + } +} + +function migrateWarnProp( obj, prop, value, msg ) { + if ( Object.defineProperty ) { + // On ES5 browsers (non-oldIE), warn if the code tries to get prop; + // allow property to be overwritten in case some other plugin wants it + try { + Object.defineProperty( obj, prop, { + configurable: true, + enumerable: true, + get: function() { + migrateWarn( msg ); + return value; + }, + set: function( newValue ) { + migrateWarn( msg ); + value = newValue; + } + }); + return; + } catch( err ) { + // IE8 is a dope about Object.defineProperty, can't warn there + } + } + + // Non-ES5 (or broken) browser; just set the property + jQuery._definePropertyBroken = true; + obj[ prop ] = value; +} + +if ( document.compatMode === "BackCompat" ) { + // jQuery has never supported or tested Quirks Mode + migrateWarn( "jQuery is not compatible with Quirks Mode" ); +} + + +var attrFn = {}, + attr = jQuery.attr, + valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get || + function() { return null; }, + valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set || + function() { return undefined; }, + rnoType = /^(?:input|button)$/i, + rnoAttrNodeType = /^[238]$/, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + ruseDefault = /^(?:checked|selected)$/i; + +// jQuery.attrFn +migrateWarnProp( jQuery, "attrFn", attrFn, "jQuery.attrFn is deprecated" ); + +jQuery.attr = function( elem, name, value, pass ) { + var lowerName = name.toLowerCase(), + nType = elem && elem.nodeType; + + if ( pass ) { + migrateWarn("jQuery.fn.attr( props, pass ) is deprecated"); + if ( elem && !rnoAttrNodeType.test( nType ) && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + } + + // Warn if user tries to set `type` since it breaks on IE 6/7/8 + if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) ) { + migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8"); + } + + // Restore boolHook for boolean property/attribute synchronization + if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) { + jQuery.attrHooks[ lowerName ] = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && + ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } + }; + + // Warn only for attributes that can remain distinct from their properties post-1.9 + if ( ruseDefault.test( lowerName ) ) { + migrateWarn( "jQuery.fn.attr(" + lowerName + ") may use property instead of attribute" ); + } + } + + return attr.call( jQuery, elem, name, value ); +}; + +// attrHooks: value +jQuery.attrHooks.value = { + get: function( elem, name ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "button" ) { + return valueAttrGet.apply( this, arguments ); + } + if ( nodeName !== "input" && nodeName !== "option" ) { + migrateWarn("property-based jQuery.fn.attr('value') is deprecated"); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "button" ) { + return valueAttrSet.apply( this, arguments ); + } + if ( nodeName !== "input" && nodeName !== "option" ) { + migrateWarn("property-based jQuery.fn.attr('value', val) is deprecated"); + } + // Does not return so that setAttribute is also used + elem.value = value; + } +}; + + +var matched, browser, + oldInit = jQuery.fn.init, + // Note this does NOT include the # XSS fix from 1.7! + rquickExpr = /^(?:.*(<[\w\W]+>)[^>]*|#([\w\-]*))$/; + +// $(html) "looks like html" rule change +jQuery.fn.init = function( selector, context, rootjQuery ) { + var match; + + if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) && + (match = rquickExpr.exec( selector )) && match[1] ) { + // This is an HTML string according to the "old" rules; is it still? + if ( selector.charAt( 0 ) !== "<" ) { + migrateWarn("$(html) HTML strings must start with '<' character"); + } + // Now process using loose rules; let pre-1.8 play too + if ( context && context.context ) { + // jQuery object as context; parseHTML expects a DOM object + context = context.context; + } + if ( jQuery.parseHTML ) { + return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ), + context, rootjQuery ); + } + } + return oldInit.apply( this, arguments ); +}; +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +// Warn if the code tries to get jQuery.browser +migrateWarnProp( jQuery, "browser", browser, "jQuery.browser is deprecated" ); + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + migrateWarn( "jQuery.sub() is deprecated" ); + return jQuerySub; +}; + + +var oldFnData = jQuery.fn.data; + +jQuery.fn.data = function( name ) { + var ret, evt, + elem = this[0]; + + // Handles 1.7 which has this behavior and 1.8 which doesn't + if ( elem && name === "events" && arguments.length === 1 ) { + ret = jQuery.data( elem, name ); + evt = jQuery._data( elem, name ); + if ( ( ret === undefined || ret === evt ) && evt !== undefined ) { + migrateWarn("Use of jQuery.fn.data('events') is deprecated"); + return evt; + } + } + return oldFnData.apply( this, arguments ); +}; + + +var rscriptType = /\/(java|ecma)script/i, + oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack, + oldFragment = jQuery.buildFragment; + +jQuery.fn.andSelf = function() { + migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()"); + return oldSelf.apply( this, arguments ); +}; + +// Since jQuery.clean is used internally on older versions, we only shim if it's missing +if ( !jQuery.clean ) { + jQuery.clean = function( elems, context, fragment, scripts ) { + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + migrateWarn("jQuery.clean() is deprecated"); + + var i, elem, handleScript, jsTags, + ret = []; + + jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes ); + + // Complex logic lifted directly from jQuery 1.8 + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }; +} + +jQuery.buildFragment = function( elems, context, scripts, selection ) { + var ret, + warning = "jQuery.buildFragment() is deprecated"; + + // Set context per 1.8 logic + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + try { + ret = oldFragment.call( jQuery, elems, context, scripts, selection ); + + // jQuery < 1.8 required arrayish context; jQuery 1.9 fails on it + } catch( x ) { + ret = oldFragment.call( jQuery, elems, context.nodeType ? [ context ] : context[ 0 ], scripts, selection ); + + // Success from tweaking context means buildFragment was called by the user + migrateWarn( warning ); + } + + // jQuery < 1.9 returned an object instead of the fragment itself + if ( !ret.fragment ) { + migrateWarnProp( ret, "fragment", ret, warning ); + migrateWarnProp( ret, "cacheable", false, warning ); + } + + return ret; +}; + +var eventAdd = jQuery.event.add, + eventRemove = jQuery.event.remove, + eventTrigger = jQuery.event.trigger, + oldToggle = jQuery.fn.toggle, + oldLive = jQuery.fn.live, + oldDie = jQuery.fn.die, + ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess", + rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ), + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + hoverHack = function( events ) { + if ( typeof( events ) != "string" || jQuery.event.special.hover ) { + return events; + } + if ( rhoverHack.test( events ) ) { + migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'"); + } + return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +// Event props removed in 1.9, put them back if needed; no practical way to warn them +if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) { + jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" ); +} + +// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7 +migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" ); + +// Support for 'hover' pseudo-event and ajax event warnings +jQuery.event.add = function( elem, types, handler, data, selector ){ + if ( elem !== document && rajaxEvent.test( types ) ) { + migrateWarn( "AJAX events should be attached to document: " + types ); + } + eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector ); +}; +jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){ + eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes ); +}; + +jQuery.fn.error = function() { + var args = Array.prototype.slice.call( arguments, 0); + migrateWarn("jQuery.fn.error() is deprecated"); + args.splice( 0, 0, "error" ); + if ( arguments.length ) { + return this.bind.apply( this, args ); + } + // error event should not bubble to window, although it does pre-1.7 + this.triggerHandler.apply( this, args ); + return this; +}; + +jQuery.fn.toggle = function( fn, fn2 ) { + + // Don't mess with animation or css toggles + if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) { + return oldToggle.apply( this, arguments ); + } + migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated"); + + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); +}; + +jQuery.fn.live = function( types, data, fn ) { + migrateWarn("jQuery.fn.live() is deprecated"); + if ( oldLive ) { + return oldLive.apply( this, arguments ); + } + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; +}; + +jQuery.fn.die = function( types, fn ) { + migrateWarn("jQuery.fn.die() is deprecated"); + if ( oldDie ) { + return oldDie.apply( this, arguments ); + } + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; +}; + +// Turn global events into document-triggered events +jQuery.event.trigger = function( event, data, elem, onlyHandlers ){ + if ( !elem & !rajaxEvent.test( event ) ) { + migrateWarn( "Global events are undocumented and deprecated" ); + } + return eventTrigger.call( this, event, data, elem || document, onlyHandlers ); +}; +jQuery.each( ajaxEvents.split("|"), + function( _, name ) { + jQuery.event.special[ name ] = { + setup: function() { + var elem = this; + + // The document needs no shimming; must be !== for oldIE + if ( elem !== document ) { + jQuery.event.add( document, name + "." + jQuery.guid, function() { + jQuery.event.trigger( name, null, elem, true ); + }); + jQuery._data( this, name, jQuery.guid++ ); + } + return false; + }, + teardown: function() { + if ( this !== document ) { + jQuery.event.remove( document, name + "." + jQuery._data( this, name ) ); + } + return false; + } + }; + } +); + + +})( jQuery, window ); From d7921462eb225c918064c2d263540b176ce30970 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:15:13 +1100 Subject: [PATCH 78/87] Removing save and open page call --- spec/features/consumer/shopping/checkout_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 6f4dc1c740..7e8a5d3cf1 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -161,7 +161,6 @@ feature "As a consumer I want to check out my cart", js: true do describe "Purchasing" do it "re-renders with errors when we submit the incomplete form" do - save_and_open_page click_button "Purchase" current_path.should == "/shop/checkout" page.should have_content "can't be blank" From 4a6c00e592a7ae93fd93ac02e9eb17d551b59815 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:15:42 +1100 Subject: [PATCH 79/87] Removing bangs from stub since it's totes deprecated --- spec/helpers/order_cycles_helper_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/helpers/order_cycles_helper_spec.rb b/spec/helpers/order_cycles_helper_spec.rb index fce1095591..3717f34462 100644 --- a/spec/helpers/order_cycles_helper_spec.rb +++ b/spec/helpers/order_cycles_helper_spec.rb @@ -28,8 +28,8 @@ describe OrderCyclesHelper do exchange = Exchange.find(oc1.exchanges.to_enterprises(d).outgoing.first.id) exchange.update_attribute :pickup_time, "turtles" - helper.stub!(:current_order_cycle).and_return oc1 - helper.stub!(:current_distributor).and_return d + helper.stub(:current_order_cycle).and_return oc1 + helper.stub(:current_distributor).and_return d helper.pickup_time.should == "turtles" end @@ -41,8 +41,8 @@ describe OrderCyclesHelper do exchange = Exchange.find(oc2.exchanges.to_enterprises(d).outgoing.first.id) exchange.update_attribute :pickup_time, "turtles" - helper.stub!(:current_order_cycle).and_return oc1 - helper.stub!(:current_distributor).and_return d + helper.stub(:current_order_cycle).and_return oc1 + helper.stub(:current_distributor).and_return d helper.pickup_time(oc2).should == "turtles" end end From d6c71d5416d3befa74bcf45f7984362a8ae328ce Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:24:10 +1100 Subject: [PATCH 80/87] Fixing a nil bug in the referrer handling on login --- app/controllers/application_controller.rb | 7 +++++-- spec/features/admin/cms_spec.rb | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index affab70c16..4b0cc37e9f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,8 +7,11 @@ class ApplicationController < ActionController::Base def after_sign_in_path_for(resource) - referer_path = URI(request.referer).path - [main_app.shop_checkout_path].include?(referer_path) ? referer_path : root_path + if request.referer and referer_path = URI(request.referer).path + [main_app.shop_checkout_path].include?(referer_path) ? referer_path : root_path + else + root_path + end end private diff --git a/spec/features/admin/cms_spec.rb b/spec/features/admin/cms_spec.rb index aec64cf291..c3b2fe3454 100644 --- a/spec/features/admin/cms_spec.rb +++ b/spec/features/admin/cms_spec.rb @@ -31,5 +31,4 @@ feature %q{ page.should_not have_content "ComfortableMexicanSofa" page.should have_content "WHERE WOULD YOU LIKE TO SHOP?" end - end From 6e05bd63c626ca4b47c260b7dd8048a213f95038 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Thu, 27 Feb 2014 16:29:03 +1100 Subject: [PATCH 81/87] Pending a test that currently breaks --- spec/features/consumer/shopping/checkout_spec.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 7e8a5d3cf1..f995edaa83 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -160,12 +160,15 @@ feature "As a consumer I want to check out my cart", js: true do end describe "Purchasing" do - it "re-renders with errors when we submit the incomplete form" do + pending "re-renders with errors when we submit the incomplete form" do + choose sm2.name click_button "Purchase" current_path.should == "/shop/checkout" page.should have_content "can't be blank" end + it "renders errors on the shipping method where appropriate" + it "takes us to the order confirmation page when we submit a complete form" do fill_in "Customer E-Mail", with: "test@test.com" fill_in "Phone", with: "0468363090" From 86a8b926f316e331013766edb639cb5d2e9e7e9c Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 28 Feb 2014 11:53:45 +1100 Subject: [PATCH 82/87] Adding conditions to the autopopulator for shipping address --- app/controllers/shop/checkout_controller.rb | 34 ++++++++++++--------- app/models/spree/order_decorator.rb | 2 +- app/views/shop/checkout/_form.html.haml | 4 +-- spec/models/spree/order_spec.rb | 26 ++++++++++++++++ 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index b253128d3e..e9bd3ca384 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -11,27 +11,31 @@ class Shop::CheckoutController < Spree::CheckoutController end def update - if @order.update_attributes(params[:order]) - fire_event('spree.checkout.update') + begin + if @order.update_attributes(params[:order]) + fire_event('spree.checkout.update') - while @order.state != "complete" - if @order.next - state_callback(:after) - else - flash[:error] = t(:payment_processing_failed) - respond_with @order, location: main_app.shop_checkout_path - return + while @order.state != "complete" + if @order.next + state_callback(:after) + else + flash[:error] = t(:payment_processing_failed) + respond_with @order, location: main_app.shop_checkout_path + return + end end - end - if @order.state == "complete" || @order.completed? - flash.notice = t(:order_processed_successfully) - flash[:commerce_tracking] = "nothing special" - respond_with(@order, :location => order_path(@order)) + if @order.state == "complete" || @order.completed? + flash.notice = t(:order_processed_successfully) + flash[:commerce_tracking] = "nothing special" + respond_with(@order, :location => order_path(@order)) + else + respond_with @order, location: main_app.shop_checkout_path + end else respond_with @order, location: main_app.shop_checkout_path end - else + rescue ActiveRecord::RecordInvalid respond_with @order, location: main_app.shop_checkout_path end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 35688259dc..ad40e4302d 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -118,7 +118,7 @@ Spree::Order.class_eval do private def shipping_address_from_distributor - if distributor + if distributor and (shipping_method.andand.require_ship_address == false) self.ship_address = distributor.address.clone if bill_address diff --git a/app/views/shop/checkout/_form.html.haml b/app/views/shop/checkout/_form.html.haml index e84a068424..2add9a5c39 100644 --- a/app/views/shop/checkout/_form.html.haml +++ b/app/views/shop/checkout/_form.html.haml @@ -50,7 +50,7 @@ "ng-change" => "shippingMethodChanged()", "data-require-ship-address" => ship_method.require_ship_address - = fields_for current_order.ship_address do |sa| + = f.fields_for :ship_address do |sa| #ship_address{"ng-show" => "require_ship_address"} .row .large-12.columns @@ -63,7 +63,7 @@ .large-6.columns = sa.text_field :city .large-6.columns - = sa.text_field :country + = sa.select :country_id, Spree::Country.order(:name).select([:id, :name]).map{|c| [c.name, c.id]} .row .large-6.columns.right = sa.text_field :zipcode diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 69ff50bd3b..6cd67ae569 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -254,7 +254,33 @@ describe Spree::Order do Spree::Order.not_state(:canceled).should_not include o end + end + end + describe "shipping address prepopulation" do + let(:distributor) { create(:distributor_enterprise) } + let(:order) { build(:order, distributor: distributor) } + + before do + order.save # just to trigger our autopopulate the first time ;) + end + + it "autopopulates the shipping address on save" do + order.should_receive(:shipping_address_from_distributor).and_return true + order.save + end + + it "populates the shipping address if the shipping method doesn't require a delivery address" do + order.shipping_method = create(:shipping_method, require_ship_address: false) + order.ship_address.update_attribute :firstname, "will" + order.save + order.ship_address.firstname.should == distributor.address.firstname + end + it "does not populate the shipping address if the shipping method requires a delivery address" do + order.shipping_method = create(:shipping_method, require_ship_address: true) + order.ship_address.update_attribute :firstname, "will" + order.save + order.ship_address.firstname.should == "will" end end From 1d5addb06ee3fcb96e64b2c274ed992abc0cd93b Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 28 Feb 2014 13:41:02 +1100 Subject: [PATCH 83/87] Patching up our checkout flow so we don't try to create shipments before we have valid shipping addresses --- app/controllers/shop/checkout_controller.rb | 32 ++++++++----------- app/models/spree/order_decorator.rb | 20 ++++++++++++ .../consumer/shopping/checkout_spec.rb | 25 ++++++++------- spec/models/spree/order_spec.rb | 8 ++++- 4 files changed, 55 insertions(+), 30 deletions(-) diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index e9bd3ca384..b253128d3e 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -11,31 +11,27 @@ class Shop::CheckoutController < Spree::CheckoutController end def update - begin - if @order.update_attributes(params[:order]) - fire_event('spree.checkout.update') + if @order.update_attributes(params[:order]) + fire_event('spree.checkout.update') - while @order.state != "complete" - if @order.next - state_callback(:after) - else - flash[:error] = t(:payment_processing_failed) - respond_with @order, location: main_app.shop_checkout_path - return - end - end - - if @order.state == "complete" || @order.completed? - flash.notice = t(:order_processed_successfully) - flash[:commerce_tracking] = "nothing special" - respond_with(@order, :location => order_path(@order)) + while @order.state != "complete" + if @order.next + state_callback(:after) else + flash[:error] = t(:payment_processing_failed) respond_with @order, location: main_app.shop_checkout_path + return end + end + + if @order.state == "complete" || @order.completed? + flash.notice = t(:order_processed_successfully) + flash[:commerce_tracking] = "nothing special" + respond_with(@order, :location => order_path(@order)) else respond_with @order, location: main_app.shop_checkout_path end - rescue ActiveRecord::RecordInvalid + else respond_with @order, location: main_app.shop_checkout_path end end diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index ad40e4302d..b8bde38922 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -14,6 +14,26 @@ Spree::Order.class_eval do before_validation :shipping_address_from_distributor + checkout_flow do + go_to_state :address + go_to_state :delivery + go_to_state :payment, :if => lambda { |order| + if order.ship_address.andand.valid? + # Fix for #2191 + if order.shipping_method + order.create_shipment! + order.update_totals + end + order.payment_required? + else + false + end + } + go_to_state :confirm, :if => lambda { |order| order.confirmation_required? } + go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.has_unprocessed_payments?) || !order.payment_required? } + remove_transition :from => :delivery, :to => :confirm + end + # -- Scopes scope :managed_by, lambda { |user| diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index f995edaa83..ffdc1cde8d 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -160,7 +160,7 @@ feature "As a consumer I want to check out my cart", js: true do end describe "Purchasing" do - pending "re-renders with errors when we submit the incomplete form" do + it "re-renders with errors when we submit the incomplete form" do choose sm2.name click_button "Purchase" current_path.should == "/shop/checkout" @@ -170,17 +170,20 @@ feature "As a consumer I want to check out my cart", js: true do it "renders errors on the shipping method where appropriate" it "takes us to the order confirmation page when we submit a complete form" do - fill_in "Customer E-Mail", with: "test@test.com" - fill_in "Phone", with: "0468363090" - fill_in "First Name", with: "Will" - fill_in "Last Name", with: "Marshall" - fill_in "Billing Address", with: "123 Your Face" - select "Australia", from: "Country" - select "Victoria", from: "State" - fill_in "City", with: "Melbourne" - fill_in "Zip Code", with: "3066" - choose sm1.name + choose sm2.name choose pm1.name + within "#details" do + fill_in "First Name", with: "Will" + fill_in "Last Name", with: "Marshall" + save_and_open_page + fill_in "Billing Address", with: "123 Your Face" + select "Australia", from: "Country" + select "Victoria", from: "State" + fill_in "Customer E-Mail", with: "test@test.com" + fill_in "Phone", with: "0468363090" + fill_in "City", with: "Melbourne" + fill_in "Zip Code", with: "3066" + end click_button "Purchase" page.should have_content "Your order has been processed successfully" end diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 6cd67ae569..91525cb83a 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -274,14 +274,20 @@ describe Spree::Order do order.shipping_method = create(:shipping_method, require_ship_address: false) order.ship_address.update_attribute :firstname, "will" order.save - order.ship_address.firstname.should == distributor.address.firstname + order.ship_address.firstname.shoul == distributor.address.firstname end + it "does not populate the shipping address if the shipping method requires a delivery address" do order.shipping_method = create(:shipping_method, require_ship_address: true) order.ship_address.update_attribute :firstname, "will" order.save order.ship_address.firstname.should == "will" end + + it "doesn't attempt to create a shipment if the order is not yet valid" do + #Shipment.should_not_r + order.create_shipment! + end end end From 4087d08e8eaa3a0b803f6ebe301d2895537b4adf Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 28 Feb 2014 13:53:58 +1100 Subject: [PATCH 84/87] Removing a save and open page call --- spec/features/consumer/shopping/checkout_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index ffdc1cde8d..4e08b9d5e4 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -175,7 +175,6 @@ feature "As a consumer I want to check out my cart", js: true do within "#details" do fill_in "First Name", with: "Will" fill_in "Last Name", with: "Marshall" - save_and_open_page fill_in "Billing Address", with: "123 Your Face" select "Australia", from: "Country" select "Victoria", from: "State" From 6934cf236fb45284e2eddebb44eeee17c622380d Mon Sep 17 00:00:00 2001 From: Rohan Mitchell Date: Fri, 28 Feb 2014 14:34:00 +1100 Subject: [PATCH 85/87] Update Rails --- Gemfile | 2 +- Gemfile.lock | 56 ++++++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Gemfile b/Gemfile index a8557a7330..6965ae1a43 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' ruby "1.9.3" -gem 'rails', '3.2.14' +gem 'rails', '3.2.17' gem 'pg' gem 'spree', :github => 'openfoodfoundation/spree', :branch => '1-3-stable' diff --git a/Gemfile.lock b/Gemfile.lock index 0cfd3f2aaa..b498268a74 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -127,12 +127,12 @@ PATH GEM remote: https://rubygems.org/ specs: - actionmailer (3.2.14) - actionpack (= 3.2.14) + actionmailer (3.2.17) + actionpack (= 3.2.17) mail (~> 2.5.4) - actionpack (3.2.14) - activemodel (= 3.2.14) - activesupport (= 3.2.14) + actionpack (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) @@ -152,18 +152,18 @@ GEM json (~> 1.7) money (< 6.0.0) nokogiri (~> 1.4) - activemodel (3.2.14) - activesupport (= 3.2.14) + activemodel (3.2.17) + activesupport (= 3.2.17) builder (~> 3.0.0) - activerecord (3.2.14) - activemodel (= 3.2.14) - activesupport (= 3.2.14) + activerecord (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.14) - activemodel (= 3.2.14) - activesupport (= 3.2.14) - activesupport (3.2.14) + activeresource (3.2.17) + activemodel (= 3.2.17) + activesupport (= 3.2.17) + activesupport (3.2.17) i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) acts_as_list (0.1.4) @@ -171,7 +171,7 @@ GEM andand (1.3.3) angularjs-rails (1.2.13) ansi (1.4.2) - arel (3.0.2) + arel (3.0.3) awesome_nested_set (2.1.5) activerecord (>= 3.0.0) awesome_print (1.0.2) @@ -330,7 +330,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) method_source (0.8.1) - mime-types (1.25) + mime-types (1.25.1) mini_portile (0.5.2) money (5.0.0) i18n (~> 0.4) @@ -357,7 +357,7 @@ GEM http_parser.rb (~> 0.5.3) polyamorous (0.5.0) activerecord (~> 3.0) - polyglot (0.3.3) + polyglot (0.3.4) pry (0.9.12.2) coderay (~> 1.0.5) method_source (~> 0.8) @@ -377,17 +377,17 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) - rails (3.2.14) - actionmailer (= 3.2.14) - actionpack (= 3.2.14) - activerecord (= 3.2.14) - activeresource (= 3.2.14) - activesupport (= 3.2.14) + rails (3.2.17) + actionmailer (= 3.2.17) + actionpack (= 3.2.17) + activerecord (= 3.2.17) + activeresource (= 3.2.17) + activesupport (= 3.2.17) bundler (~> 1.0) - railties (= 3.2.14) - railties (3.2.14) - actionpack (= 3.2.14) - activesupport (= 3.2.14) + railties (= 3.2.17) + railties (3.2.17) + actionpack (= 3.2.17) + activesupport (= 3.2.17) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) @@ -544,7 +544,7 @@ DEPENDENCIES rabl rack-livereload rack-ssl - rails (= 3.2.14) + rails (= 3.2.17) representative_view rspec-rails sass From 74238b111e227b7ec97d5f9f6f36a024705e9174 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Fri, 28 Feb 2014 15:19:57 +1100 Subject: [PATCH 86/87] Fixing the order spec --- spec/models/spree/order_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 91525cb83a..ff17188998 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -262,6 +262,7 @@ describe Spree::Order do let(:order) { build(:order, distributor: distributor) } before do + order.ship_address = distributor.address.clone order.save # just to trigger our autopopulate the first time ;) end @@ -274,7 +275,7 @@ describe Spree::Order do order.shipping_method = create(:shipping_method, require_ship_address: false) order.ship_address.update_attribute :firstname, "will" order.save - order.ship_address.firstname.shoul == distributor.address.firstname + order.ship_address.firstname.should == distributor.address.firstname end it "does not populate the shipping address if the shipping method requires a delivery address" do @@ -285,6 +286,7 @@ describe Spree::Order do end it "doesn't attempt to create a shipment if the order is not yet valid" do + order.shipping_method = create(:shipping_method, require_ship_address: false) #Shipment.should_not_r order.create_shipment! end From b5019f12b42211e7aa56be04e08b89f7c3366031 Mon Sep 17 00:00:00 2001 From: Will Marshall Date: Tue, 4 Mar 2014 15:07:00 +1100 Subject: [PATCH 87/87] Fixing up a couple of bugs introduced to the old step workflow --- .../spree/checkout_controller_decorator.rb | 7 ++++++- app/models/spree/order_decorator.rb | 18 +++++++++++------- spec/features/consumer/checkout_spec.rb | 9 ++++----- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/controllers/spree/checkout_controller_decorator.rb b/app/controllers/spree/checkout_controller_decorator.rb index a08f2aa463..3f83ca10ec 100644 --- a/app/controllers/spree/checkout_controller_decorator.rb +++ b/app/controllers/spree/checkout_controller_decorator.rb @@ -1,5 +1,9 @@ Spree::CheckoutController.class_eval do + #def update + #binding.pry + #end + private def before_payment @@ -14,8 +18,9 @@ Spree::CheckoutController.class_eval do last_used_bill_address, last_used_ship_address = find_last_used_addresses(@order.email) preferred_bill_address, preferred_ship_address = spree_current_user.bill_address, spree_current_user.ship_address if spree_current_user.respond_to?(:bill_address) && spree_current_user.respond_to?(:ship_address) + @order.bill_address ||= preferred_bill_address || last_used_bill_address || Spree::Address.default - @order.ship_address ||= preferred_ship_address || last_used_ship_address || Spree::Address.default + @order.ship_address ||= preferred_ship_address || last_used_ship_address || nil end def after_complete diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index b8bde38922..2490c30c28 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -138,13 +138,18 @@ Spree::Order.class_eval do private def shipping_address_from_distributor - if distributor and (shipping_method.andand.require_ship_address == false) - self.ship_address = distributor.address.clone + if distributor + # This method is confusing to conform to the vagaries of the multi-step checkout + # We copy over the shipping address when we have no shipping method selected + # We can refactor this when we drop the multi-step checkout option + if shipping_method.nil? or shipping_method.andand.require_ship_address == false + self.ship_address = distributor.address.clone - if bill_address - self.ship_address.firstname = bill_address.firstname - self.ship_address.lastname = bill_address.lastname - self.ship_address.phone = bill_address.phone + if bill_address + self.ship_address.firstname = bill_address.firstname + self.ship_address.lastname = bill_address.lastname + self.ship_address.phone = bill_address.phone + end end end end @@ -157,5 +162,4 @@ Spree::Order.class_eval do def product_distribution_for(line_item) line_item.variant.product.product_distribution_for self.distributor end - end diff --git a/spec/features/consumer/checkout_spec.rb b/spec/features/consumer/checkout_spec.rb index a81b7e10f2..29a7eed19d 100644 --- a/spec/features/consumer/checkout_spec.rb +++ b/spec/features/consumer/checkout_spec.rb @@ -68,7 +68,7 @@ feature %q{ @zone = create(:zone) c = Spree::Country.find_by_name('Australia') Spree::ZoneMember.create(:zoneable => c, :zone => @zone) - sm = create(:shipping_method, zone: @zone, calculator: Spree::Calculator::FlatRate.new) + sm = create(:shipping_method, zone: @zone, calculator: Spree::Calculator::FlatRate.new, require_ship_address: false) sm.calculator.set_preference(:amount, 0); sm.calculator.save! @payment_method_distributor = create(:payment_method, :name => 'Edible Garden payment method', :distributors => [@distributor]) @@ -388,13 +388,11 @@ feature %q{ # Disabled until this form takes order cycles into account # page.should have_selector "select#order_distributor_id option[value='#{@distributor_alternative.id}']" - click_checkout_continue_button # -- Checkout: Delivery order_charges = page.all("tbody#summary-order-charges tr").map {|row| row.all('td').map(&:text)}.take(2) - order_charges.should == [["Delivery:", "$0.00"], - ["Distribution:", "$51.00"]] + order_charges.should == [["Delivery:", "$0.00"], ["Distribution:", "$51.00"]] click_checkout_continue_button # -- Checkout: Payment @@ -411,6 +409,7 @@ feature %q{ page.should have_selector 'tfoot#order-charges tr.total td', text: 'Distribution' page.should have_selector 'tfoot#order-charges tr.total td', text: '51.00' + # -- Checkout: Email email = ActionMailer::Base.deliveries.last email.reply_to.include?(@distributor_oc.email).should == true @@ -559,7 +558,7 @@ feature %q{ ex4.variants << @product_4.master # Shipping method and payment method - sm = create(:shipping_method, zone: @zone, calculator: Spree::Calculator::FlatRate.new, distributors: [@distributor_oc]) + sm = create(:shipping_method, zone: @zone, calculator: Spree::Calculator::FlatRate.new, distributors: [@distributor_oc], require_ship_address: false) sm.calculator.set_preference(:amount, 0); sm.calculator.save! @payment_method_distributor_oc = create(:payment_method, :name => 'FruitAndVeg payment method', :distributors => [@distributor_oc]) end