spree_core 5.1.8 → 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 (389) 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/jobs/spree/images/save_from_url_job.rb +70 -0
  218. data/app/jobs/spree/imports/create_rows_job.rb +72 -0
  219. data/app/jobs/spree/imports/process_rows_job.rb +19 -0
  220. data/app/models/concerns/spree/adjustment_source.rb +3 -3
  221. data/app/models/concerns/spree/admin_user_methods.rb +52 -0
  222. data/app/models/concerns/spree/display_on.rb +7 -1
  223. data/app/models/concerns/spree/has_image_alt_text.rb +18 -0
  224. data/app/models/concerns/spree/has_one_link.rb +1 -1
  225. data/app/models/concerns/spree/has_page_links.rb +1 -1
  226. data/app/models/concerns/spree/linkable.rb +9 -0
  227. data/app/models/concerns/spree/metadata.rb +2 -0
  228. data/app/models/concerns/spree/metafields.rb +94 -0
  229. data/app/models/concerns/spree/named_type.rb +1 -1
  230. data/app/models/concerns/spree/user_methods.rb +20 -7
  231. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  232. data/app/models/concerns/spree/user_roles.rb +3 -2
  233. data/app/models/spree/ability.rb +4 -0
  234. data/app/models/spree/address.rb +3 -1
  235. data/app/models/spree/adjustable/adjuster/tax.rb +2 -2
  236. data/app/models/spree/adjustable/adjustments_updater.rb +26 -3
  237. data/app/models/spree/adjustment.rb +4 -1
  238. data/app/models/spree/asset.rb +16 -0
  239. data/app/models/spree/base.rb +1 -1
  240. data/app/models/spree/credit_card.rb +1 -0
  241. data/app/models/spree/current.rb +5 -1
  242. data/app/models/spree/custom_domain.rb +5 -0
  243. data/app/models/spree/customer_return.rb +1 -0
  244. data/app/models/spree/export.rb +39 -2
  245. data/app/models/spree/exports/customers.rb +17 -0
  246. data/app/models/spree/exports/gift_cards.rb +13 -0
  247. data/app/models/spree/exports/newsletter_subscribers.rb +13 -0
  248. data/app/models/spree/exports/orders.rb +6 -4
  249. data/app/models/spree/exports/products.rb +9 -3
  250. data/app/models/spree/gift_card.rb +7 -1
  251. data/app/models/spree/gift_card_batch.rb +0 -1
  252. data/app/models/spree/import.rb +279 -0
  253. data/app/models/spree/import_mapping.rb +46 -0
  254. data/app/models/spree/import_row.rb +108 -0
  255. data/app/models/spree/import_schema.rb +25 -0
  256. data/app/models/spree/import_schemas/products.rb +45 -0
  257. data/app/models/spree/imports/products.rb +9 -0
  258. data/app/models/spree/invitation.rb +1 -1
  259. data/app/models/spree/legacy_user.rb +0 -1
  260. data/app/models/spree/line_item.rb +2 -7
  261. data/app/models/spree/metafield.rb +53 -0
  262. data/app/models/spree/metafield_definition.rb +91 -0
  263. data/app/models/spree/metafields/boolean.rb +15 -0
  264. data/app/models/spree/metafields/json.rb +27 -0
  265. data/app/models/spree/metafields/long_text.rb +7 -0
  266. data/app/models/spree/metafields/number.rb +15 -0
  267. data/app/models/spree/metafields/rich_text.rb +18 -0
  268. data/app/models/spree/metafields/short_text.rb +7 -0
  269. data/app/models/spree/newsletter_subscriber/emails.rb +12 -0
  270. data/app/models/spree/newsletter_subscriber.rb +64 -0
  271. data/app/models/spree/option_type.rb +2 -0
  272. data/app/models/spree/option_value.rb +1 -0
  273. data/app/models/spree/order/checkout.rb +7 -0
  274. data/app/models/spree/order.rb +8 -3
  275. data/app/models/spree/order_merger.rb +2 -4
  276. data/app/models/spree/order_promotion.rb +3 -0
  277. data/app/models/spree/order_updater.rb +2 -2
  278. data/app/models/spree/page.rb +5 -1
  279. data/app/models/spree/page_block.rb +4 -0
  280. data/app/models/spree/page_blocks/image.rb +3 -0
  281. data/app/models/spree/page_blocks/metafields.rb +18 -0
  282. data/app/models/spree/page_blocks/products/brand.rb +15 -0
  283. data/app/models/spree/page_blocks/products/buy_buttons.rb +5 -1
  284. data/app/models/spree/page_blocks/products/description.rb +18 -0
  285. data/app/models/spree/page_blocks/products/price.rb +1 -1
  286. data/app/models/spree/page_blocks/products/quantity_selector.rb +6 -2
  287. data/app/models/spree/page_blocks/products/title.rb +5 -1
  288. data/app/models/spree/page_blocks/products/variant_picker.rb +1 -1
  289. data/app/models/spree/page_section.rb +2 -2
  290. data/app/models/spree/page_sections/breadcrumbs.rb +12 -0
  291. data/app/models/spree/page_sections/footer.rb +20 -13
  292. data/app/models/spree/page_sections/image_banner.rb +6 -1
  293. data/app/models/spree/page_sections/image_with_text.rb +5 -0
  294. data/app/models/spree/page_sections/newsletter.rb +7 -0
  295. data/app/models/spree/page_sections/product_details.rb +38 -1
  296. data/app/models/spree/pages/account.rb +4 -0
  297. data/app/models/spree/pages/cart.rb +10 -0
  298. data/app/models/spree/pages/checkout.rb +6 -0
  299. data/app/models/spree/pages/custom.rb +4 -0
  300. data/app/models/spree/pages/homepage.rb +4 -0
  301. data/app/models/spree/pages/login.rb +4 -0
  302. data/app/models/spree/pages/password.rb +2 -0
  303. data/app/models/spree/pages/post_list.rb +4 -0
  304. data/app/models/spree/pages/product_details.rb +1 -0
  305. data/app/models/spree/pages/shop_all.rb +4 -0
  306. data/app/models/spree/pages/wishlist.rb +4 -0
  307. data/app/models/spree/payment.rb +1 -0
  308. data/app/models/spree/payment_method.rb +4 -5
  309. data/app/models/spree/payment_source.rb +1 -0
  310. data/app/models/spree/policy.rb +70 -0
  311. data/app/models/spree/post.rb +4 -2
  312. data/app/models/spree/post_category.rb +1 -0
  313. data/app/models/spree/product.rb +16 -9
  314. data/app/models/spree/promotion/actions/free_shipping.rb +3 -3
  315. data/app/models/spree/promotion.rb +24 -8
  316. data/app/models/spree/promotion_action.rb +1 -1
  317. data/app/models/spree/promotion_handler/cart.rb +1 -1
  318. data/app/models/spree/promotion_handler/coupon.rb +1 -1
  319. data/app/models/spree/promotion_rule.rb +1 -1
  320. data/app/models/spree/property.rb +18 -0
  321. data/app/models/spree/refund.rb +2 -1
  322. data/app/models/spree/role_user.rb +1 -1
  323. data/app/models/spree/shipment.rb +1 -0
  324. data/app/models/spree/shipping_method.rb +1 -0
  325. data/app/models/spree/stock_item.rb +1 -0
  326. data/app/models/spree/stock_transfer.rb +1 -0
  327. data/app/models/spree/store.rb +45 -3
  328. data/app/models/spree/store_credit.rb +2 -1
  329. data/app/models/spree/tax_category.rb +1 -1
  330. data/app/models/spree/tax_rate.rb +18 -3
  331. data/app/models/spree/taxon.rb +12 -2
  332. data/app/models/spree/taxonomy.rb +11 -0
  333. data/app/models/spree/theme.rb +1 -1
  334. data/app/models/spree/variant.rb +17 -0
  335. data/app/presenters/spree/csv/customer_presenter.rb +59 -0
  336. data/app/presenters/spree/csv/gift_card_presenter.rb +49 -0
  337. data/app/presenters/spree/csv/metafields_helper.rb +17 -0
  338. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +39 -0
  339. data/app/presenters/spree/csv/order_line_item_presenter.rb +12 -5
  340. data/app/presenters/spree/csv/product_variant_presenter.rb +9 -5
  341. data/app/services/spree/cart/recalculate.rb +6 -4
  342. data/app/services/spree/cart/remove_line_item.rb +1 -2
  343. data/app/services/spree/imports/row_processors/base.rb +19 -0
  344. data/app/services/spree/imports/row_processors/product_variant.rb +228 -0
  345. data/app/services/spree/line_items/destroy.rb +1 -1
  346. data/app/services/spree/newsletter/subscribe.rb +43 -0
  347. data/app/services/spree/newsletter/verify.rb +30 -0
  348. data/app/sorters/spree/posts/sort.rb +40 -0
  349. data/app/views/spree/shared/_payment.html.erb +3 -3
  350. data/config/locales/en.yml +56 -2
  351. data/db/migrate/20250605131334_add_missing_fields_to_users.rb +23 -13
  352. data/db/migrate/20250728095629_create_spree_metafields.rb +27 -0
  353. data/db/migrate/20250730102644_add_missing_unique_indexes_to_spree_option_types_and_values.rb +40 -0
  354. data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +24 -0
  355. data/db/migrate/20250811112056_create_spree_policies.rb +22 -0
  356. data/db/migrate/20250821211705_fix_unique_index_on_spree_order_promotions.rb +20 -0
  357. data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +7 -0
  358. data/db/migrate/20250826093602_create_spree_newsletter_subscribers.rb +12 -0
  359. data/db/migrate/20250902143122_fix_policies_store_association.rb +18 -0
  360. data/db/migrate/20250911205659_add_missing_index_on_spree_products_promotionable.rb +5 -0
  361. data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +10 -0
  362. data/db/migrate/20250914101955_add_missing_indexes_on_spree_adjustments.rb +6 -0
  363. data/db/migrate/20250915093930_add_metadata_to_spree_newsletter_subscribers.rb +20 -0
  364. data/db/migrate/20250923141845_create_spree_imports.rb +47 -0
  365. data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +19 -0
  366. data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +387 -0
  367. data/lib/generators/spree/dummy/dummy_generator.rb +0 -1
  368. data/lib/generators/spree/install/templates/config/initializers/spree.rb +5 -0
  369. data/lib/spree/core/configuration.rb +1 -0
  370. data/lib/spree/core/dependencies.rb +3 -0
  371. data/lib/spree/core/engine.rb +57 -2
  372. data/lib/spree/core/version.rb +1 -1
  373. data/lib/spree/core.rb +3 -1
  374. data/lib/spree/permitted_attributes.rb +18 -3
  375. data/lib/spree/testing_support/capybara_config.rb +9 -3
  376. data/lib/spree/testing_support/factories/export_factory.rb +5 -17
  377. data/lib/spree/testing_support/factories/import_factory.rb +20 -0
  378. data/lib/spree/testing_support/factories/import_mapping_factory.rb +7 -0
  379. data/lib/spree/testing_support/factories/import_row_factory.rb +7 -0
  380. data/lib/spree/testing_support/factories/metafield_definition_factory.rb +72 -0
  381. data/lib/spree/testing_support/factories/metafield_factory.rb +68 -0
  382. data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +18 -0
  383. data/lib/spree/testing_support/factories/policy_factory.rb +8 -0
  384. data/lib/spree/testing_support/factories/post_factory.rb +8 -0
  385. data/lib/spree/testing_support/store.rb +11 -0
  386. data/lib/tasks/core.rake +128 -0
  387. metadata +279 -6
  388. data/app/models/action_text/rich_text_decorator.rb +0 -11
  389. data/app/presenters/spree/csv/product_presenter.rb +0 -63
