spree_core 3.0.5 → 3.0.6

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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +3 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/spree.js.coffee.erb +1 -1
  6. data/app/models/spree/ability.rb +1 -1
  7. data/app/models/spree/base.rb +3 -1
  8. data/app/models/spree/order_updater.rb +2 -1
  9. data/app/models/spree/price.rb +7 -12
  10. data/app/models/spree/product.rb +3 -2
  11. data/app/models/spree/reimbursement.rb +1 -1
  12. data/app/models/spree/state.rb +2 -0
  13. data/app/models/spree/zone.rb +1 -1
  14. data/lib/spree/core/version.rb +1 -1
  15. data/lib/spree/testing_support/shoulda_matcher_configuration.rb +6 -0
  16. data/script/rails +9 -0
  17. data/spec/fixtures/thinking-cat.jpg +0 -0
  18. data/spec/helpers/base_helper_spec.rb +137 -0
  19. data/spec/helpers/products_helper_spec.rb +224 -0
  20. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  21. data/spec/lib/i18n_spec.rb +123 -0
  22. data/spec/lib/search/base_spec.rb +86 -0
  23. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +101 -0
  24. data/spec/lib/spree/core/controller_helpers/order_spec.rb +95 -0
  25. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  26. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  27. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  28. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
  29. data/spec/lib/spree/core/importer/order_spec.rb +502 -0
  30. data/spec/lib/spree/core/validators/email_spec.rb +53 -0
  31. data/spec/lib/spree/localized_number_spec.rb +38 -0
  32. data/spec/lib/spree/migrations_spec.rb +34 -0
  33. data/spec/lib/spree/money_spec.rb +122 -0
  34. data/spec/lib/tasks/exchanges_spec.rb +136 -0
  35. data/spec/mailers/order_mailer_spec.rb +124 -0
  36. data/spec/mailers/reimbursement_mailer_spec.rb +47 -0
  37. data/spec/mailers/shipment_mailer_spec.rb +63 -0
  38. data/spec/mailers/test_mailer_spec.rb +24 -0
  39. data/spec/models/spree/ability_spec.rb +246 -0
  40. data/spec/models/spree/address_spec.rb +291 -0
  41. data/spec/models/spree/adjustable/adjustments_updater_spec.rb +286 -0
  42. data/spec/models/spree/adjustment_spec.rb +163 -0
  43. data/spec/models/spree/app_configuration_spec.rb +23 -0
  44. data/spec/models/spree/asset_spec.rb +25 -0
  45. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  46. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  47. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  48. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  49. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  50. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  51. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
  52. data/spec/models/spree/calculator/shipping.rb +8 -0
  53. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  54. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  55. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  56. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  57. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +29 -0
  58. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +40 -0
  59. data/spec/models/spree/calculator/tiered_percent_spec.rb +51 -0
  60. data/spec/models/spree/calculator_spec.rb +69 -0
  61. data/spec/models/spree/classification_spec.rb +93 -0
  62. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  63. data/spec/models/spree/country_spec.rb +18 -0
  64. data/spec/models/spree/credit_card_spec.rb +324 -0
  65. data/spec/models/spree/customer_return_spec.rb +262 -0
  66. data/spec/models/spree/exchange_spec.rb +75 -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 +54 -0
  70. data/spec/models/spree/image_spec.rb +5 -0
  71. data/spec/models/spree/inventory_unit_spec.rb +242 -0
  72. data/spec/models/spree/line_item_spec.rb +267 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +13 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +29 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +764 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +117 -0
  81. data/spec/models/spree/order/helpers_spec.rb +5 -0
  82. data/spec/models/spree/order/payment_spec.rb +214 -0
  83. data/spec/models/spree/order/risk_assessment_spec.rb +84 -0
  84. data/spec/models/spree/order/shipments_spec.rb +43 -0
  85. data/spec/models/spree/order/state_machine_spec.rb +216 -0
  86. data/spec/models/spree/order/tax_spec.rb +84 -0
  87. data/spec/models/spree/order/totals_spec.rb +24 -0
  88. data/spec/models/spree/order/updating_spec.rb +18 -0
  89. data/spec/models/spree/order/validations_spec.rb +15 -0
  90. data/spec/models/spree/order_contents_spec.rb +256 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_merger_spec.rb +133 -0
  93. data/spec/models/spree/order_spec.rb +954 -0
  94. data/spec/models/spree/order_updater_spec.rb +283 -0
  95. data/spec/models/spree/payment/gateway_options_spec.rb +119 -0
  96. data/spec/models/spree/payment_method_spec.rb +95 -0
  97. data/spec/models/spree/payment_spec.rb +926 -0
  98. data/spec/models/spree/preference_spec.rb +80 -0
  99. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  100. data/spec/models/spree/preferences/preferable_spec.rb +348 -0
  101. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  102. data/spec/models/spree/preferences/store_spec.rb +46 -0
  103. data/spec/models/spree/price_spec.rb +42 -0
  104. data/spec/models/spree/product/scopes_spec.rb +148 -0
  105. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  106. data/spec/models/spree/product_filter_spec.rb +26 -0
  107. data/spec/models/spree/product_option_type_spec.rb +5 -0
  108. data/spec/models/spree/product_property_spec.rb +11 -0
  109. data/spec/models/spree/product_spec.rb +474 -0
  110. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +50 -0
  111. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +148 -0
  112. data/spec/models/spree/promotion/actions/create_line_items_spec.rb +86 -0
  113. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +36 -0
  114. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  115. data/spec/models/spree/promotion/rules/item_total_spec.rb +282 -0
  116. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  117. data/spec/models/spree/promotion/rules/option_value_spec.rb +90 -0
  118. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  119. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  120. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  121. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  122. data/spec/models/spree/promotion_action_spec.rb +10 -0
  123. data/spec/models/spree/promotion_category_spec.rb +17 -0
  124. data/spec/models/spree/promotion_handler/cart_spec.rb +102 -0
  125. data/spec/models/spree/promotion_handler/coupon_spec.rb +323 -0
  126. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +48 -0
  127. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  128. data/spec/models/spree/promotion_rule_spec.rb +29 -0
  129. data/spec/models/spree/promotion_spec.rb +603 -0
  130. data/spec/models/spree/property_spec.rb +5 -0
  131. data/spec/models/spree/prototype_spec.rb +5 -0
  132. data/spec/models/spree/refund_spec.rb +195 -0
  133. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  134. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  135. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  136. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  137. data/spec/models/spree/reimbursement_spec.rb +215 -0
  138. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  139. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  140. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  141. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +55 -0
  142. data/spec/models/spree/return_authorization_spec.rb +250 -0
  143. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  144. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  145. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +61 -0
  146. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  147. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  148. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  149. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  150. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  151. data/spec/models/spree/return_item_spec.rb +682 -0
  152. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  153. data/spec/models/spree/shipment_spec.rb +740 -0
  154. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  155. data/spec/models/spree/shipping_category_spec.rb +5 -0
  156. data/spec/models/spree/shipping_method_spec.rb +88 -0
  157. data/spec/models/spree/shipping_rate_spec.rb +141 -0
  158. data/spec/models/spree/state_spec.rb +18 -0
  159. data/spec/models/spree/stock/availability_validator_spec.rb +36 -0
  160. data/spec/models/spree/stock/content_item_spec.rb +22 -0
  161. data/spec/models/spree/stock/coordinator_spec.rb +51 -0
  162. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  163. data/spec/models/spree/stock/estimator_spec.rb +154 -0
  164. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  165. data/spec/models/spree/stock/package_spec.rb +194 -0
  166. data/spec/models/spree/stock/packer_spec.rb +70 -0
  167. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  168. data/spec/models/spree/stock/quantifier_spec.rb +97 -0
  169. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  170. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  171. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +47 -0
  172. data/spec/models/spree/stock/splitter/weight_spec.rb +32 -0
  173. data/spec/models/spree/stock_item_spec.rb +410 -0
  174. data/spec/models/spree/stock_location_spec.rb +243 -0
  175. data/spec/models/spree/stock_movement_spec.rb +56 -0
  176. data/spec/models/spree/stock_transfer_spec.rb +50 -0
  177. data/spec/models/spree/store_spec.rb +50 -0
  178. data/spec/models/spree/tax_category_spec.rb +27 -0
  179. data/spec/models/spree/tax_rate_spec.rb +382 -0
  180. data/spec/models/spree/taxon_spec.rb +74 -0
  181. data/spec/models/spree/taxonomy_spec.rb +18 -0
  182. data/spec/models/spree/tracker_spec.rb +21 -0
  183. data/spec/models/spree/user_spec.rb +130 -0
  184. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +24 -0
  185. data/spec/models/spree/variant_spec.rb +523 -0
  186. data/spec/models/spree/zone_spec.rb +444 -0
  187. data/spec/spec_helper.rb +74 -0
  188. data/spec/support/big_decimal.rb +5 -0
  189. data/spec/support/concerns/adjustment_source_spec.rb +23 -0
  190. data/spec/support/concerns/default_price_spec.rb +28 -0
  191. data/spec/support/rake.rb +13 -0
  192. data/spec/support/test_gateway.rb +2 -0
  193. data/spree_core.gemspec +48 -0
  194. metadata +185 -4
