solidus_core 2.7.4 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -17
  3. data/app/assets/images/logo/solidus.svg +18 -0
  4. data/app/assets/javascripts/spree.js.erb +2 -2
  5. data/app/helpers/spree/base_helper.rb +1 -7
  6. data/app/helpers/spree/taxons_helper.rb +2 -2
  7. data/app/mailers/spree/carton_mailer.rb +4 -4
  8. data/app/models/spree/calculator/flat_percent_item_total.rb +4 -1
  9. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +5 -3
  10. data/app/models/spree/calculator/tiered_percent.rb +2 -1
  11. data/app/models/spree/country.rb +8 -0
  12. data/app/models/spree/fulfilment_changer.rb +9 -1
  13. data/app/models/spree/gallery/product_gallery.rb +18 -0
  14. data/app/models/spree/gallery/variant_gallery.rb +21 -0
  15. data/app/models/spree/image.rb +11 -1
  16. data/app/models/spree/inventory_unit.rb +8 -0
  17. data/app/models/spree/line_item.rb +1 -1
  18. data/app/models/spree/order.rb +0 -4
  19. data/app/models/spree/order_cancellations.rb +31 -10
  20. data/app/models/spree/order_contents.rb +1 -5
  21. data/app/models/spree/order_inventory.rb +5 -4
  22. data/app/models/spree/product.rb +9 -0
  23. data/app/models/spree/product/scopes.rb +3 -4
  24. data/app/models/spree/promotion.rb +1 -6
  25. data/app/models/spree/promotion_handler/coupon.rb +15 -0
  26. data/app/models/spree/reimbursement.rb +21 -6
  27. data/app/models/spree/reimbursement_performer.rb +12 -6
  28. data/app/models/spree/reimbursement_type.rb +1 -1
  29. data/app/models/spree/reimbursement_type/credit.rb +5 -2
  30. data/app/models/spree/reimbursement_type/exchange.rb +1 -1
  31. data/app/models/spree/reimbursement_type/original_payment.rb +1 -1
  32. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +6 -6
  33. data/app/models/spree/reimbursement_type/store_credit.rb +18 -3
  34. data/app/models/spree/shipment.rb +11 -11
  35. data/app/models/spree/stock/allocator/base.rb +19 -0
  36. data/app/models/spree/stock/allocator/on_hand_first.rb +42 -0
  37. data/app/models/spree/stock/content_item.rb +1 -1
  38. data/app/models/spree/stock/inventory_units_finalizer.rb +47 -0
  39. data/app/models/spree/stock/location_sorter/base.rb +38 -0
  40. data/app/models/spree/stock/location_sorter/default_first.rb +15 -0
  41. data/app/models/spree/stock/location_sorter/unsorted.rb +14 -0
  42. data/app/models/spree/stock/simple_coordinator.rb +24 -10
  43. data/app/models/spree/stock_location.rb +2 -0
  44. data/app/models/spree/store_credit.rb +4 -12
  45. data/app/models/spree/store_credit_event.rb +2 -2
  46. data/app/models/spree/store_credit_reason.rb +11 -0
  47. data/app/models/spree/taxon.rb +8 -3
  48. data/app/models/spree/unit_cancel.rb +3 -0
  49. data/app/models/spree/variant.rb +18 -7
  50. data/config/initializers/money.rb +5 -0
  51. data/config/locales/en.yml +661 -527
  52. data/db/default/spree/store_credit.rb +1 -1
  53. data/db/default/spree/zones.rb +2 -2
  54. data/db/migrate/20180710170104_create_spree_store_credit_reasons_table.rb +42 -0
  55. data/db/migrate/20190106184413_remove_code_from_spree_promotions.rb +41 -0
  56. data/lib/generators/spree/dummy/templates/rails/test.rb +0 -3
  57. data/lib/generators/spree/install/install_generator.rb +2 -2
  58. data/lib/generators/spree/install/templates/config/initializers/spree.rb.tt +2 -2
  59. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +1 -1
  60. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +1 -1
  61. data/lib/solidus/migrations/promotions_with_code_handlers.rb +66 -0
  62. data/lib/spree/app_configuration.rb +20 -4
  63. data/lib/spree/core/controller_helpers/common.rb +1 -1
  64. data/lib/spree/core/stock_configuration.rb +12 -0
  65. data/lib/spree/core/version.rb +1 -1
  66. data/lib/spree/i18n.rb +4 -0
  67. data/lib/spree/money.rb +13 -11
  68. data/lib/spree/permission_sets.rb +0 -1
  69. data/lib/spree/permission_sets/promotion_management.rb +1 -0
  70. data/lib/spree/testing_support/dummy_app.rb +13 -2
  71. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
  72. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +1 -1
  73. data/lib/spree/testing_support/factories/store_credit_event_factory.rb +5 -5
  74. data/lib/spree/testing_support/factories/{store_credit_update_reason_factory.rb → store_credit_reason_factory.rb} +1 -1
  75. data/lib/spree/testing_support/partial_double_verification.rb +13 -0
  76. data/lib/spree/testing_support/shared_examples/gallery.rb +18 -0
  77. data/spec/helpers/products_helper_spec.rb +10 -8
  78. data/spec/helpers/taxons_helper_spec.rb +3 -1
  79. data/spec/lib/i18n_spec.rb +5 -0
  80. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +6 -2
  81. data/spec/lib/spree/core/stock_configuration_spec.rb +19 -0
  82. data/spec/lib/spree/core/testing_support/factories/store_credit_reason_factory_spec.rb +14 -0
  83. data/spec/lib/spree/money_spec.rb +12 -13
  84. data/spec/migrate/20190106184413_remove_code_from_spree_promotions_spec.rb +136 -0
  85. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +10 -1
  86. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +18 -6
  87. data/spec/models/spree/calculator/tiered_percent_spec.rb +7 -1
  88. data/spec/models/spree/country_spec.rb +76 -0
  89. data/spec/models/spree/customer_return_spec.rb +2 -1
  90. data/spec/models/spree/fulfilment_changer_spec.rb +33 -0
  91. data/spec/models/spree/gallery/product_gallery_spec.rb +21 -0
  92. data/spec/models/spree/gallery/variant_gallery_spec.rb +21 -0
  93. data/spec/models/spree/inventory_unit_spec.rb +1 -4
  94. data/spec/models/spree/order/checkout_spec.rb +6 -6
  95. data/spec/models/spree/order/finalizing_spec.rb +1 -20
  96. data/spec/models/spree/order/outstanding_balance_integration_spec.rb +2 -1
  97. data/spec/models/spree/order/updating_spec.rb +1 -1
  98. data/spec/models/spree/order_cancellations_spec.rb +55 -14
  99. data/spec/models/spree/order_inventory_spec.rb +12 -5
  100. data/spec/models/spree/order_merger_spec.rb +6 -3
  101. data/spec/models/spree/order_spec.rb +3 -7
  102. data/spec/models/spree/order_updater_spec.rb +3 -8
  103. data/spec/models/spree/payment/cancellation_spec.rb +4 -3
  104. data/spec/models/spree/payment_spec.rb +1 -17
  105. data/spec/models/spree/permission_sets/promotion_management_spec.rb +2 -0
  106. data/spec/models/spree/product_spec.rb +10 -1
  107. data/spec/models/spree/promotion_handler/coupon_spec.rb +62 -8
  108. data/spec/models/spree/promotion_spec.rb +24 -10
  109. data/spec/models/spree/refund_spec.rb +2 -1
  110. data/spec/models/spree/reimbursement_performer_spec.rb +5 -4
  111. data/spec/models/spree/reimbursement_spec.rb +26 -2
  112. data/spec/models/spree/reimbursement_type/credit_spec.rb +2 -1
  113. data/spec/models/spree/reimbursement_type/exchange_spec.rb +2 -1
  114. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +2 -1
  115. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +14 -2
  116. data/spec/models/spree/return_item_spec.rb +1 -1
  117. data/spec/models/spree/shipment_spec.rb +20 -13
  118. data/spec/models/spree/shipping_calculator_spec.rb +13 -3
  119. data/spec/models/spree/stock/allocator/on_hand_first_spec.rb +146 -0
  120. data/spec/models/spree/stock/content_item_spec.rb +70 -0
  121. data/spec/models/spree/stock/estimator_spec.rb +5 -2
  122. data/spec/models/spree/stock/inventory_units_finalizer_spec.rb +34 -0
  123. data/spec/models/spree/stock/location_sorter/default_first_spec.rb +20 -0
  124. data/spec/models/spree/stock/location_sorter/unsorted_spec.rb +19 -0
  125. data/spec/models/spree/stock/simple_coordinator_spec.rb +17 -0
  126. data/spec/models/spree/store_credit_event_spec.rb +12 -12
  127. data/spec/models/spree/store_credit_spec.rb +2 -2
  128. data/spec/models/spree/unit_cancel_spec.rb +20 -1
  129. data/spec/models/spree/variant_spec.rb +46 -24
  130. data/spec/spec_helper.rb +1 -0
  131. metadata +30 -9
  132. data/.yardopts +0 -1
  133. data/app/models/spree/store_credit_update_reason.rb +0 -4
  134. data/lib/spree/permission_sets/report_display.rb +0 -11
  135. data/spec/lib/spree/core/testing_support/factories/store_credit_update_reason_factory_spec.rb +0 -14
  136. data/spec/models/spree/permission_sets/report_display_spec.rb +0 -25