@@ -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
@@ -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
@@ -22,7 +22,7 @@
22
22
  <p class="mb-1"><%= Spree.t(:gift_card) %>: <strong><%= source.originator.code.upcase %></strong></p>
23
23
  <%= @order.display_gift_card_total %>
24
24
  </div>
25
- <% 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? %>
26
26
  <div class="d-flex flex align-items-center items-center">
27
27
  <div><%= payment_method_icon_tag(payment.payment_method.payment_icon_name) %></div>
28
28
  <div class="ml-3"><%= Spree.t(:store_credit_name) %></div>
@@ -33,7 +33,7 @@
33
33
  <%= payment_method_icon_tag source.class.to_s.demodulize.downcase, height: 30 %>
34
34
  </div>
35
35
  <div>
36
- <% if payment.payment_method.respond_to?(:source_partial_name) %>
36
+ <% if payment.payment_method.present? && payment.payment_method.respond_to?(:source_partial_name) %>
37
37
  <%= render partial: "spree/payment_sources/#{payment.payment_method.source_partial_name}", locals: { payment: payment, source: source } %>
38
38
  <% elsif source.respond_to?(:name) %>
39
39
  <%= source.name %>
@@ -41,7 +41,7 @@
41
41
  <%= source.class.to_s.demodulize.downcase %>
