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,228 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::OrderInventory, :type => :model do
4
+ let(:order) { create :completed_order_with_totals }
5
+ let(:line_item) { order.line_items.first }
6
+
7
+ subject { described_class.new(order, line_item) }
8
+
9
+ context "when order is missing inventory units" do
10
+ before { line_item.update_column(:quantity, 2) }
11
+
12
+ it 'creates the proper number of inventory units' do
13
+ subject.verify
14
+ expect(subject.inventory_units.count).to eq 2
15
+ end
16
+ end
17
+
18
+ context "#add_to_shipment" do
19
+ let(:shipment) { order.shipments.first }
20
+
21
+ context "order is not completed" do
22
+ before { allow(order).to receive_messages completed?: false }
23
+
24
+ it "doesn't unstock items" do
25
+ expect(shipment.stock_location).not_to receive(:unstock)
26
+ expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
27
+ end
28
+ end
29
+
30
+ context "inventory units state" do
31
+ before { shipment.inventory_units.destroy_all }
32
+
33
+ it 'sets inventory_units state as per stock location availability' do
34
+ expect(shipment.stock_location).to receive(:fill_status).with(subject.variant, 5).and_return([3, 2])
35
+
36
+ expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
37
+
38
+ units = shipment.inventory_units_for(subject.variant).group_by(&:state)
39
+ expect(units['backordered'].size).to eq(2)
40
+ expect(units['on_hand'].size).to eq(3)
41
+ end
42
+ end
43
+
44
+ context "store doesnt track inventory" do
45
+ let(:variant) { create(:variant) }
46
+
47
+ before { Spree::Config.track_inventory_levels = false }
48
+
49
+ it "creates only on hand inventory units" do
50
+ variant.stock_items.destroy_all
51
+
52
+ # The before_save callback in LineItem would verify inventory
53
+ line_item = order.contents.add variant, 1, shipment: shipment
54
+
55
+ units = shipment.inventory_units_for(line_item.variant)
56
+ expect(units.count).to eq 1
57
+ expect(units.first).to be_on_hand
58
+ end
59
+ end
60
+
61
+ context "variant doesnt track inventory" do
62
+ let(:variant) { create(:variant) }
63
+ before { variant.track_inventory = false }
64
+
65
+ it "creates only on hand inventory units" do
66
+ variant.stock_items.destroy_all
67
+
68
+ line_item = order.contents.add variant, 1
69
+ subject.verify(shipment)
70
+
71
+ units = shipment.inventory_units_for(line_item.variant)
72
+ expect(units.count).to eq 1
73
+ expect(units.first).to be_on_hand
74
+ end
75
+ end
76
+
77
+ it 'should create stock_movement' do
78
+ expect(subject.send(:add_to_shipment, shipment, 5)).to eq(5)
79
+
80
+ stock_item = shipment.stock_location.stock_item(subject.variant)
81
+ movement = stock_item.stock_movements.last
82
+ # movement.originator.should == shipment
83
+ expect(movement.quantity).to eq(-5)
84
+ end
85
+ end
86
+
87
+ context "#determine_target_shipment" do
88
+ let(:stock_location) { create :stock_location }
89
+ let(:variant) { line_item.variant }
90
+
91
+ before do
92
+ subject.verify
93
+
94
+ order.shipments.create(:stock_location_id => stock_location.id, :cost => 5)
95
+
96
+ shipped = order.shipments.create(:stock_location_id => order.shipments.first.stock_location.id, :cost => 10)
97
+ shipped.update_column(:state, 'shipped')
98
+ end
99
+
100
+ it 'should select first non-shipped shipment that already contains given variant' do
101
+ shipment = subject.send(:determine_target_shipment)
102
+ expect(shipment.shipped?).to be false
103
+ expect(shipment.inventory_units_for(variant)).not_to be_empty
104
+
105
+ expect(variant.stock_location_ids.include?(shipment.stock_location_id)).to be true
106
+ end
107
+
108
+ context "when no shipments already contain this varint" do
109
+ before do
110
+ subject.line_item.reload
111
+ subject.inventory_units.destroy_all
112
+ end
113
+
114
+ it 'selects first non-shipped shipment that leaves from same stock_location' do
115
+ shipment = subject.send(:determine_target_shipment)
116
+ shipment.reload
117
+ expect(shipment.shipped?).to be false
118
+ expect(shipment.inventory_units_for(variant)).to be_empty
119
+ expect(variant.stock_location_ids.include?(shipment.stock_location_id)).to be true
120
+ end
121
+ end
122
+ end
123
+
124
+ context 'when order has too many inventory units' do
125
+ before do
126
+ line_item.quantity = 3
127
+ line_item.save!
128
+
129
+ line_item.update_column(:quantity, 2)
130
+ subject.line_item.reload
131
+ end
132
+
133
+ it 'should be a messed up order' do
134
+ expect(order.shipments.first.inventory_units_for(line_item.variant).size).to eq(3)
135
+ expect(line_item.quantity).to eq(2)
136
+ end
137
+
138
+ it 'should decrease the number of inventory units' do
139
+ subject.verify
140
+ expect(subject.inventory_units.count).to eq 2
141
+ end
142
+
143
+ context '#remove_from_shipment' do
144
+ let(:shipment) { order.shipments.first }
145
+ let(:variant) { subject.variant }
146
+
147
+ context "order is not completed" do
148
+ before { allow(order).to receive_messages completed?: false }
149
+
150
+ it "doesn't restock items" do
151
+ expect(shipment.stock_location).not_to receive(:restock)
152
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
153
+ end
154
+ end
155
+
156
+ it 'should create stock_movement' do
157
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
158
+
159
+ stock_item = shipment.stock_location.stock_item(variant)
160
+ movement = stock_item.stock_movements.last
161
+ # movement.originator.should == shipment
162
+ expect(movement.quantity).to eq(1)
163
+ end
164
+
165
+ it 'should destroy backordered units first' do
166
+ allow(shipment).to receive_messages(inventory_units_for_item: [
167
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'backordered'),
168
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand'),
169
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'backordered')
170
+ ])
171
+
172
+ expect(shipment.inventory_units_for_item[0]).to receive(:destroy)
173
+ expect(shipment.inventory_units_for_item[1]).not_to receive(:destroy)
174
+ expect(shipment.inventory_units_for_item[2]).to receive(:destroy)
175
+
176
+ expect(subject.send(:remove_from_shipment, shipment, 2)).to eq(2)
177
+ end
178
+
179
+ it 'should destroy unshipped units first' do
180
+ allow(shipment).to receive_messages(inventory_units_for_item: [
181
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'shipped'),
182
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand')
183
+ ])
184
+
185
+ expect(shipment.inventory_units_for_item[0]).not_to receive(:destroy)
186
+ expect(shipment.inventory_units_for_item[1]).to receive(:destroy)
187
+
188
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
189
+ end
190
+
191
+ it 'only attempts to destroy as many units as are eligible, and return amount destroyed' do
192
+ allow(shipment).to receive_messages(inventory_units_for_item: [
193
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'shipped'),
194
+ mock_model(Spree::InventoryUnit, :variant_id => variant.id, :state => 'on_hand')
195
+ ])
196
+
197
+ expect(shipment.inventory_units_for_item[0]).not_to receive(:destroy)
198
+ expect(shipment.inventory_units_for_item[1]).to receive(:destroy)
199
+
200
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
201
+ end
202
+
203
+ it 'should destroy self if not inventory units remain' do
204
+ allow(shipment.inventory_units).to receive_messages(:count => 0)
205
+ expect(shipment).to receive(:destroy)
206
+
207
+ expect(subject.send(:remove_from_shipment, shipment, 1)).to eq(1)
208
+ end
209
+
210
+ context "inventory unit line item and variant points to different products" do
211
+ let(:different_line_item) { create(:line_item) }
212
+
213
+ let!(:different_inventory) do
214
+ shipment.set_up_inventory("on_hand", variant, order, different_line_item)
215
+ end
216
+
217
+ context "completed order" do
218
+ before { order.touch :completed_at }
219
+
220
+ it "removes only units that match both line item and variant" do
221
+ subject.send(:remove_from_shipment, shipment, shipment.inventory_units.count)
222
+ expect(different_inventory.reload).to be_persisted
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::OrderMutex do
4
+ let(:order) { create(:order) }
5
+
6
+ context "without an existing lock" do
7
+ it "executes the block" do
8
+ expect { |b|
9
+ Spree::OrderMutex.with_lock!(order, &b)
10
+ }.to yield_control.once
11
+ end
12
+
13
+ it "releases the lock for subsequent calls" do
14
+ expect { |b|
15
+ Spree::OrderMutex.with_lock!(order, &b)
16
+ Spree::OrderMutex.with_lock!(order, &b)
17
+ }.to yield_control.twice
18
+ end
19
+
20
+ it "returns the value of the block" do
21
+ expect(Spree::OrderMutex.with_lock!(order) { 'yay for spree' }).to eq 'yay for spree'
22
+ end
23
+ end
24
+
25
+ context "with an existing lock on the same order" do
26
+ it "raises a LockFailed error and then releases the lock" do
27
+ Spree::OrderMutex.with_lock!(order) do
28
+ expect {
29
+ expect { |b|
30
+ Spree::OrderMutex.with_lock!(order, &b)
31
+ }.not_to yield_control
32
+ }.to raise_error(Spree::OrderMutex::LockFailed)
33
+ end
34
+
35
+ expect { |b|
36
+ Spree::OrderMutex.with_lock!(order, &b)
37
+ }.to yield_control.once
38
+ end
39
+ end
40
+
41
+ context "with an expired existing lock on the same order" do
42
+ around do |example|
43
+ Spree::OrderMutex.with_lock!(order) do
44
+ future = Spree::Config[:order_mutex_max_age].seconds.from_now + 1.second
45
+ Timecop.freeze(future) do
46
+ example.run
47
+ end
48
+ end
49
+ end
50
+
51
+ it "executes the block" do
52
+ expect { |b|
53
+ Spree::OrderMutex.with_lock!(order, &b)
54
+ }.to yield_control.once
55
+ end
56
+ end
57
+
58
+ context "with an existing lock on a different order" do
59
+ let(:order2) { create(:order) }
60
+
61
+ around do |example|
62
+ Spree::OrderMutex.with_lock!(order2) { example.run }
63
+ end
64
+
65
+ it "executes the block" do
66
+ expect { |b|
67
+ Spree::OrderMutex.with_lock!(order, &b)
68
+ }.to yield_control.once
69
+ end
70
+ end
71
+
72
+ context "when an unrelated RecordNotUnique error occurs" do
73
+ def raise_record_not_unique
74
+ raise ActiveRecord::RecordNotUnique.new("Testing")
75
+ end
76
+
77
+ it "does not rescue the unrelated error" do
78
+ expect {
79
+ Spree::OrderMutex.with_lock!(order) do
80
+ raise_record_not_unique
81
+ end
82
+ }.to raise_error(ActiveRecord::RecordNotUnique)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::OrderPromotion do
4
+ subject do
5
+ order_promotion
6
+ end
7
+
8
+ let(:order_promotion) { build(:order_promotion) }
9
+
10
+ describe "promotion code presence error" do
11
+ subject do
12
+ order_promotion.valid?
13
+ order_promotion.errors[:promotion_code]
14
+ end
15
+
16
+ let(:order_promotion) { build(:order_promotion) }
17
+ let(:promotion) { order_promotion.promotion }
18
+
19
+ context "when the promotion does not have a code" do
20
+ it { is_expected.to be_blank }
21
+ end
22
+
23
+ context "when the promotion has a code" do
24
+ let!(:promotion_code) do
25
+ promotion.codes << build(:promotion_code, promotion: promotion)
26
+ end
27
+
28
+ it { is_expected.to include("can't be blank") }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,241 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::OrderShipping do
4
+ let(:order) { create(:order_ready_to_ship, line_items_count: 1) }
5
+
6
+ def emails
7
+ ActionMailer::Base.deliveries
8
+ end
9
+
10
+ shared_examples 'shipment shipping' do
11
+
12
+ it "marks the inventory units as shipped" do
13
+ expect { subject }.to change { order.inventory_units.reload.map(&:state) }.from(['on_hand']).to(['shipped'])
14
+ end
15
+
16
+ it "creates a carton with the shipment's inventory units" do
17
+ expect { subject }.to change { order.cartons.count }.by(1)
18
+ expect(subject.inventory_units).to match_array(shipment.inventory_units)
19
+ end
20
+
21
+ describe "shipment email" do
22
+ it "should send a shipment email" do
23
+ expect { subject }.to change { emails.size }.by(1)
24
+ expect(emails.last.subject).to eq("#{order.store.name} Shipment Notification ##{order.number}")
25
+ end
26
+ end
27
+
28
+ it "updates the order shipment state" do
29
+ expect { subject }.to change { order.reload.shipment_state }.from('ready').to('shipped')
30
+ end
31
+
32
+ it "updates shipment.shipped_at" do
33
+ Timecop.freeze do |now|
34
+ expect { subject }.to change { shipment.shipped_at }.from(nil).to(now)
35
+ end
36
+ end
37
+
38
+ it "updates order.updated_at" do
39
+ future = 1.minute.from_now
40
+ expect do
41
+ Timecop.freeze(future) do
42
+ subject
43
+ end
44
+ end.to change { order.updated_at }.from(order.updated_at).to(future)
45
+ end
46
+
47
+ end
48
+
49
+ describe "#ship" do
50
+ subject do
51
+ order.shipping.ship(
52
+ inventory_units: inventory_units,
53
+ stock_location: stock_location,
54
+ address: address,
55
+ shipping_method: shipping_method,
56
+ )
57
+ end
58
+
59
+ let(:shipment) { order.shipments.to_a.first }
60
+ let(:inventory_units) { shipment.inventory_units }
61
+ let(:stock_location) { shipment.stock_location }
62
+ let(:address) { shipment.address }
63
+ let(:shipping_method) { shipment.shipping_method }
64
+
65
+ it_behaves_like 'shipment shipping'
66
+
67
+ context "with an external_number" do
68
+ subject do
69
+ order.shipping.ship(
70
+ inventory_units: inventory_units,
71
+ stock_location: stock_location,
72
+ address: address,
73
+ shipping_method: shipping_method,
74
+ external_number: 'some-external-number',
75
+ )
76
+ end
77
+
78
+ it "sets the external_number" do
79
+ expect(subject.external_number).to eq 'some-external-number'
80
+ end
81
+ end
82
+
83
+ context "with a tracking number" do
84
+ subject do
85
+ order.shipping.ship(
86
+ inventory_units: inventory_units,
87
+ stock_location: stock_location,
88
+ address: address,
89
+ shipping_method: shipping_method,
90
+ tracking_number: 'tracking-number',
91
+ )
92
+ end
93
+
94
+ it "sets the tracking-number" do
95
+ expect(subject.tracking).to eq 'tracking-number'
96
+ end
97
+ end
98
+
99
+ context "when told to suppress the mailer" do
100
+ subject do
101
+ order.shipping.ship(
102
+ inventory_units: inventory_units,
103
+ stock_location: stock_location,
104
+ address: address,
105
+ shipping_method: shipping_method,
106
+ suppress_mailer: true,
107
+ )
108
+ end
109
+
110
+ it "does not send a shipment email" do
111
+ expect { subject }.to_not change { emails.size }
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#ship_shipment" do
117
+ subject { order.shipping.ship_shipment(shipment) }
118
+
119
+ let(:shipment) { order.shipments.to_a.first }
120
+
121
+ it_behaves_like 'shipment shipping'
122
+
123
+ context "when not all units are shippable" do
124
+ let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
125
+ let(:shippable_line_item) { order.line_items.first }
126
+ let(:unshippable_line_item) { order.line_items.last }
127
+
128
+ before do
129
+ unshippable_line_item.inventory_units.each(&:cancel!)
130
+ end
131
+
132
+ it "only ships the shippable ones" do
133
+ expect(subject.inventory_units).to match_array(shippable_line_item.inventory_units)
134
+ end
135
+ end
136
+
137
+ context "when all units are canceled or shipped" do
138
+ let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
139
+
140
+ before { Spree::OrderCancellations.new(order).short_ship([order.inventory_units.first]) }
141
+
142
+ it "updates the order shipment state" do
143
+ expect { subject }.to change { order.reload.shipment_state }.from('ready').to('shipped')
144
+ end
145
+ end
146
+
147
+ context "with an external_number" do
148
+ subject do
149
+ order.shipping.ship_shipment(
150
+ shipment,
151
+ external_number: 'some-external-number',
152
+ )
153
+ end
154
+
155
+ it "sets the external_number" do
156
+ expect(subject.external_number).to eq 'some-external-number'
157
+ end
158
+ end
159
+
160
+ context "with a tracking number" do
161
+ subject do
162
+ order.shipping.ship_shipment(
163
+ shipment,
164
+ tracking_number: 'tracking-number',
165
+ )
166
+ end
167
+
168
+ it "sets the tracking-number" do
169
+ expect(subject.tracking).to eq 'tracking-number'
170
+ end
171
+ end
172
+
173
+ # TODO: We can remove this once Shipment#ship! is called by
174
+ # OrderShipping#ship rather than vice versa
175
+ context "when the tracking number is already on the shipment" do
176
+ before do
177
+ shipment.update_attributes!(tracking: 'tracking-number')
178
+ end
179
+
180
+ it "sets the tracking-number" do
181
+ expect(subject.tracking).to eq 'tracking-number'
182
+ end
183
+ end
184
+
185
+ context "when the shipment has been partially shipped previously" do
186
+ let(:order) { create(:order_ready_to_ship, line_items_count: 2) }
187
+ let(:inventory_units) { shipment.inventory_units.to_a }
188
+ let(:shipped_inventory) { [inventory_units.first] }
189
+ let(:unshipped_inventory) { [inventory_units.last] }
190
+
191
+ before do
192
+ order.shipping.ship(
193
+ inventory_units: shipped_inventory,
194
+ stock_location: shipment.stock_location,
195
+ address: shipment.address,
196
+ shipping_method: shipment.shipping_method,
197
+ )
198
+ end
199
+
200
+ it "marks the inventory units as shipped" do
201
+ expect { subject }.to change { unshipped_inventory.map(&:reload).map(&:state) }.from(['on_hand']).to(['shipped'])
202
+ end
203
+
204
+ it "creates a carton with the shipment's inventory units" do
205
+ expect { subject }.to change { order.cartons.count }.by(1)
206
+ expect(subject.inventory_units).to match_array(unshipped_inventory)
207
+ end
208
+ end
209
+
210
+ context "when told to suppress the mailer" do
211
+ subject do
212
+ order.shipping.ship_shipment(
213
+ shipment,
214
+ suppress_mailer: true,
215
+ )
216
+ end
217
+
218
+ it "does not send a shipment email" do
219
+ expect { subject }.to_not change { emails.size }
220
+ end
221
+ end
222
+
223
+ context "with stale inventory units (regression test)" do
224
+ let(:order) { FactoryGirl.create(:order_ready_to_ship, line_items_count: 1) }
225
+ let(:shipment) do
226
+ FactoryGirl.create(
227
+ :shipment,
228
+ order: order,
229
+ address: FactoryGirl.create(:address)
230
+ )
231
+ end
232
+
233
+ before { shipment.ready! }
234
+
235
+ it "updates the state to shipped" do
236
+ order.shipping.ship_shipment(shipment)
237
+ expect(shipment.reload.state).to eq "shipped"
238
+ end
239
+ end
240
+ end
241
+ end