@@ -79,12 +79,8 @@ module Spree
79
79
  line_item
80
80
  end
81
81
 
82
- def order_updater
83
- @updater ||= Spree::OrderUpdater.new(order)
84
- end
85
-
86
82
  def reload_totals
87
- order_updater.update
83
+ @order.recalculate
88
84
  end
89
85
 
90
86
  def add_to_line_item(variant, quantity, options = {})
@@ -60,18 +60,19 @@ module Spree
60
60
  end
61
61
 
62
62
  def add_to_shipment(shipment, quantity)
63
+ pending_units = []
63
64
  if variant.should_track_inventory?
64
65
  on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
65
66
 
66
- on_hand.times { shipment.set_up_inventory('on_hand', variant, order, line_item) }
67
- back_order.times { shipment.set_up_inventory('backordered', variant, order, line_item) }
67
+ on_hand.times { pending_units << shipment.set_up_inventory('on_hand', variant, order, line_item) }
68
+ back_order.times { pending_units << shipment.set_up_inventory('backordered', variant, order, line_item) }
68
69
  else
69
- quantity.times { shipment.set_up_inventory('on_hand', variant, order, line_item) }
70
+ quantity.times { pending_units << shipment.set_up_inventory('on_hand', variant, order, line_item) }
70
71
  end
