spree_core 3.2.9 → 3.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (383) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/concerns/spree/named_type.rb +1 -1
  3. data/app/models/concerns/spree/number_as_param.rb +9 -0
  4. data/app/models/concerns/spree/user_address.rb +4 -2
  5. data/app/models/concerns/spree/user_methods.rb +2 -3
  6. data/app/models/spree/ability.rb +1 -2
  7. data/app/models/spree/address.rb +1 -1
  8. data/app/models/spree/base.rb +2 -0
  9. data/app/models/spree/calculator.rb +1 -1
  10. data/app/models/spree/calculator/percent_per_item.rb +4 -3
  11. data/app/models/spree/calculator/returns/default_refund_amount.rb +16 -14
  12. data/app/models/spree/country.rb +1 -1
  13. data/app/models/spree/credit_card.rb +26 -21
  14. data/app/models/spree/customer_return.rb +2 -2
  15. data/app/models/spree/exchange.rb +7 -2
  16. data/app/models/spree/image.rb +1 -1
  17. data/app/models/spree/inventory_unit.rb +39 -3
  18. data/app/models/spree/line_item.rb +2 -7
  19. data/app/models/spree/option_type.rb +1 -1
  20. data/app/models/spree/option_type_prototype.rb +1 -1
  21. data/app/models/spree/option_value.rb +1 -1
  22. data/app/models/spree/option_value_variant.rb +3 -0
  23. data/app/models/spree/order.rb +82 -81
  24. data/app/models/spree/order/checkout.rb +23 -33
  25. data/app/models/spree/order/currency_updater.rb +1 -1
  26. data/app/models/spree/order/payments.rb +1 -1
  27. data/app/models/spree/order/store_credit.rb +6 -1
  28. data/app/models/spree/order_contents.rb +14 -2
  29. data/app/models/spree/order_inventory.rb +64 -60
  30. data/app/models/spree/payment.rb +8 -13
  31. data/app/models/spree/payment/processing.rb +2 -2
  32. data/app/models/spree/payment_method.rb +4 -2
  33. data/app/models/spree/preference.rb +1 -1
  34. data/app/models/spree/preferences/preferable.rb +1 -1
  35. data/app/models/spree/product.rb +1 -1
  36. data/app/models/spree/product/scopes.rb +2 -2
  37. data/app/models/spree/promotion.rb +2 -2
  38. data/app/models/spree/promotion/rules/option_value.rb +13 -5
  39. data/app/models/spree/promotion_rule_user.rb +1 -1
  40. data/app/models/spree/property_prototype.rb +1 -1
  41. data/app/models/spree/prototype_taxon.rb +1 -1
  42. data/app/models/spree/refund.rb +2 -2
  43. data/app/models/spree/reimbursement.rb +2 -1
  44. data/app/models/spree/reimbursement_type.rb +1 -1
  45. data/app/models/spree/return_authorization.rb +1 -0
  46. data/app/models/spree/return_item.rb +35 -9
  47. data/app/models/spree/role.rb +1 -1
  48. data/app/models/spree/role_user.rb +1 -1
  49. data/app/models/spree/shipment.rb +63 -64
  50. data/app/models/spree/shipment_handler.rb +1 -1
  51. data/app/models/spree/shipping_category.rb +1 -1
  52. data/app/models/spree/shipping_method.rb +12 -10
  53. data/app/models/spree/state_change.rb +1 -1
  54. data/app/models/spree/stock/adjuster.rb +35 -14
  55. data/app/models/spree/stock/availability_validator.rb +1 -1
  56. data/app/models/spree/stock/content_item.rb +9 -8
  57. data/app/models/spree/stock/coordinator.rb +3 -9
  58. data/app/models/spree/stock/estimator.rb +1 -1
  59. data/app/models/spree/stock/inventory_unit_builder.rb +10 -9
  60. data/app/models/spree/stock/package.rb +6 -6
  61. data/app/models/spree/stock/packer.rb +8 -15
  62. data/app/models/spree/stock/prioritizer.rb +13 -10
  63. data/app/models/spree/stock/splitter/weight.rb +62 -10
  64. data/app/models/spree/stock_item.rb +42 -29
  65. data/app/models/spree/stock_location.rb +5 -1
  66. data/app/models/spree/stock_movement.rb +1 -2
  67. data/app/models/spree/stock_transfer.rb +2 -3
  68. data/app/models/spree/store.rb +1 -1
  69. data/app/models/spree/store_credit.rb +9 -6
  70. data/app/models/spree/store_credit_category.rb +1 -1
  71. data/app/models/spree/tax_category.rb +1 -1
  72. data/app/models/spree/tax_rate.rb +1 -1
  73. data/app/models/spree/taxon.rb +5 -2
  74. data/app/models/spree/taxonomy.rb +1 -1
  75. data/app/models/spree/tracker.rb +1 -1
  76. data/app/models/spree/variant.rb +4 -3
  77. data/app/models/spree/zone.rb +19 -21
  78. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +61 -0
  79. data/config/locales/en.yml +7 -6
  80. data/db/migrate/20130807024301_upgrade_adjustments.rb +6 -0
  81. data/db/migrate/20130807024302_rename_adjustment_fields.rb +6 -0
  82. data/db/migrate/20161125065505_add_quantity_to_inventory_units.rb +5 -0
  83. data/db/migrate/20170119122701_add_original_return_item_id_to_spree_inventory_units.rb +29 -0
  84. data/db/migrate/20170315152755_add_unique_index_on_number_to_spree_orders.rb +16 -0
  85. data/db/migrate/20170316154338_add_unique_index_on_number_to_spree_stock_transfer.rb +16 -0
  86. data/db/migrate/20170316205511_add_unique_index_on_number_to_spree_shipment.rb +16 -0
  87. data/db/migrate/20170320134043_add_unique_index_on_number_to_spree_payments.rb +17 -0
  88. data/db/migrate/20170320142750_add_unique_index_on_number_to_spree_return_authorizations.rb +16 -0
  89. data/db/migrate/20170320145040_add_unique_index_on_number_to_spree_customer_returns.rb +16 -0
  90. data/db/migrate/20170320145518_add_unique_index_on_number_to_spree_reimbursements.rb +16 -0
  91. data/db/migrate/20170323151450_add_missing_unique_indexes_for_unique_attributes.rb +37 -0
  92. data/db/migrate/20170329110859_add_index_on_stock_location_to_spree_customer_returns.rb +5 -0
  93. data/db/migrate/20170329113917_add_index_on_prototype_to_spree_option_type_prototype.rb +19 -0
  94. data/db/migrate/20170330082155_add_indexes_to_spree_option_value_variant.rb +19 -0
  95. data/db/migrate/20170330132215_add_index_on_promotion_id_to_order_promotions.rb +5 -0
  96. data/db/migrate/20170331101758_add_indexes_for_property_prototype.rb +20 -0
  97. data/db/migrate/20170331103334_add_index_for_prototype_id_to_prototype_taxons.rb +5 -0
  98. data/db/migrate/20170331110454_add_indexes_to_refunds.rb +6 -0
  99. data/db/migrate/20170331111757_add_indexes_to_reimbursement_credits.rb +6 -0
  100. data/db/migrate/20170331115246_add_indexes_to_return_authorizations.rb +6 -0
  101. data/db/migrate/20170331120125_add_indexes_to_return_items.rb +11 -0
  102. data/db/migrate/20170331121725_add_index_to_role_users.rb +18 -0
  103. data/db/migrate/20170331123625_add_index_to_shipping_method_categories.rb +5 -0
  104. data/db/migrate/20170331123832_add_index_to_shipping_method_zones.rb +20 -0
  105. data/db/migrate/20170331124251_add_index_to_spree_shipping_rates.rb +6 -0
  106. data/db/migrate/20170331124513_add_index_to_spree_stock_items.rb +5 -0
  107. data/db/migrate/20170331124924_add_index_to_spree_stock_movement.rb +5 -0
  108. data/db/migrate/20170413211707_change_indexes_on_friendly_id_slugs.rb +10 -0
  109. data/lib/generators/spree/install/install_generator.rb +41 -36
  110. data/lib/spree/core.rb +0 -1
  111. data/lib/spree/core/engine.rb +3 -3
  112. data/lib/spree/core/importer/order.rb +23 -19
  113. data/lib/spree/core/number_generator.rb +3 -5
  114. data/lib/spree/core/product_duplicator.rb +7 -3
  115. data/lib/spree/core/search/base.rb +1 -0
  116. data/lib/spree/core/validators/email.rb +1 -1
  117. data/lib/spree/core/version.rb +1 -1
  118. data/lib/spree/money.rb +1 -15
  119. data/lib/spree/permitted_attributes.rb +1 -1
  120. data/lib/spree/testing_support/common_rake.rb +0 -2
  121. data/lib/spree/testing_support/factories.rb +3 -3
  122. data/lib/spree/testing_support/factories/address_factory.rb +1 -1
  123. data/lib/spree/testing_support/factories/adjustment_factory.rb +1 -1
  124. data/lib/spree/testing_support/factories/calculator_factory.rb +1 -1
  125. data/lib/spree/testing_support/factories/country_factory.rb +1 -1
  126. data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
  127. data/lib/spree/testing_support/factories/customer_return_factory.rb +1 -1
  128. data/lib/spree/testing_support/factories/image_factory.rb +1 -1
  129. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -1
  130. data/lib/spree/testing_support/factories/line_item_factory.rb +1 -1
  131. data/lib/spree/testing_support/factories/options_factory.rb +1 -1
  132. data/lib/spree/testing_support/factories/order_factory.rb +7 -1
  133. data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
  134. data/lib/spree/testing_support/factories/payment_method_factory.rb +1 -1
  135. data/lib/spree/testing_support/factories/price_factory.rb +1 -1
  136. data/lib/spree/testing_support/factories/product_factory.rb +1 -1
  137. data/lib/spree/testing_support/factories/product_option_type_factory.rb +1 -1
  138. data/lib/spree/testing_support/factories/product_property_factory.rb +1 -1
  139. data/lib/spree/testing_support/factories/promotion_category_factory.rb +1 -1
  140. data/lib/spree/testing_support/factories/promotion_factory.rb +1 -1
  141. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +1 -1
  142. data/lib/spree/testing_support/factories/property_factory.rb +1 -1
  143. data/lib/spree/testing_support/factories/prototype_factory.rb +1 -1
  144. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  145. data/lib/spree/testing_support/factories/reimbursement_factory.rb +1 -1
  146. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +1 -1
  147. data/lib/spree/testing_support/factories/return_authorization_factory.rb +1 -1
  148. data/lib/spree/testing_support/factories/return_item_factory.rb +1 -1
  149. data/lib/spree/testing_support/factories/role_factory.rb +1 -1
  150. data/lib/spree/testing_support/factories/shipment_factory.rb +1 -1
  151. data/lib/spree/testing_support/factories/shipping_category_factory.rb +1 -1
  152. data/lib/spree/testing_support/factories/shipping_method_factory.rb +2 -1
  153. data/lib/spree/testing_support/factories/state_factory.rb +1 -1
  154. data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
  155. data/lib/spree/testing_support/factories/stock_item_factory.rb +1 -1
  156. data/lib/spree/testing_support/factories/stock_location_factory.rb +1 -1
  157. data/lib/spree/testing_support/factories/stock_movement_factory.rb +1 -1
  158. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +1 -1
  159. data/lib/spree/testing_support/factories/store_credit_event_factory.rb +1 -1
  160. data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -1
  161. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +1 -1
  162. data/lib/spree/testing_support/factories/store_factory.rb +1 -1
  163. data/lib/spree/testing_support/factories/tag_factory.rb +1 -1
  164. data/lib/spree/testing_support/factories/tax_category_factory.rb +1 -1
  165. data/lib/spree/testing_support/factories/tax_rate_factory.rb +1 -1
  166. data/lib/spree/testing_support/factories/taxon_factory.rb +2 -2
  167. data/lib/spree/testing_support/factories/taxonomy_factory.rb +2 -2
  168. data/lib/spree/testing_support/factories/tracker_factory.rb +1 -1
  169. data/lib/spree/testing_support/factories/user_factory.rb +1 -1
  170. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  171. data/lib/spree/testing_support/factories/zone_factory.rb +1 -1
  172. data/lib/spree/testing_support/factories/zone_member_factory.rb +1 -1
  173. data/lib/spree/testing_support/microdata.rb +3 -0
  174. data/lib/spree/testing_support/order_walkthrough.rb +9 -9
  175. data/lib/tasks/exchanges.rake +8 -10
  176. data/spec/helpers/base_helper_spec.rb +200 -0
  177. data/spec/helpers/products_helper_spec.rb +289 -0
  178. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  179. data/spec/lib/i18n_spec.rb +123 -0
  180. data/spec/lib/search/base_spec.rb +86 -0
  181. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +103 -0
  182. data/spec/lib/spree/core/controller_helpers/order_spec.rb +110 -0
  183. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  184. data/spec/lib/spree/core/controller_helpers/store_spec.rb +72 -0
  185. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  186. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
  187. data/spec/lib/spree/core/importer/order_spec.rb +607 -0
  188. data/spec/lib/spree/core/number_generator_spec.rb +139 -0
  189. data/spec/lib/spree/core/token_generator_spec.rb +24 -0
  190. data/spec/lib/spree/core/validators/email_spec.rb +54 -0
  191. data/spec/lib/spree/core_spec.rb +23 -0
  192. data/spec/lib/spree/localized_number_spec.rb +54 -0
  193. data/spec/lib/spree/migrations_spec.rb +36 -0
  194. data/spec/lib/spree/money_spec.rb +122 -0
  195. data/spec/lib/tasks/exchanges_spec.rb +136 -0
  196. data/spec/mailers/order_mailer_spec.rb +122 -0
  197. data/spec/mailers/reimbursement_mailer_spec.rb +52 -0
  198. data/spec/mailers/shipment_mailer_spec.rb +81 -0
  199. data/spec/mailers/test_mailer_spec.rb +38 -0
  200. data/spec/models/spree/ability_spec.rb +251 -0
  201. data/spec/models/spree/address_spec.rb +402 -0
  202. data/spec/models/spree/adjustable/adjuster/base_spec.rb +10 -0
  203. data/spec/models/spree/adjustable/adjuster/promotion_spec.rb +211 -0
  204. data/spec/models/spree/adjustable/adjuster/tax_spec.rb +86 -0
  205. data/spec/models/spree/adjustable/adjustments_updater_spec.rb +26 -0
  206. data/spec/models/spree/adjustment_spec.rb +189 -0
  207. data/spec/models/spree/app_configuration_spec.rb +26 -0
  208. data/spec/models/spree/asset_spec.rb +28 -0
  209. data/spec/models/spree/calculator/default_tax_spec.rb +152 -0
  210. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  211. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  212. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  213. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  214. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  215. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +47 -0
  216. data/spec/models/spree/calculator/shipping.rb +8 -0
  217. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  218. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  219. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  220. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  221. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +29 -0
  222. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +40 -0
  223. data/spec/models/spree/calculator/tiered_percent_spec.rb +51 -0
  224. data/spec/models/spree/calculator_spec.rb +69 -0
  225. data/spec/models/spree/classification_spec.rb +93 -0
  226. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  227. data/spec/models/spree/concerns/user_methods_spec.rb +82 -0
  228. data/spec/models/spree/concerns/vat_price_calculation_spec.rb +66 -0
  229. data/spec/models/spree/country_spec.rb +55 -0
  230. data/spec/models/spree/credit_card_spec.rb +328 -0
  231. data/spec/models/spree/customer_return_spec.rb +240 -0
  232. data/spec/models/spree/exchange_spec.rb +75 -0
  233. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  234. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  235. data/spec/models/spree/gateway_spec.rb +61 -0
  236. data/spec/models/spree/image_spec.rb +8 -0
  237. data/spec/models/spree/inventory_unit_spec.rb +256 -0
  238. data/spec/models/spree/line_item_spec.rb +348 -0
  239. data/spec/models/spree/option_type_prototype_spec.rb +9 -0
  240. data/spec/models/spree/option_type_spec.rb +14 -0
  241. data/spec/models/spree/option_value_spec.rb +18 -0
  242. data/spec/models/spree/order/address_spec.rb +50 -0
  243. data/spec/models/spree/order/adjustments_spec.rb +29 -0
  244. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  245. data/spec/models/spree/order/checkout_spec.rb +770 -0
  246. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  247. data/spec/models/spree/order/finalizing_spec.rb +114 -0
  248. data/spec/models/spree/order/helpers_spec.rb +5 -0
  249. data/spec/models/spree/order/payment_spec.rb +214 -0
  250. data/spec/models/spree/order/risk_assessment_spec.rb +84 -0
  251. data/spec/models/spree/order/shipments_spec.rb +43 -0
  252. data/spec/models/spree/order/state_machine_spec.rb +212 -0
  253. data/spec/models/spree/order/store_credit_spec.rb +457 -0
  254. data/spec/models/spree/order/tax_spec.rb +84 -0
  255. data/spec/models/spree/order/totals_spec.rb +24 -0
  256. data/spec/models/spree/order/updating_spec.rb +18 -0
  257. data/spec/models/spree/order/validations_spec.rb +15 -0
  258. data/spec/models/spree/order_contents_spec.rb +332 -0
  259. data/spec/models/spree/order_inventory_spec.rb +247 -0
  260. data/spec/models/spree/order_merger_spec.rb +135 -0
  261. data/spec/models/spree/order_spec.rb +1067 -0
  262. data/spec/models/spree/order_updater_spec.rb +305 -0
  263. data/spec/models/spree/payment/gateway_options_spec.rb +127 -0
  264. data/spec/models/spree/payment/store_credit_spec.rb +60 -0
  265. data/spec/models/spree/payment_method/store_credit_spec.rb +291 -0
  266. data/spec/models/spree/payment_method_spec.rb +108 -0
  267. data/spec/models/spree/payment_spec.rb +922 -0
  268. data/spec/models/spree/preference_spec.rb +80 -0
  269. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  270. data/spec/models/spree/preferences/preferable_spec.rb +344 -0
  271. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  272. data/spec/models/spree/preferences/store_spec.rb +46 -0
  273. data/spec/models/spree/price_spec.rb +128 -0
  274. data/spec/models/spree/product/scopes_spec.rb +174 -0
  275. data/spec/models/spree/product_duplicator_spec.rb +102 -0
  276. data/spec/models/spree/product_filter_spec.rb +26 -0
  277. data/spec/models/spree/product_option_type_spec.rb +9 -0
  278. data/spec/models/spree/product_promotion_rule_spec.rb +9 -0
  279. data/spec/models/spree/product_property_spec.rb +26 -0
  280. data/spec/models/spree/product_spec.rb +626 -0
  281. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +113 -0
  282. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +148 -0
  283. data/spec/models/spree/promotion/actions/create_line_items_spec.rb +86 -0
  284. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +36 -0
  285. data/spec/models/spree/promotion/rules/country_spec.rb +36 -0
  286. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  287. data/spec/models/spree/promotion/rules/item_total_spec.rb +282 -0
  288. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  289. data/spec/models/spree/promotion/rules/option_value_spec.rb +90 -0
  290. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  291. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  292. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  293. data/spec/models/spree/promotion/rules/user_spec.rb +45 -0
  294. data/spec/models/spree/promotion_action_spec.rb +10 -0
  295. data/spec/models/spree/promotion_category_spec.rb +17 -0
  296. data/spec/models/spree/promotion_handler/cart_spec.rb +102 -0
  297. data/spec/models/spree/promotion_handler/coupon_spec.rb +323 -0
  298. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +48 -0
  299. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  300. data/spec/models/spree/promotion_rule_spec.rb +29 -0
  301. data/spec/models/spree/promotion_rule_taxon_spec.rb +9 -0
  302. data/spec/models/spree/promotion_rule_user_spec.rb +9 -0
  303. data/spec/models/spree/promotion_spec.rb +674 -0
  304. data/spec/models/spree/property_prototype_spec.rb +9 -0
  305. data/spec/models/spree/property_spec.rb +5 -0
  306. data/spec/models/spree/prototype_spec.rb +5 -0
  307. data/spec/models/spree/prototype_taxon_spec.rb +9 -0
  308. data/spec/models/spree/refund_reason_spec.rb +20 -0
  309. data/spec/models/spree/refund_spec.rb +195 -0
  310. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  311. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  312. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  313. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  314. data/spec/models/spree/reimbursement_spec.rb +188 -0
  315. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +63 -0
  316. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  317. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  318. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +55 -0
  319. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +101 -0
  320. data/spec/models/spree/return_authorization_reason_spec.rb +7 -0
  321. data/spec/models/spree/return_authorization_spec.rb +230 -0
  322. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  323. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  324. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +61 -0
  325. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  326. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  327. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  328. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  329. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  330. data/spec/models/spree/return_item_spec.rb +734 -0
  331. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  332. data/spec/models/spree/role_spec.rb +7 -0
  333. data/spec/models/spree/shipment_spec.rb +744 -0
  334. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  335. data/spec/models/spree/shipping_category_spec.rb +19 -0
  336. data/spec/models/spree/shipping_method_spec.rb +125 -0
  337. data/spec/models/spree/shipping_rate_spec.rb +140 -0
  338. data/spec/models/spree/state_spec.rb +29 -0
  339. data/spec/models/spree/stock/availability_validator_spec.rb +42 -0
  340. data/spec/models/spree/stock/content_item_spec.rb +31 -0
  341. data/spec/models/spree/stock/coordinator_spec.rb +61 -0
  342. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  343. data/spec/models/spree/stock/estimator_spec.rb +202 -0
  344. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +37 -0
  345. data/spec/models/spree/stock/package_spec.rb +182 -0
  346. data/spec/models/spree/stock/packer_spec.rb +70 -0
  347. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  348. data/spec/models/spree/stock/quantifier_spec.rb +126 -0
  349. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  350. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  351. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +47 -0
  352. data/spec/models/spree/stock/splitter/weight_spec.rb +32 -0
  353. data/spec/models/spree/stock_item_spec.rb +465 -0
  354. data/spec/models/spree/stock_location_spec.rb +243 -0
  355. data/spec/models/spree/stock_movement_spec.rb +120 -0
  356. data/spec/models/spree/stock_transfer_spec.rb +50 -0
  357. data/spec/models/spree/store_credit_event_spec.rb +101 -0
  358. data/spec/models/spree/store_credit_spec.rb +798 -0
  359. data/spec/models/spree/store_spec.rb +78 -0
  360. data/spec/models/spree/tax_category_spec.rb +32 -0
  361. data/spec/models/spree/tax_rate_spec.rb +561 -0
  362. data/spec/models/spree/taxon_spec.rb +93 -0
  363. data/spec/models/spree/taxonomy_spec.rb +18 -0
  364. data/spec/models/spree/tracker_spec.rb +21 -0
  365. data/spec/models/spree/user_spec.rb +203 -0
  366. data/spec/models/spree/variant_spec.rb +818 -0
  367. data/spec/models/spree/zone_member_spec.rb +38 -0
  368. data/spec/models/spree/zone_spec.rb +472 -0
  369. data/spec/spec_helper.rb +82 -0
  370. data/spec/support/big_decimal.rb +5 -0
  371. data/spec/support/concerns/adjustment_source.rb +23 -0
  372. data/spec/support/concerns/default_price.rb +37 -0
  373. data/spec/support/rake.rb +13 -0
  374. data/spec/support/test_gateway.rb +2 -0
  375. data/spree_core.gemspec +13 -13
  376. metadata +252 -45
  377. data/app/models/concerns/spree/user_api_authentication.rb +0 -19
  378. data/app/models/spree/calculator/free_shipping.rb +0 -23
  379. data/config/initializers/premailer_rails.rb +0 -3
  380. data/config/initializers/user_class_extensions.rb +0 -10
  381. data/spec/fixtures/microdata.html +0 -22
  382. data/spec/fixtures/microdata_itemref.html +0 -15
  383. data/spec/fixtures/microdata_no_itemscope.html +0 -20
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module PromotionHandler
5
+ describe FreeShipping, type: :model do
6
+ let(:order) { create(:order) }
7
+ let(:shipment) { create(:shipment, order: order ) }
8
+
9
+ let(:promotion) { Promotion.create(name: "Free Shipping") }
10
+ let(:calculator) { Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }
11
+ let!(:action) { Promotion::Actions::FreeShipping.create(promotion: promotion) }
12
+
13
+ subject { Spree::PromotionHandler::FreeShipping.new(order) }
14
+
15
+ context "activates in Shipment level" do
16
+ it "creates the adjustment" do
17
+ expect { subject.activate }.to change { shipment.adjustments.count }.by(1)
18
+ end
19
+ end
20
+
21
+ context "if promo has a code" do
22
+ before do
23
+ promotion.update_column(:code, "code")
24
+ end
25
+
26
+ it "does adjust the shipment when applied to order" do
27
+ order.promotions << promotion
28
+
29
+ expect { subject.activate }.to change { shipment.adjustments.count }
30
+ end
31
+
32
+ it "does not adjust the shipment when not applied to order" do
33
+ expect { subject.activate }.to_not change { shipment.adjustments.count }
34
+ end
35
+ end
36
+
37
+ context "if promo has a path" do
38
+ before do
39
+ promotion.update_column(:path, "path")
40
+ end
41
+
42
+ it "does not adjust the shipment" do
43
+ expect { subject.activate }.to_not change { shipment.adjustments.count }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module PromotionHandler
5
+ describe Page, type: :model do
6
+ let(:order) { create(:order_with_line_items, line_items_count: 1) }
7
+
8
+ let(:promotion) { Promotion.create(name: "10% off", path: '10off') }
9
+ before do
10
+ calculator = Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)
11
+ action = Promotion::Actions::CreateItemAdjustments.create(calculator: calculator)
12
+ promotion.actions << action
13
+ end
14
+
15
+ it "activates at the right path" do
16
+ expect(order.line_item_adjustments.count).to eq(0)
17
+ Spree::PromotionHandler::Page.new(order, '10off').activate
18
+ expect(order.line_item_adjustments.count).to eq(1)
19
+ end
20
+
21
+ context "when promotion is expired" do
22
+ before do
23
+ promotion.update_columns(
24
+ starts_at: 1.week.ago,
25
+ expires_at: 1.day.ago
26
+ )
27
+ end
28
+
29
+ it "is not activated" do
30
+ expect(order.line_item_adjustments.count).to eq(0)
31
+ Spree::PromotionHandler::Page.new(order, '10off').activate
32
+ expect(order.line_item_adjustments.count).to eq(0)
33
+ end
34
+ end
35
+
36
+ it "does not activate at the wrong path" do
37
+ expect(order.line_item_adjustments.count).to eq(0)
38
+ Spree::PromotionHandler::Page.new(order, 'wrongpath').activate
39
+ expect(order.line_item_adjustments.count).to eq(0)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Spree::PromotionRule, type: :model do
5
+
6
+ class BadTestRule < Spree::PromotionRule; end
7
+
8
+ class TestRule < Spree::PromotionRule
9
+ def eligible?
10
+ true
11
+ end
12
+ end
13
+
14
+ it "should force developer to implement eligible? method" do
15
+ expect { BadTestRule.new.eligible? }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it "validates unique rules for a promotion" do
19
+ p1 = TestRule.new
20
+ p1.promotion_id = 1
21
+ p1.save
22
+
23
+ p2 = TestRule.new
24
+ p2.promotion_id = 1
25
+ expect(p2).not_to be_valid
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::PromotionRuleTaxon do
4
+ describe 'Validations' do
5
+ it { is_expected.to validate_presence_of(:promotion_rule) }
6
+ it { is_expected.to validate_presence_of(:taxon) }
7
+ it { is_expected.to validate_uniqueness_of(:promotion_rule_id).scoped_to(:taxon_id).allow_nil }
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::PromotionRuleUser do
4
+ describe 'Validations' do
5
+ it { is_expected.to validate_presence_of(:promotion_rule) }
6
+ it { is_expected.to validate_presence_of(:user) }
7
+ it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:promotion_rule_id).allow_nil }
8
+ end
9
+ end
@@ -0,0 +1,674 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Promotion, type: :model do
4
+ let(:promotion) { Spree::Promotion.new }
5
+
6
+ describe "validations" do
7
+ before :each do
8
+ @valid_promotion = Spree::Promotion.new name: "A promotion"
9
+ end
10
+
11
+ it { is_expected.to validate_presence_of(:name) }
12
+ it { is_expected.to validate_uniqueness_of(:path).case_insensitive.allow_blank }
13
+ it { is_expected.to validate_uniqueness_of(:code).case_insensitive.allow_blank }
14
+ it { is_expected.to validate_numericality_of(:usage_limit).is_greater_than(0).allow_nil }
15
+ it { is_expected.to validate_length_of(:description).is_at_most(255) }
16
+ it { is_expected.to allow_values('', nil).for(:description) }
17
+
18
+ it "valid_promotion is valid" do
19
+ expect(@valid_promotion).to be_valid
20
+ end
21
+
22
+ it "validates usage limit" do
23
+ @valid_promotion.usage_limit = -1
24
+ expect(@valid_promotion).not_to be_valid
25
+
26
+ @valid_promotion.usage_limit = 100
27
+ expect(@valid_promotion).to be_valid
28
+ end
29
+
30
+ it "validates name" do
31
+ @valid_promotion.name = nil
32
+ expect(@valid_promotion).not_to be_valid
33
+ end
34
+
35
+ describe "expires_at_must_be_later_than_starts_at" do
36
+ before do
37
+ @valid_promotion.starts_at = Date.today
38
+ end
39
+
40
+ context "starts_at is a date earlier than expires_at" do
41
+ before { @valid_promotion.expires_at = 5.days.from_now }
42
+
43
+ it "is valid" do
44
+ expect(@valid_promotion).to be_valid
45
+ end
46
+ end
47
+
48
+ context "starts_at is a date earlier than expires_at" do
49
+ before { @valid_promotion.expires_at = 5.days.ago }
50
+
51
+ context "is not valid" do
52
+ before { @valid_promotion.valid? }
53
+ it { expect(@valid_promotion).not_to be_valid }
54
+ it { expect(@valid_promotion.errors[:expires_at]).to include(I18n.t(:invalid_date_range, scope: 'activerecord.errors.models.spree/promotion.attributes.expires_at')) }
55
+ end
56
+ end
57
+
58
+ context "starts_at and expires_at are nil" do
59
+ before do
60
+ @valid_promotion.expires_at = nil
61
+ @valid_promotion.starts_at = nil
62
+ end
63
+
64
+ it "is valid" do
65
+ expect(@valid_promotion).to be_valid
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ describe 'scopes' do
72
+ describe '.coupons' do
73
+ let!(:promotion_without_code) { Spree::Promotion.create! name: 'test', code: '' }
74
+ let!(:promotion_with_code) { Spree::Promotion.create! name: 'test1', code: 'code' }
75
+
76
+ subject { Spree::Promotion.coupons }
77
+
78
+ it 'is expected to not include promotion without code' do
79
+ is_expected.to_not include(promotion_without_code)
80
+ end
81
+
82
+ it 'is expected to include promotion with code' do
83
+ is_expected.to include(promotion_with_code)
84
+ end
85
+ end
86
+
87
+ describe '.applied' do
88
+ let!(:promotion_not_applied) { Spree::Promotion.create! name: 'test', code: '' }
89
+ let(:order) { create(:order) }
90
+ let!(:promotion_applied) do
91
+ promotion = Spree::Promotion.create!(name: 'test1', code: '')
92
+ promotion.orders << order
93
+ promotion
94
+ end
95
+
96
+ subject { Spree::Promotion.applied }
97
+
98
+ it 'is expected to not include promotion not applied' do
99
+ is_expected.to_not include(promotion_not_applied)
100
+ end
101
+
102
+ it 'is expected to include promotion applied' do
103
+ is_expected.to include(promotion_applied)
104
+ end
105
+ end
106
+
107
+ describe '.advertised' do
108
+ let!(:promotion_not_advertised) { Spree::Promotion.create! name: 'test', advertise: false }
109
+ let!(:promotion_advertised) { Spree::Promotion.create! name: 'test1', advertise: true }
110
+
111
+ subject { Spree::Promotion.advertised }
112
+
113
+ it 'is expected to not include promotion not advertised' do
114
+ is_expected.to_not include(promotion_not_advertised)
115
+ end
116
+
117
+ it 'is expected to include promotion advertised' do
118
+ is_expected.to include(promotion_advertised)
119
+ end
120
+ end
121
+ end
122
+
123
+ describe "#destroy" do
124
+ let(:promotion) { Spree::Promotion.create(name: "delete me") }
125
+
126
+ before(:each) do
127
+ promotion.actions << Spree::Promotion::Actions::CreateAdjustment.new
128
+ promotion.rules << Spree::Promotion::Rules::FirstOrder.new
129
+ promotion.save!
130
+ promotion.destroy
131
+ end
132
+
133
+ it "should delete actions" do
134
+ expect(Spree::PromotionAction.count).to eq(0)
135
+ end
136
+
137
+ it "should delete rules" do
138
+ expect(Spree::PromotionRule.count).to eq(0)
139
+ end
140
+ end
141
+
142
+ describe "#save" do
143
+ let(:promotion) { Spree::Promotion.create(name: "delete me") }
144
+
145
+ before(:each) do
146
+ promotion.actions << Spree::Promotion::Actions::CreateAdjustment.new
147
+ promotion.rules << Spree::Promotion::Rules::FirstOrder.new
148
+ promotion.save!
149
+ end
150
+
151
+ it "should deeply autosave records and preferences" do
152
+ promotion.actions[0].calculator.preferred_flat_percent = 10
153
+ promotion.save!
154
+ expect(Spree::Calculator.first.preferred_flat_percent).to eq(10)
155
+ end
156
+ end
157
+
158
+ describe "#activate" do
159
+ before do
160
+ @action1 = Spree::Promotion::Actions::CreateAdjustment.create!
161
+ @action2 = Spree::Promotion::Actions::CreateAdjustment.create!
162
+ allow(@action1).to receive_messages perform: true
163
+ allow(@action2).to receive_messages perform: true
164
+
165
+ promotion.promotion_actions = [@action1, @action2]
166
+ promotion.created_at = 2.days.ago
167
+
168
+ @user = stub_model(Spree::LegacyUser, email: "spree@example.com")
169
+ @order = Spree::Order.create user: @user
170
+ @payload = { order: @order, user: @user }
171
+ end
172
+
173
+ it "should check path if present" do
174
+ promotion.path = 'content/cvv'
175
+ @payload[:path] = 'content/cvv'
176
+ expect(@action1).to receive(:perform).with(@payload)
177
+ expect(@action2).to receive(:perform).with(@payload)
178
+ promotion.activate(@payload)
179
+ end
180
+
181
+ it "does not perform actions against an order in a finalized state" do
182
+ expect(@action1).not_to receive(:perform).with(@payload)
183
+
184
+ @order.state = 'complete'
185
+ promotion.activate(@payload)
186
+
187
+ @order.state = 'awaiting_return'
188
+ promotion.activate(@payload)
189
+
190
+ @order.state = 'returned'
191
+ promotion.activate(@payload)
192
+ end
193
+
194
+ it "does activate if newer then order" do
195
+ expect(@action1).to receive(:perform).with(@payload)
196
+ promotion.created_at = DateTime.current + 2
197
+ expect(promotion.activate(@payload)).to be true
198
+ end
199
+
200
+ context "keeps track of the orders" do
201
+ context "when activated" do
202
+ it "assigns the order" do
203
+ expect(promotion.orders).to be_empty
204
+ expect(promotion.activate(@payload)).to be true
205
+ expect(promotion.orders.first).to eql @order
206
+ end
207
+ end
208
+ context "when not activated" do
209
+ it "will not assign the order" do
210
+ @order.state = 'complete'
211
+ expect(promotion.orders).to be_empty
212
+ expect(promotion.activate(@payload)).to be_falsey
213
+ expect(promotion.orders).to be_empty
214
+ end
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+
221
+ context "#usage_limit_exceeded" do
222
+ let(:promotable) { double('Promotable') }
223
+ it "should not have its usage limit exceeded with no usage limit" do
224
+ promotion.usage_limit = 0
225
+ expect(promotion.usage_limit_exceeded?(promotable)).to be false
226
+ end
227
+
228
+ it "should have its usage limit exceeded" do
229
+ promotion.usage_limit = 2
230
+ allow(promotion).to receive_messages(adjusted_credits_count: 2)
231
+ expect(promotion.usage_limit_exceeded?(promotable)).to be true
232
+
233
+ allow(promotion).to receive_messages(adjusted_credits_count: 3)
234
+ expect(promotion.usage_limit_exceeded?(promotable)).to be true
235
+ end
236
+ end
237
+
238
+ context "#expired" do
239
+ it "should not be exipired" do
240
+ expect(promotion).not_to be_expired
241
+ end
242
+
243
+ it "should be expired if it hasn't started yet" do
244
+ promotion.starts_at = Time.current + 1.day
245
+ expect(promotion).to be_expired
246
+ end
247
+
248
+ it "should be expired if it has already ended" do
249
+ promotion.expires_at = Time.current - 1.day
250
+ expect(promotion).to be_expired
251
+ end
252
+
253
+ it "should not be expired if it has started already" do
254
+ promotion.starts_at = Time.current - 1.day
255
+ expect(promotion).not_to be_expired
256
+ end
257
+
258
+ it "should not be expired if it has not ended yet" do
259
+ promotion.expires_at = Time.current + 1.day
260
+ expect(promotion).not_to be_expired
261
+ end
262
+
263
+ it "should not be expired if current time is within starts_at and expires_at range" do
264
+ promotion.starts_at = Time.current - 1.day
265
+ promotion.expires_at = Time.current + 1.day
266
+ expect(promotion).not_to be_expired
267
+ end
268
+
269
+ it "should not be expired if usage limit is not exceeded" do
270
+ promotion.usage_limit = 2
271
+ allow(promotion).to receive_messages(credits_count: 1)
272
+ expect(promotion).not_to be_expired
273
+ end
274
+ end
275
+
276
+ context "#credits_count" do
277
+ let!(:promotion) do
278
+ promotion = Spree::Promotion.new
279
+ promotion.name = "Foo"
280
+ promotion.code = "XXX"
281
+ calculator = Spree::Calculator::FlatRate.new
282
+ promotion.tap(&:save)
283
+ end
284
+
285
+ let!(:action) do
286
+ calculator = Spree::Calculator::FlatRate.new
287
+ action_params = { promotion: promotion, calculator: calculator }
288
+ action = Spree::Promotion::Actions::CreateAdjustment.create(action_params)
289
+ promotion.actions << action
290
+ action
291
+ end
292
+
293
+ let!(:adjustment) do
294
+ order = create(:order)
295
+ Spree::Adjustment.create!(
296
+ order: order,
297
+ adjustable: order,
298
+ source: action,
299
+ amount: 10,
300
+ label: 'Promotional adjustment'
301
+ )
302
+ end
303
+
304
+ it "counts eligible adjustments" do
305
+ adjustment.update_column(:eligible, true)
306
+ expect(promotion.credits_count).to eq(1)
307
+ end
308
+
309
+ # Regression test for #4112
310
+ it "does not count ineligible adjustments" do
311
+ adjustment.update_column(:eligible, false)
312
+ expect(promotion.credits_count).to eq(0)
313
+ end
314
+ end
315
+
316
+ context "#adjusted_credits_count" do
317
+ let(:order) { create :order }
318
+ let(:line_item) { create :line_item, order: order }
319
+ let(:promotion) { Spree::Promotion.create name: "promo", code: "10off" }
320
+ let(:order_action) {
321
+ action = Spree::Promotion::Actions::CreateAdjustment.create(calculator: Spree::Calculator::FlatPercentItemTotal.new)
322
+ promotion.actions << action
323
+ action
324
+ }
325
+ let(:item_action) {
326
+ action = Spree::Promotion::Actions::CreateItemAdjustments.create(calculator: Spree::Calculator::FlatPercentItemTotal.new)
327
+ promotion.actions << action
328
+ action
329
+ }
330
+ let(:order_adjustment) do
331
+ Spree::Adjustment.create!(
332
+ source: order_action,
333
+ amount: 10,
334
+ adjustable: order,
335
+ order: order,
336
+ label: "Promotional adjustment"
337
+ )
338
+ end
339
+ let(:item_adjustment) do
340
+ Spree::Adjustment.create!(
341
+ source: item_action,
342
+ amount: 10,
343
+ adjustable: line_item,
344
+ order: order,
345
+ label: "Promotional adjustment"
346
+ )
347
+ end
348
+
349
+ it "counts order level adjustments" do
350
+ expect(order_adjustment.adjustable).to eq(order)
351
+ expect(promotion.credits_count).to eq(1)
352
+ expect(promotion.adjusted_credits_count(order)).to eq(0)
353
+ end
354
+
355
+ it "counts item level adjustments" do
356
+ expect(item_adjustment.adjustable).to eq(line_item)
357
+ expect(promotion.credits_count).to eq(1)
358
+ expect(promotion.adjusted_credits_count(order)).to eq(0)
359
+ end
360
+ end
361
+
362
+ context "#products" do
363
+ let(:product) { create(:product) }
364
+ let(:promotion) { create(:promotion) }
365
+
366
+ context "when it has product rules with products associated" do
367
+ let(:promotion_rule) { create(:promotion_rule, promotion: promotion, type: 'Spree::Promotion::Rules::Product') }
368
+
369
+ before do
370
+ promotion.promotion_rules << promotion_rule
371
+ product.product_promotion_rules.create(promotion_rule_id: promotion_rule.id)
372
+ end
373
+
374
+ it "should have products" do
375
+ expect(promotion.reload.products.size).to eq(1)
376
+ end
377
+ end
378
+
379
+ context "when there's no product rule associated" do
380
+ it "should not have products but still return an empty array" do
381
+ expect(promotion.products).to be_blank
382
+ end
383
+ end
384
+ end
385
+
386
+ context "#eligible?" do
387
+ let(:promotable) { create :order }
388
+ subject { promotion.eligible?(promotable) }
389
+ context "when promotion is expired" do
390
+ before { promotion.expires_at = Time.current - 10.days }
391
+ it { is_expected.to be false }
392
+ end
393
+ context "when promotable is a Spree::LineItem" do
394
+ let(:promotable) { create :line_item }
395
+ let(:product) { promotable.product }
396
+ before do
397
+ product.promotionable = promotionable
398
+ end
399
+ context "and product is promotionable" do
400
+ let(:promotionable) { true }
401
+ it { is_expected.to be true }
402
+ end
403
+ context "and product is not promotionable" do
404
+ let(:promotionable) { false }
405
+ it { is_expected.to be false }
406
+ end
407
+ end
408
+ context "when promotable is a Spree::Order" do
409
+ let(:promotable) { create :order }
410
+ context "and it is empty" do
411
+ it { is_expected.to be true }
412
+ end
413
+ context "and it contains items" do
414
+ let!(:line_item) { create(:line_item, order: promotable) }
415
+ context "and the items are all non-promotionable" do
416
+ before do
417
+ line_item.product.update_column(:promotionable, false)
418
+ end
419
+ it { is_expected.to be false }
420
+ end
421
+ context "and at least one item is promotionable" do
422
+ it { is_expected.to be true }
423
+ end
424
+ end
425
+ end
426
+ end
427
+
428
+ context "#eligible_rules" do
429
+ let(:promotable) { double('Promotable') }
430
+ it "true if there are no rules" do
431
+ expect(promotion.eligible_rules(promotable)).to eq []
432
+ end
433
+
434
+ it "true if there are no applicable rules" do
435
+ promotion.promotion_rules = [stub_model(Spree::PromotionRule, eligible?: true, applicable?: false)]
436
+ allow(promotion.promotion_rules).to receive(:for).and_return([])
437
+ expect(promotion.eligible_rules(promotable)).to eq []
438
+ end
439
+
440
+ context "with 'all' match policy" do
441
+ let(:promo1) { Spree::PromotionRule.create! }
442
+ let(:promo2) { Spree::PromotionRule.create! }
443
+
444
+ before { promotion.match_policy = 'all' }
445
+
446
+ context "when all rules are eligible" do
447
+ before do
448
+ allow(promo1).to receive_messages(eligible?: true, applicable?: true)
449
+ allow(promo2).to receive_messages(eligible?: true, applicable?: true)
450
+
451
+ promotion.promotion_rules = [promo1, promo2]
452
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
453
+ end
454
+ it "returns the eligible rules" do
455
+ expect(promotion.eligible_rules(promotable)).to eq [promo1, promo2]
456
+ end
457
+ it "does set anything to eligiblity errors" do
458
+ promotion.eligible_rules(promotable)
459
+ expect(promotion.eligibility_errors).to be_nil
460
+ end
461
+ end
462
+
463
+ context "when any of the rules is not eligible" do
464
+ let(:errors) { double ActiveModel::Errors, empty?: false }
465
+ before do
466
+ allow(promo1).to receive_messages(eligible?: true, applicable?: true, eligibility_errors: nil)
467
+ allow(promo2).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
468
+
469
+ promotion.promotion_rules = [promo1, promo2]
470
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
471
+ end
472
+ it "returns nil" do
473
+ expect(promotion.eligible_rules(promotable)).to be_nil
474
+ end
475
+ it "sets eligibility errors to the first non-nil one" do
476
+ promotion.eligible_rules(promotable)
477
+ expect(promotion.eligibility_errors).to eq errors
478
+ end
479
+ end
480
+ end
481
+
482
+ context "with 'any' match policy" do
483
+ let(:promotion) { Spree::Promotion.create(name: "Promo", match_policy: 'any') }
484
+ let(:promotable) { double('Promotable') }
485
+
486
+ it "should have eligible rules if any of the rules are eligible" do
487
+ allow_any_instance_of(Spree::PromotionRule).to receive_messages(applicable?: true)
488
+ true_rule = Spree::PromotionRule.create(promotion: promotion)
489
+ allow(true_rule).to receive_messages(eligible?: true)
490
+ allow(promotion).to receive_messages(rules: [true_rule])
491
+ allow(promotion).to receive_message_chain(:rules, :for).and_return([true_rule])
492
+ expect(promotion.eligible_rules(promotable)).to eq [true_rule]
493
+ end
494
+
495
+ context "when none of the rules are eligible" do
496
+ let(:promo) { Spree::PromotionRule.create! }
497
+ let(:errors) { double ActiveModel::Errors, empty?: false }
498
+ before do
499
+ allow(promo).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
500
+
501
+ promotion.promotion_rules = [promo]
502
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
503
+ end
504
+ it "returns nil" do
505
+ expect(promotion.eligible_rules(promotable)).to be_nil
506
+ end
507
+ it "sets eligibility errors to the first non-nil one" do
508
+ promotion.eligible_rules(promotable)
509
+ expect(promotion.eligibility_errors).to eq errors
510
+ end
511
+ end
512
+ end
513
+ end
514
+
515
+ describe '#line_item_actionable?' do
516
+ let(:order) { double Spree::Order }
517
+ let(:line_item) { double Spree::LineItem}
518
+ let(:true_rule) { double Spree::PromotionRule, eligible?: true, applicable?: true, actionable?: true }
519
+ let(:false_rule) { double Spree::PromotionRule, eligible?: true, applicable?: true, actionable?: false }
520
+ let(:rules) { [] }
521
+
522
+ before do
523
+ allow(promotion).to receive(:rules) { rules }
524
+ allow(rules).to receive(:for) { rules }
525
+ end
526
+
527
+ subject { promotion.line_item_actionable? order, line_item }
528
+
529
+ context 'when the order is eligible for promotion' do
530
+ context 'when there are no rules' do
531
+ it { is_expected.to be }
532
+ end
533
+
534
+ context 'when there are rules' do
535
+ context 'when the match policy is all' do
536
+ before { promotion.match_policy = 'all' }
537
+
538
+ context 'when all rules allow action on the line item' do
539
+ let(:rules) { [true_rule] }
540
+ it { is_expected.to be}
541
+ end
542
+
543
+ context 'when at least one rule does not allow action on the line item' do
544
+ let(:rules) { [true_rule, false_rule] }
545
+ it { is_expected.not_to be}
546
+ end
547
+ end
548
+
549
+ context 'when the match policy is any' do
550
+ before { promotion.match_policy = 'any' }
551
+
552
+ context 'when at least one rule allows action on the line item' do
553
+ let(:rules) { [true_rule, false_rule] }
554
+ it { is_expected.to be }
555
+ end
556
+
557
+ context 'when no rules allow action on the line item' do
558
+ let(:rules) { [false_rule] }
559
+ it { is_expected.not_to be}
560
+ end
561
+ end
562
+ end
563
+ end
564
+
565
+ context 'when the order is not eligible for the promotion' do
566
+ before { promotion.starts_at = Time.current + 2.days }
567
+ it { is_expected.not_to be }
568
+ end
569
+ end
570
+
571
+ # regression for #4059
572
+ # admin form posts the code and path as empty string
573
+ describe "normalize blank values for code & path" do
574
+ it "will save blank value as nil value instead" do
575
+ promotion = Spree::Promotion.create(name: "A promotion", code: "", path: "")
576
+ expect(promotion.code).to be_nil
577
+ expect(promotion.path).to be_nil
578
+ end
579
+ end
580
+
581
+ # Regression test for #4081
582
+ describe "#with_coupon_code" do
583
+ context "and code stored in uppercase" do
584
+ let!(:promotion) { create(:promotion, :with_order_adjustment, code: "MY-COUPON-123") }
585
+ it "finds the code with lowercase" do
586
+ expect(Spree::Promotion.with_coupon_code("my-coupon-123")).to eql promotion
587
+ end
588
+ end
589
+
590
+ context "when promotion has no actions" do
591
+ let!(:promotion_without_actions) { create(:promotion, code: "MY-COUPON-123").actions.clear }
592
+
593
+ it "then returns the one with an action" do
594
+ expect(Spree::Promotion.with_coupon_code("MY-COUPON-123")).to be_nil
595
+ end
596
+ end
597
+ end
598
+
599
+ describe '#used_by?' do
600
+ subject { promotion.used_by? user, [excluded_order] }
601
+
602
+ let(:promotion) { create :promotion, :with_order_adjustment }
603
+ let(:user) { create :user }
604
+ let(:order) { create :order_with_line_items, user: user }
605
+ let(:excluded_order) { create :order_with_line_items, user: user }
606
+
607
+ before do
608
+ order.user_id = user.id
609
+ order.save!
610
+ end
611
+
612
+ context 'when the user has used this promo' do
613
+ before do
614
+ promotion.activate(order: order)
615
+ order.update_with_updater!
616
+ order.completed_at = Time.current
617
+ order.save!
618
+ end
619
+
620
+ context 'when the order is complete' do
621
+ it { is_expected.to be true }
622
+
623
+ context 'when the promotion was not eligible' do
624
+ let(:adjustment) { order.adjustments.first }
625
+
626
+ before do
627
+ adjustment.eligible = false
628
+ adjustment.save!
629
+ end
630
+
631
+ it { is_expected.to be false }
632
+ end
633
+
634
+ context 'when the only matching order is the excluded order' do
635
+ let(:excluded_order) { order }
636
+ it { is_expected.to be false }
637
+ end
638
+ end
639
+
640
+ context 'when the order is not complete' do
641
+ let(:order) { create :order, user: user }
642
+ it { is_expected.to be false }
643
+ end
644
+ end
645
+
646
+ context 'when the user has not used this promo' do
647
+ it { is_expected.to be false }
648
+ end
649
+ end
650
+
651
+ describe "adding items to the cart" do
652
+ let(:order) { create :order }
653
+ let(:line_item) { create :line_item, order: order }
654
+ let(:promo) { create :promotion_with_item_adjustment, adjustment_rate: 5, code: 'promo' }
655
+ let(:variant) { create :variant }
656
+
657
+ it "updates the promotions for new line items" do
658
+ expect(line_item.adjustments).to be_empty
659
+ expect(order.adjustment_total).to eq 0
660
+
661
+ promo.activate order: order
662
+ order.update_with_updater!
663
+
664
+ expect(line_item.adjustments.size).to eq(1)
665
+ expect(order.adjustment_total).to eq -5
666
+
667
+ other_line_item = order.contents.add(variant, 1, currency: order.currency)
668
+
669
+ expect(other_line_item).not_to eq line_item
670
+ expect(other_line_item.adjustments.size).to eq(1)
671
+ expect(order.adjustment_total).to eq -10
672
+ end
673
+ end
674
+ end