spree_core 2.1.12 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/helpers/spree/base_helper.rb +4 -7
- data/app/helpers/spree/products_helper.rb +11 -9
- data/app/helpers/spree/store_helper.rb +5 -0
- data/app/models/spree/ability.rb +4 -0
- data/app/models/spree/address.rb +6 -6
- data/app/models/spree/adjustment.rb +40 -61
- data/app/models/spree/app_configuration.rb +1 -14
- data/app/models/spree/calculator.rb +12 -4
- data/app/models/spree/calculator/default_tax.rb +42 -38
- data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
- data/app/models/spree/calculator/free_shipping.rb +5 -2
- data/app/models/spree/calculator/percent_on_line_item.rb +15 -0
- data/app/models/spree/calculator/percent_per_item.rb +3 -0
- data/app/models/spree/classification.rb +3 -2
- data/app/models/spree/credit_card.rb +7 -25
- data/app/models/spree/gateway/bogus.rb +5 -5
- data/app/models/spree/gateway/bogus_simple.rb +0 -8
- data/app/models/spree/image.rb +0 -9
- data/app/models/spree/inventory_unit.rb +10 -4
- data/app/models/spree/item_adjustments.rb +65 -0
- data/app/models/spree/legacy_user.rb +1 -0
- data/app/models/spree/line_item.rb +33 -13
- data/app/models/spree/option_type.rb +2 -2
- data/app/models/spree/option_value.rb +1 -1
- data/app/models/spree/order.rb +109 -89
- data/app/models/spree/order/checkout.rb +48 -0
- data/app/models/spree/order_contents.rb +72 -37
- data/app/models/spree/order_inventory.rb +65 -68
- data/app/models/spree/order_populator.rb +3 -17
- data/app/models/spree/order_updater.rb +63 -44
- data/app/models/spree/payment.rb +20 -5
- data/app/models/spree/payment/processing.rb +19 -25
- data/app/models/spree/payment_capture_event.rb +9 -0
- data/app/models/spree/payment_method/check.rb +0 -2
- data/app/models/spree/price.rb +1 -1
- data/app/models/spree/product.rb +14 -16
- data/app/models/spree/product/scopes.rb +4 -6
- data/app/models/spree/product_option_type.rb +2 -2
- data/app/models/spree/product_property.rb +2 -2
- data/app/models/spree/promotion.rb +71 -50
- data/app/models/spree/promotion/actions/create_adjustment.rb +31 -32
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +83 -0
- data/app/models/spree/promotion/actions/free_shipping.rb +36 -0
- data/app/models/spree/promotion/rules/first_order.rb +4 -0
- data/app/models/spree/promotion/rules/item_total.rb +5 -1
- data/app/models/spree/promotion/rules/product.rb +4 -0
- data/app/models/spree/promotion/rules/user.rb +5 -6
- data/app/models/spree/promotion/rules/user_logged_in.rb +4 -0
- data/app/models/spree/promotion_action.rb +1 -5
- data/app/models/spree/promotion_handler/cart.rb +38 -0
- data/app/models/spree/promotion_handler/coupon.rb +76 -0
- data/app/models/spree/promotion_handler/free_shipping.rb +31 -0
- data/app/models/spree/promotion_handler/page.rb +24 -0
- data/app/models/spree/promotion_rule.rb +15 -7
- data/app/models/spree/property.rb +1 -1
- data/app/models/spree/return_authorization.rb +7 -1
- data/app/models/spree/shipment.rb +113 -49
- data/app/models/spree/shipping_calculator.rb +4 -5
- data/app/models/spree/shipping_category.rb +2 -2
- data/app/models/spree/shipping_method.rb +12 -6
- data/app/models/spree/shipping_rate.rb +27 -7
- data/app/models/spree/stock/availability_validator.rb +1 -1
- data/app/models/spree/stock/estimator.rb +13 -1
- data/app/models/spree/stock/package.rb +11 -7
- data/app/models/spree/stock/packer.rb +3 -3
- data/app/models/spree/stock/quantifier.rb +9 -1
- data/app/models/spree/stock_item.rb +11 -6
- data/app/models/spree/stock_movement.rb +1 -2
- data/app/models/spree/tax_category.rb +6 -1
- data/app/models/spree/tax_rate.rb +57 -49
- data/app/models/spree/taxon.rb +10 -5
- data/app/models/spree/taxonomy.rb +5 -2
- data/app/models/spree/variant.rb +33 -16
- data/app/models/spree/zone.rb +24 -24
- data/app/views/spree/shared/_routes.html.erb +3 -0
- data/config/locales/en.yml +42 -26
- data/db/migrate/20130213191427_create_default_stock.rb +3 -3
- data/db/migrate/20130413230529_add_name_to_spree_credit_cards.rb +5 -0
- data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +13 -0
- data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
- data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
- data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
- data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +7 -5
- data/db/migrate/20130807024301_upgrade_adjustments.rb +39 -0
- data/db/migrate/20130807024302_rename_adjustment_fields.rb +17 -0
- data/db/migrate/20130813004002_add_shipment_total_to_spree_orders.rb +5 -0
- data/db/migrate/20130813232134_rename_activators_to_promotions.rb +5 -0
- data/db/migrate/20130815000406_add_adjustment_total_to_line_items.rb +5 -0
- data/db/migrate/20130815024413_add_adjustment_total_to_shipments.rb +5 -0
- data/db/migrate/20130828234942_add_tax_total_to_line_items_shipments_and_orders.rb +8 -0
- data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +1 -1
- data/db/migrate/20130903183026_add_code_to_spree_promotion_rules.rb +5 -0
- data/db/migrate/20130917024658_remove_promotions_event_name_field.rb +5 -0
- data/db/migrate/20130924040529_add_promo_total_to_line_items_and_shipments_and_orders.rb +7 -0
- data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +7 -3
- data/db/migrate/20131107132123_add_tax_category_to_variants.rb +6 -0
- data/db/migrate/20131118043959_add_included_to_adjustments.rb +5 -0
- data/db/migrate/20131118050234_rename_tax_total_fields.rb +11 -0
- data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +21 -0
- data/db/migrate/20131127001002_add_position_to_classifications.rb +5 -0
- data/db/migrate/20131211112807_create_spree_orders_promotions.rb +8 -0
- data/db/migrate/20131218054603_add_item_count_to_spree_orders.rb +5 -0
- data/db/migrate/20140106224208_rename_permalink_to_slug_for_products.rb +5 -0
- data/db/migrate/20140124023232_rename_activator_id_in_rules_and_actions_to_promotion_id.rb +6 -0
- data/db/migrate/20140203161722_add_approver_id_and_approved_at_to_orders.rb +6 -0
- data/db/migrate/20140204115338_add_confirmation_delivered_to_spree_orders.rb +5 -0
- data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +12 -0
- data/db/migrate/20140205144710_add_uncaptured_amount_to_payments.rb +5 -0
- data/db/migrate/20140207085910_add_tax_category_id_to_shipping_methods.rb +5 -0
- data/db/migrate/20140207093021_add_tax_rate_id_to_shipping_rates.rb +5 -0
- data/db/migrate/20140211040159_add_pre_tax_amount_to_line_items_and_shipments.rb +6 -0
- data/db/migrate/20140213184916_add_more_indexes.rb +13 -0
- data/db/migrate/20140219060952_add_considered_risky_to_orders.rb +5 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +1 -6
- data/lib/generators/spree/install/install_generator.rb +6 -6
- data/lib/generators/spree/install/templates/{app/assets/javascripts/admin → vendor/assets/javascripts/spree/backend}/all.js +3 -3
- data/lib/generators/spree/install/templates/{app/assets/javascripts/store → vendor/assets/javascripts/spree/frontend}/all.js +3 -3
- data/lib/generators/spree/install/templates/{app/assets/stylesheets/store → vendor/assets/stylesheets/spree/backend}/all.css +3 -3
- data/lib/generators/spree/install/templates/{app/assets/stylesheets/admin → vendor/assets/stylesheets/spree/frontend}/all.css +3 -3
- data/lib/spree/core.rb +21 -8
- data/lib/spree/core/calculated_adjustments.rb +0 -40
- data/lib/spree/core/controller_helpers.rb +5 -0
- data/lib/spree/core/controller_helpers/auth.rb +2 -2
- data/lib/spree/core/controller_helpers/common.rb +0 -5
- data/lib/spree/core/controller_helpers/order.rb +8 -9
- data/lib/spree/core/engine.rb +10 -17
- data/lib/spree/core/permalinks.rb +1 -1
- data/lib/spree/core/product_duplicator.rb +3 -8
- data/lib/spree/core/user_address.rb +1 -1
- data/lib/spree/core/validators/email.rb +23 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/money.rb +1 -1
- data/lib/spree/permitted_attributes.rb +2 -2
- data/lib/spree/testing_support/caching.rb +47 -0
- data/lib/spree/testing_support/factories/adjustment_factory.rb +11 -2
- data/lib/spree/testing_support/factories/credit_card_factory.rb +2 -1
- data/lib/spree/testing_support/factories/order_factory.rb +10 -5
- data/lib/spree/testing_support/factories/payment_factory.rb +2 -2
- data/lib/spree/testing_support/factories/payment_method_factory.rb +3 -3
- data/lib/spree/testing_support/factories/promotion_factory.rb +16 -1
- data/lib/spree/testing_support/factories/shipment_factory.rb +8 -4
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
- data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
- data/lib/spree/testing_support/factories/tax_rate_factory.rb +2 -2
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/tasks/core.rake +2 -2
- data/vendor/assets/fonts/FontAwesome.otf +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.svg +399 -0
- data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
- data/vendor/assets/stylesheets/font-awesome.scss +1475 -0
- metadata +73 -44
- data/app/assets/javascripts/admin/handlebar_extensions.js +0 -9
- data/app/helpers/spree/admin/adjustments_helper.rb +0 -26
- data/app/helpers/spree/admin/images_helper.rb +0 -18
- data/app/helpers/spree/promotion_rules_helper.rb +0 -13
- data/app/models/spree/activator.rb +0 -29
- data/app/models/spree/calculator/per_item.rb +0 -41
- data/app/models/spree/stock/remaining_packer.rb +0 -22
- data/app/views/spree/payments/_payment.html.erb +0 -18
- data/db/migrate/20131118041203_add_tax_total_to_spree_orders.rb +0 -5
- data/db/migrate/20131118043021_add_order_id_to_spree_adjustments.rb +0 -6
- data/db/migrate/20131118074808_add_included_to_spree_adjustments.rb +0 -5
- data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
- data/lib/spree/core/gateway_error.rb +0 -5
- data/lib/spree/core/preference_rescue.rb +0 -25
- data/lib/spree/core/s3_support.rb +0 -25
- data/lib/spree/promo/coupon_applicator.rb +0 -71
- data/lib/spree/testing_support/factories/activator_factory.rb +0 -8
| @@ -1,3 +1,7 @@ | |
| 1 | 
            +
            # TODO: Deprecate this class.
         | 