71
72
 
72
73
  # adding to this shipment, and removing from stock_location
73
74
  if order.completed?
74
- shipment.stock_location.unstock(variant, quantity, shipment)
75
+ Spree::Stock::InventoryUnitsFinalizer.new(pending_units).run!
75
76
  end
76
77
 
77
78
  quantity
@@ -297,6 +297,7 @@ module Spree
297
297
  # variants. If all else fails, will return a new image object.
298
298
  # @return [Spree::Image] the image to display
299
299
  def display_image
300
+ Spree::Deprecation.warn('Spree::Product#display_image is DEPRECATED. Choose an image from Spree::Product#gallery instead.')
300
301
  images.first || variant_images.first || Spree::Image.new
301
302
  end
302
303
 
@@ -310,6 +311,14 @@ module Spree
310
311
  end
311
312
  end
312
313
 
314
+ # The gallery for the product, which represents all the images
315
+ # associated with it, including those on its variants
316
+ #
317
+ # @return [Spree::Gallery] the media for a variant
318
+ def gallery
319
+ @gallery ||= Spree::Config.product_gallery_class.new(self)
320
+ end
321
+
313
322
  private
314
323
 
315
324
  def any_variants_not_track_inventory?
@@ -63,10 +63,9 @@ module Spree
63
63
  #
64
64
  # SELECT COUNT(*) ...
65
65
  add_search_scope :in_taxon do |taxon|
