solidus_core 1.1.0 → 1.1.1

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Rakefile +16 -0
  4. data/script/rails +9 -0
  5. data/solidus_core.gemspec +48 -0
  6. data/spec/fixtures/thinking-cat.jpg +0 -0
  7. data/spec/helpers/base_helper_spec.rb +173 -0
  8. data/spec/helpers/order_helper_spec.rb +12 -0
  9. data/spec/helpers/products_helper_spec.rb +208 -0
  10. data/spec/helpers/taxons_helper_spec.rb +17 -0
  11. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  12. data/spec/lib/i18n_spec.rb +106 -0
  13. data/spec/lib/search/base_spec.rb +86 -0
  14. data/spec/lib/search/variant_spec.rb +112 -0
  15. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
  16. data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
  17. data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +80 -0
  18. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  19. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  20. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  21. data/spec/lib/spree/core/current_store_spec.rb +36 -0
  22. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +24 -0
  23. data/spec/lib/spree/core/importer/order_spec.rb +431 -0
  24. data/spec/lib/spree/core/role_configuration_spec.rb +156 -0
  25. data/spec/lib/spree/core/unreturned_item_charger_spec.rb +130 -0
  26. data/spec/lib/spree/core/validators/email_spec.rb +48 -0
  27. data/spec/lib/spree/localized_number_spec.rb +38 -0
  28. data/spec/lib/spree/migrations_spec.rb +36 -0
  29. data/spec/lib/spree/money_spec.rb +127 -0
  30. data/spec/lib/tasks/exchanges_spec.rb +231 -0
  31. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
  32. data/spec/lib/tasks/order_capturing_spec.rb +56 -0
  33. data/spec/mailers/carton_mailer_spec.rb +55 -0
  34. data/spec/mailers/order_mailer_spec.rb +135 -0
  35. data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
  36. data/spec/mailers/test_mailer_spec.rb +15 -0
  37. data/spec/models/spree/ability_spec.rb +276 -0
  38. data/spec/models/spree/address_spec.rb +376 -0
  39. data/spec/models/spree/adjustment_reason_spec.rb +13 -0
  40. data/spec/models/spree/adjustment_spec.rb +169 -0
  41. data/spec/models/spree/app_configuration_spec.rb +24 -0
  42. data/spec/models/spree/asset_spec.rb +24 -0
  43. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  44. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  45. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  46. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  47. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  48. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  49. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +60 -0
  50. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  51. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  52. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  53. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  54. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
  55. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
  56. data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
  57. data/spec/models/spree/calculator_spec.rb +36 -0
  58. data/spec/models/spree/carton_spec.rb +133 -0
  59. data/spec/models/spree/classification_spec.rb +93 -0
  60. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  61. data/spec/models/spree/concerns/ordered_property_value_list_spec.rb +25 -0
  62. data/spec/models/spree/concerns/user_address_book_spec.rb +332 -0
  63. data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
  64. data/spec/models/spree/credit_card_spec.rb +341 -0
  65. data/spec/models/spree/customer_return_spec.rb +276 -0
  66. data/spec/models/spree/exchange_spec.rb +79 -0
  67. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  68. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  69. data/spec/models/spree/gateway_spec.rb +104 -0
  70. data/spec/models/spree/inventory_unit_spec.rb +307 -0
  71. data/spec/models/spree/item_adjustments_spec.rb +275 -0
  72. data/spec/models/spree/line_item_spec.rb +199 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +45 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +27 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +884 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +110 -0
  81. data/spec/models/spree/order/payment_spec.rb +243 -0
  82. data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
  83. data/spec/models/spree/order/state_machine_spec.rb +209 -0
  84. data/spec/models/spree/order/tax_spec.rb +84 -0
  85. data/spec/models/spree/order/totals_spec.rb +24 -0
  86. data/spec/models/spree/order/updating_spec.rb +18 -0
  87. data/spec/models/spree/order/validations_spec.rb +15 -0
  88. data/spec/models/spree/order_cancellations_spec.rb +120 -0
  89. data/spec/models/spree/order_capturing_spec.rb +150 -0
  90. data/spec/models/spree/order_contents_spec.rb +307 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_mutex_spec.rb +85 -0
  93. data/spec/models/spree/order_promotion_spec.rb +31 -0
  94. data/spec/models/spree/order_shipping_spec.rb +241 -0
  95. data/spec/models/spree/order_spec.rb +1482 -0
  96. data/spec/models/spree/order_stock_location_spec.rb +18 -0
  97. data/spec/models/spree/order_updater_spec.rb +283 -0
  98. data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
  99. data/spec/models/spree/payment_method_spec.rb +147 -0
  100. data/spec/models/spree/payment_spec.rb +1087 -0
  101. data/spec/models/spree/permission_sets/base_spec.rb +12 -0
  102. data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
  103. data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
  104. data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
  105. data/spec/models/spree/permission_sets/order_display_spec.rb +55 -0
  106. data/spec/models/spree/permission_sets/order_management_spec.rb +42 -0
  107. data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
  108. data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
  109. data/spec/models/spree/permission_sets/promotion_display_spec.rb +40 -0
  110. data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
  111. data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
  112. data/spec/models/spree/permission_sets/restricted_stock_display_spec.rb +41 -0
  113. data/spec/models/spree/permission_sets/restricted_stock_management_spec.rb +41 -0
  114. data/spec/models/spree/permission_sets/restricted_stock_transfer_display_spec.rb +50 -0
  115. data/spec/models/spree/permission_sets/restricted_stock_transfer_management_spec.rb +160 -0
  116. data/spec/models/spree/permission_sets/stock_display_spec.rb +24 -0
  117. data/spec/models/spree/permission_sets/stock_management_spec.rb +22 -0
  118. data/spec/models/spree/permission_sets/stock_transfer_display_spec.rb +24 -0
  119. data/spec/models/spree/permission_sets/stock_transfer_management_spec.rb +25 -0
  120. data/spec/models/spree/permission_sets/user_display_spec.rb +38 -0
  121. data/spec/models/spree/permission_sets/user_management_spec.rb +48 -0
  122. data/spec/models/spree/preference_spec.rb +80 -0
  123. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  124. data/spec/models/spree/preferences/preferable_spec.rb +294 -0
  125. data/spec/models/spree/preferences/scoped_store_spec.rb +60 -0
  126. data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
  127. data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
  128. data/spec/models/spree/preferences/store_spec.rb +39 -0
  129. data/spec/models/spree/price_spec.rb +42 -0
  130. data/spec/models/spree/product/scopes_spec.rb +116 -0
  131. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  132. data/spec/models/spree/product_filter_spec.rb +26 -0
  133. data/spec/models/spree/product_property_spec.rb +18 -0
  134. data/spec/models/spree/product_spec.rb +504 -0
  135. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
  136. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
  137. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
  138. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
  139. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  140. data/spec/models/spree/promotion/rules/first_repeat_purchase_since_spec.rb +69 -0
  141. data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
  142. data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
  143. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  144. data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
  145. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  146. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  147. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  148. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  149. data/spec/models/spree/promotion_builder_spec.rb +118 -0
  150. data/spec/models/spree/promotion_category_spec.rb +17 -0
  151. data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
  152. data/spec/models/spree/promotion_code_spec.rb +187 -0
  153. data/spec/models/spree/promotion_handler/cart_spec.rb +130 -0
  154. data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
  155. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
  156. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  157. data/spec/models/spree/promotion_rule_spec.rb +28 -0
  158. data/spec/models/spree/promotion_spec.rb +776 -0
  159. data/spec/models/spree/refund_spec.rb +192 -0
  160. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  161. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  162. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  163. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  164. data/spec/models/spree/reimbursement_spec.rb +231 -0
  165. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  166. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  167. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  168. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
  169. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
  170. data/spec/models/spree/return_authorization_spec.rb +290 -0
  171. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  172. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  173. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
  174. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  175. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  176. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  177. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  178. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  179. data/spec/models/spree/return_item_spec.rb +776 -0
  180. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  181. data/spec/models/spree/shipment_spec.rb +753 -0
  182. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  183. data/spec/models/spree/shipping_manifest_spec.rb +94 -0
  184. data/spec/models/spree/shipping_method_spec.rb +88 -0
  185. data/spec/models/spree/shipping_rate_spec.rb +142 -0
  186. data/spec/models/spree/state_spec.rb +14 -0
  187. data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
  188. data/spec/models/spree/stock/coordinator_spec.rb +116 -0
  189. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  190. data/spec/models/spree/stock/estimator_spec.rb +146 -0
  191. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  192. data/spec/models/spree/stock/package_spec.rb +163 -0
  193. data/spec/models/spree/stock/packer_spec.rb +91 -0
  194. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  195. data/spec/models/spree/stock/quantifier_spec.rb +115 -0
  196. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  197. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  198. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
  199. data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
  200. data/spec/models/spree/stock_item_spec.rb +444 -0
  201. data/spec/models/spree/stock_location_spec.rb +279 -0
  202. data/spec/models/spree/stock_movement_spec.rb +56 -0
  203. data/spec/models/spree/stock_transfer_spec.rb +290 -0
  204. data/spec/models/spree/store_credit_category_spec.rb +17 -0
  205. data/spec/models/spree/store_credit_event_spec.rb +314 -0
  206. data/spec/models/spree/store_credit_spec.rb +876 -0
  207. data/spec/models/spree/store_spec.rb +55 -0
  208. data/spec/models/spree/tax_category_spec.rb +27 -0
  209. data/spec/models/spree/tax_rate_spec.rb +378 -0
  210. data/spec/models/spree/taxon_spec.rb +74 -0
  211. data/spec/models/spree/taxonomy_spec.rb +18 -0
  212. data/spec/models/spree/tracker_spec.rb +21 -0
  213. data/spec/models/spree/transfer_item_spec.rb +264 -0
  214. data/spec/models/spree/unit_cancel_spec.rb +149 -0
  215. data/spec/models/spree/user_spec.rb +246 -0
  216. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
  217. data/spec/models/spree/variant/scopes_spec.rb +55 -0
  218. data/spec/models/spree/variant_property_rule_condition_spec.rb +15 -0
  219. data/spec/models/spree/variant_property_rule_spec.rb +83 -0
  220. data/spec/models/spree/variant_property_rule_value_spec.rb +18 -0
  221. data/spec/models/spree/variant_spec.rb +601 -0
  222. data/spec/models/spree/zone_spec.rb +305 -0
  223. data/spec/spec_helper.rb +80 -0
  224. data/spec/support/big_decimal.rb +5 -0
  225. data/spec/support/concerns/default_price.rb +34 -0
  226. data/spec/support/dummy_ability.rb +4 -0
  227. data/spec/support/test_gateway.rb +2 -0
  228. metadata +242 -2
