spree_core 5.1.7 → 5.2.0.rc1

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 (400) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/payment_icons/ach.svg +1 -0
  3. data/app/assets/images/payment_icons/achdirectdebit.svg +1 -0
  4. data/app/assets/images/payment_icons/ada.svg +1 -0
  5. data/app/assets/images/payment_icons/adyen.svg +1 -0
  6. data/app/assets/images/payment_icons/affinbank.svg +1 -0
  7. data/app/assets/images/payment_icons/affirm.svg +1 -0
  8. data/app/assets/images/payment_icons/afterpay.svg +1 -0
  9. data/app/assets/images/payment_icons/airtel_money.svg +1 -0
  10. data/app/assets/images/payment_icons/airteltigo_mobile_money.svg +1 -0
  11. data/app/assets/images/payment_icons/alandsbanken.svg +1 -0
  12. data/app/assets/images/payment_icons/alipay.svg +1 -0
  13. data/app/assets/images/payment_icons/alipay_hk.svg +1 -0
  14. data/app/assets/images/payment_icons/alliancebank.svg +1 -0
  15. data/app/assets/images/payment_icons/alma.svg +1 -0
  16. data/app/assets/images/payment_icons/amazon.svg +1 -0
  17. data/app/assets/images/payment_icons/ambank.svg +1 -0
  18. data/app/assets/images/payment_icons/american_express.svg +1 -0
  19. data/app/assets/images/payment_icons/apecoin.svg +1 -0
  20. data/app/assets/images/payment_icons/apple_pay.svg +1 -0
  21. data/app/assets/images/payment_icons/arbitrum.svg +1 -0
  22. data/app/assets/images/payment_icons/atome.svg +1 -0
  23. data/app/assets/images/payment_icons/authorizenet.svg +1 -0
  24. data/app/assets/images/payment_icons/avalanche.svg +1 -0
  25. data/app/assets/images/payment_icons/bacs.svg +1 -0
  26. data/app/assets/images/payment_icons/bancontact.svg +1 -0
  27. data/app/assets/images/payment_icons/bangkokbank.svg +1 -0
  28. data/app/assets/images/payment_icons/bankislam.svg +1 -0
  29. data/app/assets/images/payment_icons/bankmuamalat.svg +1 -0
  30. data/app/assets/images/payment_icons/bankofamerica.svg +1 -0
  31. data/app/assets/images/payment_icons/bankrakyat.svg +1 -0
  32. data/app/assets/images/payment_icons/base.svg +1 -0
  33. data/app/assets/images/payment_icons/becs.svg +1 -0
  34. data/app/assets/images/payment_icons/benefit.svg +1 -0
  35. data/app/assets/images/payment_icons/billie.svg +1 -0
  36. data/app/assets/images/payment_icons/bitcoin.svg +1 -0
  37. data/app/assets/images/payment_icons/bitcoin_cash.svg +1 -0
  38. data/app/assets/images/payment_icons/bizum.svg +1 -0
  39. data/app/assets/images/payment_icons/blik.svg +1 -0
  40. data/app/assets/images/payment_icons/bnbchain.svg +1 -0
  41. data/app/assets/images/payment_icons/bogus.svg +1 -0
  42. data/app/assets/images/payment_icons/bogus_app_coin.svg +1 -0
  43. data/app/assets/images/payment_icons/boleto.svg +1 -0
  44. data/app/assets/images/payment_icons/boost.svg +1 -0
  45. data/app/assets/images/payment_icons/braintree.svg +1 -0
  46. data/app/assets/images/payment_icons/buckaroopaybybank.svg +1 -0
  47. data/app/assets/images/payment_icons/busd.svg +1 -0
  48. data/app/assets/images/payment_icons/cartes_bancaires.svg +1 -0
  49. data/app/assets/images/payment_icons/cash.svg +1 -0
  50. data/app/assets/images/payment_icons/cashappafterpay.svg +1 -0
  51. data/app/assets/images/payment_icons/cashapppay.svg +1 -0
  52. data/app/assets/images/payment_icons/checkout_finance.svg +1 -0
  53. data/app/assets/images/payment_icons/chinabank.svg +1 -0
  54. data/app/assets/images/payment_icons/clearpay.svg +1 -0
  55. data/app/assets/images/payment_icons/collector_bank.svg +1 -0
  56. data/app/assets/images/payment_icons/dai.svg +1 -0
  57. data/app/assets/images/payment_icons/dana.svg +1 -0
  58. data/app/assets/images/payment_icons/dankort.svg +1 -0
  59. data/app/assets/images/payment_icons/danske_bank.svg +1 -0
  60. data/app/assets/images/payment_icons/deutschebank.svg +1 -0
  61. data/app/assets/images/payment_icons/diners_club.svg +1 -0
  62. data/app/assets/images/payment_icons/directbanktransferlatinamerica.svg +1 -0
  63. data/app/assets/images/payment_icons/discover.svg +1 -0
  64. data/app/assets/images/payment_icons/dogecoin.svg +1 -0
  65. data/app/assets/images/payment_icons/duitnow.svg +1 -0
  66. data/app/assets/images/payment_icons/eft_secure.svg +1 -0
  67. data/app/assets/images/payment_icons/eftpos_au.svg +1 -0
  68. data/app/assets/images/payment_icons/eftposau.svg +1 -0
  69. data/app/assets/images/payment_icons/elo.svg +1 -0
  70. data/app/assets/images/payment_icons/eos.svg +1 -0
  71. data/app/assets/images/payment_icons/eps.svg +1 -0
  72. data/app/assets/images/payment_icons/ethereum.svg +1 -0
  73. data/app/assets/images/payment_icons/eurobonuscheckout.svg +1 -0
  74. data/app/assets/images/payment_icons/ezcash.svg +1 -0
  75. data/app/assets/images/payment_icons/fantom.svg +1 -0
  76. data/app/assets/images/payment_icons/fpx.svg +1 -0
  77. data/app/assets/images/payment_icons/gcash.svg +1 -0
  78. data/app/assets/images/payment_icons/generic.svg +1 -0
  79. data/app/assets/images/payment_icons/genericbank.svg +1 -0
  80. data/app/assets/images/payment_icons/gift-card.svg +1 -0
  81. data/app/assets/images/payment_icons/giropay.svg +1 -0
  82. data/app/assets/images/payment_icons/glbepaypal.svg +1 -0
  83. data/app/assets/images/payment_icons/gmobanktransfer.svg +1 -0
  84. data/app/assets/images/payment_icons/gnosis.svg +1 -0
  85. data/app/assets/images/payment_icons/google_pay.svg +1 -0
  86. data/app/assets/images/payment_icons/google_wallet.svg +1 -0
  87. data/app/assets/images/payment_icons/gopay.svg +1 -0
  88. data/app/assets/images/payment_icons/grabpay.svg +1 -0
  89. data/app/assets/images/payment_icons/gusd.svg +1 -0
  90. data/app/assets/images/payment_icons/handelsbanken.svg +1 -0
  91. data/app/assets/images/payment_icons/hongleongbank.svg +1 -0
  92. data/app/assets/images/payment_icons/hypercard.svg +1 -0
  93. data/app/assets/images/payment_icons/hypercash.svg +1 -0
  94. data/app/assets/images/payment_icons/ideal.svg +1 -0
  95. data/app/assets/images/payment_icons/in3viaideal.svg +1 -0
  96. data/app/assets/images/payment_icons/inbank.svg +1 -0
  97. data/app/assets/images/payment_icons/jcb.svg +1 -0
  98. data/app/assets/images/payment_icons/kakao_pay.svg +1 -0
  99. data/app/assets/images/payment_icons/kasikornbank.svg +1 -0
  100. data/app/assets/images/payment_icons/klarna-pay-later.svg +1 -0
  101. data/app/assets/images/payment_icons/klarna-pay-now.svg +1 -0
  102. data/app/assets/images/payment_icons/klarna-slice-it.svg +1 -0
  103. data/app/assets/images/payment_icons/klarna.svg +1 -0
  104. data/app/assets/images/payment_icons/knet.svg +1 -0
  105. data/app/assets/images/payment_icons/krungthaibank.svg +1 -0
  106. data/app/assets/images/payment_icons/landbank.svg +1 -0
  107. data/app/assets/images/payment_icons/laybuybyklarna.svg +1 -0
  108. data/app/assets/images/payment_icons/litecoin.svg +1 -0
  109. data/app/assets/images/payment_icons/lunchcheck.svg +1 -0
  110. data/app/assets/images/payment_icons/mach.svg +1 -0
  111. data/app/assets/images/payment_icons/mada.svg +1 -0
  112. data/app/assets/images/payment_icons/maestro.svg +1 -0
  113. data/app/assets/images/payment_icons/master.svg +1 -0
  114. data/app/assets/images/payment_icons/mayabank.svg +1 -0
  115. data/app/assets/images/payment_icons/maybank.svg +1 -0
  116. data/app/assets/images/payment_icons/maybankm2u.svg +1 -0
  117. data/app/assets/images/payment_icons/maybankqrpay.svg +1 -0
  118. data/app/assets/images/payment_icons/mbway.svg +1 -0
  119. data/app/assets/images/payment_icons/mcash.svg +1 -0
  120. data/app/assets/images/payment_icons/medicinosbankas.svg +1 -0
  121. data/app/assets/images/payment_icons/mercadopago.svg +1 -0
  122. data/app/assets/images/payment_icons/metrobank.svg +1 -0
  123. data/app/assets/images/payment_icons/mobilepay.svg +1 -0
  124. data/app/assets/images/payment_icons/momopay.svg +1 -0
  125. data/app/assets/images/payment_icons/monero.svg +1 -0
  126. data/app/assets/images/payment_icons/mtn_mobile_money.svg +1 -0
  127. data/app/assets/images/payment_icons/mybank.svg +1 -0
  128. data/app/assets/images/payment_icons/naver_pay.svg +1 -0
  129. data/app/assets/images/payment_icons/nelo.svg +1 -0
  130. data/app/assets/images/payment_icons/netbanking.svg +1 -0
  131. data/app/assets/images/payment_icons/novalnetdirectdebitach.svg +1 -0
  132. data/app/assets/images/payment_icons/nubank.svg +1 -0
  133. data/app/assets/images/payment_icons/ocbcbank.svg +1 -0
  134. data/app/assets/images/payment_icons/offlinebanktransferlatinamerica.svg +1 -0
  135. data/app/assets/images/payment_icons/ola_money.svg +1 -0
  136. data/app/assets/images/payment_icons/oney.svg +1 -0
  137. data/app/assets/images/payment_icons/onlinebanking.svg +1 -0
  138. data/app/assets/images/payment_icons/onlinebanktransfer.svg +1 -0
  139. data/app/assets/images/payment_icons/orangemobilemoney.svg +1 -0
  140. data/app/assets/images/payment_icons/otpbank.svg +1 -0
  141. data/app/assets/images/payment_icons/oxxo.svg +1 -0
  142. data/app/assets/images/payment_icons/paybybank.svg +1 -0
  143. data/app/assets/images/payment_icons/paybybankmollie.svg +1 -0
  144. data/app/assets/images/payment_icons/paybybankus.svg +1 -0
  145. data/app/assets/images/payment_icons/paycash.svg +1 -0
  146. data/app/assets/images/payment_icons/payco.svg +1 -0
  147. data/app/assets/images/payment_icons/payeverpaybybank.svg +1 -0
  148. data/app/assets/images/payment_icons/payme.svg +1 -0
  149. data/app/assets/images/payment_icons/paynow.svg +1 -0
  150. data/app/assets/images/payment_icons/paynowmbank.svg +1 -0
  151. data/app/assets/images/payment_icons/paypal.svg +1 -0
  152. data/app/assets/images/payment_icons/paypo.svg +1 -0
  153. data/app/assets/images/payment_icons/payrexxbanktransfer.svg +1 -0
  154. data/app/assets/images/payment_icons/payrexxpaybybank.svg +1 -0
  155. data/app/assets/images/payment_icons/paysafecash.svg +1 -0
  156. data/app/assets/images/payment_icons/pix.svg +1 -0
  157. data/app/assets/images/payment_icons/polygon.svg +1 -0
  158. data/app/assets/images/payment_icons/przelewy24.svg +1 -0
  159. data/app/assets/images/payment_icons/publicbank.svg +1 -0
  160. data/app/assets/images/payment_icons/publicbank_pbe.svg +1 -0
  161. data/app/assets/images/payment_icons/qr_promptpay.svg +1 -0
  162. data/app/assets/images/payment_icons/rabobank.svg +1 -0
  163. data/app/assets/images/payment_icons/revolut.svg +1 -0
  164. data/app/assets/images/payment_icons/rhbbank.svg +1 -0
  165. data/app/assets/images/payment_icons/riverty.svg +1 -0
  166. data/app/assets/images/payment_icons/safetypaybanktransfer.svg +1 -0
  167. data/app/assets/images/payment_icons/samsung_pay.svg +1 -0
  168. data/app/assets/images/payment_icons/satispay.svg +1 -0
  169. data/app/assets/images/payment_icons/scalapay.svg +1 -0
  170. data/app/assets/images/payment_icons/scotiabank.svg +1 -0
  171. data/app/assets/images/payment_icons/seabankid.svg +1 -0
  172. data/app/assets/images/payment_icons/seabankph.svg +1 -0
  173. data/app/assets/images/payment_icons/sepa_bank_transfer.svg +1 -0
  174. data/app/assets/images/payment_icons/sepadirectdebit.svg +1 -0
  175. data/app/assets/images/payment_icons/seveneleven.svg +1 -0
  176. data/app/assets/images/payment_icons/shib.svg +1 -0
  177. data/app/assets/images/payment_icons/shopcash.svg +1 -0
  178. data/app/assets/images/payment_icons/siauliubankas.svg +1 -0
  179. data/app/assets/images/payment_icons/skeps.svg +1 -0
  180. data/app/assets/images/payment_icons/snap_checkout.svg +1 -0
  181. data/app/assets/images/payment_icons/sofort.svg +1 -0
  182. data/app/assets/images/payment_icons/softbank.svg +1 -0
  183. data/app/assets/images/payment_icons/spei.svg +1 -0
  184. data/app/assets/images/payment_icons/storecredit.svg +1 -0
  185. data/app/assets/images/payment_icons/stripe.svg +1 -0
  186. data/app/assets/images/payment_icons/sveacheckout.svg +1 -0
  187. data/app/assets/images/payment_icons/swedbank.svg +1 -0
  188. data/app/assets/images/payment_icons/swish.svg +1 -0
  189. data/app/assets/images/payment_icons/tbibank.svg +1 -0
  190. data/app/assets/images/payment_icons/thanachartbank.svg +1 -0
  191. data/app/assets/images/payment_icons/tnmmoney.svg +1 -0
  192. data/app/assets/images/payment_icons/touchngo.svg +1 -0
  193. data/app/assets/images/payment_icons/trustly.svg +1 -0
  194. data/app/assets/images/payment_icons/twint.svg +1 -0
  195. data/app/assets/images/payment_icons/uaevisa.svg +1 -0
  196. data/app/assets/images/payment_icons/unionpay.svg +1 -0
  197. data/app/assets/images/payment_icons/unzerbanktransfer.svg +1 -0
  198. data/app/assets/images/payment_icons/upi.svg +1 -0
  199. data/app/assets/images/payment_icons/usbank.svg +1 -0
  200. data/app/assets/images/payment_icons/usdc.svg +1 -0
  201. data/app/assets/images/payment_icons/usdt.svg +1 -0
  202. data/app/assets/images/payment_icons/venmo.svg +1 -0
  203. data/app/assets/images/payment_icons/vipps.svg +1 -0
  204. data/app/assets/images/payment_icons/visa.svg +1 -0
  205. data/app/assets/images/payment_icons/volksbank.svg +1 -0
  206. data/app/assets/images/payment_icons/waavepaybybank.svg +1 -0
  207. data/app/assets/images/payment_icons/walley.svg +1 -0
  208. data/app/assets/images/payment_icons/wbtc.svg +1 -0
  209. data/app/assets/images/payment_icons/wechatpay.svg +1 -0
  210. data/app/assets/images/payment_icons/whishcheckout.svg +1 -0
  211. data/app/assets/images/payment_icons/wingbank.svg +1 -0
  212. data/app/assets/images/payment_icons/xrp.svg +1 -0
  213. data/app/assets/images/payment_icons/zip.svg +1 -0
  214. data/app/finders/spree/posts/find.rb +137 -0
  215. data/app/helpers/spree/addresses_helper.rb +9 -3
  216. data/app/helpers/spree/base_helper.rb +8 -4
  217. data/app/helpers/spree/currency_helper.rb +21 -6
  218. data/app/jobs/spree/images/save_from_url_job.rb +70 -0
  219. data/app/jobs/spree/imports/create_rows_job.rb +72 -0
  220. data/app/jobs/spree/imports/process_rows_job.rb +19 -0
  221. data/app/models/concerns/spree/adjustment_source.rb +3 -3
  222. data/app/models/concerns/spree/admin_user_methods.rb +52 -0
  223. data/app/models/concerns/spree/display_on.rb +7 -1
  224. data/app/models/concerns/spree/has_image_alt_text.rb +18 -0
  225. data/app/models/concerns/spree/has_one_link.rb +1 -1
  226. data/app/models/concerns/spree/has_page_links.rb +1 -1
  227. data/app/models/concerns/spree/linkable.rb +9 -0
  228. data/app/models/concerns/spree/metadata.rb +2 -0
  229. data/app/models/concerns/spree/metafields.rb +94 -0
  230. data/app/models/concerns/spree/named_type.rb +1 -1
  231. data/app/models/concerns/spree/user_methods.rb +26 -9
  232. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  233. data/app/models/concerns/spree/user_roles.rb +3 -2
  234. data/app/models/spree/ability.rb +4 -0
  235. data/app/models/spree/address.rb +6 -2
  236. data/app/models/spree/adjustable/adjuster/tax.rb +2 -2
  237. data/app/models/spree/adjustable/adjustments_updater.rb +26 -3
  238. data/app/models/spree/adjustment.rb +4 -1
  239. data/app/models/spree/asset.rb +16 -0
  240. data/app/models/spree/base.rb +1 -1
  241. data/app/models/spree/calculator.rb +1 -1
  242. data/app/models/spree/credit_card.rb +1 -0
  243. data/app/models/spree/current.rb +5 -1
  244. data/app/models/spree/custom_domain.rb +5 -0
  245. data/app/models/spree/customer_return.rb +1 -0
  246. data/app/models/spree/export.rb +39 -2
  247. data/app/models/spree/exports/customers.rb +17 -0
  248. data/app/models/spree/exports/gift_cards.rb +13 -0
  249. data/app/models/spree/exports/newsletter_subscribers.rb +13 -0
  250. data/app/models/spree/exports/orders.rb +6 -4
  251. data/app/models/spree/exports/products.rb +9 -3
  252. data/app/models/spree/gateway/bogus.rb +4 -0
  253. data/app/models/spree/gateway/custom_payment_source_method.rb +4 -0
  254. data/app/models/spree/gift_card.rb +9 -1
  255. data/app/models/spree/gift_card_batch.rb +0 -1
  256. data/app/models/spree/import.rb +279 -0
  257. data/app/models/spree/import_mapping.rb +46 -0
  258. data/app/models/spree/import_row.rb +108 -0
  259. data/app/models/spree/import_schema.rb +25 -0
  260. data/app/models/spree/import_schemas/products.rb +45 -0
  261. data/app/models/spree/imports/products.rb +9 -0
  262. data/app/models/spree/invitation.rb +1 -1
  263. data/app/models/spree/legacy_user.rb +0 -1
  264. data/app/models/spree/line_item.rb +3 -8
  265. data/app/models/spree/metafield.rb +53 -0
  266. data/app/models/spree/metafield_definition.rb +91 -0
  267. data/app/models/spree/metafields/boolean.rb +15 -0
  268. data/app/models/spree/metafields/json.rb +27 -0
  269. data/app/models/spree/metafields/long_text.rb +7 -0
  270. data/app/models/spree/metafields/number.rb +15 -0
  271. data/app/models/spree/metafields/rich_text.rb +18 -0
  272. data/app/models/spree/metafields/short_text.rb +7 -0
  273. data/app/models/spree/newsletter_subscriber/emails.rb +12 -0
  274. data/app/models/spree/newsletter_subscriber.rb +64 -0
  275. data/app/models/spree/option_type.rb +2 -0
  276. data/app/models/spree/option_value.rb +1 -0
  277. data/app/models/spree/order/checkout.rb +7 -0
  278. data/app/models/spree/order/payments.rb +1 -1
  279. data/app/models/spree/order.rb +14 -11
  280. data/app/models/spree/order_merger.rb +2 -4
  281. data/app/models/spree/order_promotion.rb +3 -0
  282. data/app/models/spree/order_updater.rb +2 -2
  283. data/app/models/spree/page.rb +5 -1
  284. data/app/models/spree/page_block.rb +4 -0
  285. data/app/models/spree/page_blocks/image.rb +3 -0
  286. data/app/models/spree/page_blocks/metafields.rb +18 -0
  287. data/app/models/spree/page_blocks/products/brand.rb +15 -0
  288. data/app/models/spree/page_blocks/products/buy_buttons.rb +5 -1
  289. data/app/models/spree/page_blocks/products/description.rb +18 -0
  290. data/app/models/spree/page_blocks/products/price.rb +1 -1
  291. data/app/models/spree/page_blocks/products/quantity_selector.rb +6 -2
  292. data/app/models/spree/page_blocks/products/title.rb +5 -1
  293. data/app/models/spree/page_blocks/products/variant_picker.rb +1 -1
  294. data/app/models/spree/page_section.rb +2 -2
  295. data/app/models/spree/page_sections/breadcrumbs.rb +12 -0
  296. data/app/models/spree/page_sections/footer.rb +20 -13
  297. data/app/models/spree/page_sections/image_banner.rb +6 -1
  298. data/app/models/spree/page_sections/image_with_text.rb +5 -0
  299. data/app/models/spree/page_sections/newsletter.rb +7 -0
  300. data/app/models/spree/page_sections/product_details.rb +38 -1
  301. data/app/models/spree/pages/account.rb +4 -0
  302. data/app/models/spree/pages/cart.rb +10 -0
  303. data/app/models/spree/pages/checkout.rb +6 -0
  304. data/app/models/spree/pages/custom.rb +4 -0
  305. data/app/models/spree/pages/homepage.rb +4 -0
  306. data/app/models/spree/pages/login.rb +4 -0
  307. data/app/models/spree/pages/password.rb +2 -0
  308. data/app/models/spree/pages/post_list.rb +5 -1
  309. data/app/models/spree/pages/product_details.rb +1 -0
  310. data/app/models/spree/pages/search_results.rb +1 -1
  311. data/app/models/spree/pages/shop_all.rb +5 -1
  312. data/app/models/spree/pages/wishlist.rb +4 -0
  313. data/app/models/spree/payment.rb +1 -0
  314. data/app/models/spree/payment_method.rb +8 -5
  315. data/app/models/spree/payment_source.rb +1 -0
  316. data/app/models/spree/policy.rb +70 -0
  317. data/app/models/spree/post.rb +4 -2
  318. data/app/models/spree/post_category.rb +1 -0
  319. data/app/models/spree/product.rb +16 -9
  320. data/app/models/spree/promotion/actions/free_shipping.rb +3 -3
  321. data/app/models/spree/promotion.rb +24 -8
  322. data/app/models/spree/promotion_action.rb +1 -1
  323. data/app/models/spree/promotion_handler/cart.rb +1 -1
  324. data/app/models/spree/promotion_handler/coupon.rb +1 -1
  325. data/app/models/spree/promotion_rule.rb +1 -1
  326. data/app/models/spree/property.rb +18 -0
  327. data/app/models/spree/refund.rb +2 -1
  328. data/app/models/spree/role_user.rb +1 -1
  329. data/app/models/spree/shipment.rb +1 -0
  330. data/app/models/spree/shipping_method.rb +1 -0
  331. data/app/models/spree/stock_item.rb +1 -0
  332. data/app/models/spree/stock_transfer.rb +1 -0
  333. data/app/models/spree/store.rb +45 -3
  334. data/app/models/spree/store_credit.rb +2 -1
  335. data/app/models/spree/tax_category.rb +1 -1
  336. data/app/models/spree/tax_rate.rb +18 -3
  337. data/app/models/spree/taxon.rb +12 -2
  338. data/app/models/spree/taxonomy.rb +11 -0
  339. data/app/models/spree/theme.rb +1 -1
  340. data/app/models/spree/variant.rb +17 -0
  341. data/app/presenters/spree/csv/customer_presenter.rb +59 -0
  342. data/app/presenters/spree/csv/gift_card_presenter.rb +49 -0
  343. data/app/presenters/spree/csv/metafields_helper.rb +17 -0
  344. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +39 -0
  345. data/app/presenters/spree/csv/order_line_item_presenter.rb +12 -5
  346. data/app/presenters/spree/csv/product_variant_presenter.rb +9 -5
  347. data/app/services/spree/addresses/update.rb +8 -1
  348. data/app/services/spree/cart/recalculate.rb +6 -4
  349. data/app/services/spree/cart/remove_line_item.rb +1 -2
  350. data/app/services/spree/imports/row_processors/base.rb +19 -0
  351. data/app/services/spree/imports/row_processors/product_variant.rb +228 -0
  352. data/app/services/spree/line_items/destroy.rb +1 -1
  353. data/app/services/spree/newsletter/subscribe.rb +43 -0
  354. data/app/services/spree/newsletter/verify.rb +30 -0
  355. data/app/services/spree/orders/create_user_account.rb +5 -1
  356. data/app/services/spree/products/prepare_nested_attributes.rb +44 -0
  357. data/app/sorters/spree/posts/sort.rb +40 -0
  358. data/app/{services → validators}/spree/addresses/phone_validator.rb +1 -1
  359. data/app/views/spree/shared/_payment.html.erb +11 -9
  360. data/config/locales/en.yml +56 -2
  361. data/db/migrate/20250605131334_add_missing_fields_to_users.rb +23 -13
  362. data/db/migrate/20250728095629_create_spree_metafields.rb +27 -0
  363. data/db/migrate/20250730102644_add_missing_unique_indexes_to_spree_option_types_and_values.rb +40 -0
  364. data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +24 -0
  365. data/db/migrate/20250811112056_create_spree_policies.rb +22 -0
  366. data/db/migrate/20250821211705_fix_unique_index_on_spree_order_promotions.rb +20 -0
  367. data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +7 -0
  368. data/db/migrate/20250826093602_create_spree_newsletter_subscribers.rb +12 -0
  369. data/db/migrate/20250902143122_fix_policies_store_association.rb +18 -0
  370. data/db/migrate/20250911205659_add_missing_index_on_spree_products_promotionable.rb +5 -0
  371. data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +10 -0
  372. data/db/migrate/20250914101955_add_missing_indexes_on_spree_adjustments.rb +6 -0
  373. data/db/migrate/20250915093930_add_metadata_to_spree_newsletter_subscribers.rb +20 -0
  374. data/db/migrate/20250923141845_create_spree_imports.rb +47 -0
  375. data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +19 -0
  376. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +387 -0
  377. data/lib/generators/spree/dummy/dummy_generator.rb +0 -1
  378. data/lib/generators/spree/install/templates/config/initializers/spree.rb +5 -0
  379. data/lib/spree/core/configuration.rb +1 -0
  380. data/lib/spree/core/dependencies.rb +3 -0
  381. data/lib/spree/core/engine.rb +57 -2
  382. data/lib/spree/core/version.rb +1 -1
  383. data/lib/spree/core.rb +3 -1
  384. data/lib/spree/permitted_attributes.rb +18 -3
  385. data/lib/spree/testing_support/capybara_config.rb +9 -3
  386. data/lib/spree/testing_support/common_rake.rb +5 -5
  387. data/lib/spree/testing_support/factories/export_factory.rb +5 -17
  388. data/lib/spree/testing_support/factories/import_factory.rb +20 -0
  389. data/lib/spree/testing_support/factories/import_mapping_factory.rb +7 -0
  390. data/lib/spree/testing_support/factories/import_row_factory.rb +7 -0
  391. data/lib/spree/testing_support/factories/metafield_definition_factory.rb +72 -0
  392. data/lib/spree/testing_support/factories/metafield_factory.rb +68 -0
  393. data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +18 -0
  394. data/lib/spree/testing_support/factories/policy_factory.rb +8 -0
  395. data/lib/spree/testing_support/factories/post_factory.rb +8 -0
  396. data/lib/spree/testing_support/store.rb +11 -0
  397. data/lib/tasks/core.rake +130 -3
  398. metadata +280 -7
  399. data/app/models/action_text/rich_text_decorator.rb +0 -11
  400. data/app/presenters/spree/csv/product_presenter.rb +0 -63