66
- includes(:classifications)
67
- .where('spree_products_taxons.taxon_id' => taxon.self_and_descendants.pluck(:id))
68
- .select(Spree::Classification.arel_table[:position])
69
- .order(Spree::Classification.arel_table[:position].asc)
66
+ includes(:classifications).
67
+ where("spree_products_taxons.taxon_id" => taxon.self_and_descendants.pluck(:id)).
68
+ order(Spree::Classification.arel_table[:position].asc)
70
69
  end
71
70
 
72
71
  # This scope selects products in all taxons AND all its descendants
@@ -36,7 +36,7 @@ module Spree
36
36
 
37
37
  before_save :normalize_blank_values
38
38
 
39
- scope :coupons, -> { where.not(code: nil) }
39
+ scope :coupons, -> { joins(:codes).distinct }
40
40
  scope :advertised, -> { where(advertise: true) }
41
41
  scope :active, -> do
42
42
  table = arel_table
@@ -49,11 +49,6 @@ module Spree
49
49
  self.whitelisted_ransackable_associations = ['codes']
50
50
  self.whitelisted_ransackable_attributes = ['path', 'promotion_category_id']
51
51
 
52
- # temporary code. remove after the column is dropped from the db.
53
- def columns
54
- super.reject { |column| column.name == "code" }
55
- end
56
-
57
52
  def self.order_activatable?(order)
58
53
  order && !UNACTIVATABLE_ORDER_STATES.include?(order.state)
59
54
  end
@@ -25,6 +25,20 @@ module Spree
25
25
  self
26
26
  end
27
27
 
28
+ def remove
29
+ if promotion.blank?
30
+ set_error_code :coupon_code_not_found
31
+ elsif !promotion_exists_on_order?(order, promotion)
32
+ set_error_code :coupon_code_not_present
33
+ else
34
+ promotion.remove_from(order)
35
+ order.recalculate
36
+ set_success_code :coupon_code_removed
37
+ end
38
+
39
+ self
40
+ end
41
+
28
42
  def set_success_code(status_code)
29
43
  @status_code = status_code
30
44
  @success = I18n.t(status_code, scope: 'spree')
@@ -56,6 +70,7 @@ module Spree
56
70
  def handle_present_promotion(promotion)
57
71
  return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded? || promotion_code.usage_limit_exceeded?
58
72
  return promotion_applied if promotion_exists_on_order?(order, promotion)
73
+
59
74
  unless promotion.eligible?(order, promotion_code: promotion_code)
60
75
  self.error = promotion.eligibility_errors.full_messages.first unless promotion.eligibility_errors.blank?
61
76
  return (error || ineligible_for_this_order)
@@ -18,6 +18,7 @@ module Spree
18
18
  accepts_nested_attributes_for :return_items, allow_destroy: true
19
19
 
20
20
  before_create :generate_number
21
+ before_create :calculate_total
21
22
 
22
23
  scope :reimbursed, -> { where(reimbursement_status: 'reimbursed') }
23
24
 
@@ -98,12 +99,15 @@ module Spree
98
99
  total - paid_amount
99
100
  end
100
101
 
101
- def perform!
102
+ def perform!(created_by: nil)
103
+ unless created_by
104
+ Spree::Deprecation.warn("Calling #perform on #{self} without created_by is deprecated")
105
+ end
102
106
  reimbursement_tax_calculator.call(self)
103
107
  reload
104
108
  update!(total: calculated_total)
105
109
 
106
- reimbursement_performer.perform(self)
110
+ reimbursement_performer.perform(self, created_by: created_by)
107
111
 
108
112
  if unpaid_amount_within_tolerance?
109
113
  reimbursed!
@@ -116,12 +120,15 @@ module Spree
116
120
  end
117
121
  end
118
122
 
119
- def simulate
123
+ def simulate(created_by: nil)
124
+ unless created_by
125
+ Spree::Deprecation.warn("Calling #simulate on #{self} without created_by is deprecated")
126
+ end
120
127
  reimbursement_simulator_tax_calculator.call(self)
121
128
  reload
122
129
  update!(total: calculated_total)
123
130
 
124
- reimbursement_performer.simulate(self)
131
+ reimbursement_performer.simulate(self, created_by: created_by)
125
132
  end
126
133
 
127
134
  def return_items_requiring_exchange