42
42
  <% end %>
43
43
  </div>
44
- <% else %>
44
+ <% elsif payment.payment_method.present? %>
45
45
  <div class="flex-shrink-0">
46
46
  <%= payment_method_icon_tag payment.payment_method.payment_icon_name %>
47
47
  </div>
@@ -53,6 +53,13 @@ en:
53
53
  spree/gift_card:
54
54
  amount_used: Amount used
55
55
  expires_at: Expires at
56
+ spree/import:
57
+ preferred_delimiter: Delimiter
58
+ spree/import_mapping:
59
+ file_column: File Column
60
+ schema_field: Schema Field
61
+ spree/import_row:
62
+ row_number: Number
56
63
  spree/inventory_unit:
57
64
  state: State
58
65
  spree/invitation:
@@ -60,6 +67,8 @@ en:
60
67
  spree/line_item:
61
68
  price: Price
62
69
  quantity: Quantity
70
+ spree/metafield_definition:
71
+ namespace: Namespace
63
72
  spree/option_type:
64
73
  filterable: Filterable
65
74
  name: Name
@@ -101,6 +110,9 @@ en:
101
110
  number: Number
102
111
  spree/payment_method:
103
112
  name: Name
113
+ spree/policy:
114
+ body: Body
115
+ owner: Policy for
104
116
  spree/product:
105
117
  available_on: Available On
106
118
  cost_currency: Cost Currency
@@ -165,6 +177,9 @@ en:
165
177
  preferred_limit_digital_download_days: Limit digital links download days
166
178
  seo_robots: SEO Robots
167
179
  seo_title: SEO Title
180
+ storefront_custom_code_body_end: Custom Code before closing body tag
181
+ storefront_custom_code_body_start: Custom Code after opening body tag
182
+ storefront_custom_code_head: Custom Code in page head
168
183
  url: Site URL
169
184
  spree/store_credit:
170
185
  amount_used: Amount used
@@ -241,6 +256,12 @@ en:
241
256
  attachment:
242
257
  attachment_must_be_present: must be present
243
258
  not_allowed_content_type: has not allowed content type
259
+ spree/import:
260
+ attributes:
261
+ attachment:
262
+ content_type: has not allowed content type
263
+ type:
264
+ inclusion: is not included in the list of allowed types
244
265
  spree/legacy_user:
245
266
  attributes:
246
267
  bill_address_id:
@@ -677,6 +698,9 @@ en:
677
698
  analytics: Analytics
678
699
  and: and
679
700
  api_keys: API keys
701
+ apis:
702
+ platform: Platform API
703
+ storefront: Storefront API
680
704
  apply: Apply
681
705
  apply_only_on_full_priced_items: Apply only on full priced items
682
706
  apply_store_credit: Apply Store Credit
@@ -728,6 +752,7 @@ en:
728
752
  body_tag_end: Body tag end
729
753
  body_tag_start: Body tag start
730
754
  both: Both
755
+ brand: Brand
731
756
  breadcrumbs: Breadcrumbs
732
757
  calculated_reimbursements: Calculated Reimbursements
733
758
  calculator: Calculator
@@ -821,6 +846,7 @@ en:
821
846
  continue_selling_when_out_of_stock: Continue selling when out of stock
822
847
  continue_shopping: Continue shopping
823
848
  continue_without_logging_in: Continue without logging in
849
+ copied: Copied!
824
850
  copy: Copy
825
851
  copy_id: Copy ID
826
852
  copy_link: Copy link
@@ -871,7 +897,6 @@ en:
871
897
  credits: Credits
872
898
  currencies: Currencies
873
899
  currency: Currency
874
- currency_and_country_help: This determines which vendors you can connect with and what customers see on your store.
875
900
  currency_settings: Currency Settings
876
901
  current: Current
877
902
  current_password: Current password
@@ -900,6 +925,7 @@ en:
900
925
  default_billing_address: Default billing address
901
926
  default_country: Default Country
902
927
  default_country_cannot_be_deleted: Default country cannot be deleted
928
+ default_country_help: Country that will be used for the default shipping and billing addresses on Checkout
903
929
  default_currency: Default currency
904
930
  default_locale: Default locale
905
931
  default_post_categories:
@@ -1061,6 +1087,7 @@ en:
1061
1087
  field: Field
1062
1088
  filename: Filename
1063
1089
  fill_in_customer_info: Please fill in customer info
1090
+ filled: Filled
1064
1091
  filter: Filter
1065
1092
  filter_results: Filter Results
1066
1093
  filterable: Filterable
@@ -1143,6 +1170,7 @@ en:
1143
1170
  images: Images
1144
1171
  implement_eligible_for_return: 'Must implement #eligible_for_return? for your EligibilityValidator.'