| 2 | 
            +
            # This calculator will be removed in future versions of Spree.
         | 
| 3 | 
            +
            # The only case where it was used was for Free Shipping Promotions.
         | 
| 4 | 
            +
            # There is now a Promotion Action which deals with these types of promotions instead.
         | 
| 1 5 | 
             
            module Spree
         | 
| 2 6 | 
             
              class Calculator::FreeShipping < Calculator
         | 
| 3 7 | 
             
                def self.description
         | 
| @@ -15,5 +19,4 @@ module Spree | |
| 15 19 | 
             
                  order.ship_total
         | 
| 16 20 | 
             
                end
         | 
| 17 21 | 
             
              end
         | 
| 18 | 
            -
            end
         | 
| 19 | 
            -
             | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Spree
         | 
| 2 | 
            +
              class Calculator
         | 
| 3 | 
            +
                class PercentOnLineItem < Calculator
         | 
| 4 | 
            +
                  preference :percent, :decimal, default: 0
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def self.description
         | 
| 7 | 
            +
                    Spree.t(:percent_per_item)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def compute(object)
         | 
| 11 | 
            +
                    ((object.price * object.quantity) * preferred_percent) / 100
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -4,6 +4,9 @@ module Spree | |
| 4 4 | 
             
              # for all matching products in an order. This should not be used as a
         | 
| 5 5 | 
             
              # shipping calculator since it would be the same thing as a flat percent
         | 
| 6 6 | 
             
              # off the entire order.
         | 
| 7 | 
            +
              #
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # TODO Should be deprecated now that we have adjustments at the line item level in spree core
         | 
| 7 10 |  | 
| 8 11 | 
             
              class Calculator::PercentPerItem < Calculator
         | 
| 9 12 | 
             
                preference :percent, :decimal, default: 0
         | 
| @@ -1,8 +1,9 @@ | |
| 1 1 | 
             
            module Spree
         | 
| 2 2 | 
             
              class Classification < ActiveRecord::Base
         | 
| 3 3 | 
             
                self.table_name = 'spree_products_taxons'
         | 
| 4 | 
            -
                 | 
| 5 | 
            -
                belongs_to : | 
| 4 | 
            +
                acts_as_list
         | 
| 5 | 
            +
                belongs_to :product, class_name: "Spree::Product", inverse_of: :classifications
         | 
| 6 | 
            +
                belongs_to :taxon, class_name: "Spree::Taxon", inverse_of: :classifications
         | 
| 6 7 |  | 
| 7 8 | 
             
                # For #3494
         | 
| 8 9 | 
             
                validates_uniqueness_of :taxon_id, :scope => :product_id, :message => :already_linked
         | 
| @@ -8,6 +8,7 @@ module Spree | |
| 8 8 |  | 
| 9 9 | 
             
                validates :month, :year, numericality: { only_integer: true }, unless: :has_payment_profile?
         | 
| 10 10 | 
             
                validates :number, presence: true, unless: :has_payment_profile?, on: :create
         | 
| 11 | 
            +
                validates :name, presence: true
         | 
| 11 12 | 
             
                validates :verification_value, presence: true, unless: :has_payment_profile?, on: :create
         | 
| 12 13 | 
             
                validate :expiry_not_in_the_past
         | 
| 13 14 |  | 
| @@ -26,19 +27,12 @@ module Spree | |
| 26 27 | 
             
                }
         | 
| 27 28 |  | 
| 28 29 | 
             
                def expiry=(expiry)
         | 
| 29 | 
            -
                   | 
| 30 | 
            -
             | 
| 31 | 
            -
                  self[:month], self[:year] =
         | 
| 32 | 
            -
                  if expiry.match(/\d\s?\/\s?\d/) # will match mm/yy and mm / yyyy
         | 
