goca-spree-core 3.1.14.rails.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (594) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +26 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/images/logo/spree_50.png +0 -0
  6. data/app/assets/images/noimage/large.png +0 -0
  7. data/app/assets/images/noimage/mini.png +0 -0
  8. data/app/assets/images/noimage/product.png +0 -0
  9. data/app/assets/images/noimage/small.png +0 -0
  10. data/app/assets/javascripts/spree.js.coffee.erb +59 -0
  11. data/app/controllers/spree/base_controller.rb +15 -0
  12. data/app/helpers/spree/base_helper.rb +123 -0
  13. data/app/helpers/spree/products_helper.rb +91 -0
  14. data/app/mailers/spree/base_mailer.rb +27 -0
  15. data/app/mailers/spree/order_mailer.rb +17 -0
  16. data/app/mailers/spree/reimbursement_mailer.rb +10 -0
  17. data/app/mailers/spree/shipment_mailer.rb +10 -0
  18. data/app/mailers/spree/test_mailer.rb +8 -0
  19. data/app/models/concerns/spree/adjustment_source.rb +53 -0
  20. data/app/models/concerns/spree/calculated_adjustments.rb +34 -0
  21. data/app/models/concerns/spree/default_price.rb +40 -0
  22. data/app/models/concerns/spree/display_money.rb +32 -0
  23. data/app/models/concerns/spree/named_type.rb +12 -0
  24. data/app/models/concerns/spree/ransackable_attributes.rb +24 -0
  25. data/app/models/concerns/spree/user_address.rb +30 -0
  26. data/app/models/concerns/spree/user_api_authentication.rb +19 -0
  27. data/app/models/concerns/spree/user_methods.rb +57 -0
  28. data/app/models/concerns/spree/user_payment_source.rb +19 -0
  29. data/app/models/concerns/spree/user_reporting.rb +22 -0
  30. data/app/models/concerns/spree/vat_price_calculation.rb +47 -0
  31. data/app/models/friendly_id/slug_decorator.rb +3 -0
  32. data/app/models/spree/ability.rb +80 -0
  33. data/app/models/spree/address.rb +145 -0
  34. data/app/models/spree/adjustable/adjuster/base.rb +25 -0
  35. data/app/models/spree/adjustable/adjuster/promotion.rb +41 -0
  36. data/app/models/spree/adjustable/adjuster/tax.rb +26 -0
  37. data/app/models/spree/adjustable/adjustments_updater.rb +47 -0
  38. data/app/models/spree/adjustable/promotion_accumulator.rb +75 -0
  39. data/app/models/spree/adjustment.rb +104 -0
  40. data/app/models/spree/app_configuration.rb +74 -0
  41. data/app/models/spree/asset.rb +6 -0
  42. data/app/models/spree/base.rb +24 -0
  43. data/app/models/spree/calculator/default_tax.rb +59 -0
  44. data/app/models/spree/calculator/flat_percent_item_total.rb +22 -0
  45. data/app/models/spree/calculator/flat_rate.rb +20 -0
  46. data/app/models/spree/calculator/flexi_rate.rb +33 -0
  47. data/app/models/spree/calculator/free_shipping.rb +22 -0
  48. data/app/models/spree/calculator/percent_on_line_item.rb +15 -0
  49. data/app/models/spree/calculator/percent_per_item.rb +50 -0
  50. data/app/models/spree/calculator/price_sack.rb +29 -0
  51. data/app/models/spree/calculator/returns/default_refund_amount.rb +36 -0
  52. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +22 -0
  53. data/app/models/spree/calculator/shipping/flat_rate.rb +18 -0
  54. data/app/models/spree/calculator/shipping/flexi_rate.rb +35 -0
  55. data/app/models/spree/calculator/shipping/per_item.rb +22 -0
  56. data/app/models/spree/calculator/shipping/price_sack.rb +28 -0
  57. data/app/models/spree/calculator/tiered_flat_rate.rb +37 -0
  58. data/app/models/spree/calculator/tiered_percent.rb +44 -0
  59. data/app/models/spree/calculator.rb +47 -0
  60. data/app/models/spree/classification.rb +15 -0
  61. data/app/models/spree/country.rb +40 -0
  62. data/app/models/spree/credit_card.rb +166 -0
  63. data/app/models/spree/customer_return.rb +67 -0
  64. data/app/models/spree/exchange.rb +46 -0
  65. data/app/models/spree/gateway/bogus.rb +91 -0
  66. data/app/models/spree/gateway/bogus_simple.rb +26 -0
  67. data/app/models/spree/gateway.rb +80 -0
  68. data/app/models/spree/image.rb +43 -0
  69. data/app/models/spree/inventory_unit.rb +105 -0
  70. data/app/models/spree/legacy_user.rb +13 -0
  71. data/app/models/spree/line_item.rb +158 -0
  72. data/app/models/spree/log_entry.rb +17 -0
  73. data/app/models/spree/option_type.rb +32 -0
  74. data/app/models/spree/option_type_prototype.rb +9 -0
  75. data/app/models/spree/option_value.rb +26 -0
  76. data/app/models/spree/option_value_variant.rb +6 -0
  77. data/app/models/spree/order/checkout.rb +336 -0
  78. data/app/models/spree/order/currency_updater.rb +40 -0
  79. data/app/models/spree/order/payments.rb +66 -0
  80. data/app/models/spree/order/store_credit.rb +96 -0
  81. data/app/models/spree/order.rb +685 -0
  82. data/app/models/spree/order_contents.rb +120 -0
  83. data/app/models/spree/order_inventory.rb +103 -0
  84. data/app/models/spree/order_merger.rb +65 -0
  85. data/app/models/spree/order_promotion.rb +6 -0
  86. data/app/models/spree/order_updater.rb +177 -0
  87. data/app/models/spree/payment/gateway_options.rb +90 -0
  88. data/app/models/spree/payment/processing.rb +175 -0
  89. data/app/models/spree/payment.rb +276 -0
  90. data/app/models/spree/payment_capture_event.rb +9 -0
  91. data/app/models/spree/payment_method/check.rb +43 -0
  92. data/app/models/spree/payment_method/store_credit.rb +130 -0
  93. data/app/models/spree/payment_method.rb +71 -0
  94. data/app/models/spree/preference.rb +5 -0
  95. data/app/models/spree/preferences/configuration.rb +71 -0
  96. data/app/models/spree/preferences/preferable.rb +133 -0
  97. data/app/models/spree/preferences/preferable_class_methods.rb +51 -0
  98. data/app/models/spree/preferences/scoped_store.rb +33 -0
  99. data/app/models/spree/preferences/store.rb +98 -0
  100. data/app/models/spree/price.rb +53 -0
  101. data/app/models/spree/product/scopes.rb +271 -0
  102. data/app/models/spree/product.rb +384 -0
  103. data/app/models/spree/product_option_type.rb +12 -0
  104. data/app/models/spree/product_promotion_rule.rb +9 -0
  105. data/app/models/spree/product_property.rb +28 -0
  106. data/app/models/spree/promotion/actions/create_adjustment.rb +21 -0
  107. data/app/models/spree/promotion/actions/create_item_adjustments.rb +24 -0
  108. data/app/models/spree/promotion/actions/create_line_items.rb +62 -0
  109. data/app/models/spree/promotion/actions/free_shipping.rb +18 -0
  110. data/app/models/spree/promotion/rules/first_order.rb +37 -0
  111. data/app/models/spree/promotion/rules/item_total.rb +61 -0
  112. data/app/models/spree/promotion/rules/one_use_per_user.rb +24 -0
  113. data/app/models/spree/promotion/rules/option_value.rb +50 -0
  114. data/app/models/spree/promotion/rules/product.rb +65 -0
  115. data/app/models/spree/promotion/rules/taxon.rb +70 -0
  116. data/app/models/spree/promotion/rules/user.rb +30 -0
  117. data/app/models/spree/promotion/rules/user_logged_in.rb +18 -0
  118. data/app/models/spree/promotion.rb +198 -0
  119. data/app/models/spree/promotion_action.rb +25 -0
  120. data/app/models/spree/promotion_action_line_item.rb +6 -0
  121. data/app/models/spree/promotion_category.rb +6 -0
  122. data/app/models/spree/promotion_handler/cart.rb +51 -0
  123. data/app/models/spree/promotion_handler/coupon.rb +107 -0
  124. data/app/models/spree/promotion_handler/free_shipping.rb +33 -0
  125. data/app/models/spree/promotion_handler/page.rb +24 -0
  126. data/app/models/spree/promotion_rule.rb +43 -0
  127. data/app/models/spree/promotion_rule_taxon.rb +9 -0
  128. data/app/models/spree/promotion_rule_user.rb +9 -0
  129. data/app/models/spree/property.rb +23 -0
  130. data/app/models/spree/property_prototype.rb +9 -0
  131. data/app/models/spree/prototype.rb +14 -0
  132. data/app/models/spree/prototype_taxon.rb +9 -0
  133. data/app/models/spree/refund.rb +89 -0
  134. data/app/models/spree/refund_reason.rb +13 -0
  135. data/app/models/spree/reimbursement/credit.rb +25 -0
  136. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +56 -0
  137. data/app/models/spree/reimbursement/reimbursement_type_validator.rb +15 -0
  138. data/app/models/spree/reimbursement.rb +164 -0
  139. data/app/models/spree/reimbursement_performer.rb +43 -0
  140. data/app/models/spree/reimbursement_tax_calculator.rb +44 -0
  141. data/app/models/spree/reimbursement_type/credit.rb +13 -0
  142. data/app/models/spree/reimbursement_type/exchange.rb +9 -0
  143. data/app/models/spree/reimbursement_type/original_payment.rb +13 -0
  144. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +67 -0
  145. data/app/models/spree/reimbursement_type/store_credit.rb +28 -0
  146. data/app/models/spree/reimbursement_type.rb +16 -0
  147. data/app/models/spree/return_authorization.rb +98 -0
  148. data/app/models/spree/return_authorization_reason.rb +7 -0
  149. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +24 -0
  150. data/app/models/spree/return_item/eligibility_validator/default.rb +30 -0
  151. data/app/models/spree/return_item/eligibility_validator/inventory_shipped.rb +16 -0
  152. data/app/models/spree/return_item/eligibility_validator/no_reimbursements.rb +16 -0
  153. data/app/models/spree/return_item/eligibility_validator/order_completed.rb +16 -0
  154. data/app/models/spree/return_item/eligibility_validator/rma_required.rb +17 -0
  155. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +16 -0
  156. data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +34 -0
  157. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +9 -0
  158. data/app/models/spree/return_item.rb +236 -0
  159. data/app/models/spree/returns_calculator.rb +8 -0
  160. data/app/models/spree/role.rb +8 -0
  161. data/app/models/spree/role_user.rb +6 -0
  162. data/app/models/spree/shipment.rb +419 -0
  163. data/app/models/spree/shipment_handler.rb +43 -0
  164. data/app/models/spree/shipping_calculator.rb +22 -0
  165. data/app/models/spree/shipping_category.rb +11 -0
  166. data/app/models/spree/shipping_method.rb +69 -0
  167. data/app/models/spree/shipping_method_category.rb +6 -0
  168. data/app/models/spree/shipping_method_zone.rb +6 -0
  169. data/app/models/spree/shipping_rate.rb +44 -0
  170. data/app/models/spree/state.rb +41 -0
  171. data/app/models/spree/state_change.rb +10 -0
  172. data/app/models/spree/stock/adjuster.rb +27 -0
  173. data/app/models/spree/stock/availability_validator.rb +30 -0
  174. data/app/models/spree/stock/content_item.rb +54 -0
  175. data/app/models/spree/stock/coordinator.rb +65 -0
  176. data/app/models/spree/stock/differentiator.rb +44 -0
  177. data/app/models/spree/stock/estimator.rb +70 -0
  178. data/app/models/spree/stock/inventory_unit_builder.rb +22 -0
  179. data/app/models/spree/stock/package.rb +104 -0
  180. data/app/models/spree/stock/packer.rb +48 -0
  181. data/app/models/spree/stock/prioritizer.rb +47 -0
  182. data/app/models/spree/stock/quantifier.rb +29 -0
  183. data/app/models/spree/stock/splitter/backordered.rb +23 -0
  184. data/app/models/spree/stock/splitter/base.rb +28 -0
  185. data/app/models/spree/stock/splitter/shipping_category.rb +32 -0
  186. data/app/models/spree/stock/splitter/weight.rb +34 -0
  187. data/app/models/spree/stock_item.rb +90 -0
  188. data/app/models/spree/stock_location.rb +115 -0
  189. data/app/models/spree/stock_movement.rb +35 -0
  190. data/app/models/spree/stock_transfer.rb +50 -0
  191. data/app/models/spree/store.rb +48 -0
  192. data/app/models/spree/store_credit.rb +252 -0
  193. data/app/models/spree/store_credit_category.rb +22 -0
  194. data/app/models/spree/store_credit_event.rb +38 -0
  195. data/app/models/spree/store_credit_type.rb +6 -0
  196. data/app/models/spree/tax_category.rb +18 -0
  197. data/app/models/spree/tax_rate.rb +110 -0
  198. data/app/models/spree/taxon.rb +102 -0
  199. data/app/models/spree/taxonomy.rb +24 -0
  200. data/app/models/spree/tracker.rb +19 -0
  201. data/app/models/spree/validations/db_maximum_length_validator.rb +23 -0
  202. data/app/models/spree/variant.rb +312 -0
  203. data/app/models/spree/zone.rb +190 -0
  204. data/app/models/spree/zone_member.rb +12 -0
  205. data/app/validators/db_maximum_length_validator.rb +11 -0
  206. data/app/views/layouts/spree/base_mailer.html.erb +41 -0
  207. data/app/views/spree/admin/orders/customer_details/_autocomplete.js.erb +19 -0
  208. data/app/views/spree/order_mailer/_adjustment.html.erb +8 -0
  209. data/app/views/spree/order_mailer/_subtotal.html.erb +8 -0
  210. data/app/views/spree/order_mailer/_total.html.erb +8 -0
  211. data/app/views/spree/order_mailer/cancel_email.html.erb +30 -0
  212. data/app/views/spree/order_mailer/cancel_email.text.erb +16 -0
  213. data/app/views/spree/order_mailer/confirm_email.html.erb +70 -0
  214. data/app/views/spree/order_mailer/confirm_email.text.erb +38 -0
  215. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +22 -0
  216. data/app/views/spree/shared/_base_mailer_footer.html.erb +20 -0
  217. data/app/views/spree/shared/_base_mailer_header.html.erb +29 -0
  218. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +777 -0
  219. data/app/views/spree/shared/_error_messages.html.erb +11 -0
  220. data/app/views/spree/shared/_mailer_line_item.html.erb +12 -0
  221. data/app/views/spree/shipment_mailer/shipped_email.html.erb +41 -0
  222. data/app/views/spree/shipment_mailer/shipped_email.text.erb +16 -0
  223. data/app/views/spree/test_mailer/test_email.html.erb +40 -0
  224. data/app/views/spree/test_mailer/test_email.text.erb +4 -0
  225. data/config/initializers/assets.rb +1 -0
  226. data/config/initializers/friendly_id.rb +88 -0
  227. data/config/initializers/premailer_assets.rb +1 -0
  228. data/config/initializers/premailer_rails.rb +3 -0
  229. data/config/initializers/user_class_extensions.rb +10 -0
  230. data/config/locales/en.yml +1499 -0
  231. data/config/routes.rb +8 -0
  232. data/db/default/spree/countries.rb +29 -0
  233. data/db/default/spree/default_reimbursement_type.rb +1 -0
  234. data/db/default/spree/roles.rb +2 -0
  235. data/db/default/spree/states.rb +28 -0
  236. data/db/default/spree/stores.rb +9 -0
  237. data/db/default/spree/zones.rb +11 -0
  238. data/db/migrate/20120831092320_spree_one_two.rb +481 -0
  239. data/db/migrate/20120831092359_spree_promo_one_two.rb +45 -0
  240. data/db/migrate/20120905145253_add_tax_rate_label.rb +5 -0
  241. data/db/migrate/20120905151823_add_toggle_tax_rate_display.rb +5 -0
  242. data/db/migrate/20120929093553_remove_unused_preference_columns.rb +8 -0
  243. data/db/migrate/20121009142519_add_lock_version_to_variant.rb +5 -0
  244. data/db/migrate/20121010142909_add_states_required_to_countries.rb +5 -0
  245. data/db/migrate/20121012071449_add_on_demand_to_product_and_variant.rb +6 -0
  246. data/db/migrate/20121017010007_remove_not_null_constraint_from_products_on_hand.rb +11 -0
  247. data/db/migrate/20121031162139_split_prices_from_variants.rb +31 -0
  248. data/db/migrate/20121107003422_remove_not_null_from_spree_prices_amount.rb +9 -0
  249. data/db/migrate/20121107184631_add_currency_to_line_items.rb +5 -0
  250. data/db/migrate/20121107194006_add_currency_to_orders.rb +5 -0
  251. data/db/migrate/20121109173623_add_cost_currency_to_variants.rb +5 -0
  252. data/db/migrate/20121111231553_remove_display_on_from_payment_methods.rb +5 -0
  253. data/db/migrate/20121124203911_add_position_to_taxonomies.rb +5 -0
  254. data/db/migrate/20121126040517_add_last_ip_to_spree_orders.rb +5 -0
  255. data/db/migrate/20121213162028_add_state_to_spree_adjustments.rb +6 -0
  256. data/db/migrate/20130114053446_add_display_on_to_spree_payment_methods.rb +9 -0
  257. data/db/migrate/20130120201805_add_position_to_product_properties.spree.rb +6 -0
  258. data/db/migrate/20130203232234_add_identifier_to_spree_payments.rb +5 -0
  259. data/db/migrate/20130207155350_add_order_id_index_to_payments.rb +9 -0
  260. data/db/migrate/20130208032954_add_primary_to_spree_products_taxons.rb +5 -0
  261. data/db/migrate/20130211190146_create_spree_stock_items.rb +14 -0
  262. data/db/migrate/20130211191120_create_spree_stock_locations.rb +11 -0
  263. data/db/migrate/20130213191427_create_default_stock.rb +33 -0
  264. data/db/migrate/20130222032153_add_order_id_index_to_shipments.rb +5 -0
  265. data/db/migrate/20130226032817_change_meta_description_on_spree_products_to_text.rb +5 -0
  266. data/db/migrate/20130226191231_add_stock_location_id_to_spree_shipments.rb +5 -0
  267. data/db/migrate/20130227143905_add_pending_to_inventory_unit.rb +6 -0
  268. data/db/migrate/20130228164411_remove_on_demand_from_product_and_variant.rb +6 -0
  269. data/db/migrate/20130228210442_create_shipping_method_zone.rb +21 -0
  270. data/db/migrate/20130301162745_remove_shipping_category_id_from_shipping_method.rb +5 -0
  271. data/db/migrate/20130301162924_create_shipping_method_categories.rb +13 -0
  272. data/db/migrate/20130301205200_add_tracking_url_to_spree_shipping_methods.rb +5 -0
  273. data/db/migrate/20130304162240_create_spree_shipping_rates.rb +24 -0
  274. data/db/migrate/20130304192936_remove_category_match_attributes_from_shipping_method.rb +7 -0
  275. data/db/migrate/20130305143310_create_stock_movements.rb +12 -0
  276. data/db/migrate/20130306181701_add_address_fields_to_stock_location.rb +22 -0
  277. data/db/migrate/20130306191917_add_active_field_to_stock_locations.rb +5 -0
  278. data/db/migrate/20130306195650_add_backorderable_to_stock_item.rb +5 -0
  279. data/db/migrate/20130307161754_add_default_quantity_to_stock_movement.rb +5 -0
  280. data/db/migrate/20130318151756_add_source_and_destination_to_stock_movements.rb +8 -0
  281. data/db/migrate/20130319062004_change_orders_total_precision.rb +8 -0
  282. data/db/migrate/20130319063911_change_spree_payments_amount_precision.rb +7 -0
  283. data/db/migrate/20130319064308_change_spree_return_authorization_amount_precision.rb +7 -0
  284. data/db/migrate/20130319082943_change_adjustments_amount_precision.rb +7 -0
  285. data/db/migrate/20130319183250_add_originator_to_stock_movement.rb +7 -0
  286. data/db/migrate/20130319190507_drop_source_and_destination_from_stock_movement.rb +15 -0
  287. data/db/migrate/20130325163316_migrate_inventory_unit_sold_to_on_hand.rb +9 -0
  288. data/db/migrate/20130326175857_add_stock_location_to_rma.rb +5 -0
  289. data/db/migrate/20130328130308_update_shipment_state_for_canceled_orders.rb +15 -0
  290. data/db/migrate/20130328195253_add_seo_metas_to_taxons.rb +9 -0
  291. data/db/migrate/20130329134939_remove_stock_item_and_variant_lock.rb +14 -0
  292. data/db/migrate/20130413230529_add_name_to_spree_credit_cards.rb +5 -0
  293. data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +13 -0
  294. data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +5 -0
  295. data/db/migrate/20130417120035_update_adjustment_states.rb +16 -0
  296. data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +15 -0
  297. data/db/migrate/20130418125341_create_spree_stock_transfers.rb +14 -0
  298. data/db/migrate/20130423110707_drop_products_count_on_hand.rb +5 -0
  299. data/db/migrate/20130423223847_set_default_shipping_rate_cost.rb +5 -0
  300. data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +23 -0
  301. data/db/migrate/20130514151929_add_sku_index_to_spree_variants.rb +5 -0
  302. data/db/migrate/20130515180736_add_backorderable_default_to_spree_stock_location.rb +5 -0
  303. data/db/migrate/20130516151222_add_propage_all_variants_to_spree_stock_location.rb +5 -0
  304. data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +5 -0
  305. data/db/migrate/20130611185927_add_user_id_index_to_spree_orders.rb +5 -0
  306. data/db/migrate/20130618041418_add_updated_at_to_spree_countries.rb +9 -0
  307. data/db/migrate/20130619012236_add_updated_at_to_spree_states.rb +9 -0
  308. data/db/migrate/20130626232741_add_cvv_result_code_and_cvv_result_message_to_spree_payments.rb +6 -0
  309. data/db/migrate/20130628021056_add_unique_index_to_permalink_on_spree_products.rb +5 -0
  310. data/db/migrate/20130628022817_add_unique_index_to_orders_shipments_and_stock_transfers.rb +7 -0
  311. data/db/migrate/20130708052307_add_deleted_at_to_spree_tax_rates.rb +5 -0
  312. data/db/migrate/20130711200933_remove_lock_version_from_inventory_units.rb +6 -0
  313. data/db/migrate/20130718042445_add_cost_price_to_line_item.rb +5 -0
  314. data/db/migrate/20130718233855_set_backorderable_to_default_to_false.rb +6 -0
  315. data/db/migrate/20130725031716_add_created_by_id_to_spree_orders.rb +5 -0
  316. data/db/migrate/20130729214043_index_completed_at_on_spree_orders.rb +5 -0
  317. data/db/migrate/20130802014537_add_tax_category_id_to_spree_line_items.rb +5 -0
  318. data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +10 -0
  319. data/db/migrate/20130806022521_drop_spree_mail_methods.rb +12 -0
  320. data/db/migrate/20130806145853_set_default_stock_location_on_shipments.rb +8 -0
  321. data/db/migrate/20130807024301_upgrade_adjustments.rb +40 -0
  322. data/db/migrate/20130807024302_rename_adjustment_fields.rb +14 -0
  323. data/db/migrate/20130809164245_add_admin_name_column_to_spree_shipping_methods.rb +5 -0
  324. data/db/migrate/20130809164330_add_admin_name_column_to_spree_stock_locations.rb +5 -0
  325. data/db/migrate/20130813004002_add_shipment_total_to_spree_orders.rb +5 -0
  326. data/db/migrate/20130813140619_expand_order_number_size.rb +9 -0
  327. data/db/migrate/20130813232134_rename_activators_to_promotions.rb +5 -0
  328. data/db/migrate/20130815000406_add_adjustment_total_to_line_items.rb +5 -0
  329. data/db/migrate/20130815024413_add_adjustment_total_to_shipments.rb +5 -0
  330. data/db/migrate/20130826062534_add_depth_to_spree_taxons.rb +16 -0
  331. data/db/migrate/20130828234942_add_tax_total_to_line_items_shipments_and_orders.rb +8 -0
  332. data/db/migrate/20130830001033_add_shipping_category_to_shipping_methods_and_products.rb +15 -0
  333. data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +19 -0
  334. data/db/migrate/20130903183026_add_code_to_spree_promotion_rules.rb +5 -0
  335. data/db/migrate/20130909115621_change_states_required_for_countries.rb +9 -0
  336. data/db/migrate/20130915032339_add_deleted_at_to_spree_stock_items.rb +5 -0
  337. data/db/migrate/20130917024658_remove_promotions_event_name_field.rb +5 -0
  338. data/db/migrate/20130924040529_add_promo_total_to_line_items_and_shipments_and_orders.rb +7 -0
  339. data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +16 -0
  340. data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +5 -0
  341. data/db/migrate/20131107132123_add_tax_category_to_variants.rb +6 -0
  342. data/db/migrate/20131113035136_add_channel_to_spree_orders.rb +5 -0
  343. data/db/migrate/20131118043959_add_included_to_adjustments.rb +5 -0
  344. data/db/migrate/20131118050234_rename_tax_total_fields.rb +11 -0
  345. data/db/migrate/20131118183431_add_line_item_id_to_spree_inventory_units.rb +21 -0
  346. data/db/migrate/20131120234456_add_updated_at_to_variants.rb +5 -0
  347. data/db/migrate/20131127001002_add_position_to_classifications.rb +5 -0
  348. data/db/migrate/20131211112807_create_spree_orders_promotions.rb +8 -0
  349. data/db/migrate/20131211192741_unique_shipping_method_categories.rb +24 -0
  350. data/db/migrate/20131218054603_add_item_count_to_spree_orders.rb +5 -0
  351. data/db/migrate/20140106065820_remove_value_type_from_spree_preferences.rb +8 -0
  352. data/db/migrate/20140106224208_rename_permalink_to_slug_for_products.rb +5 -0
  353. data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +5 -0
  354. data/db/migrate/20140124023232_rename_activator_id_in_rules_and_actions_to_promotion_id.rb +6 -0
  355. data/db/migrate/20140129024326_add_deleted_at_to_spree_prices.rb +5 -0
  356. data/db/migrate/20140203161722_add_approver_id_and_approved_at_to_orders.rb +6 -0
  357. data/db/migrate/20140204115338_add_confirmation_delivered_to_spree_orders.rb +5 -0
  358. data/db/migrate/20140204192230_add_auto_capture_to_payment_methods.rb +5 -0
  359. data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +12 -0
  360. data/db/migrate/20140205144710_add_uncaptured_amount_to_payments.rb +5 -0
  361. data/db/migrate/20140205181631_default_variant_weight_to_zero.rb +11 -0
  362. data/db/migrate/20140207085910_add_tax_category_id_to_shipping_methods.rb +5 -0
  363. data/db/migrate/20140207093021_add_tax_rate_id_to_shipping_rates.rb +5 -0
  364. data/db/migrate/20140211040159_add_pre_tax_amount_to_line_items_and_shipments.rb +6 -0
  365. data/db/migrate/20140213184916_add_more_indexes.rb +13 -0
  366. data/db/migrate/20140219060952_add_considered_risky_to_orders.rb +5 -0
  367. data/db/migrate/20140227112348_add_preference_store_to_everything.rb +8 -0
  368. data/db/migrate/20140307235515_add_user_id_to_spree_credit_cards.rb +13 -0
  369. data/db/migrate/20140309023735_migrate_old_preferences.rb +23 -0
  370. data/db/migrate/20140309024355_create_spree_stores.rb +25 -0
  371. data/db/migrate/20140309033438_create_store_from_preferences.rb +37 -0
  372. data/db/migrate/20140315053743_add_timestamps_to_spree_assets.rb +6 -0
  373. data/db/migrate/20140318191500_create_spree_taxons_promotion_rules.rb +8 -0
  374. data/db/migrate/20140331100557_add_additional_store_fields.rb +8 -0
  375. data/db/migrate/20140410141842_add_many_missing_indexes.rb +18 -0
  376. data/db/migrate/20140410150358_correct_some_polymorphic_index_and_add_more_missing.rb +66 -0
  377. data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +5 -0
  378. data/db/migrate/20140508151342_change_spree_price_amount_precision.rb +8 -0
  379. data/db/migrate/20140518174634_add_token_to_spree_orders.rb +5 -0
  380. data/db/migrate/20140530024945_move_order_token_from_tokenized_permission.rb +29 -0
  381. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +10 -0
  382. data/db/migrate/20140604135309_drop_credit_card_first_name_and_last_name.rb +6 -0
  383. data/db/migrate/20140609201656_add_deleted_at_to_spree_promotion_actions.rb +6 -0
  384. data/db/migrate/20140616202624_remove_uncaptured_amount_from_spree_payments.rb +5 -0
  385. data/db/migrate/20140625214618_create_spree_refunds.rb +12 -0
  386. data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +12 -0
  387. data/db/migrate/20140707125621_rename_return_authorization_inventory_unit_to_return_items.rb +5 -0
  388. data/db/migrate/20140709160534_backfill_line_item_pre_tax_amount.rb +10 -0
  389. data/db/migrate/20140710041921_recreate_spree_return_authorizations.rb +55 -0
  390. data/db/migrate/20140710181204_add_amount_fields_to_return_items.rb +7 -0
  391. data/db/migrate/20140710190048_drop_return_authorization_amount.rb +5 -0
  392. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +28 -0
  393. data/db/migrate/20140713140527_create_spree_refund_reasons.rb +14 -0
  394. data/db/migrate/20140713142214_rename_return_authorization_reason.rb +5 -0
  395. data/db/migrate/20140715182625_create_spree_promotion_categories.rb +11 -0
  396. data/db/migrate/20140716204111_drop_received_at_on_return_items.rb +9 -0
  397. data/db/migrate/20140716212330_add_reception_and_acceptance_status_to_return_items.rb +6 -0
  398. data/db/migrate/20140717155155_create_default_refund_reason.rb +9 -0
  399. data/db/migrate/20140717185932_add_default_to_spree_stock_locations.rb +7 -0
  400. data/db/migrate/20140718133010_create_spree_customer_returns.rb +9 -0
  401. data/db/migrate/20140718133349_add_customer_return_id_to_return_item.rb +6 -0
  402. data/db/migrate/20140718195325_create_friendly_id_slugs.rb +15 -0
  403. data/db/migrate/20140723004419_rename_spree_refund_return_authorization_id.rb +5 -0
  404. data/db/migrate/20140723152808_increase_return_item_pre_tax_amount_precision.rb +13 -0
  405. data/db/migrate/20140723214541_copy_product_slugs_to_slug_history.rb +15 -0
  406. data/db/migrate/20140725131539_create_spree_reimbursements.rb +21 -0
  407. data/db/migrate/20140728225422_add_promotionable_to_spree_products.rb +5 -0
  408. data/db/migrate/20140729133613_add_exchange_inventory_unit_foreign_keys.rb +7 -0
  409. data/db/migrate/20140730155938_add_acceptance_status_errors_to_return_item.rb +5 -0
  410. data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +20 -0
  411. data/db/migrate/20140804185157_add_default_to_shipment_cost.rb +10 -0
  412. data/db/migrate/20140805171035_add_default_to_spree_credit_cards.rb +5 -0
  413. data/db/migrate/20140805171219_make_existing_credit_cards_default.rb +10 -0
  414. data/db/migrate/20140806144901_add_type_to_reimbursement_type.rb +9 -0
  415. data/db/migrate/20140808184039_create_spree_reimbursement_credits.rb +10 -0
  416. data/db/migrate/20140827170513_add_meta_title_to_spree_products.rb +7 -0
  417. data/db/migrate/20140911173301_add_kind_to_zone.rb +11 -0
  418. data/db/migrate/20140924164824_add_code_to_spree_tax_categories.rb +5 -0
  419. data/db/migrate/20140927193717_default_pre_tax_amount_should_be_zero.rb +6 -0
  420. data/db/migrate/20141002191113_add_code_to_spree_shipping_methods.rb +5 -0
  421. data/db/migrate/20141007230328_add_cancel_audit_fields_to_spree_orders.rb +6 -0
  422. data/db/migrate/20141009204607_add_store_id_to_orders.rb +8 -0
  423. data/db/migrate/20141012083513_create_spree_taxons_prototypes.rb +8 -0
  424. data/db/migrate/20141021194502_add_state_lock_version_to_order.rb +5 -0
  425. data/db/migrate/20141023005240_add_counter_cache_from_spree_variants_to_spree_stock_items.rb +13 -0
  426. data/db/migrate/20141101231208_fix_adjustment_order_presence.rb +13 -0
  427. data/db/migrate/20141105213646_update_classifications_positions.rb +9 -0
  428. data/db/migrate/20141120135441_add_guest_token_index_to_spree_orders.rb +5 -0
  429. data/db/migrate/20141215232040_remove_token_permissions_table.rb +6 -0
  430. data/db/migrate/20141215235502_remove_extra_products_slug_index.rb +5 -0
  431. data/db/migrate/20141217215630_update_product_slug_index.rb +6 -0
  432. data/db/migrate/20141218025915_rename_identifier_to_number_for_payment.rb +5 -0
  433. data/db/migrate/20150118210639_create_spree_store_credits.rb +24 -0
  434. data/db/migrate/20150118211500_create_spree_store_credit_categories.rb +8 -0
  435. data/db/migrate/20150118212051_create_spree_store_credit_events.rb +17 -0
  436. data/db/migrate/20150118212101_create_spree_store_credit_types.rb +10 -0
  437. data/db/migrate/20150121022521_remove_environment_from_payment_method.rb +6 -0
  438. data/db/migrate/20150122145607_add_resellable_to_return_items.rb +5 -0
  439. data/db/migrate/20150122202432_add_code_to_spree_promotion_categories.rb +5 -0
  440. data/db/migrate/20150128032538_remove_environment_from_tracker.rb +6 -0
  441. data/db/migrate/20150128060325_remove_spree_configurations.rb +16 -0
  442. data/db/migrate/20150216173445_add_index_to_spree_stock_items_variant_id.rb +13 -0
  443. data/db/migrate/20150309161154_ensure_payments_have_numbers.rb +9 -0
  444. data/db/migrate/20150314013438_add_missing_indexes_on_spree_tables.rb +67 -0
  445. data/db/migrate/20150317174308_remove_duplicated_indexes_from_multi_columns.rb +18 -0
  446. data/db/migrate/20150324104002_remove_user_index_from_spree_state_changes.rb +14 -0
  447. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +70 -0
  448. data/db/migrate/20150522071831_add_position_to_spree_payment_methods.rb +5 -0
  449. data/db/migrate/20150522181728_add_deleted_at_to_friendly_id_slugs.rb +6 -0
  450. data/db/migrate/20150609093816_increase_scale_on_pre_tax_amounts.rb +16 -0
  451. data/db/migrate/20150626181949_add_taxable_adjustment_total_to_line_item.rb +19 -0
  452. data/db/migrate/20150627090949_migrate_payment_methods_display.rb +12 -0
  453. data/db/migrate/20150707204155_enable_acts_as_paranoid_on_calculators.rb +6 -0
  454. data/db/migrate/20150714154102_spree_payment_method_store_credits.rb +12 -0
  455. data/db/migrate/20150726141425_rename_has_and_belongs_to_associations_to_model_names.rb +18 -0
  456. data/db/migrate/20150727191614_spree_store_credit_types.rb +11 -0
  457. data/db/migrate/20150819154308_add_discontinued_to_products_and_variants.rb +68 -0
  458. data/db/migrate/20151220072838_remove_shipping_method_id_from_spree_orders.rb +13 -0
  459. data/db/migrate/20160207191757_add_id_column_to_earlier_habtm_tables.rb +16 -0
  460. data/db/migrate/20160219165458_add_indexes.rb +14 -0
  461. data/db/seeds.rb +5 -0
  462. data/lib/generators/spree/custom_user/custom_user_generator.rb +56 -0
  463. data/lib/generators/spree/custom_user/templates/authentication_helpers.rb.tt +36 -0
  464. data/lib/generators/spree/custom_user/templates/initializer.rb.tt +1 -0
  465. data/lib/generators/spree/custom_user/templates/migration.rb.tt +7 -0
  466. data/lib/generators/spree/dummy/dummy_generator.rb +140 -0
  467. data/lib/generators/spree/dummy/templates/initializers/custom_user.rb +1 -0
  468. data/lib/generators/spree/dummy/templates/initializers/devise.rb +3 -0
  469. data/lib/generators/spree/dummy/templates/rails/application.rb +10 -0
  470. data/lib/generators/spree/dummy/templates/rails/boot.rb +6 -0
  471. data/lib/generators/spree/dummy/templates/rails/database.yml +72 -0
  472. data/lib/generators/spree/dummy/templates/rails/routes.rb +2 -0
  473. data/lib/generators/spree/dummy/templates/rails/script/rails +6 -0
  474. data/lib/generators/spree/dummy/templates/rails/test.rb +35 -0
  475. data/lib/generators/spree/install/install_generator.rb +233 -0
  476. data/lib/generators/spree/install/templates/config/initializers/spree.rb +18 -0
  477. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +17 -0
  478. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +17 -0
  479. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/backend/all.css +16 -0
  480. data/lib/generators/spree/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +16 -0
  481. data/lib/spree/core/controller_helpers/auth.rb +84 -0
  482. data/lib/spree/core/controller_helpers/common.rb +74 -0
  483. data/lib/spree/core/controller_helpers/order.rb +103 -0
  484. data/lib/spree/core/controller_helpers/respond_with.rb +65 -0
  485. data/lib/spree/core/controller_helpers/search.rb +14 -0
  486. data/lib/spree/core/controller_helpers/store.rb +51 -0
  487. data/lib/spree/core/controller_helpers/strong_parameters.rb +42 -0
  488. data/lib/spree/core/delegate_belongs_to.rb +95 -0
  489. data/lib/spree/core/engine.rb +134 -0
  490. data/lib/spree/core/environment/calculators.rb +12 -0
  491. data/lib/spree/core/environment.rb +15 -0
  492. data/lib/spree/core/environment_extension.rb +25 -0
  493. data/lib/spree/core/importer/order.rb +284 -0
  494. data/lib/spree/core/importer/product.rb +62 -0
  495. data/lib/spree/core/importer.rb +9 -0
  496. data/lib/spree/core/number_generator.rb +52 -0
  497. data/lib/spree/core/product_duplicator.rb +74 -0
  498. data/lib/spree/core/product_filters.rb +194 -0
  499. data/lib/spree/core/routes.rb +46 -0
  500. data/lib/spree/core/search/base.rb +102 -0
  501. data/lib/spree/core/token_generator.rb +22 -0
  502. data/lib/spree/core/validators/email.rb +11 -0
  503. data/lib/spree/core/version.rb +5 -0
  504. data/lib/spree/core.rb +96 -0
  505. data/lib/spree/i18n/base.rb +17 -0
  506. data/lib/spree/i18n/initializer.rb +1 -0
  507. data/lib/spree/i18n.rb +37 -0
  508. data/lib/spree/localized_number.rb +22 -0
  509. data/lib/spree/migrations.rb +77 -0
  510. data/lib/spree/money.rb +61 -0
  511. data/lib/spree/permitted_attributes.rb +115 -0
  512. data/lib/spree/promo/environment.rb +9 -0
  513. data/lib/spree/responder.rb +45 -0
  514. data/lib/spree/testing_support/ability_helpers.rb +105 -0
  515. data/lib/spree/testing_support/authorization_helpers.rb +63 -0
  516. data/lib/spree/testing_support/bar_ability.rb +14 -0
  517. data/lib/spree/testing_support/caching.rb +47 -0
  518. data/lib/spree/testing_support/capybara_ext.rb +179 -0
  519. data/lib/spree/testing_support/common_rake.rb +39 -0
  520. data/lib/spree/testing_support/controller_requests.rb +93 -0
  521. data/lib/spree/testing_support/extension_rake.rb +10 -0
  522. data/lib/spree/testing_support/factories/address_factory.rb +22 -0
  523. data/lib/spree/testing_support/factories/adjustment_factory.rb +26 -0
  524. data/lib/spree/testing_support/factories/calculator_factory.rb +20 -0
  525. data/lib/spree/testing_support/factories/country_factory.rb +9 -0
  526. data/lib/spree/testing_support/factories/credit_card_factory.rb +10 -0
  527. data/lib/spree/testing_support/factories/customer_return_factory.rb +31 -0
  528. data/lib/spree/testing_support/factories/image_factory.rb +5 -0
  529. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +10 -0
  530. data/lib/spree/testing_support/factories/line_item_factory.rb +12 -0
  531. data/lib/spree/testing_support/factories/options_factory.rb +13 -0
  532. data/lib/spree/testing_support/factories/order_factory.rb +91 -0
  533. data/lib/spree/testing_support/factories/payment_factory.rb +28 -0
  534. data/lib/spree/testing_support/factories/payment_method_factory.rb +23 -0
  535. data/lib/spree/testing_support/factories/price_factory.rb +7 -0
  536. data/lib/spree/testing_support/factories/product_factory.rb +36 -0
  537. data/lib/spree/testing_support/factories/product_option_type_factory.rb +6 -0
  538. data/lib/spree/testing_support/factories/product_property_factory.rb +6 -0
  539. data/lib/spree/testing_support/factories/promotion_category_factory.rb +6 -0
  540. data/lib/spree/testing_support/factories/promotion_factory.rb +52 -0
  541. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +5 -0
  542. data/lib/spree/testing_support/factories/property_factory.rb +6 -0
  543. data/lib/spree/testing_support/factories/prototype_factory.rb +11 -0
  544. data/lib/spree/testing_support/factories/refund_factory.rb +22 -0
  545. data/lib/spree/testing_support/factories/reimbursement_factory.rb +16 -0
  546. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +7 -0
  547. data/lib/spree/testing_support/factories/return_authorization_factory.rb +20 -0
  548. data/lib/spree/testing_support/factories/return_item_factory.rb +14 -0
  549. data/lib/spree/testing_support/factories/role_factory.rb +9 -0
  550. data/lib/spree/testing_support/factories/shipment_factory.rb +23 -0
  551. data/lib/spree/testing_support/factories/shipping_category_factory.rb +5 -0
  552. data/lib/spree/testing_support/factories/shipping_method_factory.rb +21 -0
  553. data/lib/spree/testing_support/factories/state_factory.rb +13 -0
  554. data/lib/spree/testing_support/factories/stock_factory.rb +31 -0
  555. data/lib/spree/testing_support/factories/stock_item_factory.rb +9 -0
  556. data/lib/spree/testing_support/factories/stock_location_factory.rb +28 -0
  557. data/lib/spree/testing_support/factories/stock_movement_factory.rb +11 -0
  558. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +9 -0
  559. data/lib/spree/testing_support/factories/store_credit_event_factory.rb +8 -0
  560. data/lib/spree/testing_support/factories/store_credit_factory.rb +17 -0
  561. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +11 -0
  562. data/lib/spree/testing_support/factories/store_factory.rb +8 -0
  563. data/lib/spree/testing_support/factories/tax_category_factory.rb +6 -0
  564. data/lib/spree/testing_support/factories/tax_rate_factory.rb +8 -0
  565. data/lib/spree/testing_support/factories/taxon_factory.rb +7 -0
  566. data/lib/spree/testing_support/factories/taxonomy_factory.rb +5 -0
  567. data/lib/spree/testing_support/factories/tracker_factory.rb +6 -0
  568. data/lib/spree/testing_support/factories/user_factory.rb +22 -0
  569. data/lib/spree/testing_support/factories/variant_factory.rb +39 -0
  570. data/lib/spree/testing_support/factories/zone_factory.rb +25 -0
  571. data/lib/spree/testing_support/factories/zone_member_factory.rb +6 -0
  572. data/lib/spree/testing_support/factories.rb +19 -0
  573. data/lib/spree/testing_support/flash.rb +27 -0
  574. data/lib/spree/testing_support/i18n.rb +97 -0
  575. data/lib/spree/testing_support/microdata.rb +189 -0
  576. data/lib/spree/testing_support/order_walkthrough.rb +72 -0
  577. data/lib/spree/testing_support/preferences.rb +31 -0
  578. data/lib/spree/testing_support/url_helpers.rb +9 -0
  579. data/lib/spree_core.rb +1 -0
  580. data/lib/tasks/core.rake +169 -0
  581. data/lib/tasks/email.rake +10 -0
  582. data/lib/tasks/exchanges.rake +70 -0
  583. data/script/rails +9 -0
  584. data/spec/fixtures/microdata.html +22 -0
  585. data/spec/fixtures/microdata_itemref.html +15 -0
  586. data/spec/fixtures/microdata_no_itemscope.html +20 -0
  587. data/spec/fixtures/thinking-cat.jpg +0 -0
  588. data/spree_core.gemspec +47 -0
  589. data/vendor/assets/javascripts/jquery-migrate-1.0.0.js +498 -0
  590. data/vendor/assets/javascripts/jquery.payment.js +652 -0
  591. data/vendor/assets/javascripts/jsuri.js +2 -0
  592. data/vendor/assets/stylesheets/normalize.css +375 -0
  593. data/vendor/assets/stylesheets/skeleton.css +242 -0
  594. metadata +971 -0