1145
1172
  implement_requires_manual_intervention: 'Must implement #requires_manual_intervention? for your EligibilityValidator.'
1173
+ import: Import
1146
1174
  import_payment_methods_from: Import payment methods from
1147
1175
  import_products_from: Import products from
1148
1176
  in_stock: In Stock
@@ -1165,6 +1193,8 @@ en:
1165
1193
  intercept_email_address: Intercept Email Address
1166
1194
  intercept_email_instructions: Override email recipient and replace with this address.
1167
1195
  internal_name: Internal Name
1196
+ internal_server_error: Internal Server Error
1197
+ internal_server_error_message: The server encountered an internal error and was unable to complete your request.
1168
1198
  internationalization: Internationalization
1169
1199
  invalid_credit_card: Invalid credit card.
1170
1200
  invalid_exchange_variant: Invalid exchange variant.
@@ -1209,6 +1239,7 @@ en:
1209
1239
  items_reimbursed: Items reimbursed
1210
1240
  items_to_be_reimbursed: Items to be reimbursed
1211
1241
  join_slack: Join Slack
1242
+ key: Key
1212
1243
  kind: Kind
1213
1244
  language: Language
1214
1245
  last_name: Last Name
@@ -1264,6 +1295,8 @@ en:
1264
1295
  meta_keywords: Meta Keywords
1265
1296
  meta_title: Meta Title
1266
1297
  metadata: Metadata
1298
+ metafield_definitions: Metafield Definitions
1299
+ metafields: Metafields
1267
1300
  min: Min
1268
1301
  minimal_amount: Minimal Amount
1269
1302
  minimum_password_length:
@@ -1301,7 +1334,7 @@ en:
1301
1334
  new_gift_card: New Gift Card
1302
1335
  new_image: New Image
1303
1336
  new_line_item: Add line item
1304
- new_market: New market
1337
+ new_metafield_definition: New Metafield Definition
1305
1338
  new_nested_taxon: New nested taxon
1306
1339
  new_option_type: New Option Type
1307
1340
  new_order: New Order
@@ -1311,6 +1344,7 @@ en:
1311
1344
  new_password: New password
1312
1345
  new_payment: New Payment
1313
1346
  new_payment_method: New Payment Method
1347
+ new_policy: New Policy
1314
1348
  new_post: New Post
1315
1349
  new_post_category: New Post Category
1316
1350
  new_product: New Product
@@ -1343,6 +1377,17 @@ en:
1343
1377
  new_user: New User
1344
1378
  new_variant: New Variant
1345
1379
  new_zone: New Zone
1380
+ newsletter_mailer:
1381
+ email_confirmation:
1382
+ dear_customer: Hey,
1383
+ ignore_disclaimer: If you didn't request this, please ignore this email.
1384
+ instructions: Please click the button below to verify your email address.
1385
+ message_copy_link: 'If the button doesn''t work please copy and paste the following link to your browser:'
1386
+ store_team: "%{store_name} Team"
1387
+ subject: Verify your email address
1388
+ thanks: Thank you
1389
+ verify_email_address_link: Verify email address
1390
+ newsletter_subscribers: Newsletter Subscribers
1346
1391
  newsletters: Newsletters
1347
1392
  next: Next
1348
1393
  no_actions_added: No actions added
@@ -1495,6 +1540,7 @@ en:
1495
1540
  buy_buttons:
1496
1541
  display_name: Add To Cart
1497
1542
  page_not_found: Sorry! Page you are looking can’t be found.
1543
+ page_not_found_message: This page just doesn't exist.
1498
1544
  page_sections:
1499
1545
  announcement_bar:
1500
1546
  default_text: Welcome to my Store!
@@ -1779,6 +1825,7 @@ en:
1779
1825
  resellable: Resellable
1780
1826
  resend: Resend
1781
1827
  reset_password: Reset my password
1828
+ resource: Resource
1782
1829
  response_code: Response Code
1783
1830
  restore_defaults: Restore defaults
1784
1831
  resume: resume
@@ -1813,6 +1860,7 @@ en:
1813
1860
  rma_value: RMA Value
1814
1861
  role_id: Role ID
1815
1862
  roles: Roles
1863
+ row: Row
1816
1864
  rules: Rules
1817
1865
  safe: Safe
1818
1866
  sale: Sale
@@ -1869,6 +1917,8 @@ en:
1869
1917
  seo_title: SEO Title
1870
1918
  server: Server
1871
1919
  server_error: The server returned an error