| 33 | 
            -
                    expiry.delete(' ').split('/')
         | 
| 34 | 
            -
                  elsif match = expiry.match(/(\d{2})(\d{2,4})/) # will match mmyy and mmyyyy
         | 
| 35 | 
            -
                    [match[1], match[2]]
         | 
| 36 | 
            -
                  end
         | 
| 37 | 
            -
                  if self[:year]
         | 
| 30 | 
            +
                  if expiry.present?
         | 
| 31 | 
            +
                    self[:month], self[:year] = expiry.delete(' ').split('/')
         | 
| 38 32 | 
             
                    self[:year] = "20" + self[:year] if self[:year].length == 2
         | 
| 39 33 | 
             
                    self[:year] = self[:year].to_i
         | 
| 34 | 
            +
                    self[:month] = self[:month].to_i
         | 
| 40 35 | 
             
                  end
         | 
| 41 | 
            -
                  self[:month] = self[:month].to_i
         | 
| 42 36 | 
             
                end
         | 
| 43 37 |  | 
| 44 38 | 
             
                def number=(num)
         | 
| @@ -68,14 +62,6 @@ module Spree | |
| 68 62 | 
             
                  CARD_TYPES.find{|type, pattern| return type.to_s if numbers =~ pattern}.to_s
         | 
| 69 63 | 
             
                end
         | 
| 70 64 |  | 
| 71 | 
            -
                def name?
         | 
| 72 | 
            -
                  first_name? && last_name?
         | 
| 73 | 
            -
                end
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                def name
         | 
| 76 | 
            -
                  "#{first_name} #{last_name}"
         | 
| 77 | 
            -
                end
         | 
| 78 | 
            -
             | 
| 79 65 | 
             
                def verification_value?
         | 
| 80 66 | 
             
                  verification_value.present?
         | 
| 81 67 | 
             
                end
         | 
| @@ -126,13 +112,9 @@ module Spree | |
| 126 112 |  | 
| 127 113 | 
             
                def expiry_not_in_the_past
         | 
| 128 114 | 
             
                  if year.present? && month.present?
         | 
| 129 | 
            -
                     | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                      current = Time.current
         | 
| 133 | 
            -
                      if year.to_i < current.year or (year.to_i == current.year and month.to_i < current.month)
         | 
| 134 | 
            -
                        errors.add(:base, :card_expired)
         | 
| 135 | 
            -
                      end
         | 
| 115 | 
            +
                    time = "#{year}-#{month}-1".to_time
         | 
| 116 | 
            +
                    if time < Time.zone.now.to_time.beginning_of_month
         | 
| 117 | 
            +
                      errors.add(:base, :card_expired)
         | 
| 136 118 | 
             
                    end
         | 
| 137 119 | 
             
                  end
         | 
| 138 120 | 
             
                end
         | 
| @@ -26,7 +26,7 @@ module Spree | |
| 26 26 | 
             
                def authorize(money, credit_card, options = {})
         | 
| 27 27 | 
             
                  profile_id = credit_card.gateway_customer_profile_id
         | 
| 28 28 | 
             
                  if VALID_CCS.include? credit_card.number or (profile_id and profile_id.starts_with? 'BGS-')
         | 
| 29 | 
            -
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345', :avs_result => { :code => ' | 
| 29 | 
            +
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345', :avs_result => { :code => 'D' })
         | 
| 30 30 | 
             
                  else
         | 
| 31 31 | 
             
                    ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { :message => 'Bogus Gateway: Forced failure' }, :test => true)
         | 
| 32 32 | 
             
                  end
         | 
| @@ -35,7 +35,7 @@ module Spree | |
| 35 35 | 
             
                def purchase(money, credit_card, options = {})
         | 
| 36 36 | 
             
                  profile_id = credit_card.gateway_customer_profile_id
         | 
| 37 37 | 
             
                  if VALID_CCS.include? credit_card.number  or (profile_id and profile_id.starts_with? 'BGS-')
         | 
| 38 | 
            -
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345', :avs_result => { :code => ' | 
| 38 | 
            +
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345', :avs_result => { :code => 'M' })
         | 
| 39 39 | 
             
                  else
         | 
| 40 40 | 
             
                    ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', :message => 'Bogus Gateway: Forced failure', :test => true)
         | 
| 41 41 | 
             
                  end
         | 
| @@ -45,9 +45,9 @@ module Spree | |
| 45 45 | 
             
                  ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345')
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| 48 | 
            -
                def capture( | 
| 49 | 
            -
                  if authorization | 
| 50 | 
            -
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true | 
| 48 | 
            +
                def capture(money, authorization, gateway_options)
         | 
| 49 | 
            +
                  if authorization == '12345'
         | 
| 50 | 
            +
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true)
         | 
| 51 51 | 
             
                  else
         | 
| 52 52 | 
             
                    ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', :error => 'Bogus Gateway: Forced failure', :test => true)
         | 
| 53 53 | 
             
                  end
         | 
| @@ -6,14 +6,6 @@ module Spree | |
| 6 6 | 
             
                  false
         | 
| 7 7 | 
             
                end
         | 
| 8 8 |  | 
| 9 | 
            -
                def capture(money, response_code, options = {})
         | 
| 10 | 
            -
                  if response_code == '12345'
         | 
| 11 | 
            -
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '67890')
         | 
| 12 | 
            -
                  else
         | 
| 13 | 
            -
                    ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', :error => 'Bogus Gateway: Forced failure', :test => true)
         | 
| 14 | 
            -
                  end
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
             | 
| 17 9 | 
             
                def authorize(money, credit_card, options = {})
         | 
| 18 10 | 
             
                  if VALID_CCS.include? credit_card.number
         | 
| 19 11 | 
             
                    ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345', :avs_result => { :code => 'A' })
         | 
    
        data/app/models/spree/image.rb
    CHANGED
    
    | @@ -14,15 +14,6 @@ module Spree | |
| 14 14 | 
             
                # we need to look at the write-queue for images which have not been saved yet
         | 
| 15 15 | 
             
                after_post_process :find_dimensions
         | 
| 16 16 |  | 
| 17 | 
            -
                include Spree::Core::S3Support
         | 
| 18 | 
            -
                supports_s3 :attachment
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                Spree::Image.attachment_definitions[:attachment][:styles] = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles]).symbolize_keys!
         | 
| 21 | 
            -
                Spree::Image.attachment_definitions[:attachment][:path] = Spree::Config[:attachment_path]
         | 
| 22 | 
            -
                Spree::Image.attachment_definitions[:attachment][:url] = Spree::Config[:attachment_url]
         | 
| 23 | 
            -
                Spree::Image.attachment_definitions[:attachment][:default_url] = Spree::Config[:attachment_default_url]
         | 
| 24 | 
            -
                Spree::Image.attachment_definitions[:attachment][:default_style] = Spree::Config[:attachment_default_style]
         | 
| 25 | 
            -
             | 
| 26 17 | 
             
                #used by admin products autocomplete
         | 
| 27 18 | 
             
                def mini_url
         | 
| 28 19 | 
             
                  attachment.url(:mini, false)
         | 
| @@ -1,9 +1,10 @@ | |
| 1 1 | 
             
            module Spree
         | 
| 2 2 | 
             
              class InventoryUnit < ActiveRecord::Base
         | 
| 3 | 
            -
                belongs_to :variant, class_name: "Spree::Variant"
         | 
| 4 | 
            -
                belongs_to :order, class_name: "Spree::Order"
         | 