@@ -139,15 +146,23 @@ module Spree
139
146
  # Accepts all return items, saves the reimbursement, and performs the reimbursement
140
147
  #
141
148
  # @api public
149
+ # @param [Spree.user_class] created_by the user that is performing this action
142
150
  # @return [void]
143
- def return_all
151
+ def return_all(created_by: nil)
152
+ unless created_by
153
+ Spree::Deprecation.warn("Calling #return_all on #{self} without created_by is deprecated")
154
+ end
144
155
  return_items.each(&:accept!)
145
156
  save!
146
- perform!
157
+ perform!(created_by: created_by)
147
158
  end
148
159
 
149
160
  private
150
161
 
162
+ def calculate_total
163
+ self.total ||= calculated_total
164
+ end
165
+
151
166
  def generate_number
152
167
  self.number ||= loop do
153
168
  random = "RI#{Array.new(9){ rand(9) }.join}"
@@ -11,22 +11,28 @@ module Spree
11
11
  # - #description
12
12
  # - #display_amount
13
13
  # so they can be displayed in the Admin UI appropriately.
14
- def simulate(reimbursement)
15
- execute(reimbursement, true)
14
+ def simulate(reimbursement, created_by: nil)
15
+ unless created_by
16
+ Spree::Deprecation.warn("Calling #simulate on #{self} without created_by is deprecated")
17
+ end
18
+ execute(reimbursement, true, created_by: created_by)
16
19
  end
17
20
 
18
21
  # Actually perform the reimbursement
19
- def perform(reimbursement)
20
- execute(reimbursement, false)
22
+ def perform(reimbursement, created_by: nil)
23
+ unless created_by
24
+ Spree::Deprecation.warn("Calling #perform on #{self} without created_by is deprecated")
25
+ end
26
+ execute(reimbursement, false, created_by: created_by)
21
27
  end
22
28
 
23
29
  private
24
30
 
25
- def execute(reimbursement, simulate)
31
+ def execute(reimbursement, simulate, created_by:)
26
32
  reimbursement_type_hash = calculate_reimbursement_types(reimbursement)
27
33
 
28
34
  reimbursement_type_hash.flat_map do |reimbursement_type, return_items|
29
- reimbursement_type.reimburse(reimbursement, return_items, simulate)
35
+ reimbursement_type.reimburse(reimbursement, return_items, simulate, created_by: created_by)
30
36
  end
31
37
  end
32
38
 
@@ -11,7 +11,7 @@ module Spree
11
11
  # This method will reimburse the return items based on however it child implements it
12
12
  # By default it takes a reimbursement, the return items it needs to reimburse, and if
13
13
  # it is a simulation or a real reimbursement. This should return an array
14
- def self.reimburse(_reimbursement, _return_items, _simulate)
14
+ def self.reimburse(_reimbursement, _return_items, _simulate, *_optional_args)
15
15
  raise "Implement me"
16
16
  end
17
17
  end
@@ -5,9 +5,12 @@ module Spree
5
5
  extend Spree::ReimbursementType::ReimbursementHelpers
6
6
 
7
7
  class << self
8
- def reimburse(reimbursement, return_items, simulate)
8
+ def reimburse(reimbursement, return_items, simulate, created_by: nil)
9
+ unless created_by
10
+ Spree::Deprecation.warn("Calling #reimburse on #{self} without created_by is deprecated")
11
+ end
9
12
  unpaid_amount = return_items.sum(&:total).round(2, :down)
10
- reimbursement_list, _unpaid_amount = create_credits(reimbursement, unpaid_amount, simulate)
13
+ reimbursement_list, _unpaid_amount = create_credits(reimbursement, unpaid_amount, simulate, created_by: created_by)
11
14
  reimbursement_list
12
15
  end
13
16
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Spree::ReimbursementType::Exchange < Spree::ReimbursementType
4
- def self.reimburse(reimbursement, return_items, simulate)
4
+ def self.reimburse(reimbursement, return_items, simulate, *_optional_args)
5
5
  return [] unless return_items.present?
6
6
 
7
7
  exchange = Spree::Exchange.new(reimbursement.order, return_items)