@@ -0,0 +1,271 @@
1
+ module Spree
2
+ class Product < Spree::Base
3
+ cattr_accessor :search_scopes do
4
+ []
5
+ end
6
+
7
+ def self.add_search_scope(name, &block)
8
+ self.singleton_class.send(:define_method, name.to_sym, &block)
9
+ search_scopes << name.to_sym
10
+ end
11
+
12
+ def self.simple_scopes
13
+ [
14
+ :ascend_by_updated_at,
15
+ :descend_by_updated_at,
16
+ :ascend_by_name,
17
+ :descend_by_name
18
+ ]
19
+ end
20
+
21
+ def self.add_simple_scopes(scopes)
22
+ scopes.each do |name|
23
+ # We should not define price scopes here, as they require something slightly different
24
+ next if name.to_s.include?("master_price")
25
+ parts = name.to_s.match(/(.*)_by_(.*)/)
26
+ self.scope(name.to_s, -> { order("#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? "ASC" : "DESC"}") })
27
+ end
28
+ end
29
+
30
+ def self.property_conditions(property)
31
+ properties = Property.table_name
32
+ conditions = case property
33
+ when String then { "#{properties}.name" => property }
34
+ when Property then { "#{properties}.id" => property.id }
35
+ else { "#{properties}.id" => property.to_i }
36
+ end
37
+ end
38
+
39
+ add_simple_scopes simple_scopes
40
+
41
+ add_search_scope :ascend_by_master_price do
42
+ joins(:master => :default_price).order("#{price_table_name}.amount ASC")
43
+ end
44
+
45
+ add_search_scope :descend_by_master_price do
46
+ joins(:master => :default_price).order("#{price_table_name}.amount DESC")
47
+ end
48
+
49
+ add_search_scope :price_between do |low, high|
50
+ joins(:master => :default_price).where(Price.table_name => { :amount => low..high })
51
+ end
52
+
53
+ add_search_scope :master_price_lte do |price|
54
+ joins(:master => :default_price).where("#{price_table_name}.amount <= ?", price)
55
+ end
56
+
57
+ add_search_scope :master_price_gte do |price|
58
+ joins(:master => :default_price).where("#{price_table_name}.amount >= ?", price)
59
+ end
60
+
61
+ # This scope selects products in taxon AND all its descendants
62
+ # If you need products only within one taxon use
63
+ #
64
+ # Spree::Product.joins(:taxons).where(Taxon.table_name => { :id => taxon.id })
65
+ #
66
+ # If you're using count on the result of this scope, you must use the
67
+ # `:distinct` option as well:
68
+ #
69
+ # Spree::Product.in_taxon(taxon).count(:distinct => true)
70
+ #
71
+ # This is so that the count query is distinct'd:
72
+ #
73
+ # SELECT COUNT(DISTINCT "spree_products"."id") ...
74
+ #
75
+ # vs.
76
+ #
77
+ # SELECT COUNT(*) ...
78
+ add_search_scope :in_taxon do |taxon|
79
+ includes(:classifications).
80
+ where("spree_products_taxons.taxon_id" => taxon.self_and_descendants.pluck(:id)).
81
+ order("spree_products_taxons.position ASC")
82
+ end
83
+
84
+ # This scope selects products in all taxons AND all its descendants
85
+ # If you need products only within one taxon use
86
+ #
87
+ # Spree::Product.taxons_id_eq([x,y])
88
+ add_search_scope :in_taxons do |*taxons|
89
+ taxons = get_taxons(taxons)
90
+ taxons.first ? prepare_taxon_conditions(taxons) : where(nil)
91
+ end
92
+
93
+ # a scope that finds all products having property specified by name, object or id
94
+ add_search_scope :with_property do |property|
95
+ joins(:properties).where(property_conditions(property))
96
+ end
97
+
98
+ # a simple test for product with a certain property-value pairing
99
+ # note that it can test for properties with NULL values, but not for absent values
100
+ add_search_scope :with_property_value do |property, value|
101
+ joins(:properties)
102
+ .where("#{ProductProperty.table_name}.value = ?", value)
103
+ .where(property_conditions(property))
104
+ end
105
+
106
+ add_search_scope :with_option do |option|
107
+ option_types = OptionType.table_name
108
+ conditions = case option
109
+ when String then { "#{option_types}.name" => option }
110
+ when OptionType then { "#{option_types}.id" => option.id }
111
+ else { "#{option_types}.id" => option.to_i }
112
+ end
113
+
114
+ joins(:option_types).where(conditions)
115
+ end
116
+
117
+ add_search_scope :with_option_value do |option, value|
118
+ option_values = OptionValue.table_name
119
+ option_type_id = case option
120
+ when String then OptionType.find_by(name: option) || option.to_i
121
+ when OptionType then option.id
122
+ else option.to_i
123
+ end
124
+
125
+ conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
126
+ group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
127
+ end
128
+
129
+ # Finds all products which have either:
130
+ # 1) have an option value with the name matching the one given
131
+ # 2) have a product property with a value matching the one given
132
+ add_search_scope :with do |value|
133
+ includes(variants_including_master: :option_values).
134
+ includes(:product_properties).
135
+ where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
136
+ end
137
+
138
+ # Finds all products that have a name containing the given words.
139
+ add_search_scope :in_name do |words|
140
+ like_any([:name], prepare_words(words))
141
+ end
142
+
143
+ # Finds all products that have a name or meta_keywords containing the given words.
144
+ add_search_scope :in_name_or_keywords do |words|
145
+ like_any([:name, :meta_keywords], prepare_words(words))
146
+ end
147
+
148
+ # Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
149
+ add_search_scope :in_name_or_description do |words|
150
+ like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
151
+ end
152
+
153
+ # Finds all products that have the ids matching the given collection of ids.
154
+ # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
155
+ add_search_scope :with_ids do |*ids|
156
+ where(id: ids)
157
+ end
158
+
159
+ # Sorts products from most popular (popularity is extracted from how many
160
+ # times use has put product in cart, not completed orders)
161
+ #
162
+ # there is alternative faster and more elegant solution, it has small drawback though,
163
+ # it doesn stack with other scopes :/
164
+ #
165
+ # :joins => "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
166
+ # :order => 'COALESCE(cnt, 0) DESC'
167
+ add_search_scope :descend_by_popularity do
168
+ joins(:master).
169
+ order(%Q{
170
+ COALESCE((
171
+ SELECT
172
+ COUNT(#{LineItem.quoted_table_name}.id)
173
+ FROM
174
+ #{LineItem.quoted_table_name}
175
+ JOIN
176
+ #{Variant.quoted_table_name} AS popular_variants
177
+ ON
178
+ popular_variants.id = #{LineItem.quoted_table_name}.variant_id
179
+ WHERE
180
+ popular_variants.product_id = #{Product.quoted_table_name}.id
181
+ ), 0) DESC
182
+ })
183
+ end
184
+
185
+ add_search_scope :not_deleted do
186
+ where("#{Product.quoted_table_name}.deleted_at IS NULL or #{Product.quoted_table_name}.deleted_at >= ?", Time.zone.now)
187
+ end
188
+
189
+ def self.not_discontinued(only_not_discontinued = true)
190
+ if only_not_discontinued != '0' && only_not_discontinued
191
+ where("#{Product.quoted_table_name}.discontinue_on IS NULL or #{Product.quoted_table_name}.discontinue_on >= ?", Time.zone.now)
192
+ else
193
+ all
194
+ end
195
+ end
196
+ search_scopes << :not_discontinued
197
+ # Can't use add_search_scope for this as it needs a default argument
198
+ def self.available(available_on = nil, currency = nil)
199
+ available_on ||= Time.current
200
+ not_discontinued.joins(master: :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on).uniq
201
+ end
202
+ search_scopes << :available
203
+
204
+ def self.active(currency = nil)
205
+ not_discontinued.available(nil, currency)
206
+ end
207
+ search_scopes << :active
208
+
209
+ add_search_scope :taxons_name_eq do |name|
210
+ group("spree_products.id").joins(:taxons).where(Taxon.arel_table[:name].eq(name))
211
+ end
212
+
213
+ def self.distinct_by_product_ids(sort_order = nil)
214
+ sort_column = sort_order.split(" ").first
215
+
216
+ # Postgres will complain when using ordering by expressions not present in
217
+ # SELECT DISTINCT. e.g.
218
+ #
219
+ # PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY
220
+ # expressions must appear in select list. e.g.
221
+ #
222
+ # SELECT DISTINCT "spree_products".* FROM "spree_products" LEFT OUTER JOIN
223
+ # "spree_variants" ON "spree_variants"."product_id" = "spree_products"."id" AND "spree_variants"."is_master" = 't'
224
+ # AND "spree_variants"."deleted_at" IS NULL LEFT OUTER JOIN "spree_prices" ON
225
+ # "spree_prices"."variant_id" = "spree_variants"."id" AND "spree_prices"."currency" = 'USD'
226
+ # AND "spree_prices"."deleted_at" IS NULL WHERE "spree_products"."deleted_at" IS NULL AND ('t'='t')
227
+ # ORDER BY "spree_prices"."amount" ASC LIMIT 10 OFFSET 0
228
+ #
229
+ # Don't allow sort_column, a variable coming from params,
230
+ # to be anything but a column in the database
231
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' && !column_names.include?(sort_column)
232
+ all
233
+ else
234
+ distinct
235
+ end
236
+ end
237
+
238
+ private
239
+
240
+ def self.price_table_name
241
+ Price.quoted_table_name
242
+ end
243
+
244
+ # specifically avoid having an order for taxon search (conflicts with main order)
245
+ def self.prepare_taxon_conditions(taxons)
246
+ ids = taxons.map { |taxon| taxon.self_and_descendants.pluck(:id) }.flatten.uniq
247
+ joins(:taxons).where("#{Taxon.table_name}.id" => ids)
248
+ end
249
+
250
+ # Produce an array of keywords for use in scopes.
251
+ # Always return array with at least an empty string to avoid SQL errors
252
+ def self.prepare_words(words)
253
+ return [''] if words.blank?
254
+ a = words.split(/[,\s]/).map(&:strip)
255
+ a.any? ? a : ['']
256
+ end
257
+
258
+ def self.get_taxons(*ids_or_records_or_names)
259
+ taxons = Taxon.table_name
260
+ ids_or_records_or_names.flatten.map { |t|
261
+ case t
262
+ when Integer then Taxon.find_by(id: t)
263
+ when ActiveRecord::Base then t
264
+ when String
265
+ Taxon.find_by(name: t) ||
266
+ Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
267
+ end
268
+ }.compact.flatten.uniq
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,384 @@
1
+ # PRODUCTS
2
+ # Products represent an entity for sale in a store.
3
+ # Products can have variations, called variants
4
+ # Products properties include description, permalink, availability,
5
+ # shipping category, etc. that do not change by variant.
6
+ #
7
+ # MASTER VARIANT
8
+ # Every product has one master variant, which stores master price and sku, size and weight, etc.
9
+ # The master variant does not have option values associated with it.
10
+ # Price, SKU, size, weight, etc. are all delegated to the master variant.
11
+ # Contains on_hand inventory levels only when there are no variants for the product.
12
+ #
13
+ # VARIANTS
14
+ # All variants can access the product properties directly (via reverse delegation).
15
+ # Inventory units are tied to Variant.
16
+ # The master variant can have inventory units, but not option values.
17
+ # All other variants have option values and may have inventory units.
18
+ # Sum of on_hand each variant's inventory level determine "on_hand" level for the product.
19
+ #
20
+
21
+ module Spree
22
+ class Product < Spree::Base
23
+ extend FriendlyId
24
+ friendly_id :slug_candidates, use: :history
25
+
26
+ acts_as_paranoid
27
+
28
+ # we need to have this callback before any dependent: :destroy associations
29
+ # https://github.com/rails/rails/issues/3458
30
+ before_destroy :ensure_no_line_items
31
+
32
+ has_many :product_option_types, dependent: :destroy, inverse_of: :product
33
+ has_many :option_types, through: :product_option_types
34
+ has_many :product_properties, dependent: :destroy, inverse_of: :product
35
+ has_many :properties, through: :product_properties
36
+
37
+ has_many :classifications, dependent: :delete_all, inverse_of: :product
38
+ has_many :taxons, through: :classifications, before_remove: :remove_taxon
39
+
40
+ has_many :product_promotion_rules, class_name: 'Spree::ProductPromotionRule'
41
+ has_many :promotion_rules, through: :product_promotion_rules, class_name: 'Spree::PromotionRule'
42
+
43
+ has_many :promotions, through: :promotion_rules, class_name: 'Spree::Promotion'
44
+
45
+ has_many :possible_promotions, -> { advertised.active }, through: :promotion_rules,
46
+ class_name: 'Spree::Promotion',
47
+ source: :promotion
48
+
49
+ belongs_to :tax_category, class_name: 'Spree::TaxCategory'
50
+ belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products
51
+
52
+ has_one :master,
53
+ -> { where is_master: true },
54
+ inverse_of: :product,
55
+ class_name: 'Spree::Variant'
56
+
57
+ has_many :variants,
58
+ -> { where(is_master: false).order(:position) },
59
+ inverse_of: :product,
60
+ class_name: 'Spree::Variant'
61
+
62
+ has_many :variants_including_master,
63
+ -> { order(:position) },
64
+ inverse_of: :product,
65
+ class_name: 'Spree::Variant',
66
+ dependent: :destroy
67
+
68
+ has_many :prices, -> { order('spree_variants.position, spree_variants.id, currency') }, through: :variants
69
+
70
+ has_many :stock_items, through: :variants_including_master
71
+
72
+ has_many :line_items, through: :variants_including_master
73
+ has_many :orders, through: :line_items
74
+
75
+ delegate_belongs_to :master, :sku, :price, :currency, :display_amount, :display_price, :weight, :height, :width, :depth,
76
+ :is_master, :has_default_price?, :cost_currency, :price_in, :amount_in, :cost_price, :images
77
+
78
+ alias_method :master_images, :images
79
+
80
+ has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
81
+
82
+ after_create :add_associations_from_prototype
83
+ after_create :build_variants_from_option_values_hash, if: :option_values_hash
84
+
85
+ after_destroy :punch_slug
86
+ after_restore :update_slug_history
87
+
88
+ after_initialize :ensure_master
89
+
90
+ after_save :save_master
91
+ after_save :run_touch_callbacks, if: :anything_changed?
92
+ after_save :reset_nested_changes
93
+ after_touch :touch_taxons
94
+
95
+ before_validation :normalize_slug, on: :update
96
+ before_validation :validate_master
97
+
98
+ with_options length: { maximum: 255 }, allow_blank: true do
99
+ validates :meta_keywords
100
+ validates :meta_title
101
+ end
102
+ with_options presence: true do
103
+ validates :name, :shipping_category
104
+ validates :price, if: proc { Spree::Config[:require_master_price] }
105
+ end
106
+
107
+ validates :slug, length: { minimum: 3 }, allow_blank: true, uniqueness: true
108
+ validate :discontinue_on_must_be_later_than_available_on, if: -> { available_on && discontinue_on }
109
+
110
+ attr_accessor :option_values_hash
111
+
112
+ accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
113
+
114
+ alias :options :product_option_types
115
+
116
+ self.whitelisted_ransackable_associations = %w[stores variants_including_master master variants]
117
+ self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on]
118
+ self.whitelisted_ransackable_scopes = %w[not_discontinued]
119
+
120
+ # the master variant is not a member of the variants array
121
+ def has_variants?
122
+ variants.any?
123
+ end
124
+
125
+ def tax_category
126
+ super || TaxCategory.find_by(is_default: true)
127
+ end
128
+
129
+ # Adding properties and option types on creation based on a chosen prototype
130
+ attr_reader :prototype_id
131
+ def prototype_id=(value)
132
+ @prototype_id = value.to_i
133
+ end
134
+
135
+ # Ensures option_types and product_option_types exist for keys in option_values_hash
136
+ def ensure_option_types_exist_for_values_hash
137
+ return if option_values_hash.nil?
138
+ required_option_type_ids = option_values_hash.keys.map(&:to_i)
139
+ missing_option_type_ids = required_option_type_ids - option_type_ids
140
+ missing_option_type_ids.each do |id|
141
+ product_option_types.create(option_type_id: id)
142
+ end
143
+ end
144
+
145
+ # for adding products which are closely related to existing ones
146
+ # define "duplicate_extra" for site-specific actions, eg for additional fields
147
+ def duplicate
148
+ duplicator = ProductDuplicator.new(self)
149
+ duplicator.duplicate
150
+ end
151
+
152
+ # use deleted? rather than checking the attribute directly. this
153
+ # allows extensions to override deleted? if they want to provide
154
+ # their own definition.
155
+ def deleted?
156
+ !!deleted_at
157
+ end
158
+
159
+ # determine if product is available.
160
+ # deleted products and products with nil or future available_on date
161
+ # are not available
162
+ def available?
163
+ !(available_on.nil? || available_on.future?) && !deleted? && !discontinued?
164
+ end
165
+
166
+ def discontinue!
167
+ update_attribute(:discontinue_on, Time.current)
168
+ end
169
+
170
+ def discontinued?
171
+ !!discontinue_on && discontinue_on <= Time.current
172
+ end
173
+
174
+ # determine if any variant (including master) can be supplied
175
+ def can_supply?
176
+ variants_including_master.any?(&:can_supply?)
177
+ end
178
+
179
+ # split variants list into hash which shows mapping of opt value onto matching variants
180
+ # eg categorise_variants_from_option(color) => {"red" -> [...], "blue" -> [...]}
181
+ def categorise_variants_from_option(opt_type)
182
+ return {} unless option_types.include?(opt_type)
183
+ variants.active.group_by { |v| v.option_values.detect { |o| o.option_type == opt_type} }
184
+ end
185
+
186
+ def self.like_any(fields, values)
187
+ conditions = fields.product(values).map do |(field, value)|
188
+ arel_table[field].matches("%#{value}%")
189
+ end
190
+ where conditions.inject(:or)
191
+ end
192
+
193
+ # Suitable for displaying only variants that has at least one option value.
194
+ # There may be scenarios where an option type is removed and along with it
195
+ # all option values. At that point all variants associated with only those
196
+ # values should not be displayed to frontend users. Otherwise it breaks the
197
+ # idea of having variants
198
+ def variants_and_option_values(current_currency = nil)
199
+ variants.includes(:option_values).active(current_currency).select do |variant|
200
+ variant.option_values.any?
201
+ end
202
+ end
203
+
204
+ def empty_option_values?
205
+ options.empty? || options.any? do |opt|
206
+ opt.option_type.option_values.empty?
207
+ end
208
+ end
209
+
210
+ def property(property_name)
211
+ product_properties.joins(:property).find_by(spree_properties: { name: property_name }).try(:value)
212
+ end
213
+
214
+ def set_property(property_name, property_value, property_presentation = property_name)
215
+ ActiveRecord::Base.transaction do
216
+ # Works around spree_i18n #301
217
+ property = Property.create_with(presentation: property_presentation).find_or_create_by(name: property_name)
218
+ product_property = ProductProperty.where(product: self, property: property).first_or_initialize
219
+ product_property.value = property_value
220
+ product_property.save!
221
+ end
222
+ end
223
+
224
+ def total_on_hand
225
+ if any_variants_not_track_inventory?
226
+ Float::INFINITY
227
+ else
228
+ stock_items.sum(:count_on_hand)
229
+ end
230
+ end
231
+
232
+ # Master variant may be deleted (i.e. when the product is deleted)
233
+ # which would make AR's default finder return nil.
234
+ # This is a stopgap for that little problem.
235
+ def master
236
+ super || variants_including_master.with_deleted.find_by(is_master: true)
237
+ end
238
+
239
+ private
240
+
241
+ def add_associations_from_prototype
242
+ if prototype_id && prototype = Spree::Prototype.find_by(id: prototype_id)
243
+ prototype.properties.each do |property|
244
+ product_properties.create(property: property)
245
+ end
246
+ self.option_types = prototype.option_types
247
+ self.taxons = prototype.taxons
248
+ end
249
+ end
250
+
251
+ def any_variants_not_track_inventory?
252
+ return true unless Spree::Config.track_inventory_levels
253
+ if variants_including_master.loaded?
254
+ variants_including_master.any? { |v| !v.track_inventory? }
255
+ else
256
+ variants_including_master.where(track_inventory: false).exists?
257
+ end
258
+ end
259
+
260
+ # Builds variants from a hash of option types & values
261
+ def build_variants_from_option_values_hash
262
+ ensure_option_types_exist_for_values_hash
263
+ values = option_values_hash.values
264
+ values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
265
+
266
+ values.each do |ids|
267
+ variants.create(
268
+ option_value_ids: ids,
269
+ price: master.price
270
+ )
271
+ end
272
+ save
273
+ end
274
+
275
+ def ensure_master
276
+ return unless new_record?
277
+ self.master ||= build_master
278
+ end
279
+
280
+ def normalize_slug
281
+ self.slug = normalize_friendly_id(slug)
282
+ end
283
+
284
+ def punch_slug
285
+ # punch slug with date prefix to allow reuse of original
286
+ update_column :slug, "#{Time.current.to_i}_#{slug}"[0..254] unless frozen?
287
+ end
288
+
289
+ def update_slug_history
290
+ self.save!
291
+ end
292
+
293
+ def anything_changed?
294
+ changed? || @nested_changes
295
+ end
296
+
297
+ def reset_nested_changes
298
+ @nested_changes = false
299
+ end
300
+
301
+ def master_updated?
302
+ master && (
303
+ master.new_record? ||
304
+ master.changed? ||
305
+ (
306
+ master.default_price &&
307
+ (
308
+ master.default_price.new_record? ||
309
+ master.default_price.changed?
310
+ )
311
+ )
312
+ )
313
+ end
314
+
315
+ # there's a weird quirk with the delegate stuff that does not automatically save the delegate object
316
+ # when saving so we force a save using a hook
317
+ # Fix for issue #5306
318
+ def save_master
319
+ if master_updated?
320
+ master.save!
321
+ @nested_changes = true
322
+ end
323
+ end
324
+
325
+ # If the master cannot be saved, the Product object will get its errors
326
+ # and will be destroyed
327
+ def validate_master
328
+ # We call master.default_price here to ensure price is initialized.
329
+ # Required to avoid Variant#check_price validation failing on create.
330
+ unless master.default_price && master.valid?
331
+ master.errors.each do |att, error|
332
+ self.errors.add(att, error)
333
+ end
334
+ end
335
+ end
336
+
337
+ # Try building a slug based on the following fields in increasing order of specificity.
338
+ def slug_candidates
339
+ [
340
+ :name,
341
+ [:name, :sku]
342
+ ]
343
+ end
344
+
345
+ def run_touch_callbacks
346
+ run_callbacks(:touch)
347
+ end
348
+
349
+ def taxon_and_ancestors
350
+ taxons.map(&:self_and_ancestors).flatten.uniq
351
+ end
352
+
353
+ # Get the taxonomy ids of all taxons assigned to this product and their ancestors.
354
+ def taxonomy_ids
355
+ taxon_and_ancestors.map(&:taxonomy_id).flatten.uniq
356
+ end
357
+
358
+ # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
359
+ def touch_taxons
360
+ Spree::Taxon.where(id: taxon_and_ancestors.map(&:id)).update_all(updated_at: Time.current)
361
+ Spree::Taxonomy.where(id: taxonomy_ids).update_all(updated_at: Time.current)
362
+ end
363
+
364
+ def ensure_no_line_items
365
+ if line_items.any?
366
+ errors.add(:base, Spree.t(:cannot_destroy_if_attached_to_line_items))
367
+ return false
368
+ end
369
+ end
370
+
371
+ def remove_taxon(taxon)
372
+ removed_classifications = classifications.where(taxon: taxon)
373
+ removed_classifications.each &:remove_from_list
374
+ end
375
+
376
+ def discontinue_on_must_be_later_than_available_on
377
+ if discontinue_on < available_on
378
+ errors.add(:discontinue_on, :invalid_date_range)
379
+ end
380
+ end
381
+ end
382
+ end
383
+
384
+ require_dependency 'spree/product/scopes'
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ class ProductOptionType < Spree::Base
3
+ with_options inverse_of: :product_option_types do
4
+ belongs_to :product, class_name: 'Spree::Product'
5
+ belongs_to :option_type, class_name: 'Spree::OptionType'
6
+ end
7
+ acts_as_list scope: :product
8
+
9
+ validates :product, :option_type, presence: true
10
+ validates :product_id, uniqueness: { scope: :option_type_id }, allow_nil: true
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Spree
2
+ class ProductPromotionRule < Spree::Base
3
+ belongs_to :product, class_name: 'Spree::Product'
4
+ belongs_to :promotion_rule, class_name: 'Spree::PromotionRule'
5
+
6
+ validates :product, :promotion_rule, presence: true
7
+ validates :product_id, uniqueness: { scope: :promotion_rule_id }, allow_nil: true
8
+ end
9
+ end