@@ -0,0 +1,116 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe Coordinator, :type => :model do
6
+ let!(:order) { create(:order_with_line_items, line_items_count: 2) }
7
+
8
+ subject { Coordinator.new(order) }
9
+
10
+ context "packages" do
11
+ it "builds, prioritizes and estimates" do
12
+ expect(subject).to receive(:build_location_configured_packages).ordered
13
+ expect(subject).to receive(:build_packages).ordered
14
+ expect(subject).to receive(:prioritize_packages).ordered
15
+ expect(subject).to receive(:estimate_packages).ordered
16
+ subject.packages
17
+ end
18
+ end
19
+
20
+ describe "#shipments" do
21
+ let(:packages) { [build(:stock_package_fulfilled), build(:stock_package_fulfilled)] }
22
+
23
+ before { allow(subject).to receive(:packages).and_return(packages) }
24
+
25
+ it "turns packages into shipments" do
26
+ shipments = subject.shipments
27
+ expect(shipments.count).to eq packages.count
28
+ shipments.each { |shipment| expect(shipment).to be_a Shipment }
29
+ end
30
+
31
+ it "puts the order's ship address on the shipments" do
32
+ shipments = subject.shipments
33
+ shipments.each { |shipment| expect(shipment.address).to eq order.ship_address }
34
+ end
35
+ end
36
+
37
+ context "build packages" do
38
+ it "builds a package for all active stock locations" do
39
+ subject.packages.count == StockLocation.count
40
+ end
41
+
42
+ context "missing stock items in active stock location" do
43
+ let!(:another_location) { create(:stock_location, propagate_all_variants: false) }
44
+
45
+ it "builds packages only for valid active stock locations" do
46
+ expect(subject.build_packages.count).to eq(StockLocation.count - 1)
47
+ end
48
+ end
49
+ end
50
+
51
+ # regression spec
52
+ context "when there is one unit that has stock in a stock location that a non-tracked unit has no stock item in" do
53
+ let!(:stock_location_1) { create(:stock_location, propagate_all_variants: false, active: true) }
54
+ let!(:stock_location_2) { create(:stock_location, propagate_all_variants: false, active: true) }
55
+
56
+ let!(:variant_1) do
57
+ create(:variant, track_inventory: true).tap do |variant|
58
+ variant.stock_items.destroy_all
59
+ stock_item = variant.stock_items.create!(stock_location: stock_location_1)
60
+ stock_item.set_count_on_hand(10)
61
+ end
62
+ end
63
+ let!(:variant_2) do
64
+ create(:variant, track_inventory: false).tap do |variant|
65
+ variant.stock_items.destroy_all
66
+ stock_item = variant.stock_items.create!(stock_location: stock_location_2)
67
+ stock_item.set_count_on_hand(0)
68
+ end
69
+ end
70
+
71
+ let!(:order) { create(:order, line_items: [create(:line_item, variant: variant_1), create(:line_item, variant: variant_2)]) }
72
+
73
+ it "splits the inventory units to stock locations that they have stock items for" do
74
+ packages = subject.packages
75
+
76
+ expect(subject.packages.size).to eq 2
77
+
78
+ location_1_package = packages.detect { |p| p.stock_location == stock_location_1 }
79
+ location_2_package = packages.detect { |p| p.stock_location == stock_location_2 }
80
+
81
+ expect(location_1_package).to be_present
82
+ expect(location_2_package).to be_present
83
+
84
+ expect(location_1_package.contents.map(&:inventory_unit).map(&:variant)).to eq [variant_1]
85
+ expect(location_2_package.contents.map(&:inventory_unit).map(&:variant)).to eq [variant_2]
86
+ end
87
+ end
88
+
89
+ context "build location configured packages" do
90
+ context "there are configured stock locations" do
91
+ let(:stock_location) { order.variants.first.stock_locations.first }
92
+ let!(:stock_location_2) { create(:stock_location) }
93
+
94
+ before do
95
+ line_item_1 = order.line_items.first
96
+ line_item_2 = order.line_items.last
97
+ order.order_stock_locations.create(stock_location_id: stock_location.id, quantity: line_item_1.quantity, variant_id: line_item_1.variant_id)
98
+ order.order_stock_locations.create(stock_location_id: stock_location_2.id, quantity: line_item_2.quantity, variant_id: line_item_2.variant_id)
99
+ end
100
+
101
+ it "builds a package for each associated stock location" do
102
+ packages = subject.build_location_configured_packages
103
+ expect(packages.count).to eq(2)
104
+ expect(packages.map(&:stock_location)).to eq([stock_location, stock_location_2])
105
+ end
106
+ end
107
+ context "there are no configured stock locations" do
108
+ it "doesn't build any packages" do
109
+ packages = subject.build_location_configured_packages
110
+ expect(packages.count).to eq(0)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe Differentiator, :type => :model do
6
+ let(:variant1) { mock_model(Variant) }
7
+ let(:variant2) { mock_model(Variant) }
8
+
9
+ let(:line_item1) { build(:line_item, variant: variant1, quantity: 2) }
10
+ let(:line_item2) { build(:line_item, variant: variant2, quantity: 2) }
11
+
12
+ let(:stock_location) { mock_model(StockLocation) }
13
+
14
+ let(:inventory_unit1) { build(:inventory_unit, variant: variant1, line_item: line_item1) }
15
+ let(:inventory_unit2) { build(:inventory_unit, variant: variant2, line_item: line_item2) }
16
+
17
+ let(:order) { mock_model(Order, line_items: [line_item1, line_item2]) }
18
+
19
+ let(:package1) do
20
+ Package.new(stock_location).tap { |p| p.add(inventory_unit1) }
21
+ end
22
+
23
+ let(:package2) do
24
+ Package.new(stock_location).tap { |p| p.add(inventory_unit2) }
25
+ end
26
+
27
+ let(:packages) { [package1, package2] }
28
+
29
+ subject { Differentiator.new(order, packages) }
30
+
31
+ it { is_expected.to be_missing }
32
+
33
+ it 'calculates the missing items' do
34
+ expect(subject.missing[variant1]).to eq 1
35
+ expect(subject.missing[variant2]).to eq 1
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe Estimator, :type => :model do
6
+ let!(:shipping_method) { create(:shipping_method) }
7
+ let(:package) { build(:stock_package, contents: inventory_units.map { |i| ContentItem.new(inventory_unit) }) }
8
+ let(:order) { build(:order_with_line_items) }
9
+ let(:inventory_units) { order.inventory_units }
10
+
11
+ subject { Estimator.new(order) }
12
+
13
+ context "#shipping rates" do
14
+ before(:each) do
15
+ shipping_method.zones.first.members.create(:zoneable => order.ship_address.country)
16
+ allow_any_instance_of(ShippingMethod).to receive_message_chain(:calculator, :available?).and_return(true)
17
+ allow_any_instance_of(ShippingMethod).to receive_message_chain(:calculator, :compute).and_return(4.00)
18
+ allow_any_instance_of(ShippingMethod).to receive_message_chain(:calculator, :preferences).and_return({:currency => currency})
19
+ allow_any_instance_of(ShippingMethod).to receive_message_chain(:calculator, :marked_for_destruction?)
20
+
21
+ allow(package).to receive_messages(:shipping_methods => [shipping_method])
22
+ end
23
+
24
+ let(:currency) { "USD" }
25
+
26
+ shared_examples_for "shipping rate matches" do
27
+ it "returns shipping rates" do
28
+ shipping_rates = subject.shipping_rates(package)
29
+ expect(shipping_rates.first.cost).to eq 4.00
30
+ end
31
+ end
32
+
33
+ shared_examples_for "shipping rate doesn't match" do
34
+ it "does not return shipping rates" do
35
+ shipping_rates = subject.shipping_rates(package)
36
+ expect(shipping_rates).to eq([])
37
+ end
38
+ end
39
+
40
+ context "when the order's ship address is in the same zone" do
41
+ it_should_behave_like "shipping rate matches"
42
+ end
43
+
44
+ context "when the order's ship address is in a different zone" do
45
+ before { shipping_method.zones.each{|z| z.members.delete_all} }
46
+ it_should_behave_like "shipping rate doesn't match"
47
+ end
48
+
49
+ context "when the calculator is not available for that order" do
50
+ before { allow_any_instance_of(ShippingMethod).to receive_message_chain(:calculator, :available?).and_return(false) }
51
+ it_should_behave_like "shipping rate doesn't match"
52
+ end
53
+
54
+ context "when the currency is nil" do
55
+ let(:currency) { nil }
56
+ it_should_behave_like "shipping rate matches"
57
+ end
58
+
59
+ context "when the currency is an empty string" do
60
+ let(:currency) { "" }
61
+ it_should_behave_like "shipping rate matches"
62
+ end
63
+
64
+ context "when the current matches the order's currency" do
65
+ it_should_behave_like "shipping rate matches"
66
+ end
67
+
68
+ context "if the currency is different than the order's currency" do
69
+ let(:currency) { "GBP" }
70
+ it_should_behave_like "shipping rate doesn't match"
71
+ end
72
+
73
+ it "sorts shipping rates by cost" do
74
+ shipping_methods = 3.times.map { create(:shipping_method) }
75
+ allow(shipping_methods[0]).to receive_message_chain(:calculator, :compute).and_return(5.00)
76
+ allow(shipping_methods[1]).to receive_message_chain(:calculator, :compute).and_return(3.00)
77
+ allow(shipping_methods[2]).to receive_message_chain(:calculator, :compute).and_return(4.00)
78
+
79
+ allow(subject).to receive(:shipping_methods).and_return(shipping_methods)
80
+
81
+ expect(subject.shipping_rates(package).map(&:cost)).to eq %w[3.00 4.00 5.00].map(&BigDecimal.method(:new))
82
+ end
83
+
84
+ context "general shipping methods" do
85
+ let(:shipping_methods) { 2.times.map { create(:shipping_method) } }
86
+
87
+ it "selects the most affordable shipping rate" do
88
+ allow(shipping_methods[0]).to receive_message_chain(:calculator, :compute).and_return(5.00)
89
+ allow(shipping_methods[1]).to receive_message_chain(:calculator, :compute).and_return(3.00)
90
+
91
+ allow(subject).to receive(:shipping_methods).and_return(shipping_methods)
92
+
93
+ expect(subject.shipping_rates(package).sort_by(&:cost).map(&:selected)).to eq [true, false]
94
+ end
95
+
96
+ it "selects the most affordable shipping rate and doesn't raise exception over nil cost" do
97
+ allow(shipping_methods[0]).to receive_message_chain(:calculator, :compute).and_return(1.00)
98
+ allow(shipping_methods[1]).to receive_message_chain(:calculator, :compute).and_return(nil)
99
+
100
+ allow(subject).to receive(:shipping_methods).and_return(shipping_methods)
101
+
102
+ subject.shipping_rates(package)
103
+ end
104
+ end
105
+
106
+ context "involves backend only shipping methods" do
107
+ let(:backend_method) { create(:shipping_method, display_on: "back_end") }
108
+ let(:generic_method) { create(:shipping_method) }
109
+
110
+ before do
111
+ allow(backend_method).to receive_message_chain(:calculator, :compute).and_return(0.00)
112
+ allow(generic_method).to receive_message_chain(:calculator, :compute).and_return(5.00)
113
+ allow(subject).to receive(:shipping_methods).and_return([backend_method, generic_method])
114
+ end
115
+
116
+ it "does not return backend rates at all" do
117
+ expect(subject.shipping_rates(package).map(&:shipping_method_id)).to eq([generic_method.id])
118
+ end
119
+
120
+ # regression for #3287
121
+ it "doesn't select backend rates even if they're more affordable" do
122
+ expect(subject.shipping_rates(package).map(&:selected)).to eq [true]
123
+ end
124
+ end
125
+
126
+ context "includes tax adjustments if applicable" do
127
+ let!(:tax_rate) { create(:tax_rate, zone: order.tax_zone) }
128
+
129
+ before do
130
+ Spree::ShippingMethod.all.each do |sm|
131
+ sm.tax_category_id = tax_rate.tax_category_id
132
+ sm.save
133
+ end
134
+ package.shipping_methods.map(&:reload)
135
+ end
136
+
137
+
138
+ it "links the shipping rate and the tax rate" do
139
+ shipping_rates = subject.shipping_rates(package)
140
+ expect(shipping_rates.first.tax_rate).to eq(tax_rate)
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe InventoryUnitBuilder, :type => :model do
6
+ let(:line_item_1) { build(:line_item) }
7
+ let(:line_item_2) { build(:line_item, quantity: 2) }
8
+ let(:order) { build(:order, line_items: [line_item_1, line_item_2]) }
9
+
10
+ subject { InventoryUnitBuilder.new(order) }
11
+
12
+ describe "#units" do
13
+ it "returns an inventory unit for each quantity for the order's line items" do
14
+ units = subject.units
15
+ expect(units.count).to eq 3
16
+ expect(units.first.line_item).to eq line_item_1
17
+ expect(units.first.variant).to eq line_item_1.variant
18
+
19
+ expect(units[1].line_item).to eq line_item_2
20
+ expect(units[1].variant).to eq line_item_2.variant
21
+
22
+ expect(units[2].line_item).to eq line_item_2
23
+ expect(units[2].variant).to eq line_item_2.variant
24
+ end
25
+
26
+ it "builds the inventory units as pending" do
27
+ expect(subject.units.map(&:pending).uniq).to eq [true]
28
+ end
29
+
30
+ it "associates the inventory units to the order" do
31
+ expect(subject.units.map(&:order).uniq).to eq [order]
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,163 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module Stock
5
+ describe Package, :type => :model do
6
+ let(:variant) { build(:variant, weight: 25.0) }
7
+ let(:stock_location) { build(:stock_location) }
8
+ let(:order) { build(:order) }
9
+
10
+ subject { Package.new(stock_location) }
11
+
12
+ def build_inventory_unit
13
+ build(:inventory_unit, variant: variant)
14
+ end
15
+
16
+ it 'calculates the weight of all the contents' do
17
+ 4.times { subject.add build_inventory_unit }
18
+ expect(subject.weight).to eq(100.0)
19
+ end
20
+
21
+ it 'filters by on_hand and backordered' do
22
+ 4.times { subject.add build_inventory_unit }
23
+ 3.times { subject.add build_inventory_unit, :backordered }
24
+ expect(subject.on_hand.count).to eq 4
25
+ expect(subject.backordered.count).to eq 3
26
+ end
27
+
28
+ it 'calculates the quantity by state' do
29
+ 4.times { subject.add build_inventory_unit }
30
+ 3.times { subject.add build_inventory_unit, :backordered }
31
+
32
+ expect(subject.quantity).to eq 7
33
+ expect(subject.quantity(:on_hand)).to eq 4
34
+ expect(subject.quantity(:backordered)).to eq 3
35
+ end
36
+
37
+ it 'returns nil for content item not found' do
38
+ unit = build_inventory_unit
39
+ item = subject.find_item(unit, :on_hand)
40
+ expect(item).to be_nil
41
+ end
42
+
43
+ it 'finds content item for an inventory unit' do
44
+ unit = build_inventory_unit
45
+ subject.add unit
46
+ item = subject.find_item(unit, :on_hand)
47
+ expect(item.quantity).to eq 1
48
+ end
49
+
50
+ # Contains regression test for #2804
51
+ it 'builds a list of shipping methods common to all categories' do
52
+ category1 = create(:shipping_category)
53
+ category2 = create(:shipping_category)
54
+ method1 = create(:shipping_method)
55
+ method2 = create(:shipping_method)
56
+ method1.shipping_categories = [category1, category2]
57
+ method2.shipping_categories = [category1]
58
+ variant1 = mock_model(Variant, shipping_category: category1)
59
+ variant2 = mock_model(Variant, shipping_category: category2)
60
+ variant3 = mock_model(Variant, shipping_category: nil)
61
+ contents = [ContentItem.new(build(:inventory_unit, variant: variant1)),
62
+ ContentItem.new(build(:inventory_unit, variant: variant1)),
63
+ ContentItem.new(build(:inventory_unit, variant: variant2)),
64
+ ContentItem.new(build(:inventory_unit, variant: variant3))]
65
+
66
+ package = Package.new(stock_location, contents)
67
+ expect(package.shipping_methods).to eq([method1])
68
+ end
69
+
70
+ it 'builds an empty list of shipping methods when no categories' do
71
+ variant = mock_model(Variant, shipping_category: nil)
72
+ contents = [ContentItem.new(build(:inventory_unit, variant: variant))]
73
+ package = Package.new(stock_location, contents)
74
+ expect(package.shipping_methods).to be_empty
75
+ end
76
+
77
+ it "can convert to a shipment" do
78
+ 2.times { subject.add build_inventory_unit }
79
+ subject.add build_inventory_unit, :backordered
80
+
81
+ shipping_method = build(:shipping_method)
82
+ subject.shipping_rates = [ Spree::ShippingRate.new(shipping_method: shipping_method, cost: 10.00, selected: true) ]
83
+
84
+ shipment = subject.to_shipment
85
+ expect(shipment.stock_location).to eq subject.stock_location
86
+ expect(shipment.inventory_units.size).to eq 3
87
+
88
+ first_unit = shipment.inventory_units.first
89
+ expect(first_unit.variant).to eq variant
90
+ expect(first_unit.state).to eq 'on_hand'
91
+ expect(first_unit).to be_pending
92
+
93
+ last_unit = shipment.inventory_units.last
94
+ expect(last_unit.variant).to eq variant
95
+ expect(last_unit.state).to eq 'backordered'
96
+
97
+ expect(shipment.shipping_method).to eq shipping_method
98
+ end
99
+
100
+ it 'does not add an inventory unit to a package twice' do
101
+ # since inventory units currently don't have a quantity
102
+ unit = build_inventory_unit
103
+ subject.add unit
104
+ subject.add unit
105
+ expect(subject.quantity).to eq 1
106
+ expect(subject.contents.first.inventory_unit).to eq unit
107
+ expect(subject.contents.first.quantity).to eq 1
108
+ end
109
+
110
+ describe "#add_multiple" do
111
+ it "adds multiple inventory units" do
112
+ expect { subject.add_multiple [build_inventory_unit, build_inventory_unit] }.to change { subject.quantity }.by(2)
113
+ end
114
+
115
+ it "allows adding with a state" do
116
+ expect { subject.add_multiple [build_inventory_unit, build_inventory_unit], :backordered }.to change { subject.backordered.count }.by(2)
117
+ end
118
+
119
+ it "defaults to adding with the on hand state" do
120
+ expect { subject.add_multiple [build_inventory_unit, build_inventory_unit] }.to change { subject.on_hand.count }.by(2)
121
+ end
122
+ end
123
+
124
+ describe "#remove" do
125
+ let(:unit) { build_inventory_unit }
126
+ context "there is a content item for the inventory unit" do
127
+
128
+ before { subject.add unit }
129
+
130
+ it "removes that content item" do
131
+ expect { subject.remove(unit) }.to change { subject.quantity }.by(-1)
132
+ expect(subject.contents.map(&:inventory_unit)).not_to include unit
133
+ end
134
+ end
135
+
136
+ context "there is no content item for the inventory unit" do
137
+ it "doesn't change the set of content items" do
138
+ expect { subject.remove(unit) }.not_to change { subject.quantity }
139
+ end
140
+ end
141
+ end
142
+
143
+ describe "#order" do
144
+ let(:unit) { build_inventory_unit }
145
+ context "there is an inventory unit" do
146
+
147
+ before { subject.add unit }
148
+
149
+ it "returns an order" do
150
+ expect(subject.order).to be_a_kind_of Spree::Order
151
+ expect(subject.order).to eq unit.order
152
+ end
153
+ end
154
+
155
+ context "there is no inventory unit" do
156
+ it "returns nil" do
157
+ expect(subject.order).to eq nil
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end