@@ -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,133 @@
1
+ require 'spec_helper'
2
+
3
+ # Regression tests for #2179
4
+ module Spree
5
+ describe OrderMerger, type: :model do
6
+ let(:variant) { create(:variant) }
7
+ let(:order_1) { Spree::Order.create }
8
+ let(:order_2) { Spree::Order.create }
9
+ let(:user) { stub_model(Spree::LegacyUser, email: "spree@example.com") }
10
+ let(:subject) { Spree::OrderMerger.new(order_1) }
11
+
12
+ it "destroys the other order" do
13
+ subject.merge!(order_2)
14
+ expect { order_2.reload }.to raise_error(ActiveRecord::RecordNotFound)
15
+ end
16
+
17
+ it "persist the merge" do
18
+ expect(subject).to receive(:persist_merge)
19
+ subject.merge!(order_2)
20
+ end
21
+
22
+ context "user is provided" do
23
+ it "assigns user to new order" do
24
+ subject.merge!(order_2, user)
25
+ expect(order_1.user).to eq user
26
+ end
27
+ end
28
+
29
+ context "merging together two orders with line items for the same variant" do
30
+ before do
31
+ order_1.contents.add(variant, 1)
32
+ order_2.contents.add(variant, 1)
33
+ end
34
+
35
+ specify do
36
+ subject.merge!(order_2, user)
37
+ expect(order_1.line_items.count).to eq(1)
38
+
39
+ line_item = order_1.line_items.first
40
+ expect(line_item.quantity).to eq(2)
41
+ expect(line_item.variant_id).to eq(variant.id)
42
+ end
43
+ end
44
+
45
+ context "merging using extension-specific line_item_comparison_hooks" do
46
+ before do
47
+ Spree::Order.register_line_item_comparison_hook(:foos_match)
48
+ allow(Spree::Variant).to receive(:price_modifier_amount).and_return(0.00)
49
+ end
50
+
51
+ after do
52
+ # reset to avoid test pollution
53
+ Spree::Order.line_item_comparison_hooks = Set.new
54
+ end
55
+
56
+ context "2 equal line items" do
57
+ before do
58
+ @line_item_1 = order_1.contents.add(variant, 1, foos: {})
59
+ @line_item_2 = order_2.contents.add(variant, 1, foos: {})
60
+ end
61
+
62
+ specify do
63
+ expect(order_1).to receive(:foos_match).with(@line_item_1, kind_of(Hash)).and_return(true)
64
+ subject.merge!(order_2)
65
+ expect(order_1.line_items.count).to eq(1)
66
+
67
+ line_item = order_1.line_items.first
68
+ expect(line_item.quantity).to eq(2)
69
+ expect(line_item.variant_id).to eq(variant.id)
70
+ end
71
+ end
72
+
73
+ context "2 different line items" do
74
+ before do
75
+ allow(order_1).to receive(:foos_match).and_return(false)
76
+
77
+ order_1.contents.add(variant, 1, foos: {})
78
+ order_2.contents.add(variant, 1, foos: { bar: :zoo })
79
+ end
80
+
81
+ specify do
82
+ subject.merge!(order_2)
83
+ expect(order_1.line_items.count).to eq(2)
84
+
85
+ line_item = order_1.line_items.first
86
+ expect(line_item.quantity).to eq(1)
87
+ expect(line_item.variant_id).to eq(variant.id)
88
+
89
+ line_item = order_1.line_items.last
90
+ expect(line_item.quantity).to eq(1)
91
+ expect(line_item.variant_id).to eq(variant.id)
92
+ end
93
+ end
94
+ end
95
+
96
+ context "merging together two orders with different line items" do
97
+ let(:variant_2) { create(:variant) }
98
+
99
+ before do
100
+ order_1.contents.add(variant, 1)
101
+ order_2.contents.add(variant_2, 1)
102
+ end
103
+
104
+ specify do
105
+ subject.merge!(order_2)
106
+ line_items = order_1.line_items.reload
107
+ expect(line_items.count).to eq(2)
108
+
109
+ expect(order_1.item_count).to eq 2
110
+ expect(order_1.item_total).to eq line_items.map(&:amount).sum
111
+
112
+ # No guarantee on ordering of line items, so we do this:
113
+ expect(line_items.pluck(:quantity)).to match_array([1, 1])
114
+ expect(line_items.pluck(:variant_id)).to match_array([variant.id, variant_2.id])
115
+ end
116
+ end
117
+
118
+ context "merging together orders with invalid line items" do
119
+ let(:variant_2) { create(:variant) }
120
+
121
+ before do
122
+ order_1.contents.add(variant, 1)
123
+ order_2.contents.add(variant_2, 1)
124
+ end
125
+
126
+ it "should create errors with invalid line items" do
127
+ variant_2.destroy
128
+ subject.merge!(order_2)
129
+ expect(order_1.errors.full_messages).not_to be_empty
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,954 @@
1
+ require 'spec_helper'
2
+
3
+ class FakeCalculator < Spree::Calculator
4
+ def compute(computable)
5
+ 5
6
+ end
7
+ end
8
+
9
+ describe Spree::Order, :type => :model do
10
+ let(:user) { stub_model(Spree::LegacyUser, :email => "spree@example.com") }
11
+ let(:order) { stub_model(Spree::Order, :user => user) }
12
+
13
+ before do
14
+ create(:store)
15
+ allow(Spree::LegacyUser).to receive_messages(:current => mock_model(Spree::LegacyUser, :id => 123))
16
+ end
17
+
18
+ context "#cancel" do
19
+ let(:order) { create(:completed_order_with_totals) }
20
+ let!(:payment) do
21
+ create(
22
+ :payment,
23
+ order: order,
24
+ amount: order.total,
25
+ state: "completed"
26
+ )
27
+ end
28
+ let(:payment_method) { double }
29
+
30
+ it "should mark the payments as void" do
31
+ allow_any_instance_of(Spree::Shipment).to receive(:refresh_rates).and_return(true)
32
+ order.cancel
33
+ order.reload
34
+
35
+ expect(order.payments.first).to be_void
36
+ end
37
+ end
38
+
39
+ context "#canceled_by" do
40
+ let(:admin_user) { create :admin_user }
41
+ let(:order) { create :order }
42
+
43
+ before do
44
+ allow(order).to receive(:cancel!)
45
+ end
46
+
47
+ subject { order.canceled_by(admin_user) }
48
+
49
+ it 'should cancel the order' do
50
+ expect(order).to receive(:cancel!)
51
+ subject
52
+ end
53
+
54
+ it 'should save canceler_id' do
55
+ subject
56
+ expect(order.reload.canceler_id).to eq(admin_user.id)
57
+ end
58
+
59
+ it 'should save canceled_at' do
60
+ subject
61
+ expect(order.reload.canceled_at).to_not be_nil
62
+ end
63
+
64
+ it 'should have canceler' do
65
+ subject
66
+ expect(order.reload.canceler).to eq(admin_user)
67
+ end
68
+ end
69
+
70
+ context "#create" do
71
+ let(:order) { Spree::Order.create }
72
+
73
+ it "should assign an order number" do
74
+ expect(order.number).not_to be_nil
75
+ end
76
+
77
+ it 'should create a randomized 22 character token' do
78
+ expect(order.guest_token.size).to eq(22)
79
+ end
80
+ end
81
+
82
+ context "creates shipments cost" do
83
+ let(:shipment) { double }
84
+
85
+ before { allow(order).to receive_messages shipments: [shipment] }
86
+
87
+ it "update and persist totals" do
88
+ expect(shipment).to receive :update_amounts
89
+ expect(order.updater).to receive :update_shipment_total
90
+ expect(order.updater).to receive :persist_totals
91
+
92
+ order.set_shipments_cost
93
+ end
94
+ end
95
+
96
+ context "#finalize!" do
97
+ let(:order) { Spree::Order.create(email: 'test@example.com') }
98
+
99
+ before do
100
+ order.update_column :state, 'complete'
101
+ end
102
+
103
+ it "should set completed_at" do
104
+ expect(order).to receive(:touch).with(:completed_at)
105
+ order.finalize!
106
+ end
107
+
108
+ it "should sell inventory units" do
109
+ order.shipments.each do |shipment|
110
+ expect(shipment).to receive(:update!)
111
+ expect(shipment).to receive(:finalize!)
112
+ end
113
+ order.finalize!
114
+ end
115
+
116
+ it "should decrease the stock for each variant in the shipment" do
117
+ order.shipments.each do |shipment|
118
+ expect(shipment.stock_location).to receive(:decrease_stock_for_variant)
119
+ end
120
+ order.finalize!
121
+ end
122
+
123
+ it "should change the shipment state to ready if order is paid" do
124
+ Spree::Shipment.create(order: order, stock_location: create(:stock_location))
125
+ order.shipments.reload
126
+
127
+ allow(order).to receive_messages(paid?: true, complete?: true)
128
+ order.finalize!
129
+ order.reload # reload so we're sure the changes are persisted
130
+ expect(order.shipment_state).to eq('ready')
131
+ end
132
+
133
+ after { Spree::Config.set track_inventory_levels: true }
134
+ it "should not sell inventory units if track_inventory_levels is false" do
135
+ Spree::Config.set track_inventory_levels: false
136
+ expect(Spree::InventoryUnit).not_to receive(:sell_units)
137
+ order.finalize!
138
+ end
139
+
140
+ it "should send an order confirmation email" do
141
+ mail_message = double "Mail::Message"
142
+ expect(Spree::OrderMailer).to receive(:confirm_email).with(order.id).and_return mail_message
143
+ expect(mail_message).to receive :deliver_later
144
+ order.finalize!
145
+ end
146
+
147
+ it "sets confirmation delivered when finalizing" do
148
+ expect(order.confirmation_delivered?).to be false
149
+ order.finalize!
150
+ expect(order.confirmation_delivered?).to be true
151
+ end
152
+
153
+ it "should not send duplicate confirmation emails" do
154
+ allow(order).to receive_messages(:confirmation_delivered? => true)
155
+ expect(Spree::OrderMailer).not_to receive(:confirm_email)
156
+ order.finalize!
157
+ end
158
+
159
+ it "should freeze all adjustments" do
160
+ # Stub this method as it's called due to a callback
161
+ # and it's irrelevant to this test
162
+ allow(order).to receive :has_available_shipment
163
+ allow(Spree::OrderMailer).to receive_message_chain :confirm_email, :deliver_later
164
+ adjustments = [double]
165
+ expect(order).to receive(:all_adjustments).and_return(adjustments)
166
+ adjustments.each do |adj|
167
+ expect(adj).to receive(:close)
168
+ end
169
+ order.finalize!
170
+ end
171
+
172
+ context "order is considered risky" do
173
+ before do
174
+ allow(order).to receive_messages :is_risky? => true
175
+ end
176
+
177
+ it "should change state to risky" do
178
+ expect(order).to receive(:considered_risky!)
179
+ order.finalize!
180
+ end
181
+
182
+ context "and order is approved" do
183
+ before do
184
+ allow(order).to receive_messages :approved? => true
185
+ end
186
+
187
+ it "should leave order in complete state" do
188
+ order.finalize!
189
+ expect(order.state).to eq 'complete'
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ context "insufficient_stock_lines" do
196
+ let(:line_item) { mock_model Spree::LineItem, :insufficient_stock? => true }
197
+
198
+ before { allow(order).to receive_messages(:line_items => [line_item]) }
199
+
200
+ it "should return line_item that has insufficient stock on hand" do
201
+ expect(order.insufficient_stock_lines.size).to eq(1)
202
+ expect(order.insufficient_stock_lines.include?(line_item)).to be true
203
+ end
204
+ end
205
+
206
+ describe '#ensure_line_item_variants_are_not_deleted' do
207
+ subject { order.ensure_line_item_variants_are_not_deleted }
208
+
209
+ let(:order) { create :order_with_line_items }
210
+
211
+ context 'when variant is destroyed' do
212
+ before do
213
+ allow(order).to receive(:restart_checkout_flow)
214
+ order.line_items.first.variant.destroy
215
+ end
216
+
217
+ it 'should restart checkout flow' do
218
+ expect(order).to receive(:restart_checkout_flow).once
219
+ subject
220
+ end
221
+
222
+ it 'should have error message' do
223
+ subject
224
+ expect(order.errors[:base]).to include(Spree.t(:deleted_variants_present))
225
+ end
226
+
227
+ it 'should be false' do
228
+ expect(subject).to be_falsey
229
+ end
230
+ end
231
+
232
+ context 'when no variants are destroyed' do
233
+ it 'should not restart checkout' do
234
+ expect(order).to receive(:restart_checkout_flow).never
235
+ subject
236
+ end
237
+
238
+ it 'should be true' do
239
+ expect(subject).to be_truthy
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '#ensure_line_items_are_in_stock' do
245
+ subject { order.ensure_line_items_are_in_stock }
246
+
247
+ let(:line_item) { mock_model Spree::LineItem, :insufficient_stock? => true }
248
+
249
+ before do
250
+ allow(order).to receive(:restart_checkout_flow)
251
+ allow(order).to receive_messages(:line_items => [line_item])
252
+ end
253
+
254
+ it 'should restart checkout flow' do
255
+ expect(order).to receive(:restart_checkout_flow).once
256
+ subject
257
+ end
258
+
259
+ it 'should have error message' do
260
+ subject
261
+ expect(order.errors[:base]).to include(Spree.t(:insufficient_stock_lines_present))
262
+ end
263
+
264
+ it 'should be false' do
265
+ expect(subject).to be_falsey
266
+ end
267
+ end
268
+
269
+ context "empty!" do
270
+ let(:order) { stub_model(Spree::Order, item_count: 2) }
271
+
272
+ before do
273
+ allow(order).to receive_messages(line_items: [1, 2])
274
+ allow(order).to receive_messages(adjustments: [])
275
+ allow(order).to receive_message_chain(:line_items, sum: 0)
276
+ end
277
+
278
+ it "clears out line items, adjustments and update totals" do
279
+ expect(order.line_items).to receive(:destroy_all)
280
+ expect(order.adjustments).to receive(:destroy_all)
281
+ expect(order.shipments).to receive(:destroy_all)
282
+ expect(order.updater).to receive(:update_totals)
283
+ expect(order.updater).to receive(:persist_totals)
284
+
285
+ order.empty!
286
+ expect(order.item_total).to eq 0
287
+ end
288
+ end
289
+
290
+ context "#display_outstanding_balance" do
291
+ it "returns the value as a spree money" do
292
+ allow(order).to receive(:outstanding_balance) { 10.55 }
293
+ expect(order.display_outstanding_balance).to eq(Spree::Money.new(10.55))
294
+ end
295
+ end
296
+
297
+ context "#display_item_total" do
298
+ it "returns the value as a spree money" do
299
+ allow(order).to receive(:item_total) { 10.55 }
300
+ expect(order.display_item_total).to eq(Spree::Money.new(10.55))
301
+ end
302
+ end
303
+
304
+ context "#display_adjustment_total" do
305
+ it "returns the value as a spree money" do
306
+ order.adjustment_total = 10.55
307
+ expect(order.display_adjustment_total).to eq(Spree::Money.new(10.55))
308
+ end
309
+ end
310
+
311
+ context "#display_promo_total" do
312
+ it "returns the value as a spree money" do
313
+ order.promo_total = 10.55
314
+ expect(order.display_promo_total).to eq(Spree::Money.new(10.55))
315
+ end
316
+ end
317
+
318
+ context "#display_total" do
319
+ it "returns the value as a spree money" do
320
+ order.total = 10.55
321
+ expect(order.display_total).to eq(Spree::Money.new(10.55))
322
+ end
323
+ end
324
+
325
+ context "#currency" do
326
+ context "when object currency is ABC" do
327
+ before { order.currency = "ABC" }
328
+
329
+ it "returns the currency from the object" do
330
+ expect(order.currency).to eq("ABC")
331
+ end
332
+ end
333
+
334
+ context "when object currency is nil" do
335
+ before { order.currency = nil }
336
+
337
+ it "returns the globally configured currency" do
338
+ expect(order.currency).to eq("USD")
339
+ end
340
+ end
341
+ end
342
+
343
+ context "#confirmation_required?" do
344
+
345
+ # Regression test for #4117
346
+ it "is required if the state is currently 'confirm'" do
347
+ order = Spree::Order.new
348
+ assert !order.confirmation_required?
349
+ order.state = 'confirm'
350
+ assert order.confirmation_required?
351
+ end
352
+
353
+ context 'Spree::Config[:always_include_confirm_step] == true' do
354
+
355
+ before do
356
+ Spree::Config[:always_include_confirm_step] = true
357
+ end
358
+
359
+ it "returns true if payments empty" do
360
+ order = Spree::Order.new
361
+ assert order.confirmation_required?
362
+ end
363
+ end
364
+
365
+ context 'Spree::Config[:always_include_confirm_step] == false' do
366
+
367
+ it "returns false if payments empty and Spree::Config[:always_include_confirm_step] == false" do
368
+ order = Spree::Order.new
369
+ assert !order.confirmation_required?
370
+ end
371
+
372
+ it "does not bomb out when an order has an unpersisted payment" do
373
+ order = Spree::Order.new
374
+ order.payments.build
375
+ assert !order.confirmation_required?
376
+ end
377
+ end
378
+ end
379
+
380
+
381
+ context "add_update_hook" do
382
+ before do
383
+ Spree::Order.class_eval do
384
+ register_update_hook :add_awesome_sauce
385
+ end
386
+ end
387
+
388
+ after do
389
+ Spree::Order.update_hooks = Set.new
390
+ end
391
+
392
+ it "calls hook during update" do
393
+ order = create(:order)
394
+ expect(order).to receive(:add_awesome_sauce)
395
+ order.update!
396
+ end
397
+
398
+ it "calls hook during finalize" do
399
+ order = create(:order)
400
+ expect(order).to receive(:add_awesome_sauce)
401
+ order.finalize!
402
+ end
403
+ end
404
+
405
+ describe "#tax_address" do
406
+ before { Spree::Config[:tax_using_ship_address] = tax_using_ship_address }
407
+ subject { order.tax_address }
408
+
409
+ context "when tax_using_ship_address is true" do
410
+ let(:tax_using_ship_address) { true }
411
+
412
+ it 'returns ship_address' do
413
+ expect(subject).to eq(order.ship_address)
414
+ end
415
+ end
416
+
417
+ context "when tax_using_ship_address is not true" do
418
+ let(:tax_using_ship_address) { false }
419
+
420
+ it "returns bill_address" do
421
+ expect(subject).to eq(order.bill_address)
422
+ end
423
+ end
424
+ end
425
+
426
+ describe "#restart_checkout_flow" do
427
+ it "updates the state column to the first checkout_steps value" do
428
+ order = create(:order_with_totals, state: "delivery")
429
+ expect(order.checkout_steps).to eql ["address", "delivery", "complete"]
430
+ expect{ order.restart_checkout_flow }.to change{order.state}.from("delivery").to("address")
431
+ end
432
+
433
+ context "without line items" do
434
+ it "updates the state column to cart" do
435
+ order = create(:order, state: "delivery")
436
+ expect{ order.restart_checkout_flow }.to change{order.state}.from("delivery").to("cart")
437
+ end
438
+ end
439
+ end
440
+
441
+ # Regression tests for #4072
442
+ context "#state_changed" do
443
+ let(:order) { FactoryGirl.create(:order) }
444
+
445
+ it "logs state changes" do
446
+ order.update_column(:payment_state, 'balance_due')
447
+ order.payment_state = 'paid'
448
+ expect(order.state_changes).to be_empty
449
+ order.state_changed('payment')
450
+ state_change = order.state_changes.find_by(:name => 'payment')
451
+ expect(state_change.previous_state).to eq('balance_due')
452
+ expect(state_change.next_state).to eq('paid')
453
+ end
454
+
455
+ it "does not do anything if state does not change" do
456
+ order.update_column(:payment_state, 'balance_due')
457
+ expect(order.state_changes).to be_empty
458
+ order.state_changed('payment')
459
+ expect(order.state_changes).to be_empty
460
+ end
461
+ end
462
+
463
+ # Regression test for #4199
464
+ context "#available_payment_methods" do
465
+ it "includes frontend payment methods" do
466
+ payment_method = Spree::PaymentMethod.create!({
467
+ :name => "Fake",
468
+ :active => true,
469
+ :display_on => "front_end",
470
+ })
471
+ expect(order.available_payment_methods).to include(payment_method)
472
+ end
473
+
474
+ it "includes 'both' payment methods" do
475
+ payment_method = Spree::PaymentMethod.create!({
476
+ :name => "Fake",
477
+ :active => true,
478
+ :display_on => "both",
479
+ })
480
+ expect(order.available_payment_methods).to include(payment_method)
481
+ end
482
+
483
+ it "does not include a payment method twice if display_on is blank" do
484
+ payment_method = Spree::PaymentMethod.create!({
485
+ :name => "Fake",
486
+ :active => true,
487
+ :display_on => "both",
488
+ })
489
+ expect(order.available_payment_methods.count).to eq(1)
490
+ expect(order.available_payment_methods).to include(payment_method)
491
+ end
492
+ end
493
+
494
+ context "#apply_free_shipping_promotions" do
495
+ it "calls out to the FreeShipping promotion handler" do
496
+ shipment = double('Shipment')
497
+ allow(order).to receive_messages :shipments => [shipment]
498
+ expect(Spree::PromotionHandler::FreeShipping).to receive(:new).and_return(handler = double)
499
+ expect(handler).to receive(:activate)
500
+
501
+ expect(Spree::Adjustable::AdjustmentsUpdater).to receive(:update).with(shipment)
502
+
503
+ expect(order.updater).to receive(:update_shipment_total)
504
+ expect(order.updater).to receive(:persist_totals)
505
+ order.apply_free_shipping_promotions
506
+ end
507
+ end
508
+
509
+
510
+ context "#products" do
511
+ before :each do
512
+ @variant1 = mock_model(Spree::Variant, :product => "product1")
513
+ @variant2 = mock_model(Spree::Variant, :product => "product2")
514
+ @line_items = [mock_model(Spree::LineItem, :product => "product1", :variant => @variant1, :variant_id => @variant1.id, :quantity => 1),
515
+ mock_model(Spree::LineItem, :product => "product2", :variant => @variant2, :variant_id => @variant2.id, :quantity => 2)]
516
+ allow(order).to receive_messages(:line_items => @line_items)
517
+ end
518
+
519
+ it "gets the quantity of a given variant" do
520
+ expect(order.quantity_of(@variant1)).to eq(1)
521
+
522
+ @variant3 = mock_model(Spree::Variant, :product => "product3")
523
+ expect(order.quantity_of(@variant3)).to eq(0)
524
+ end
525
+
526
+ it "can find a line item matching a given variant" do
527
+ expect(order.find_line_item_by_variant(@variant1)).not_to be_nil
528
+ expect(order.find_line_item_by_variant(mock_model(Spree::Variant))).to be_nil
529
+ end
530
+
531
+ context "match line item with options" do
532
+ before do
533
+ Spree::Order.register_line_item_comparison_hook(:foos_match)
534
+ end
535
+
536
+ after do
537
+ # reset to avoid test pollution
538
+ Spree::Order.line_item_comparison_hooks = Set.new
539
+ end
540
+
541
+ it "matches line item when options match" do
542
+ allow(order).to receive(:foos_match).and_return(true)
543
+ expect(order.line_item_options_match(@line_items.first, {foos: {bar: :zoo}})).to be true
544
+ end
545
+
546
+ it "does not match line item without options" do
547
+ allow(order).to receive(:foos_match).and_return(false)
548
+ expect(order.line_item_options_match(@line_items.first, {})).to be false
549
+ end
550
+ end
551
+ end
552
+
553
+ context "#generate_order_number" do
554
+ context "when no configure" do
555
+ let(:default_length) { 9 + 'R'.length }
556
+ subject(:order_number) { order.generate_number }
557
+ its(:class) { should eq String }
558
+ its(:length) { should eq default_length }
559
+ it { should match /^R/ }
560
+ end
561
+
562
+ context "when length option is 5" do
563
+ let(:option_length) { 5 + 'R'.length }
564
+ it "should be option length for order number" do
565
+ expect(order.generate_number(length: 5).length).to eq option_length
566
+ end
567
+ end
568
+
569
+ context "when letters option is true" do
570
+ it "generates order number include letter" do
571
+ expect(order.generate_number(length: 100, letters: true)).to match /[A-Z]/
572
+ end
573
+ end
574
+
575
+ context "when prefix option is 'P'" do
576
+ it "generates order number and it prefix is 'P'" do
577
+ expect(order.generate_number(prefix: 'P')).to match /^P/
578
+ end
579
+ end
580
+ end
581
+
582
+ describe "#associate_user!" do
583
+ let(:user) { FactoryGirl.create(:user_with_addreses) }
584
+ let(:email) { user.email }
585
+ let(:created_by) { user }
586
+ let(:bill_address) { user.bill_address }
587
+ let(:ship_address) { user.ship_address }
588
+ let(:override_email) { true }
589
+
590
+ let(:order) { FactoryGirl.build(:order, order_attributes) }
591
+
592
+ let(:order_attributes) do
593
+ {
594
+ user: nil,
595
+ email: nil,
596
+ created_by: nil,
597
+ bill_address: nil,
598
+ ship_address: nil
599
+ }
600
+ end
601
+
602
+ def assert_expected_order_state
603
+ expect(order.user).to eql(user)
604
+ expect(order.user_id).to eql(user.id)
605
+
606
+ expect(order.email).to eql(email)
607
+
608
+ expect(order.created_by).to eql(created_by)
609
+ expect(order.created_by_id).to eql(created_by.id)
610
+
611
+ expect(order.bill_address).to eql(bill_address)
612
+ expect(order.bill_address_id).to eql(bill_address.id)
613
+
614
+ expect(order.ship_address).to eql(ship_address)
615
+ expect(order.ship_address_id).to eql(ship_address.id)
616
+ end
617
+
618
+ shared_examples_for "#associate_user!" do |persisted = false|
619
+ it "associates a user to an order" do
620
+ order.associate_user!(user, override_email)
621
+ assert_expected_order_state
622
+ end
623
+
624
+ unless persisted
625
+ it "does not persist the order" do
626
+ expect { order.associate_user!(user) }
627
+ .to_not change(order, :persisted?)
628
+ .from(false)
629
+ end
630
+ end
631
+ end
632
+
633
+ context "when email is set" do
634
+ let(:order_attributes) { super().merge(email: 'test@example.com') }
635
+
636
+ context "when email should be overridden" do
637
+ it_should_behave_like "#associate_user!"
638
+ end
639
+
640
+ context "when email should not be overridden" do
641
+ let(:override_email) { false }
642
+ let(:email) { 'test@example.com' }
643
+
644
+ it_should_behave_like "#associate_user!"
645
+ end
646
+ end
647
+
648
+ context "when created_by is set" do
649
+ let(:order_attributes) { super().merge(created_by: created_by) }
650
+ let(:created_by) { create(:user_with_addreses) }
651
+
652
+ it_should_behave_like "#associate_user!"
653
+ end
654
+
655
+ context "when bill_address is set" do
656
+ let(:order_attributes) { super().merge(bill_address: bill_address) }
657
+ let(:bill_address) { FactoryGirl.build(:address) }
658
+
659
+ it_should_behave_like "#associate_user!"
660
+ end
661
+
662
+ context "when ship_address is set" do
663
+ let(:order_attributes) { super().merge(ship_address: ship_address) }
664
+ let(:ship_address) { FactoryGirl.build(:address) }
665
+
666
+ it_should_behave_like "#associate_user!"
667
+ end
668
+
669
+ context "when the user is not persisted" do
670
+ let(:user) { FactoryGirl.build(:user_with_addreses) }
671
+
672
+ it "does not persist the user" do
673
+ expect { order.associate_user!(user) }
674
+ .to_not change(user, :persisted?)
675
+ .from(false)
676
+ end
677
+
678
+ it_should_behave_like "#associate_user!"
679
+ end
680
+
681
+ context "when the order is persisted" do
682
+ let(:order) { FactoryGirl.create(:order, order_attributes) }
683
+
684
+ it "associates a user to a persisted order" do
685
+ order.associate_user!(user)
686
+ order.reload
687
+ assert_expected_order_state
688
+ end
689
+
690
+ it "does not persist other changes to the order" do
691
+ order.state = 'complete'
692
+ order.associate_user!(user)
693
+ order.reload
694
+ expect(order.state).to eql('cart')
695
+ end
696
+
697
+ it "does not change any other orders" do
698
+ other = FactoryGirl.create(:order)
699
+ order.associate_user!(user)
700
+ expect(other.reload.user).to_not eql(user)
701
+ end
702
+
703
+ it "is not affected by scoping" do
704
+ order.class.where.not(id: order).scoping do
705
+ order.associate_user!(user)
706
+ end
707
+ order.reload
708
+ assert_expected_order_state
709
+ end
710
+
711
+ it_should_behave_like "#associate_user!", true
712
+ end
713
+ end
714
+
715
+ context "#can_ship?" do
716
+ let(:order) { Spree::Order.create }
717
+
718
+ it "should be true for order in the 'complete' state" do
719
+ allow(order).to receive_messages(:complete? => true)
720
+ expect(order.can_ship?).to be true
721
+ end
722
+
723
+ it "should be true for order in the 'resumed' state" do
724
+ allow(order).to receive_messages(:resumed? => true)
725
+ expect(order.can_ship?).to be true
726
+ end
727
+
728
+ it "should be true for an order in the 'awaiting return' state" do
729
+ allow(order).to receive_messages(:awaiting_return? => true)
730
+ expect(order.can_ship?).to be true
731
+ end
732
+
733
+ it "should be true for an order in the 'returned' state" do
734
+ allow(order).to receive_messages(:returned? => true)
735
+ expect(order.can_ship?).to be true
736
+ end
737
+
738
+ it "should be false if the order is neither in the 'complete' nor 'resumed' state" do
739
+ allow(order).to receive_messages(:resumed? => false, :complete? => false)
740
+ expect(order.can_ship?).to be false
741
+ end
742
+ end
743
+
744
+ context "#completed?" do
745
+ it "should indicate if order is completed" do
746
+ order.completed_at = nil
747
+ expect(order.completed?).to be false
748
+
749
+ order.completed_at = Time.now
750
+ expect(order.completed?).to be true
751
+ end
752
+ end
753
+
754
+ context "#allow_checkout?" do
755
+ it "should be true if there are line_items in the order" do
756
+ allow(order).to receive_message_chain(:line_items, :count => 1)
757
+ expect(order.checkout_allowed?).to be true
758
+ end
759
+ it "should be false if there are no line_items in the order" do
760
+ allow(order).to receive_message_chain(:line_items, :count => 0)
761
+ expect(order.checkout_allowed?).to be false
762
+ end
763
+ end
764
+
765
+ context "#amount" do
766
+ before do
767
+ @order = create(:order, :user => user)
768
+ @order.line_items = [create(:line_item, :price => 1.0, :quantity => 2),
769
+ create(:line_item, :price => 1.0, :quantity => 1)]
770
+ end
771
+ it "should return the correct lum sum of items" do
772
+ expect(@order.amount).to eq(3.0)
773
+ end
774
+ end
775
+
776
+ context "#backordered?" do
777
+ it 'is backordered if one of the shipments is backordered' do
778
+ allow(order).to receive_messages(:shipments => [mock_model(Spree::Shipment, :backordered? => false),
779
+ mock_model(Spree::Shipment, :backordered? => true)])
780
+ expect(order).to be_backordered
781
+ end
782
+ end
783
+
784
+ context "#can_cancel?" do
785
+ it "should be false for completed order in the canceled state" do
786
+ order.state = 'canceled'
787
+ order.shipment_state = 'ready'
788
+ order.completed_at = Time.now
789
+ expect(order.can_cancel?).to be false
790
+ end
791
+
792
+ it "should be true for completed order with no shipment" do
793
+ order.state = 'complete'
794
+ order.shipment_state = nil
795
+ order.completed_at = Time.now
796
+ expect(order.can_cancel?).to be true
797
+ end
798
+ end
799
+
800
+ context "#tax_total" do
801
+ it "adds included tax and additional tax" do
802
+ allow(order).to receive_messages(:additional_tax_total => 10, :included_tax_total => 20)
803
+
804
+ expect(order.tax_total).to eq 30
805
+ end
806
+ end
807
+
808
+ # Regression test for #4923
809
+ context "locking" do
810
+ let(:order) { Spree::Order.create } # need a persisted in order to test locking
811
+
812
+ it 'can lock' do
813
+ expect { order.with_lock {} }.to_not raise_error
814
+ end
815
+ end
816
+
817
+ describe "#pre_tax_item_amount" do
818
+ it "sums all of the line items' pre tax amounts" do
819
+ subject.line_items = [
820
+ Spree::LineItem.new(price: 10, quantity: 2, pre_tax_amount: 5.0),
821
+ Spree::LineItem.new(price: 30, quantity: 1, pre_tax_amount: 14.0),
822
+ ]
823
+
824
+ expect(subject.pre_tax_item_amount).to eq 19.0
825
+ end
826
+ end
827
+
828
+ describe '#quantity' do
829
+ # Uses a persisted record, as the quantity is retrieved via a DB count
830
+ let(:order) { create :order_with_line_items, line_items_count: 3 }
831
+
832
+ it 'sums the quantity of all line items' do
833
+ expect(order.quantity).to eq 3
834
+ end
835
+ end
836
+
837
+ describe '#has_non_reimbursement_related_refunds?' do
838
+ subject do
839
+ order.has_non_reimbursement_related_refunds?
840
+ end
841
+
842
+ context 'no refunds exist' do
843
+ it { is_expected.to eq false }
844
+ end
845
+
846
+ context 'a non-reimbursement related refund exists' do
847
+ let(:order) { refund.payment.order }
848
+ let(:refund) { create(:refund, reimbursement_id: nil, amount: 5) }
849
+
850
+ it { is_expected.to eq true }
851
+ end
852
+
853
+ context 'an old-style refund exists' do
854
+ let(:order) { create(:order_ready_to_ship) }
855
+ let(:payment) { order.payments.first.tap { |p| allow(p).to receive_messages(profiles_supported: false) } }
856
+ let!(:refund_payment) {
857
+ build(:payment, amount: -1, order: order, state: 'completed', source: payment).tap do |p|
858
+ allow(p).to receive_messages(profiles_supported?: false)
859
+ p.save!
860
+ end
861
+ }
862
+
863
+ it { is_expected.to eq true }
864
+ end
865
+
866
+ context 'a reimbursement related refund exists' do
867
+ let(:order) { refund.payment.order }
868
+ let(:refund) { create(:refund, reimbursement_id: 123, amount: 5)}
869
+
870
+ it { is_expected.to eq false }
871
+ end
872
+ end
873
+
874
+ describe "#create_proposed_shipments" do
875
+ it "assigns the coordinator returned shipments to its shipments" do
876
+ shipment = build(:shipment)
877
+ allow_any_instance_of(Spree::Stock::Coordinator).to receive(:shipments).and_return([shipment])
878
+ subject.create_proposed_shipments
879
+ expect(subject.shipments).to eq [shipment]
880
+ end
881
+ end
882
+
883
+ describe "#all_inventory_units_returned?" do
884
+ let(:order) { create(:order_with_line_items, line_items_count: 3) }
885
+
886
+ subject { order.all_inventory_units_returned? }
887
+
888
+ context "all inventory units are returned" do
889
+ before { order.inventory_units.update_all(state: 'returned') }
890
+
891
+ it "is true" do
892
+ expect(subject).to eq true
893
+ end
894
+ end
895
+
896
+ context "some inventory units are returned" do
897
+ before do
898
+ order.inventory_units.first.update_attribute(:state, 'returned')
899
+ end
900
+
901
+ it "is false" do
902
+ expect(subject).to eq false
903
+ end
904
+ end
905
+
906
+ context "no inventory units are returned" do
907
+ it "is false" do
908
+ expect(subject).to eq false
909
+ end
910
+ end
911
+ end
912
+
913
+ describe "#fully_discounted?" do
914
+ let(:line_item) { Spree::LineItem.new(price: 10, quantity: 1) }
915
+ let(:shipment) { Spree::Shipment.new(cost: 10) }
916
+ let(:payment) { Spree::Payment.new(amount: 10) }
917
+
918
+ before do
919
+ allow(order).to receive(:line_items) { [line_item] }
920
+ allow(order).to receive(:shipments) { [shipment] }
921
+ allow(order).to receive(:payments) { [payment] }
922
+ end
923
+
924
+ context "the order had no inventory-related cost" do
925
+ before do
926
+ # discount the cost of the line items
927
+ allow(order).to receive(:adjustment_total) { -5 }
928
+ allow(line_item).to receive(:adjustment_total) { -5 }
929
+
930
+ # but leave some shipment payment amount
931
+ allow(shipment).to receive(:adjustment_total) { 0 }
932
+ end
933
+
934
+ it { expect(order.fully_discounted?).to eq true }
935
+
936
+ end
937
+
938
+ context "the order had inventory-related cost" do
939
+ before do
940
+ # partially discount the cost of the line item
941
+ allow(order).to receive(:adjustment_total) { 0 }
942
+ allow(line_item).to receive(:adjustment_total) { -5 }
943
+
944
+ # and partially discount the cost of the shipment so the total
945
+ # discount matches the item total for test completeness
946
+ allow(shipment).to receive(:adjustment_total) { -5 }
947
+ end
948
+
949
+ it { expect(order.fully_discounted?).to eq false }
950
+
951
+ end
952
+ end
953
+
954
+ end