| 5 | 
            -
                belongs_to :shipment, class_name: "Spree::Shipment", touch: true
         | 
| 3 | 
            +
                belongs_to :variant, class_name: "Spree::Variant", inverse_of: :inventory_units
         | 
| 4 | 
            +
                belongs_to :order, class_name: "Spree::Order", inverse_of: :inventory_units
         | 
| 5 | 
            +
                belongs_to :shipment, class_name: "Spree::Shipment", touch: true, inverse_of: :inventory_units
         | 
| 6 6 | 
             
                belongs_to :return_authorization, class_name: "Spree::ReturnAuthorization"
         | 
| 7 | 
            +
                belongs_to :line_item, class_name: "Spree::LineItem", inverse_of: :inventory_units
         | 
| 7 8 |  | 
| 8 9 | 
             
                scope :backordered, -> { where state: 'backordered' }
         | 
| 9 10 | 
             
                scope :shipped, -> { where state: 'shipped' }
         | 
| @@ -44,7 +45,12 @@ module Spree | |
| 44 45 | 
             
                end
         | 
| 45 46 |  | 
| 46 47 | 
             
                def self.finalize_units!(inventory_units)
         | 
| 47 | 
            -
                  inventory_units.map  | 
| 48 | 
            +
                  inventory_units.map do |iu|
         | 
| 49 | 
            +
                    iu.update_columns(
         | 
| 50 | 
            +
                      pending: false,
         | 
| 51 | 
            +
                      updated_at: Time.now,
         | 
| 52 | 
            +
                    )
         | 
| 53 | 
            +
                  end
         | 
| 48 54 | 
             
                end
         | 
| 49 55 |  | 
| 50 56 | 
             
                def find_stock_item
         | 
| @@ -0,0 +1,65 @@ | |
| 1 | 
            +
            module Spree
         | 
| 2 | 
            +
              # Manage (recalculate) item (LineItem or Shipment) adjustments
         | 
| 3 | 
            +
              class ItemAdjustments
         | 
| 4 | 
            +
                attr_reader :item
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                delegate :adjustments, :order, to: :item
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(item)
         | 
| 9 | 
            +
                  @item = item
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def update
         | 
| 13 | 
            +
                  update_adjustments if item.persisted?
         | 
| 14 | 
            +
                  item
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # TODO this should be probably the place to calculate proper item taxes
         | 
| 18 | 
            +
                # values after promotions are applied
         | 
| 19 | 
            +
                def update_adjustments
         | 
| 20 | 
            +
                  # Promotion adjustments must be applied first, then tax adjustments.
         | 
| 21 | 
            +
                  # This fits the criteria for VAT tax as outlined here:
         | 
| 22 | 
            +
                  # http://www.hmrc.gov.uk/vat/managing/charging/discounts-etc.htm#1
         | 
| 23 | 
            +
                  #
         | 
| 24 | 
            +
                  # It also fits the criteria for sales tax as outlined here:
         | 
| 25 | 
            +
                  # http://www.boe.ca.gov/formspubs/pub113/
         | 
| 26 | 
            +
                  # 
         | 
| 27 | 
            +
                  # Tax adjustments come in not one but *two* exciting flavours:
         | 
| 28 | 
            +
                  # Included & additional
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Included tax adjustments are those which are included in the price.
         | 
| 31 | 
            +
                  # These ones should not effect the eventual total price.
         | 
| 32 | 
            +
                  #
         | 
| 33 | 
            +
                  # Additional tax adjustments are the opposite; effecting the final total. 
         | 
| 34 | 
            +
                  promotion_total = adjustments.promotion.reload.map(&:update!).compact.sum
         | 
| 35 | 
            +
                  unless promotion_total == 0
         | 
| 36 | 
            +
                    choose_best_promotion_adjustment
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                  promo_total = best_promotion_adjustment.try(:amount).to_f
         | 
| 39 | 
            +
                  included_tax_total = adjustments.tax.included.reload.map(&:update!).compact.sum
         | 
| 40 | 
            +
                  additional_tax_total = adjustments.tax.additional.reload.map(&:update!).compact.sum
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  item.update_columns(
         | 
| 43 | 
            +
                    :promo_total => promo_total,
         | 
| 44 | 
            +
                    :included_tax_total => included_tax_total,
         | 
| 45 | 
            +
                    :additional_tax_total => additional_tax_total,
         | 
| 46 | 
            +
                    :adjustment_total => promo_total + additional_tax_total,
         | 
| 47 | 
            +
                    :updated_at => Time.now,
         | 
| 48 | 
            +
                  )
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                # Picks one (and only one) promotion to be eligible for this order
         | 
| 52 | 
            +
                # This promotion provides the most discount, and if two promotions
         | 
| 53 | 
            +
                # have the same amount, then it will pick the latest one.
         | 
| 54 | 
            +
                def choose_best_promotion_adjustment
         | 
| 55 | 
            +
                  if best_promotion_adjustment
         | 
| 56 | 
            +
                    other_promotions = self.adjustments.promotion.where("id NOT IN (?)", best_promotion_adjustment.id)
         | 
| 57 | 
            +
                    other_promotions.update_all(:eligible => false)
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def best_promotion_adjustment
         | 
| 62 | 
            +
                  @best_promotion_adjustment ||= adjustments.promotion.eligible.reorder("amount ASC, created_at DESC").first
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| @@ -1,12 +1,14 @@ | |
| 1 1 | 
             
            module Spree
         | 
| 2 2 | 
             
              class LineItem < ActiveRecord::Base
         | 
| 3 3 | 
             
                before_validation :adjust_quantity
         | 
| 4 | 
            -
                belongs_to :order, class_name: "Spree::Order",  | 
| 5 | 
            -
                belongs_to :variant, class_name: "Spree::Variant"
         | 
| 4 | 
            +
                belongs_to :order, class_name: "Spree::Order", inverse_of: :line_items
         | 
| 5 | 
            +
                belongs_to :variant, class_name: "Spree::Variant", inverse_of: :line_items
         | 
| 6 6 | 
             
                belongs_to :tax_category, class_name: "Spree::TaxCategory"
         | 
| 7 7 |  | 
| 8 8 | 
             
                has_one :product, through: :variant
         | 
| 9 | 
            +
             | 
| 9 10 | 
             
                has_many :adjustments, as: :adjustable, dependent: :destroy
         | 
| 11 | 
            +
                has_many :inventory_units, inverse_of: :line_item
         | 
| 10 12 |  | 
| 11 13 | 
             
                before_validation :copy_price
         | 
| 12 14 | 
             
                before_validation :copy_tax_category
         | 
| @@ -20,9 +22,12 @@ module Spree | |
| 20 22 | 
             
                validates :price, numericality: true
         | 
| 21 23 | 
             
                validates_with Stock::AvailabilityValidator
         | 
| 22 24 |  | 
| 25 | 
            +
                before_destroy :update_inventory
         | 
| 26 | 
            +
             | 
| 23 27 | 
             
                after_save :update_inventory
         | 
| 24 | 
            -
                after_save : | 
| 25 | 
            -
             | 
| 28 | 
            +
                after_save :update_adjustments
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                after_create :create_tax_charge
         | 
| 26 31 |  | 
| 27 32 | 
             
                delegate :name, :description, :should_track_inventory?, to: :variant
         | 