@@ -4,7 +4,7 @@ class Spree::ReimbursementType::OriginalPayment < Spree::ReimbursementType
4
4
  extend Spree::ReimbursementType::ReimbursementHelpers
5
5
 
6
6
  class << self
7
- def reimburse(reimbursement, return_items, simulate)
7
+ def reimburse(reimbursement, return_items, simulate, _created_by)
8
8
  unpaid_amount = return_items.sum(&:total).round(2, :down)
9
9
  payments = reimbursement.order.payments.completed
10
10
 
@@ -20,8 +20,8 @@ module Spree
20
20
  [reimbursement_list, unpaid_amount]
21
21
  end
22
22
 
23
- def create_credits(reimbursement, unpaid_amount, simulate, reimbursement_list = [])
24
- credits = [create_credit(reimbursement, unpaid_amount, simulate)]
23
+ def create_credits(reimbursement, unpaid_amount, simulate, reimbursement_list = [], created_by:)
24
+ credits = [create_credit(reimbursement, unpaid_amount, simulate, created_by: created_by)]
25
25
  unpaid_amount -= credits.sum(&:amount)
26
26
  reimbursement_list += credits
27
27
 
@@ -43,19 +43,19 @@ module Spree
43
43
 
44
44
  # If you have multiple methods of crediting a customer, overwrite this method
45
45
  # Must return an array of objects the respond to #description, #display_amount
46
- def create_credit(reimbursement, unpaid_amount, simulate)
47
- creditable = create_creditable(reimbursement, unpaid_amount)
46
+ def create_credit(reimbursement, unpaid_amount, simulate, created_by:)
47
+ creditable = create_creditable(reimbursement, unpaid_amount, created_by: created_by)
48
48
  credit = reimbursement.credits.build(creditable: creditable, amount: unpaid_amount)
49
49
  simulate ? credit.readonly! : credit.save!
50
50
  credit
51
51
  end
52
52
 
53
- def create_creditable(reimbursement, unpaid_amount)
53
+ def create_creditable(reimbursement, unpaid_amount, created_by:)
54
54
  Spree::Reimbursement::Credit.default_creditable_class.new(
55
55
  user: reimbursement.order.user,
56
56
  amount: unpaid_amount,
57
57
  category: Spree::StoreCreditCategory.reimbursement_category(reimbursement),
58
- created_by: Spree::StoreCredit.default_created_by,
58
+ created_by: created_by,
59
59
  memo: "Refund for uncreditable payments on order #{reimbursement.order.number}",
60
60
  currency: reimbursement.order.currency
61
61
  )
@@ -4,17 +4,32 @@ class Spree::ReimbursementType::StoreCredit < Spree::ReimbursementType
4
4
  extend Spree::ReimbursementType::ReimbursementHelpers
5
5
 
6
6
  class << self
7
- def reimburse(reimbursement, return_items, simulate)
7
+ def reimburse(reimbursement, return_items, simulate, created_by: nil)
8
+ unless created_by
9
+ Spree::Deprecation.warn("Calling #reimburse on #{self} without created_by is deprecated")
10
+ end
8
11
  unpaid_amount = return_items.sum(&:total).to_d.round(2, :down)
9
12
  payments = store_credit_payments(reimbursement)
10
13
  reimbursement_list = []
11
14
 
12
15
  # Credit each store credit that was used on the order
13
- reimbursement_list, unpaid_amount = create_refunds(reimbursement, payments, unpaid_amount, simulate, reimbursement_list)
16
+ reimbursement_list, unpaid_amount = create_refunds(
17
+ reimbursement,
18
+ payments,
19
+ unpaid_amount,
20
+ simulate,
21
+ reimbursement_list
22
+ )
14
23
 
15
24
  # If there is any amount left to pay out to the customer, then create credit with that amount
16
25
  if unpaid_amount > 0.0
17
- reimbursement_list, _unpaid_amount = create_credits(reimbursement, unpaid_amount, simulate, reimbursement_list)
26
+ reimbursement_list, _unpaid_amount = create_credits(
27
+ reimbursement,
28
+ unpaid_amount,
29
+ simulate,
30
+ reimbursement_list,
31
+ created_by: created_by
32
+ )
18
33
  end
19
34
 
20
35
  reimbursement_list