@@ -47,7 +47,7 @@ module Spree
47
47
  'category3',
48
48
  ].freeze
49
49
 
50
- def initialize(product, variant, index = 0, properties = [], taxons = [], store = nil)
50
+ def initialize(product, variant, index = 0, properties = [], taxons = [], store = nil, metafields = [])
51
51
  @product = product
52
52
  @variant = variant
53
53
  @index = index
@@ -55,9 +55,10 @@ module Spree
55
55
  @taxons = taxons
56
56
  @store = store || product.stores.first
57
57
  @currency = @store.default_currency
58
+ @metafields = metafields
58
59
  end
59
60
 
60
- attr_accessor :product, :variant, :index, :properties, :taxons, :store, :currency
61
+ attr_accessor :product, :variant, :index, :properties, :taxons, :store, :currency, :metafields
61
62
 
62
63
  ##
63
64
  # Generates an array representing a CSV row of product variant data.
@@ -71,11 +72,13 @@ module Spree
71
72
  #
72
73
  # @return [Array] An array containing the combined product and variant CSV data.
73
74
  def call
75
+ total_on_hand = variant.total_on_hand
76
+
74
77
  csv = [
75
78
  product.id,
76
79
  variant.sku,
77
80
  index.zero? ? product.name : nil,
78
- index.zero? ? product.slug : nil,
81
+ product.slug,
79
82
  index.zero? ? product.status : nil,
80
83
  index.zero? ? product.try(:vendor_name) : nil,
81
84
  index.zero? ? product.try(:brand_name) : nil,
@@ -86,7 +89,7 @@ module Spree
86
89
  index.zero? ? product.tag_list.to_s : nil,
87
90
  index.zero? ? product.label_list.to_s : nil,
88
91
  variant.amount_in(currency).to_f,
89
- variant.compare_at_price&.to_f,
92
+ variant.compare_at_amount_in(currency).to_f,
90
93
  currency,
91
94
  variant.width,
92
95
  variant.height,
@@ -97,7 +100,7 @@ module Spree
97
100
  variant.available_on&.strftime('%Y-%m-%d %H:%M:%S'),
98
101
  (variant.discontinue_on || product.discontinue_on)&.strftime('%Y-%m-%d %H:%M:%S'),
99
102
  variant.track_inventory?,
100
- variant.total_on_hand == BigDecimal::INFINITY ? '∞' : variant.total_on_hand,
103
+ total_on_hand == BigDecimal::INFINITY ? '∞' : total_on_hand,
101
104
  variant.backorderable?,
102
105
  variant.tax_category&.name,
103
106
  variant.digital?,
@@ -115,6 +118,7 @@ module Spree
115
118
  if index.zero?
116
119
  csv += taxons
117
120
  csv += properties
121
+ csv += metafields
118
122
  end
119
123
 
120
124
  csv
@@ -12,12 +12,19 @@ module Spree
12
12
  default_shipping = opts.fetch(:default_shipping, false)
13
13
  address_changes_except = opts.fetch(:address_changes_except, [])
14
14
  create_new_address_on_update = opts.fetch(:create_new_address_on_update, false)
15
+ Spree::Deprecation.warn('Spree::Addresses::Update create_new_address_on_update parameter is deprecated and will be removed in Spree 6.') if create_new_address_on_update
15
16
 
16
17
  prepare_address_params!(address, address_params)
17
18
  address.assign_attributes(address_params)
18
19
 
19
- address_changed = address.changes.except(*address_changes_except).any?
20
+ address_changes = address.changes.except(*address_changes_except)
20
21
 
22
+ # Ignore changes that are only different in case as encrypted fields are processed by rails as downcased
23
+ address_changes.reject! do |attr, (old_val, new_val)|
24
+ old_val.to_s.casecmp?(new_val.to_s)
25
+ end
26
+
27
+ address_changed = address_changes.any?
21
28
  if !address_changed && defaults_changed?(address, default_billing, default_shipping)
22
29
  assign_to_user_as_default(
23
30
  user: address.user,
@@ -4,11 +4,13 @@ module Spree
4
4
  prepend Spree::ServiceModule::Base
5
5
 
6
6
  def call(order:, line_item:, line_item_created: false, options: {})
7
- order.remove_gift_card if order.gift_card.present?
8
- order_updater = ::Spree::OrderUpdater.new(order)
7
+ order_updater = order.updater
9
8
 
10
- order.payments.store_credits.checkout.destroy_all
11
- order_updater.update
9
+ order.remove_gift_card if order.gift_card.present?
10
+ order.payments.store_credits.checkout.destroy_all if order.payments.store_credits.checkout.any?
11
+ order_updater.update_item_count
12
+ order_updater.update_totals
13
+ order_updater.persist_totals
12
14
 
13
15
  shipment = options[:shipment]
14
16
  if shipment.present?
@@ -6,12 +6,11 @@ module Spree
6
6
  def call(order:, line_item:, options: nil)
7
7
  options ||= {}
8
8
  ActiveRecord::Base.transaction do
9
- line_item.destroy!
9
+ order.line_items.destroy(line_item)
10
10
  Spree::Dependencies.cart_recalculate_service.constantize.new.call(order: order,
11
11
  line_item: line_item,
12
12
  options: options)
13
13
  end
14
- order.reload
15
14
  success(line_item)
16
15
  end
17
16
  end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Imports
3
+ module RowProcessors
4
+ class Base
5
+ def initialize(row)
6
+ @row = row
7
+ @import = row.import
8
+ @attributes = row.to_schema_hash
9
+ end
10
+
11
+ attr_reader :row, :import, :attributes
12
+
13
+ def process!
14
+ raise NotImplementedError, 'Subclasses must implement the process! method'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,228 @@
1
+ module Spree
2
+ module Imports
3
+ module RowProcessors
4
+ class ProductVariant < Base
5
+ OPTION_TYPES_COUNT = 3
6
+
7
+ def initialize(row)
8
+ super
9
+ @store = row.store
10
+ @product = ensure_product_exists
11
+ end
12
+
13
+ attr_reader :product, :store
14
+
15
+ def process!
16
+ variant_scope = options.empty? ? product.variants_including_master : product.variants
17
+
18
+ variant = if attributes['sku'].present?
19
+ variant_scope.where(
20
+ Spree::Variant.arel_table[:sku].lower.eq(attributes['sku'].strip.downcase)
21
+ ).first || product.variants.new
22
+ elsif options.empty?
23
+ product.master
24
+ else
25
+ product.variants.new
26
+ end
27
+
28
+ variant.sku = attributes['sku'] if attributes['sku'].present?
29
+ variant.cost_price = attributes['cost_price'] if attributes['cost_price'].present?
30
+ variant.weight = attributes['weight'] if attributes['weight'].present?
31
+ variant.height = attributes['height'] if attributes['height'].present?
32
+ variant.width = attributes['width'] if attributes['width'].present?
33
+ variant.depth = attributes['depth'] if attributes['depth'].present?
34
+ variant.track_inventory = attributes['track_inventory'] if attributes['track_inventory'].present?
35
+ variant.option_value_variants = prepare_option_value_variants
36
+ variant.save!
37
+
38
+ if attributes['price'].present?
39
+ currency = (attributes['currency'].presence || store.default_currency).to_s.strip.upcase
40
+ variant.set_price(currency, attributes['price'], attributes['compare_at_price'])
41
+ end
42
+
43
+ if attributes['inventory_count'].present?
44
+ variant.set_stock(attributes['inventory_count'].to_i, attributes['inventory_backorderable']&.to_b, store.default_stock_location)
45
+ end
46
+
47
+ handle_images(variant)
48
+
49
+ variant
50
+ end
51
+
52
+ private
53
+
54
+ def ensure_product_exists
55
+ product = Spree::Product.new
56
+ if attributes['slug'].present?
57
+ existing_product = product_scope.find_by(slug: attributes['slug'].strip.downcase)
58
+ product = existing_product if existing_product.present?
59
+ end
60
+
61
+ product = assign_attributes_to_product(product)
62
+ product.save!
63
+ handle_metafields(product)
64
+ product
65
+ end
66
+
67
+ def product_scope
68
+ Spree::Product.accessible_by(import.current_ability, :manage)
69
+ end
70
+
71
+ def assign_attributes_to_product(product)
72
+ # setting SKU for master variant so it will be picked up in process! and won't try to create a non-master variant
73
+ if product.new_record?
74
+ product.slug = attributes['slug']
75
+ product.sku = attributes['sku'] if attributes['sku'].present? && options.empty?
76
+ end
77
+
78
+ product.stores << store if product.stores.exclude?(store)
79
+ product.name = attributes['name'] if attributes['name'].present?
80
+ product.description = attributes['description'] if attributes['description'].present?
81
+ product.meta_title = attributes['meta_title'] if attributes['meta_title'].present?
82
+ product.meta_description = attributes['meta_description'] if attributes['meta_description'].present?
83
+ product.meta_keywords = attributes['meta_keywords'] if attributes['meta_keywords'].present?
84
+ product.status = to_spree_status(attributes['status']) if attributes['status'].present?
85
+ product.tag_list = attributes['tags'] if attributes['tags'].present?
86
+
87
+ product.taxons = prepare_taxons if options.empty?
88
+ product
89
+ end
90
+
91
+ def prepare_taxons
92
+ taxon_pretty_names = [
93
+ attributes['category1'],
94
+ attributes['category2'],
95
+ attributes['category3']
96
+ ].compact_blank.map(&:strip).uniq
97
+
98
+ return [] if taxon_pretty_names.empty?
99
+
100
+ taxons = taxon_pretty_names.map { |taxon_pretty_name| handle_taxon_line(taxon_pretty_name) }
101
+ taxons.compact
102
+ end
103
+
104
+ def handle_taxon_line(taxon_pretty_name)
105
+ taxon_names = taxon_pretty_name.strip.split('->').map(&:strip).map(&:presence).compact
106
+ return if taxon_names.empty?
107
+
108
+ taxonomy_name = taxon_names.shift
109
+ taxonomy = store.taxonomies.with_matching_name(taxonomy_name).first || store.taxonomies.create!(name: taxonomy_name)
110
+
111
+ last_taxon = taxonomy.root
112
+
113
+ taxon_names.each do |taxon_name|
114
+ last_taxon = taxonomy.taxons.with_matching_name(taxon_name).where(parent: last_taxon).first || taxonomy.taxons.create!(name: taxon_name, parent: last_taxon)
115
+ end
116
+
117
+ last_taxon
118
+ end
119
+
120
+ def prepare_option_value_variants
121
+ return [] if options.empty?
122
+
123
+ options.map do |option|
124
+ option_type = Spree::OptionType.search_by_name(option[:option_name]).first || Spree::OptionType.create!(presentation: option[:option_name])
125
+ option_value = option_type.option_values.search_by_name(option[:option_value]).first || option_type.option_values.create!(presentation: option[:option_value])
126
+
127
+ # ensure product option types include new option type
128
+ Spree::ProductOptionType.find_or_create_by!(product: product, option_type: option_type)
129
+
130
+ Spree::OptionValueVariant.new(option_value: option_value)
131
+ end
132
+ end
133
+
134
+ def handle_images(variant)
135
+ image_urls = [
136
+ attributes['image1_src'],
137
+ attributes['image2_src'],
138
+ attributes['image3_src'],
139
+ ].compact.map(&:strip).compact_blank.uniq
140
+
141
+ return if image_urls.empty?
142
+
143
+ image_urls.each do |image_url|
144
+ Spree::Images::SaveFromUrlJob.perform_later(variant.id, 'Spree::Variant', image_url)
145
+ end
146
+ end
147
+
148
+ def options
149
+ @options ||= begin
150
+ options = []
151
+
152
+ OPTION_TYPES_COUNT.times.map do |index|
153
+ next if attributes["option#{index + 1}_name"].blank?
154
+ next if attributes["option#{index + 1}_value"].blank?
155
+
156
+ options << {
157
+ index: index + 1,
158
+ option_name: attributes["option#{index + 1}_name"],
159
+ option_value: attributes["option#{index + 1}_value"]
160
+ }
161
+ end
162
+
163
+ options
164
+ end
165
+ end
166
+
167
+ def handle_metafields(product)
168
+ return unless product.class.included_modules.include?(Spree::Metafields)
169
+
170
+ metafield_attributes = attributes.select { |key, _value| key.to_s.start_with?('metafield.') }
171
+ return if metafield_attributes.empty?
172
+
173
+ # Build nested attributes for metafields
174
+ nested_attrs = []
175
+
176
+ metafield_attributes.each do |attribute_key, value|
177
+ # Extract namespace.key from "metafield.namespace.key"
178
+ full_key = attribute_key.to_s.sub(/^metafield\./, '')
179
+ namespace, key = product.extract_namespace_and_key(full_key)
180
+
181
+ # Find or initialize metafield definition
182
+ metafield_definition = Spree::MetafieldDefinition.find_by(
183
+ namespace: namespace,
184
+ key: key,
185
+ resource_type: product.class.name
186
+ )
187
+
188
+ next unless metafield_definition
189
+
190
+ # Find existing metafield if product is persisted
191
+ existing_metafield = product.persisted? ? product.metafields.find_by(metafield_definition: metafield_definition) : nil
192
+
193
+ # Skip blank values for new metafields
194
+ next if value.blank? && existing_metafield.nil?
195
+
196
+ # For existing metafields with blank values, we'll mark them for destruction
197
+ # For new metafields, we skip them (handled above)
198
+ # For existing or new metafields with values, we create/update them
199
+ metafield_attrs = {
200
+ metafield_definition_id: metafield_definition.id,
201
+ value: value.to_s.strip,
202
+ type: metafield_definition.metafield_type
203
+ }
204
+
205
+ metafield_attrs[:id] = existing_metafield.id if existing_metafield
206
+
207
+ nested_attrs << metafield_attrs
208
+ end
209
+
210
+ product.update(metafields_attributes: nested_attrs) unless nested_attrs.empty?
211
+ end
212
+
213
+ def to_spree_status(status)
214
+ case status.strip.downcase
215
+ when 'active'
216
+ 'active'
217
+ when 'draft'
218
+ 'draft'
219
+ when 'archived'
220
+ 'archived'
221
+ else
222
+ 'draft'
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -8,7 +8,7 @@ module Spree
8
8
  order = line_item.order
9
9
 
10
10
  ActiveRecord::Base.transaction do
11
- line_item.destroy!
11
+ order.line_items.destroy(line_item)
12
12
  recalculate_service.call(order: order, line_item: line_item, options: options)
13
13
  end
14
14
  success(line_item)
@@ -0,0 +1,43 @@
1
+ module Spree
2
+ module Newsletter
3
+ class Subscribe
4
+ def initialize(email:, current_user: nil)
5
+ @email = email
6
+ @current_user = current_user
7
+ end
8
+
9
+ def call
10
+ return existed_subscription if existed_subscription.present?
11
+
12
+ ActiveRecord::Base.transaction do
13
+ upsert_subscriber
14
+ return subscriber if subscriber.errors.any?
15
+
16
+ if subscriber.email == current_user&.email
17
+ # no need to verified since user email is already verified
18
+ Spree::Newsletter::Verify.new(subscriber: subscriber).call
19
+ end
20
+ end
21
+
22
+ # deliver confirmation email after the transaction is completed
23
+ subscriber.deliver_newsletter_email_verification unless subscriber.verified?
24
+ subscriber
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :email, :current_user
30
+
31
+ def upsert_subscriber
32
+ @upsert_subscriber ||= Spree::NewsletterSubscriber.find_or_create_by(email: email) do |new_record|
33
+ new_record.user = Spree.user_class.find_by(email: new_record.email) || current_user
34
+ end
35
+ end
36
+ alias_method :subscriber, :upsert_subscriber
37
+
38
+ def existed_subscription
39
+ @existed_subscription ||= Spree::NewsletterSubscriber.verified.find_by(email: email)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Newsletter
3
+ class Verify
4
+ def initialize(subscriber:)
5
+ @subscriber = subscriber
6
+ end
7
+
8
+ def call
9
+ verify_subscriber
10
+ set_user_email_marketing_to_true
11
+
12
+ subscriber
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :subscriber
18
+
19
+ def verify_subscriber
20
+ subscriber.update!(verified_at: Time.current, verification_token: nil)
21
+ end
22
+
23
+ def set_user_email_marketing_to_true
24
+ return if subscriber.user.blank?
25
+
26
+ subscriber.user.update!(accepts_email_marketing: true)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -7,7 +7,11 @@ module Spree
7
7
 
8
8
  def call(order:, accepts_email_marketing: false)
9
9
  existing_user = Spree.user_class.find_by(email: order.email)
10
- return existing_user if existing_user.present?
10
+ if existing_user.present?
11
+ order.update_columns(user_id: existing_user.id, updated_at: Time.current)
12
+ order.user = existing_user
13
+ return success(existing_user)
14
+ end
11
15
 
12
16
  user = create_new_user(order, accepts_email_marketing)
13
17
  return failure(:user_creation_failed) unless user.persisted?
@@ -1,5 +1,21 @@
1
1
  module Spree
2
2
  module Products
3
+ # Prepares nested attributes for product updates, handling multi-store scenarios
4
+ # and permissions.
5
+ #
6
+ # This service ensures that when editing a product in one store, taxon associations
7
+ # from other stores are preserved. This prevents accidental data loss when a store
8
+ # admin updates product categories in their store without affecting other stores.
9
+ #
10
+ # @example
11
+ # service = Spree::Products::PrepareNestedAttributes.new(
12
+ # product,
13
+ # current_store,
14
+ # params,
15
+ # current_ability
16
+ # )
17
+ # prepared_params = service.call
18
+ #
3
19
  class PrepareNestedAttributes
4
20
  def initialize(product, store, params, ability)
5
21
  @product = product
@@ -60,6 +76,12 @@ module Spree
60
76
  # ensure there is at least one store
61
77
  params[:store_ids] = [store.id] if params[:store_ids].blank?
62
78
 
79
+ # Preserve taxon associations from other stores
80
+ # Only merge taxon_ids from other stores if taxon_ids are being updated
81
+ if params.key?(:taxon_ids)
82
+ params[:taxon_ids] = merge_taxons_from_other_stores(params[:taxon_ids])
83
+ end
84
+
63
85
  # Add empty list for option_type_ids and mark variants as removed if there are no variants and options
64
86
  if params[:variants_attributes].blank? && variants_to_remove.any? && !params.key?(:option_type_ids)
65
87
  params[:option_type_ids] = []
@@ -133,6 +155,28 @@ module Spree
133
155
  attributes
134
156
  end
135
157
 
158
+ # Merges taxon IDs from other stores with submitted taxon IDs from current store.
159
+ #
160
+ # This prevents the loss of taxon associations from other stores when a product
161
+ # is edited in one store. Each store's taxonomy is independent, so editing
162
+ # categories in Store A should not affect categories in Store B.
163
+ #
164
+ # @param submitted_taxon_ids [Array<String>] Taxon IDs from the current store
165
+ # @return [Array<String>] Combined unique taxon IDs
166
+ def merge_taxons_from_other_stores(submitted_taxon_ids)
167
+ return submitted_taxon_ids if product.new_record?
168
+
169
+ # Get taxon IDs from other stores that should be preserved
170
+ other_stores_taxon_ids = product.taxons
171
+ .joins(:taxonomy)
172
+ .where.not(spree_taxonomies: { store_id: store.id })
173
+ .pluck(:id)
174
+ .map(&:to_s)
175
+
176
+ # Merge with submitted taxon IDs from current store and remove duplicates
177
+ (submitted_taxon_ids + other_stores_taxon_ids).uniq
178
+ end
179
+
136
180
  def update_option_value_variants(option_value_params, existing_variant)
137
181
  return {} unless option_value_params.present?
138
182
  return {} unless can_manage_option_types?
@@ -0,0 +1,40 @@
1
+ module Spree
2
+ module Posts
3
+ class Sort < ::Spree::BaseSorter
4
+ def initialize(scope, params = {}, allowed_sort_attributes = [])
5
+ super(scope, params, allowed_sort_attributes)
6
+ end
7
+
8
+ def call
9
+ posts = by_param_attributes(scope)
10
+ posts = select_translatable_fields(posts) if Spree.use_translations?
11
+
12
+ posts.distinct
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :sort, :scope, :allowed_sort_attributes
18
+
19
+ # Add translatable fields to SELECT statement to avoid InvalidColumnReference error (workaround for Mobility issue #596)
20
+ def select_translatable_fields(scope)
21
+ translatable_fields = translatable_sortable_fields
22
+ return scope if translatable_fields.empty?
23
+
24
+ scope.i18n.select("#{Post.table_name}.*").select(*translatable_fields)
25
+ end
26
+
27
+ def translatable_sortable_fields
28
+ fields = []
29
+ Post.translatable_fields.each do |field|
30
+ fields << field if sort_by?(field.to_s)
31
+ end
32
+ fields
33
+ end
34
+
35
+ def sort_by?(field)
36
+ sort.detect { |s| s[0] == field }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -8,7 +8,7 @@ module Spree
8
8
  module Addresses
9
9
  class PhoneValidator < ActiveModel::Validator
10
10
  def validate(address)
11
- return if !Spree::Config[:address_requires_phone] || address.phone.blank? || address.country.blank? || address.country_iso.blank?
11
+ return if !address.require_phone? || address.phone.blank? || address.country.blank? || address.country_iso.blank?
12
12
 
13
13
  phone = Phonelib.parse(address.phone)
14
14
  unless phone.valid_for_country?(address.country_iso)
@@ -13,14 +13,16 @@
13
13
  </div>
14
14
  <% elsif source.is_a?(Spree::StoreCredit) %>
15
15
  <% if source.originator_type == 'Spree::GiftCard' %>
16
- <div class="d-flex align-items-center">
17
- <%= icon('gift', height: 30, class: 'mr-3 text-muted opacity-50') %>
18
- <div>
19
- <p class="mb-1"><%= Spree.t(:gift_card) %>: <strong><%= source.originator.code.upcase %></strong></p>
20
- <%= @order.display_gift_card_total %>
21
- </div>
16
+ <% if respond_to?(:heroicon) %>
17
+ <%= heroicon('gift', options: { style: 'margin-right: 0 !important;' }) %>
18
+ <% else %>
19
+ <%= icon('gift', height: 30) %>
20
+ <% end %>
21
+ <div>
22
+ <p class="mb-1"><%= Spree.t(:gift_card) %>: <strong><%= source.originator.code.upcase %></strong></p>
23
+ <%= @order.display_gift_card_total %>
22
24
  </div>
23
- <% elsif payment_method_icon_tag(payment.payment_method.payment_icon_name).present? %>
25
+ <% elsif payment.payment_method.present? && payment_method_icon_tag(payment.payment_method.payment_icon_name).present? %>
24
26
  <div class="d-flex flex align-items-center items-center">
25
27
  <div><%= payment_method_icon_tag(payment.payment_method.payment_icon_name) %></div>
26
28
  <div class="ml-3"><%= Spree.t(:store_credit_name) %></div>
@@ -31,7 +33,7 @@
31
33
  <%= payment_method_icon_tag source.class.to_s.demodulize.downcase, height: 30 %>
32
34
  </div>
33
35
  <div>
34
- <% if payment.payment_method.respond_to?(:source_partial_name) %>
36
+ <% if payment.payment_method.present? && payment.payment_method.respond_to?(:source_partial_name) %>
35
37
  <%= render partial: "spree/payment_sources/#{payment.payment_method.source_partial_name}", locals: { payment: payment, source: source } %>
36
38
  <% elsif source.respond_to?(:name) %>
37
39
  <%= source.name %>
@@ -39,7 +41,7 @@
39
41
  <%= source.class.to_s.demodulize.downcase %>
40
42
  <% end %>
41
43
  </div>
42
- <% else %>
44
+ <% elsif payment.payment_method.present? %>
43
45
  <div class="flex-shrink-0">
44
46
  <%= payment_method_icon_tag payment.payment_method.payment_icon_name %>
45
47
  </div>