solidus_core 2.7.4 → 2.8.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.
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