@@ -13,6 +13,7 @@ module Spree
13
13
  has_many :shipping_methods, through: :shipping_rates
14
14
  has_many :state_changes, as: :stateful
15
15
  has_many :cartons, -> { distinct }, through: :inventory_units
16
+ has_many :line_items, -> { distinct }, through: :inventory_units
16
17
 
17
18
  before_validation :set_cost_zero_when_nil
18
19
 
@@ -32,7 +33,10 @@ module Spree
32
33
  scope :trackable, -> { where("tracking IS NOT NULL AND tracking != ''") }
33
34
  scope :with_state, ->(*s) { where(state: s) }
34
35
  # sort by most recent shipped_at, falling back to created_at. add "id desc" to make specs that involve this scope more deterministic.
35
- scope :reverse_chronological, -> { order('coalesce(spree_shipments.shipped_at, spree_shipments.created_at) desc', id: :desc) }
36
+ scope :reverse_chronological, -> {
37
+ order(Arel.sql("coalesce(#{Spree::Shipment.table_name}.shipped_at, #{Spree::Shipment.table_name}.created_at) desc"), id: :desc)
38
+ }
39
+
36
40
  scope :by_store, ->(store) { joins(:order).merge(Spree::Order.by_store(store)) }
37
41
 
38
42
  # shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
@@ -164,12 +168,7 @@ module Spree
164
168
  # Any previous non-pending inventory units are skipped as their stock had
165
169
  # already been allocated.
166
170
  def finalize!
167
- transaction do
168
- pending_units = inventory_units.select(&:pending?)
169
- pending_manifest = Spree::ShippingManifest.new(inventory_units: pending_units)
170
- pending_manifest.items.each { |item| manifest_unstock(item) }
171
- Spree::InventoryUnit.finalize_units!(pending_units)
172
- end
171
+ finalize_pending_inventory_units
173
172
  end
174
173
 
175
174
  def include?(variant)
@@ -188,10 +187,6 @@ module Spree
188
187
  line_items.map(&:total).sum
189
188
  end
190
189
 
191
- def line_items
192
- inventory_units.includes(:line_item).map(&:line_item).uniq
193
- end
194
-
195
190
  def ready_or_pending?
196
191
  ready? || pending?
197
192
  end
@@ -398,6 +393,11 @@ module Spree
398
393
 
399
394
  private
400
395
 
396
+ def finalize_pending_inventory_units
397
+ pending_units = inventory_units.select(&:pending?)
398
+ Spree::Stock::InventoryUnitsFinalizer.new(pending_units).run!
399
+ end
400
+
401
401
  def after_ship
402
402
  order.shipping.ship_shipment(self, suppress_mailer: suppress_mailer)
403
403
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Stock
5
+ module Allocator
6
+ class Base
7
+ attr_reader :availability
8
+
9
+ def initialize(availability)
10
+ @availability = availability
11
+ end
12
+
13
+ def allocate_inventory(_desired)
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Stock
5
+ module Allocator
6
+ class OnHandFirst < Spree::Stock::Allocator::Base
7
+ def allocate_inventory(desired)
8
+ # Allocate any available on hand inventory
9
+ on_hand = allocate_on_hand(desired)
10
+ desired -= on_hand.values.sum if on_hand.present?
11
+
12
+ # Allocate remaining desired inventory from backorders
13
+ backordered = allocate_backordered(desired)
14
+ desired -= backordered.values.sum if backordered.present?
15
+
16
+ # If all works at this point desired must be empty
17
+ [on_hand, backordered, desired]
18
+ end
19
+
20
+ protected
21
+
22
+ def allocate_on_hand(desired)
23
+ allocate(availability.on_hand_by_stock_location_id, desired)
24
+ end
25
+
26
+ def allocate_backordered(desired)
27
+ allocate(availability.backorderable_by_stock_location_id, desired)
28
+ end
29
+
30
+ def allocate(availability_by_location, desired)
31
+ availability_by_location.transform_values do |available|
32
+ # Find the desired inventory which is available at this location
33
+ packaged = available & desired
34
+ # Remove found inventory from desired
35
+ desired -= packaged
36
+ packaged
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end