| 28 33 |  | 
| @@ -38,14 +43,23 @@ module Spree | |
| 38 43 |  | 
| 39 44 | 
             
                def copy_tax_category
         | 
| 40 45 | 
             
                  if variant
         | 
| 41 | 
            -
                    self.tax_category = variant. | 
| 46 | 
            +
                    self.tax_category = variant.tax_category
         | 
| 42 47 | 
             
                  end
         | 
| 43 48 | 
             
                end
         | 
| 44 49 |  | 
| 45 50 | 
             
                def amount
         | 
| 46 51 | 
             
                  price * quantity
         | 
| 47 52 | 
             
                end
         | 
| 48 | 
            -
                alias  | 
| 53 | 
            +
                alias subtotal amount
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def discounted_amount
         | 
| 56 | 
            +
                  amount + promo_total
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def final_amount
         | 
| 60 | 
            +
                  amount + adjustment_total.to_f
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
                alias total final_amount
         | 
| 49 63 |  | 
| 50 64 | 
             
                def single_money
         | 
| 51 65 | 
             
                  Spree::Money.new(price, { currency: currency })
         | 
| @@ -63,7 +77,7 @@ module Spree | |
| 63 77 | 
             
                end
         | 
| 64 78 |  | 
| 65 79 | 
             
                def sufficient_stock?
         | 