1920
+ service_unavailable: Service Unavailable
1921
+ service_unavailable_message: The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.
1872
1922
  settings: Settings
1873
1923
  ship: Ship
1874
1924
  ship_address: Ship Address
@@ -2205,6 +2255,8 @@ en:
2205
2255
  metric: metric
2206
2256
  metric_system: Metric system
2207
2257
  unlock: Unlock
2258
+ unprocessable_entity: Unprocessable Entity
2259
+ unprocessable_entity_message: Your request could not be processed. Please check the form for errors and try again.
2208
2260
  unrecognized_card_type: Unrecognized card type
2209
2261
  unshippable_items: Unshippable Items
2210
2262
  update: Update
@@ -2246,6 +2298,8 @@ en:
2246
2298
  variants_count: Variants count
2247
2299
  vendor: Vendor
2248
2300
  vendors: Vendors
2301
+ verified: Verified
2302
+ verified_at: Verified at
2249
2303
  version: Version
2250
2304
  view: View
2251
2305
  view_all: View all
@@ -1,25 +1,35 @@
1
- # this fields were not present when someone used a custom user class
1
+ # these fields were not present when someone used a custom user class
2
2
  # so we need to ensure this is setup properly
3
3
  class AddMissingFieldsToUsers < ActiveRecord::Migration[7.2]
4
4
  def change
5
5
  users_table = Spree.user_class.table_name
6
- add_column users_table, :login, :string, if_not_exists: true
6
+ admin_users_table = Spree.admin_user_class.table_name
7
7
 
8
- json_type = if respond_to?(:jsonb)
9
- :jsonb
10
- else
11
- :json
12
- end
8
+ change_table users_table do |t|
9
+ t.string :login unless column_exists?(users_table, :login)
13
10
 
14
- add_column users_table, :public_metadata, json_type, if_not_exists: true
15
- add_column users_table, :private_metadata, json_type, if_not_exists: true
11
+ if t.respond_to? :jsonb
12
+ t.jsonb :public_metadata unless column_exists?(users_table, :public_metadata)
13
+ t.jsonb :private_metadata unless column_exists?(users_table, :private_metadata)
14
+ else
15
+ t.json :public_metadata unless column_exists?(users_table, :public_metadata)
16
+ t.json :private_metadata unless column_exists?(users_table, :private_metadata)
17
+ end
18
+ end
16
19
 
17
20
  add_reference users_table, :bill_address, if_not_exists: true
18
21
  add_reference users_table, :ship_address, if_not_exists: true
19
22
 
20
- admin_users_table = Spree.admin_user_class.table_name
21
- add_column admin_users_table, :login, :string, if_not_exists: true
22
- add_column admin_users_table, :public_metadata, json_type, if_not_exists: true
23
- add_column admin_users_table, :private_metadata, json_type, if_not_exists: true
23
+ change_table admin_users_table do |t|
24
+ t.string :login unless column_exists?(admin_users_table, :login)
25
+
26
+ if t.respond_to? :jsonb
27
+ t.jsonb :public_metadata unless column_exists?(admin_users_table, :public_metadata)
28
+ t.jsonb :private_metadata unless column_exists?(admin_users_table, :private_metadata)
29
+ else
30
+ t.json :public_metadata unless column_exists?(admin_users_table, :public_metadata)
31
+ t.json :private_metadata unless column_exists?(admin_users_table, :private_metadata)
32
+ end
33
+ end
24
34
  end
25
35
  end
@@ -0,0 +1,27 @@
1
+ class CreateSpreeMetafields < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_metafield_definitions do |t|
4
+ t.string :namespace, null: false
5
+ t.string :key, null: false
6
+ t.string :name, null: false
7
+ t.string :metafield_type, null: false
8
+ t.string :resource_type, null: false, index: true
9
+ t.string :display_on, null: false, default: 'both', index: true
10
+ t.timestamps
11
+
12
+ t.index [:namespace, :key]
13
+ t.index [:resource_type, :namespace, :key], unique: true
14
+ end
15
+
16
+ create_table :spree_metafields do |t|
17
+ t.string :type, null: false, index: true
18
+ t.references :resource, polymorphic: true, null: false, index: true
19
+ t.references :metafield_definition, null: false, index: true
20
+ t.text :value
21
+
22
+ t.timestamps
23
+
24
+ t.index [:resource_type, :resource_id, :metafield_definition_id], name: 'index_metafields_on_resource_and_definition', unique: true
25
+ end
26
+ end
27
+ end