| 66 | 
            -
                  Stock::Quantifier.new( | 
| 80 | 
            +
                  Stock::Quantifier.new(variant_id).can_supply? quantity
         | 
| 67 81 | 
             
                end
         | 
| 68 82 |  | 
| 69 83 | 
             
                def insufficient_stock?
         | 
| @@ -87,17 +101,23 @@ module Spree | |
| 87 101 | 
             
                private
         | 
| 88 102 | 
             
                  def update_inventory
         | 
| 89 103 | 
             
                    if changed?
         | 
| 90 | 
            -
                      Spree::OrderInventory.new(self.order).verify( | 
| 104 | 
            +
                      Spree::OrderInventory.new(self.order, self).verify(target_shipment)
         | 
| 91 105 | 
             
                    end
         | 
| 92 106 | 
             
                  end
         | 
| 93 107 |  | 
| 94 | 
            -
                  def  | 
| 95 | 
            -
                    if  | 
| 96 | 
            -
                       | 
| 97 | 
            -
                      order.create_tax_charge!
         | 
| 98 | 
            -
                      order.update!
         | 
| 108 | 
            +
                  def update_adjustments
         | 
| 109 | 
            +
                    if quantity_changed?
         | 
| 110 | 
            +
                      recalculate_adjustments
         | 
| 99 111 | 
             
                    end
         | 
| 100 112 | 
             
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def recalculate_adjustments
         | 
| 115 | 
            +
                    Spree::ItemAdjustments.new(self).update
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def create_tax_charge
         | 
| 119 | 
            +
                    Spree::TaxRate.adjust(order, [self])
         | 
| 120 | 
            +
                  end
         | 
| 101 121 | 
             
              end
         | 
| 102 122 | 
             
            end
         | 
| 103 123 |  | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            module Spree
         | 
| 2 2 | 
             
              class OptionType < ActiveRecord::Base
         | 
| 3 | 
            -
                has_many :option_values, -> { order(:position) }, dependent: :destroy
         | 
| 4 | 
            -
                has_many :product_option_types, dependent: :destroy
         | 
| 3 | 
            +
                has_many :option_values, -> { order(:position) }, dependent: :destroy, inverse_of: :option_type
         | 
| 4 | 
            +
                has_many :product_option_types, dependent: :destroy, inverse_of: :option_type
         | 
| 5 5 | 
             
                has_many :products, through: :product_option_types
         | 
| 6 6 | 
             
                has_and_belongs_to_many :prototypes, join_table: 'spree_option_types_prototypes'
         | 
| 7 7 |  | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            module Spree
         | 
| 2 2 | 
             
              class OptionValue < ActiveRecord::Base
         | 
| 3 | 
            -
                belongs_to :option_type, : | 
| 3 | 
            +
                belongs_to :option_type, class_name: 'Spree::OptionType', touch: true, inverse_of: :option_values
         | 
| 4 4 | 
             
                acts_as_list scope: :option_type
         | 
| 5 5 | 
             
                has_and_belongs_to_many :variants, join_table: 'spree_option_values_variants', class_name: "Spree::Variant"
         | 
| 6 6 |  | 
    
        data/app/models/spree/order.rb
    CHANGED
    
    | @@ -9,7 +9,6 @@ module Spree | |
| 9 9 | 
             
                  go_to_state :address
         | 
| 10 10 | 
             
                  go_to_state :delivery
         | 
| 11 11 | 
             
                  go_to_state :payment, if: ->(order) {
         | 
| 12 | 
            -
                    order.update_totals
         | 
| 13 12 | 
             
                    order.payment_required?
         | 
| 14 13 | 
             
                  }
         | 
| 15 14 | 
             
                  go_to_state :confirm, if: ->(order) { order.confirmation_required? }
         | 
| @@ -24,9 +23,11 @@ module Spree | |
| 24 23 | 
             
                if Spree.user_class
         | 
| 25 24 | 
             
                  belongs_to :user, class_name: Spree.user_class.to_s
         | 
| 26 25 | 
             
                  belongs_to :created_by, class_name: Spree.user_class.to_s
         | 
| 26 | 
            +
                  belongs_to :approver, class_name: Spree.user_class.to_s
         | 
| 27 27 | 
             
                else
         | 
| 28 28 | 
             
                  belongs_to :user
         | 
| 29 29 | 
             
                  belongs_to :created_by
         | 
| 30 | 
            +
                  belongs_to :approver
         | 
| 30 31 | 
             
                end
         | 
| 31 32 |  | 
| 32 33 | 
             
                belongs_to :bill_address, foreign_key: :bill_address_id, class_name: 'Spree::Address'
         | 
| @@ -35,16 +36,20 @@ module Spree | |
| 35 36 | 
             
                belongs_to :ship_address, foreign_key: :ship_address_id, class_name: 'Spree::Address'
         | 
| 36 37 | 
             
                alias_attribute :shipping_address, :ship_address
         | 
| 37 38 |  | 
| 39 | 
            +
                alias_attribute :ship_total, :shipment_total
         | 
| 40 | 
            +
             | 
| 38 41 | 
             
                has_many :state_changes, as: :stateful
         | 
| 39 | 
            -
                has_many :line_items, -> { order('created_at ASC') }, dependent: :destroy
         | 
| 42 | 
            +
                has_many :line_items, -> { order('created_at ASC') }, dependent: :destroy, inverse_of: :order
         | 
| 40 43 | 
             
                has_many :payments, dependent: :destroy
         | 
| 41 44 | 
             
                has_many :return_authorizations, dependent: :destroy
         | 
| 42 45 | 
             
                has_many :adjustments, -> { order("#{Adjustment.table_name}.created_at ASC") }, as: :adjustable, dependent: :destroy
         | 
| 43 46 | 
             
                has_many :line_item_adjustments, through: :line_items, source: :adjustments
         | 
| 44 | 
            -
                has_many : | 
| 45 | 
            -
                has_many :inventory_units
         | 
| 47 | 
            +
                has_many :shipment_adjustments, through: :shipments, source: :adjustments
         | 
| 48 | 
            +
                has_many :inventory_units, inverse_of: :order
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                has_and_belongs_to_many :promotions, join_table: 'spree_orders_promotions'
         | 
| 46 51 |  | 
| 47 | 
            -
                has_many :shipments, dependent: :destroy do
         | 
| 52 | 
            +
                has_many :shipments, dependent: :destroy, inverse_of: :order do
         | 
| 48 53 | 
             
                  def states
         | 
| 49 54 | 
             
                    pluck(:state).uniq
         | 
| 50 55 | 
             
                  end
         | 
| @@ -63,16 +68,15 @@ module Spree | |
| 63 68 | 
             
                attr_accessor :use_billing
         | 
| 64 69 |  | 
| 65 70 | 
             
                before_create :link_by_email
         | 
| 66 | 
            -
                after_create :create_tax_charge!
         | 
| 67 71 |  | 
| 68 72 | 
             
                validates :email, presence: true, if: :require_email
         | 
| 69 73 | 
             
                validates :email, email: true, if: :require_email, allow_blank: true
         | 
| 70 | 
            -
                validates :number, uniqueness: true
         | 
| 71 74 | 
             
                validate :has_available_shipment
         | 
| 72 | 
            -
                validate :has_available_payment
         | 
| 73 75 |  | 
| 74 76 | 
             
                make_permalink field: :number
         | 
| 75 77 |  | 
| 78 | 
            +
                delegate :update_totals, :persist_totals, :to => :updater
         | 
| 79 | 
            +
             | 
| 76 80 | 
             
                class_attribute :update_hooks
         | 
| 77 81 | 
             
                self.update_hooks = Set.new
         | 
| 78 82 |  | 
| @@ -80,12 +84,8 @@ module Spree | |
| 80 84 | 
             
                  where(number: number)
         | 
| 81 85 | 
             
                end
         | 
| 82 86 |  | 
| 83 | 
            -
                scope :created_between, ->(start_date, end_date) { where(created_at: start_date..end_date) }
         | 
| 84 | 
            -
                scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
         | 
| 85 | 
            -
             | 
| 86 87 | 
             
                def self.between(start_date, end_date)
         | 
| 87 | 
            -
                   | 
| 88 | 
            -
                  self.created_between(start_date, end_date)
         | 
| 88 | 
            +
                  where(created_at: start_date..end_date)
         | 
| 89 89 | 
             
                end
         | 
| 90 90 |  | 
| 91 91 | 
             
                def self.by_customer(customer)
         | 
| @@ -110,6 +110,10 @@ module Spree | |
| 110 110 | 
             
                  self.update_hooks.add(hook)
         | 
| 111 111 | 
             
                end
         | 
| 112 112 |  | 
| 113 | 
            +
                def all_adjustments
         | 
| 114 | 
            +
                  Adjustment.where("order_id = :order_id OR adjustable_id = :order_id", :order_id => self.id)
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 113 117 | 
             
                # For compatiblity with Calculator::PriceSack
         | 
| 114 118 | 
             
                def amount
         | 
| 115 119 | 
             
                  line_items.inject(0.0) { |sum, li| sum + li.amount }
         | 
| @@ -131,24 +135,33 @@ module Spree | |
| 131 135 | 
             
                  Spree::Money.new(adjustment_total, { currency: currency })
         | 
| 132 136 | 
             
                end
         | 
| 133 137 |  | 
| 134 | 
            -
                def  | 
| 135 | 
            -
                  Spree::Money.new( | 
| 138 | 
            +
                def display_included_tax_total
         | 
| 139 | 
            +
                  Spree::Money.new(included_tax_total, { currency: currency })
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                def display_additional_tax_total
         | 
| 143 | 
            +
                  Spree::Money.new(additional_tax_total, { currency: currency })
         | 
| 136 144 | 
             
                end
         | 
| 137 145 |  | 
| 138 | 
            -
                def  | 
| 139 | 
            -
                  Spree::Money.new( | 
| 146 | 
            +
                def display_shipment_total
         | 
| 147 | 
            +
                  Spree::Money.new(shipment_total, { currency: currency })
         | 
| 140 148 | 
             
                end
         | 
| 149 | 
            +
                alias :display_ship_total :display_shipment_total
         | 
| 141 150 |  | 
| 142 151 | 
             
                def display_total
         | 
| 143 152 | 
             
                  Spree::Money.new(total, { currency: currency })
         | 
| 144 153 | 
             
                end
         | 
| 145 154 |  | 
| 155 | 
            +
                def shipping_discount
         | 
| 156 | 
            +
                  shipment_adjustments.eligible.sum(:amount) * - 1
         | 
| 157 | 
            +
                end
         | 
| 158 | 
            +
             | 
| 146 159 | 
             
                def to_param
         | 
| 147 160 | 
             
                  number.to_s.to_url.upcase
         | 
| 148 161 | 
             
                end
         | 
| 149 162 |  | 
| 150 163 | 
             
                def completed?
         | 
| 151 | 
            -
                  completed_at.present? | 
| 164 | 
            +
                  completed_at.present?
         | 
| 152 165 | 
             
                end
         | 
| 153 166 |  | 
| 154 167 | 
             
                # Indicates whether or not the user is allowed to proceed to checkout.
         | 
| @@ -174,11 +187,6 @@ module Spree | |
| 174 187 | 
             
                    state == 'confirm'
         | 
| 175 188 | 
             
                end
         | 
| 176 189 |  | 
| 177 | 
            -
                # Indicates the number of items in the order
         | 
| 178 | 
            -
                def item_count
         | 
| 179 | 
            -
                  line_items.inject(0) { |sum, li| sum + li.quantity }
         | 
| 180 | 
            -
                end
         | 
| 181 | 
            -
             | 
| 182 190 | 
             
                def backordered?
         | 
| 183 191 | 
             
                  shipments.any?(&:backordered?)
         | 
| 184 192 | 
             
                end
         | 
| @@ -202,16 +210,6 @@ module Spree | |
| 202 210 | 
             
                  Spree::Config[:tax_using_ship_address] ? ship_address : bill_address
         | 
| 203 211 | 
             
                end
         | 
| 204 212 |  | 
| 205 | 
            -
                # Array of totals grouped by Adjustment#label. Useful for displaying line item
         | 
| 206 | 
            -
                # adjustments on an invoice. For example, you can display tax breakout for
         | 
| 207 | 
            -
                # cases where tax is included in price.
         | 
| 208 | 
            -
                def line_item_adjustment_totals
         | 
| 209 | 
            -
                  Hash[self.line_item_adjustments.eligible.group_by(&:label).map do |label, adjustments|
         | 
| 210 | 
            -
                    total = adjustments.sum(&:amount)
         | 
| 211 | 
            -
                    [label, Spree::Money.new(total, { currency: currency })]
         | 
| 212 | 
            -
                  end]
         | 
| 213 | 
            -
                end
         | 
| 214 | 
            -
             | 
| 215 213 | 
             
                def updater
         | 
| 216 214 | 
             
                  @updater ||= OrderUpdater.new(self)
         | 
| 217 215 | 
             
                end
         | 
| @@ -220,10 +218,6 @@ module Spree | |
| 220 218 | 
             
                  updater.update
         | 
| 221 219 | 
             
                end
         | 
| 222 220 |  | 
| 223 | 
            -
                def update_totals
         | 
| 224 | 
            -
                  updater.update_totals
         | 
| 225 | 
            -
                end
         | 
| 226 | 
            -
             | 
| 227 221 | 
             
                def clone_billing_address
         | 
| 228 222 | 
             
                  if bill_address and self.ship_address.nil?
         | 
| 229 223 | 
             
                    self.ship_address = bill_address.clone
         | 
| @@ -258,11 +252,15 @@ module Spree | |
| 258 252 | 
             
                  end
         | 
| 259 253 | 
             
                end
         | 
| 260 254 |  | 
| 255 | 
            +
                # FIXME refactor this method and implement validation using validates_* utilities
         | 
| 261 256 | 
             
                def generate_order_number
         | 
| 262 | 
            -
                   | 
| 257 | 
            +
                  record = true
         | 
| 258 | 
            +
                  while record
         | 
| 263 259 | 
             
                    random = "R#{Array.new(9){rand(9)}.join}"
         | 
| 264 | 
            -
                     | 
| 260 | 
            +
                    record = self.class.where(number: random).first
         | 
| 265 261 | 
             
                  end
         | 
| 262 | 
            +
                  self.number = random if self.number.blank?
         | 
| 263 | 
            +
                  self.number
         | 
| 266 264 | 
             
                end
         | 
| 267 265 |  | 
| 268 266 | 
             
                def shipped_shipments
         | 
| @@ -282,14 +280,11 @@ module Spree | |
| 282 280 | 
             
                  line_items.detect { |line_item| line_item.variant_id == variant.id }
         | 
| 283 281 | 
             
                end
         | 
| 284 282 |  | 
| 285 | 
            -
                def ship_total
         | 
| 286 | 
            -
                  adjustments.shipping.sum(:amount)
         | 
| 287 | 
            -
                end
         | 
| 288 | 
            -
             | 
| 289 283 | 
             
                # Creates new tax charges if there are any applicable rates. If prices already
         | 
| 290 284 | 
             
                # include taxes then price adjustments are created instead.
         | 
| 291 285 | 
             
                def create_tax_charge!
         | 
| 292 | 
            -
                  Spree::TaxRate.adjust(self)
         | 
| 286 | 
            +
                  Spree::TaxRate.adjust(self, line_items)
         | 
| 287 | 
            +
                  Spree::TaxRate.adjust(self, shipments) if shipments.any?
         | 
| 293 288 | 
             
                end
         | 
| 294 289 |  | 
| 295 290 | 
             
                def outstanding_balance
         | 
| @@ -318,10 +313,8 @@ module Spree | |
| 318 313 | 
             
                # Finalizes an in progress order after checkout is complete.
         | 
| 319 314 | 
             
                # Called after transition to complete state when payments will have been processed
         | 
| 320 315 | 
             
                def finalize!
         | 
| 321 | 
            -
                  touch :completed_at
         | 
| 322 | 
            -
             | 
| 323 316 | 
             
                  # lock all adjustments (coupon promotions, etc.)
         | 
| 324 | 
            -
                   | 
| 317 | 
            +
                  all_adjustments.each{|a| a.close}
         | 
| 325 318 |  | 
| 326 319 | 
             
                  # update payment and shipment(s) states, and save
         | 
| 327 320 | 
             
                  updater.update_payment_state
         | 
| @@ -334,16 +327,16 @@ module Spree | |
| 334 327 | 
             
                  save
         | 
| 335 328 | 
             
                  updater.run_hooks
         | 
| 336 329 |  | 
| 337 | 
            -
                   | 
| 330 | 
            +
                  touch :completed_at
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                  deliver_order_confirmation_email unless confirmation_delivered?
         | 
| 333 | 
            +
             | 
| 334 | 
            +
                  consider_risk
         | 
| 338 335 | 
             
                end
         | 
| 339 336 |  | 
| 340 337 | 
             
                def deliver_order_confirmation_email
         | 
| 341 | 
            -
                   | 
| 342 | 
            -
             | 
| 343 | 
            -
                  rescue Exception => e
         | 
| 344 | 
            -
                    logger.error("#{e.class.name}: #{e.message}")
         | 
| 345 | 
            -
                    logger.error(e.backtrace * "\n")
         | 
| 346 | 
            -
                  end
         | 
| 338 | 
            +
                  OrderMailer.confirm_email(self.id).deliver
         | 
| 339 | 
            +
                  update_column(:confirmation_delivered, true)
         | 
| 347 340 | 
             
                end
         | 
| 348 341 |  | 
| 349 342 | 
             
                # Helper methods for checkout steps
         | 
| @@ -356,7 +349,7 @@ module Spree | |
| 356 349 | 
             
                end
         | 
| 357 350 |  | 
| 358 351 | 
             
                def pending_payments
         | 
| 359 | 
            -
                  payments.select | 
| 352 | 
            +
                  payments.select { |payment| payment.checkout? || payment.pending? }
         | 
| 360 353 | 
             
                end
         | 
| 361 354 |  | 
| 362 355 | 
             
                # processes any pending payments and must return a boolean as it's
         | 
| @@ -408,7 +401,7 @@ module Spree | |
| 408 401 | 
             
                end
         | 
| 409 402 |  | 
| 410 403 | 
             
                def insufficient_stock_lines
         | 
| 411 | 
            -
             | 
| 404 | 
            +
                 @insufficient_stock_lines ||= line_items.select(&:insufficient_stock?)
         | 
| 412 405 | 
             
                end
         | 
| 413 406 |  | 
| 414 407 | 
             
                def merge!(order, user = nil)
         | 
| @@ -432,13 +425,10 @@ module Spree | |
| 432 425 | 
             
                end
         | 
| 433 426 |  | 
| 434 427 | 
             
                def empty!
         | 
| 435 | 
            -
                  adjustments.destroy_all
         | 
| 436 428 | 
             
                  line_items.destroy_all
         | 
| 437 | 
            -
             | 
| 438 | 
            -
             | 
| 439 | 
            -
             | 
| 440 | 
            -
                  self.adjustments.destroy_all
         | 
| 441 | 
            -
                  self.line_item_adjustments.destroy_all
         | 
| 429 | 
            +
                  adjustments.destroy_all
         | 
| 430 | 
            +
                  update_totals
         | 
| 431 | 
            +
                  persist_totals
         | 
| 442 432 | 
             
                end
         | 
| 443 433 |  | 
| 444 434 | 
             
                def has_step?(step)
         | 
| @@ -465,27 +455,10 @@ module Spree | |
| 465 455 | 
             
                  @coupon_code = code.strip.downcase rescue nil
         | 
| 466 456 | 
             
                end
         | 
| 467 457 |  | 
| 468 | 
            -
                 | 
| 469 | 
            -
             | 
| 470 | 
            -
                # you would only want a promotion action to apply to order no more than once.
         | 
| 471 | 
            -
                #
         | 
| 472 | 
            -
                # Receives an adjustment +originator+ (here a PromotionAction object) and tells
         | 
| 473 | 
            -
                # if the order has adjustments from that already
         | 
| 474 | 
            -
                def promotion_credit_exists?(originator)
         | 
| 475 | 
            -
                  !! adjustments.includes(:originator).promotion.reload.detect { |credit| credit.originator.id == originator.id }
         | 
| 476 | 
            -
                end
         | 
| 477 | 
            -
             | 
| 478 | 
            -
                def promo_total
         | 
| 479 | 
            -
                  adjustments.eligible.promotion.sum(:amount)
         | 
| 458 | 
            +
                def can_add_coupon?
         | 
| 459 | 
            +
                  Spree::Promotion.order_activatable?(self)
         | 
| 480 460 | 
             
                end
         | 
| 481 461 |  | 
| 482 | 
            -
                def manual_adjustment_total
         | 
| 483 | 
            -
                  adjustments.eligible.manual.sum(:amount)
         | 
| 484 | 
            -
                end
         | 
| 485 | 
            -
             | 
| 486 | 
            -
                def discount_total
         | 
| 487 | 
            -
                  promo_total + manual_adjustment_total
         | 
| 488 | 
            -
                end
         | 
| 489 462 |  | 
| 490 463 | 
             
                def shipped?
         | 
| 491 464 | 
             
                  %w(partial shipped).include?(shipment_state)
         | 
| @@ -503,20 +476,31 @@ module Spree | |
| 503 476 | 
             
                  shipments
         | 
| 504 477 | 
             
                end
         | 
| 505 478 |  | 
| 479 | 
            +
                def apply_free_shipping_promotions
         | 
| 480 | 
            +
                  Spree::PromotionHandler::FreeShipping.new(self).activate
         | 
| 481 | 
            +
                  shipments.each { |shipment| ItemAdjustments.new(shipment).update }
         | 
| 482 | 
            +
                  updater.update_shipment_total
         | 
| 483 | 
            +
                  persist_totals
         | 
| 484 | 
            +
                end
         | 
| 485 | 
            +
             | 
| 506 486 | 
             
                # Clean shipments and make order back to address state
         | 
| 507 487 | 
             
                #
         | 
| 508 488 | 
             
                # At some point the might need to force the order to transition from address
         | 
| 509 489 | 
             
                # to delivery again so that proper updated shipments are created.
         | 
| 510 490 | 
             
                # e.g. customer goes back from payment step and changes order items
         | 
| 511 491 | 
             
                def ensure_updated_shipments
         | 
| 512 | 
            -
                  if shipments.any? | 
| 492 | 
            +
                  if shipments.any?
         | 
| 513 493 | 
             
                    self.shipments.destroy_all
         | 
| 494 | 
            +
                    self.update_column(:shipment_total, 0)
         | 
| 514 495 | 
             
                    restart_checkout_flow
         | 
| 515 496 | 
             
                  end
         | 
| 516 497 | 
             
                end
         | 
| 517 498 |  | 
| 518 499 | 
             
                def restart_checkout_flow
         | 
| 519 | 
            -
                  self. | 
| 500 | 
            +
                  self.update_columns(
         | 
| 501 | 
            +
                    state: checkout_steps.first,
         | 
| 502 | 
            +
                    updated_at: Time.now,
         | 
| 503 | 
            +
                  )
         | 
| 520 504 | 
             
                end
         | 
| 521 505 |  | 
| 522 506 | 
             
                def refresh_shipment_rates
         | 
| @@ -527,6 +511,12 @@ module Spree | |
| 527 511 | 
             
                  (bill_address.empty? && ship_address.empty?) || bill_address.same_as?(ship_address)
         | 
| 528 512 | 
             
                end
         | 
| 529 513 |  | 
| 514 | 
            +
                def set_shipments_cost
         | 
| 515 | 
            +
                  shipments.each(&:update_amounts)
         | 
| 516 | 
            +
                  updater.update_shipment_total
         | 
| 517 | 
            +
                  persist_totals
         | 
| 518 | 
            +
                end
         | 
| 519 | 
            +
             | 
| 530 520 | 
             
                def is_risky?
         | 
| 531 521 | 
             
                  self.payments.where(%{
         | 
| 532 522 | 
             
                    (avs_response IS NOT NULL and avs_response != '' and avs_response != 'D' and avs_response != 'M') or
         | 
| @@ -536,6 +526,39 @@ module Spree | |
| 536 526 | 
             
                  }.squish!).uniq.count > 0
         | 
| 537 527 | 
             
                end
         | 
| 538 528 |  | 
| 529 | 
            +
                def approved_by(user)
         | 
| 530 | 
            +
                  self.transaction do
         | 
| 531 | 
            +
                    approve!
         | 
| 532 | 
            +
                    self.update_columns(
         | 
| 533 | 
            +
                      approver_id: user.id,
         | 
| 534 | 
            +
                      approved_at: Time.now,
         | 
| 535 | 
            +
                      considered_risky: false,
         | 
| 536 | 
            +
                    )
         | 
| 537 | 
            +
                  end
         | 
| 538 | 
            +
                end
         | 
| 539 | 
            +
             | 
| 540 | 
            +
                def approved?
         | 
| 541 | 
            +
                  !!self.approved_at
         | 
| 542 | 
            +
                end
         | 
| 543 | 
            +
             | 
| 544 | 
            +
                def can_approve?
         | 
| 545 | 
            +
                  !approved?
         | 
| 546 | 
            +
                end
         | 
| 547 | 
            +
             | 
| 548 | 
            +
                def consider_risk
         | 
| 549 | 
            +
                  if is_risky? && !approved?
         | 
| 550 | 
            +
                    considered_risky!
         | 
| 551 | 
            +
                  end
         | 
| 552 | 
            +
                end
         | 
| 553 | 
            +
             | 
| 554 | 
            +
                def considered_risky!
         | 
| 555 | 
            +
                  update_column(:considered_risky, true)
         | 
| 556 | 
            +
                end
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                def approve!
         | 
| 559 | 
            +
                  update_column(:considered_risky, false)
         | 
| 560 | 
            +
                end
         | 
| 561 | 
            +
             | 
| 539 562 | 
             
                private
         | 
| 540 563 |  | 
| 541 564 | 
             
                  def link_by_email
         | 
| @@ -566,14 +589,9 @@ module Spree | |
| 566 589 | 
             
                    end
         | 
| 567 590 | 
             
                  end
         | 
| 568 591 |  | 
| 569 | 
            -
                  def has_available_payment
         | 
| 570 | 
            -
                    return unless has_step?("delivery") && delivery?
         | 
| 571 | 
            -
                    # errors.add(:base, :no_payment_methods_available) if available_payment_methods.empty?
         | 
| 572 | 
            -
                  end
         | 
| 573 | 
            -
             | 
| 574 592 | 
             
                  def after_cancel
         | 
| 575 593 | 
             
                    shipments.each { |shipment| shipment.cancel! }
         | 
| 576 | 
            -
                    payments.completed.each { |payment| payment. | 
| 594 | 
            +
                    payments.completed.each { |payment| payment.credit! }
         | 
| 577 595 |  | 
| 578 596 | 
             
                    send_cancel_email
         | 
| 579 597 | 
             
                    self.update_column(:payment_state, 'credit_owed') unless shipped?
         | 
| @@ -585,6 +603,7 @@ module Spree | |
| 585 603 |  | 
| 586 604 | 
             
                  def after_resume
         | 
| 587 605 | 
             
                    shipments.each { |shipment| shipment.resume! }
         | 
| 606 | 
            +
                    consider_risk
         | 
| 588 607 | 
             
                  end
         | 
| 589 608 |  | 
| 590 609 | 
             
                  def use_billing?
         | 
| @@ -594,5 +613,6 @@ module Spree | |
| 594 613 | 
             
                  def set_currency
         | 
| 595 614 | 
             
                    self.currency = Spree::Config[:currency] if self[:currency].nil?
         | 
| 596 615 | 
             
                  end
         | 
| 616 | 
            +
             | 
| 597 617 | 
             
              end
         | 
| 598 618 | 
             
            end
         |