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,94 @@
1
+ module Spree
2
+ module Metafields
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def ensure_metafield_definition_exists!(key_with_namespace)
7
+ namespace, key = extract_namespace_and_key(key_with_namespace)
8
+ Spree::MetafieldDefinition.find_or_create_by!(namespace: namespace, key: key, resource_type: self.name)
9
+ end
10
+
11
+ def extract_namespace_and_key(key_with_namespace)
12
+ namespace = key_with_namespace.to_s.split('.').first
13
+ key = key_with_namespace.to_s.split('.').last
14
+ [namespace, key]
15
+ end
16
+ end
17
+
18
+ included do
19
+ has_many :metafields, -> { includes(:metafield_definition) }, as: :resource, class_name: 'Spree::Metafield', dependent: :destroy
20
+ has_many :public_metafields, -> { includes(:metafield_definition).available_on_front_end }, as: :resource, class_name: 'Spree::Metafield'
21
+ has_many :private_metafields, -> { includes(:metafield_definition).available_on_back_end }, as: :resource, class_name: 'Spree::Metafield'
22
+
23
+ accepts_nested_attributes_for :metafields, allow_destroy: true, reject_if: lambda { |mf|
24
+ mf[:metafield_definition_id].blank? || (mf[:id].blank? && mf[:value].blank?)
25
+ }
26
+
27
+ # Override metafields_attributes= to automatically mark existing metafields
28
+ # with empty values for destruction
29
+ def metafields_attributes=(attributes)
30
+ attributes = attributes.values if attributes.is_a?(Hash)
31
+
32
+ attributes.each do |attrs|
33
+ # If this is an existing metafield (has an id) and value is blank,
34
+ # mark it for destruction
35
+ if attrs[:id].present? && value_blank?(attrs[:value])
36
+ attrs[:_destroy] = true
37
+ end
38
+ end
39
+
40
+ super(attributes)
41
+ end
42
+
43
+ scope :with_metafield_key, ->(key_with_namespace) {
44
+ namespace, key = extract_namespace_and_key(key_with_namespace)
45
+ joins(metafields: :metafield_definition).where(spree_metafield_definitions: { namespace: namespace, key: key })
46
+ }
47
+ scope :with_metafield_key_value, ->(key_with_namespace, value) {
48
+ namespace, key = extract_namespace_and_key(key_with_namespace)
49
+
50
+ joins(metafields: :metafield_definition)
51
+ .where(spree_metafield_definitions: { namespace: namespace, key: key })
52
+ .where(spree_metafields: { value: value })
53
+ }
54
+
55
+ def extract_namespace_and_key(key_with_namespace)
56
+ self.class.extract_namespace_and_key(key_with_namespace)
57
+ end
58
+
59
+ def set_metafield(key_with_namespace, value)
60
+ namespace, key = extract_namespace_and_key(key_with_namespace)
61
+ metafield_definition = Spree::MetafieldDefinition.find_or_create_by!(namespace: namespace, key: key, resource_type: self.class.name)
62
+
63
+ metafield = metafields.find_or_initialize_by(metafield_definition: metafield_definition)
64
+ metafield.value = value
65
+ metafield.save!
66
+ metafield
67
+ end
68
+
69
+ def get_metafield(key_with_namespace)
70
+ namespace, key = extract_namespace_and_key(key_with_namespace)
71
+ metafields.with_key(namespace, key).first
72
+ end
73
+
74
+ def has_metafield?(key_with_namespace)
75
+ if key_with_namespace.is_a?(Spree::MetafieldDefinition)
76
+ namespace = key_with_namespace.namespace
77
+ key = key_with_namespace.key
78
+ elsif key_with_namespace.is_a?(String)
79
+ namespace, key = extract_namespace_and_key(key_with_namespace)
80
+ else
81
+ raise ArgumentError, "Invalid key_with_namespace: #{key_with_namespace.inspect}"
82
+ end
83
+
84
+ metafields.with_key(namespace, key).exists?
85
+ end
86
+
87
+ private
88
+
89
+ def value_blank?(value)
90
+ value.blank?
91
+ end
92
+ end
93
+ end
94
+ end
@@ -4,7 +4,7 @@ module Spree
4
4
 
5
5
  included do
6
6
  scope :active, -> { where(active: true) }
7
- default_scope { order(Arel.sql("LOWER(#{table_name}.name)")) }
7
+ default_scope { order(name: :asc) }
8
8
 
9
9
  include Spree::UniqueName
10
10
  end
@@ -2,9 +2,11 @@ module Spree
2
2
  module UserMethods
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ include Spree::Metafields
5
6
  include Spree::UserPaymentSource
6
7
  include Spree::UserReporting
7
8
  include Spree::UserRoles
9
+ include Spree::AdminUserMethods
8
10
  include Spree::RansackableAttributes
9
11
  include Spree::MultiSearchable
10
12
  included do
@@ -12,7 +14,6 @@ module Spree
12
14
  # https://github.com/rails/rails/issues/3458
13
15
  before_validation :clone_billing_address, if: :use_billing?
14
16
  before_destroy :check_completed_orders
15
- after_destroy :nullify_approver_id_in_approved_orders
16
17
 
17
18
  attr_accessor :use_billing
18
19
 
@@ -83,6 +84,11 @@ module Spree
83
84
  where("#{Spree::Address.table_name}.firstname LIKE ? or #{Spree::Address.table_name}.lastname LIKE ? or #{table_name}.email LIKE ?",
84
85
  "%#{address}%", "%#{address}%", "%#{email}%")
85
86
  end
87
+
88
+ # We override this method because we cannot use for_store on users because it will return admin users
89
+ def self.for_store(store)
90
+ self
91
+ end
86
92
  end
87
93
 
88
94
  # Returns the last incomplete spree order for the current store
@@ -138,16 +144,23 @@ module Spree
138
144
  end
139
145
  end
140
146
 
141
- private
147
+ # Returns the CSV row representation of the user
148
+ # @param [Spree::Store] store
149
+ # @return [Array<String>]
150
+ def to_csv(_store = nil)
151
+ Spree::CSV::CustomerPresenter.new(self).call
152
+ end
142
153
 
143
- def check_completed_orders
144
- raise Spree::Core::DestroyWithOrdersError if orders.complete.present?
154
+ # Returns the full name of the user
155
+ # @return [String]
156
+ def full_name
157
+ name&.full
145
158
  end
146
159
 
147
- def nullify_approver_id_in_approved_orders
148
- return unless Spree.admin_user_class != Spree.user_class
160
+ private
149
161
 
150
- Spree::Order.where(approver_id: id).update_all(approver_id: nil)
162
+ def check_completed_orders
163
+ raise Spree::Core::DestroyWithOrdersError if !has_spree_role?(Spree::Role::ADMIN_ROLE) && orders.complete.present?
151
164
  end
152
165
 
153
166
  def clone_billing_address
@@ -3,7 +3,7 @@ module Spree
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- has_many :credit_cards, class_name: 'Spree::CreditCard', foreign_key: :user_id
6
+ has_many :credit_cards, class_name: 'Spree::CreditCard', foreign_key: :user_id, dependent: :destroy
7
7
  def default_credit_card
8
8
  credit_cards.default.first
9
9
  end
@@ -3,10 +3,11 @@ module Spree
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- has_many :role_users, class_name: 'Spree::RoleUser', foreign_key: :user_id, as: :user, dependent: :destroy
6
+ has_many :role_users, class_name: 'Spree::RoleUser', foreign_key: :user_id, as: :user, dependent: :destroy_async # async as we need to check if the user has admin role before destroying
7
7
  has_many :spree_roles, through: :role_users, class_name: 'Spree::Role', source: :role
8
8
  has_many :stores, through: :role_users, source: :resource, source_type: 'Spree::Store'
9
- has_many :invitations, class_name: 'Spree::Invitation', foreign_key: :invitee_id, dependent: :destroy
9
+ has_many :invitations, class_name: 'Spree::Invitation', as: :invitee, dependent: :destroy
10
+ has_many :sent_invitations, class_name: 'Spree::Invitation', as: :inviter, dependent: :destroy
10
11
 
11
12
  scope :spree_admin, -> { joins(:spree_roles).where(Spree::Role.table_name => { name: Spree::Role::ADMIN_ROLE }) }
12
13
 
@@ -99,6 +99,10 @@ module Spree
99
99
  wished_item.wishlist.user == user
100
100
  end
101
101
  can :accept, Spree::Invitation, invitee_id: [user.id, nil], invitee_type: user.class.name, status: 'pending'
102
+ can :read, ::Spree::Policy
103
+ can :read, ::Spree::Page
104
+ can :read, ::Spree::Post
105
+ can :read, ::Spree::PostCategory
102
106
  end
103
107
 
104
108
  def protect_admin_role
@@ -2,6 +2,7 @@ module Spree
2
2
  class Address < Spree.base_class
3
3
  require 'validates_zipcode'
4
4
 
5
+ include Spree::Metafields
5
6
  include Spree::Metadata
6
7
  if defined?(Spree::Webhooks::HasWebhooks)
7
8
  include Spree::Webhooks::HasWebhooks
@@ -43,7 +44,8 @@ module Spree
43
44
 
44
45
  belongs_to :country, class_name: 'Spree::Country'
45
46
  belongs_to :state, class_name: 'Spree::State', optional: true
46
- belongs_to :user, class_name: Spree.user_class.name, optional: true, touch: true
47
+ # we need a safe operator here as Address is added to metafield_enabled_resources in Engine
48
+ belongs_to :user, class_name: Spree.user_class&.name, optional: true, touch: true
47
49
 
48
50
  has_many :shipments, inverse_of: :address
49
51
 
@@ -4,8 +4,8 @@ module Spree
4
4
  class Tax < Spree::Adjustable::Adjuster::Base
5
5
  def update
6
6
  tax = adjustments.tax
7
- included_tax_total = tax.is_included.reload.map(&:update!).compact.sum
8
- additional_tax_total = tax.additional.reload.map(&:update!).compact.sum
7
+ included_tax_total = tax.find_all(&:included?).map(&:update!).compact.sum || 0
8
+ additional_tax_total = tax.find_all(&:additional?).map(&:update!).compact.sum || 0
9
9
 
10
10
  update_totals(included_tax_total, additional_tax_total)
11
11
  end
@@ -17,10 +17,16 @@ module Spree
17
17
  non_taxable_adjustment_total: 0,
18
18
  taxable_adjustment_total: 0
19
19
  }
20
- adjusters.each do |klass|
20
+ non_tax_adjusters.each do |klass|
21
21
  klass.adjust(@adjustable, totals)
22
22
  end
23
23
 
24
+ if tax_adjuster.present?
25
+ # Other adjustments can change the taxable amount so we need to update it first
26
+ update_adjustable_attributes(totals)
27
+ tax_adjuster.adjust(@adjustable, totals)
28
+ end
29
+
24
30
  persist_totals totals
25
31
  end
26
32
 
@@ -30,9 +36,18 @@ module Spree
30
36
  attributes = totals
31
37
  attributes[:adjustment_total] = totals[:non_taxable_adjustment_total] +
32
38
  totals[:taxable_adjustment_total] +
33
- totals[:additional_tax_total]
39
+ totals.fetch(:additional_tax_total, 0)
40
+
41
+ update_adjustable_attributes(attributes)
42
+ end
43
+
44
+ def update_adjustable_attributes(attributes)
45
+ # Only update if attributes have changed
46
+ current_attributes = @adjustable.attributes.slice(*attributes.keys.map(&:to_s))
47
+ return if attributes.all? { |key, value| current_attributes[key.to_s] == value }
48
+
34
49
  attributes[:updated_at] = Time.current
35
- @adjustable.update_columns(totals)
50
+ @adjustable.update_columns(attributes)
36
51
  end
37
52
 
38
53
  def shipment?
@@ -43,6 +58,14 @@ module Spree
43
58
  Rails.application.config.spree.adjusters
44
59
  end
45
60
 
61
+ def tax_adjuster
62
+ @tax_adjuster ||= adjusters.find { |adjuster| adjuster.name == 'Spree::Adjustable::Adjuster::Tax' }
63
+ end
64
+
65
+ def non_tax_adjusters
66
+ adjusters - [tax_adjuster].compact
67
+ end
68
+
46
69
  def adjustable_still_exists?
47
70
  @adjustable&.class&.exists?(@adjustable.id)
48
71
  end
@@ -92,6 +92,10 @@ module Spree
92
92
  source_type == 'Spree::TaxRate'
93
93
  end
94
94
 
95
+ def additional?
96
+ !included?
97
+ end
98
+
95
99
  # Passing a target here would always be recommended as it would avoid
96
100
  # hitting the database again and would ensure you're compute values over
97
101
  # the specific object amount passed here.
@@ -103,7 +107,6 @@ module Spree
103
107
  attributes[:eligible] = source.promotion.eligible?(target) if promotion?
104
108
 
105
109
  update_columns(attributes)
106
- source.promotion.touch if promotion?
107
110
 
108
111
  amount
109
112
  end
@@ -1,11 +1,14 @@
1
1
  module Spree
2
2
  class Asset < Spree.base_class
3
3
  include Support::ActiveStorage
4
+ include Spree::Metafields
4
5
  include Spree::Metadata
5
6
  if defined?(Spree::Webhooks::HasWebhooks)
6
7
  include Spree::Webhooks::HasWebhooks
7
8
  end
8
9
 
10
+ EXTERNAL_URL_METAFIELD_KEY = 'external.url'
11
+
9
12
  belongs_to :viewable, polymorphic: true, touch: true
10
13
  acts_as_list scope: [:viewable_id, :viewable_type]
11
14
 
@@ -19,9 +22,22 @@ module Spree
19
22
  scope :with_session_uploaded_assets_uuid, lambda { |uuid|
20
23
  where(session_id: uuid)
21
24
  }
25
+ scope :with_external_url, ->(url) { url.present? ? with_metafield_key_value(EXTERNAL_URL_METAFIELD_KEY, url.downcase.strip) : none }
22
26
 
23
27
  def product
24
28
  @product ||= viewable_type == 'Spree::Variant' ? viewable&.product : nil
25
29
  end
30
+
31
+ def external_url
32
+ get_metafield(EXTERNAL_URL_METAFIELD_KEY)&.value
33
+ end
34
+
35
+ def external_url=(url)
36
+ set_metafield(EXTERNAL_URL_METAFIELD_KEY, url.downcase.strip)
37
+ end
38
+
39
+ def skip_import?
40
+ false
41
+ end
26
42
  end
27
43
  end
@@ -87,7 +87,7 @@ class Spree::Base < ApplicationRecord
87
87
 
88
88
  # Try building a slug based on the following fields in increasing order of specificity.
89
89
  def slug_candidates
90
- if defined?(:deleted_at) && deleted_at.present?
90
+ if defined?(deleted_at) && deleted_at.present?
91
91
  [
92
92
  ['deleted', :name],
93
93
  ['deleted', :name, :uuid_for_friendly_id]
@@ -1,6 +1,7 @@
1
1
  module Spree
2
2
  class CreditCard < Spree.base_class
3
3
  include ActiveMerchant::Billing::CreditCardMethods
4
+ include Spree::Metafields
4
5
  include Spree::Metadata
5
6
  include Spree::PaymentSourceConcern
6
7
 
@@ -1,9 +1,13 @@
1
1
  module Spree
2
2
  class Current < ::ActiveSupport::CurrentAttributes
3
- attribute :store
3
+ attribute :store, :webhooks_subscribers
4
4
 
5
5
  def store
6
6
  super || Spree::Store.default
7
7
  end
8
+
9
+ def webhooks_subscribers
10
+ super || store&.active_webhooks_subscribers || Spree::Webhooks::Subscriber.none
11
+ end
8
12
  end
9
13
  end
@@ -1,6 +1,7 @@
1
1
  module Spree
2
2
  class CustomDomain < Spree::Base
3
3
  include Spree::SingleStoreResource
4
+ include Spree::Metafields
4
5
  include Spree::Metadata
5
6
 
6
7
  auto_strip_attributes :url
@@ -44,6 +45,10 @@ module Spree
44
45
  true
45
46
  end
46
47
 
48
+ def name
49
+ url
50
+ end
51
+
47
52
  private
48
53
 
49
54
  # remove https:// and http:// from the url
@@ -2,6 +2,7 @@ module Spree
2
2
  class CustomerReturn < Spree.base_class
3
3
  include Spree::Core::NumberGenerator.new(prefix: 'CR', length: 9)
4
4
  include Spree::NumberIdentifier
5
+ include Spree::Metafields
5
6
  include Spree::Metadata
6
7
  if defined?(Spree::Webhooks::HasWebhooks)
7
8
  include Spree::Webhooks::HasWebhooks
@@ -42,6 +42,7 @@ module Spree
42
42
  # Callbacks
43
43
  #
44
44
  before_validation :set_default_format, on: :create
45
+ before_validation :normalize_search_params, on: :create, if: -> { search_params.present? }
45
46
  before_create :clear_search_params, if: -> { record_selection == 'all' }
46
47
  after_commit :generate_async, on: :create
47
48
 
@@ -89,6 +90,13 @@ module Spree
89
90
  raise NotImplementedError, 'csv_headers must be implemented'
90
91
  end
91
92
 
93
+ # Returns an array of metafield headers for the model
94
+ #
95
+ # @return [Array<String>]
96
+ def metafields_headers
97
+ @metafields_headers ||= Spree::MetafieldDefinition.for_resource_type(model_class.to_s).order(:namespace, :key).map(&:csv_header_name)
98
+ end
99
+
92
100
  def build_csv_line(_record)
93
101
  raise NotImplementedError, 'build_csv_line must be implemented'
94
102
  end
@@ -120,7 +128,32 @@ module Spree
120
128
 
121
129
  # eg. Spree::Exports::Products => Spree::Product
122
130
  def model_class
123
- "Spree::#{type.demodulize.singularize}".constantize
131
+ if type == 'Spree::Exports::Customers'
132
+ Spree.user_class
133
+ else
134
+ "Spree::#{type.demodulize.singularize}".constantize
135
+ end
136
+ end
137
+
138
+ # Ensures search params maintain consistent format between UI and exports
139
+ # - Preserves valid JSON unchanged
140
+ # - Converts Ruby hashes to JSON strings
141
+ # - Handles malformed input gracefully
142
+ def normalize_search_params
143
+ return if search_params.blank?
144
+
145
+ if search_params.is_a?(Hash)
146
+ self.search_params = search_params.to_json
147
+ return
148
+ end
149
+
150
+ begin
151
+ # It's a string, so we parse and re-dump to ensure consistency
152
+ parsed = JSON.parse(search_params.to_s)
153
+ self.search_params = parsed.to_json
154
+ rescue JSON::ParserError
155
+ # Leave as-is if not valid JSON string
156
+ end
124
157
  end
125
158
 
126
159
  def current_ability
@@ -155,7 +188,11 @@ module Spree
155
188
 
156
189
  # eg. Spree::Exports::Products => Spree::Product
157
190
  def model_class
158
- "Spree::#{to_s.demodulize.singularize}".constantize
191
+ klass = "Spree::#{to_s.demodulize.singularize}".safe_constantize
192
+
193
+ raise NameError, "Missing model class for #{to_s}" unless klass
194
+
195
+ klass
159
196
  end
160
197
  end
161
198
 
@@ -0,0 +1,17 @@
1
+ module Spree
2
+ module Exports
3
+ class Customers < Spree::Export
4
+ def scope_includes
5
+ [
6
+ { bill_address: :state },
7
+ { ship_address: :state },
8
+ { metafields: :metafield_definition }
9
+ ]
10
+ end
11
+
12
+ def csv_headers
13
+ Spree::CSV::CustomerPresenter::HEADERS + metafields_headers
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ module Exports
3
+ class GiftCards < Spree::Export
4
+ def scope_includes
5
+ [:user, { metafields: :metafield_definition }]
6
+ end
7
+
8
+ def csv_headers
9
+ Spree::CSV::GiftCardPresenter::HEADERS + metafields_headers
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ module Exports
3
+ class NewsletterSubscribers < Spree::Export
4
+ def scope_includes
5
+ [:user, { metafields: :metafield_definition }]
6
+ end
7
+
8
+ def csv_headers
9
+ Spree::CSV::NewsletterSubscriberPresenter::HEADERS + metafields_headers
10
+ end
11
+ end
12
+ end
13
+ end
@@ -3,11 +3,13 @@ module Spree
3
3
  class Orders < Spree::Export
4
4
  def scope_includes
5
5
  [
6
+ :store,
6
7
  :payments,
7
8
  :shipments,
8
- { bill_address: :state },
9
- { ship_address: :state },
10
- { line_items: { variant: { product: [:taxons] } } }
9
+ { bill_address: [:state, :country] },
10
+ { ship_address: [:state, :country] },
11
+ { line_items: { variant: { product: [:taxons] } } },
12
+ { metafields: :metafield_definition }
11
13
  ]
12
14
  end
13
15
 
@@ -16,7 +18,7 @@ module Spree
16
18
  end
17
19
 
18
20
  def csv_headers
19
- Spree::CSV::OrderLineItemPresenter::HEADERS
21
+ Spree::CSV::OrderLineItemPresenter::HEADERS + metafields_headers
20
22
  end
21
23
  end
22
24
  end
@@ -3,11 +3,14 @@ module Spree
3
3
  class Products < Spree::Export
4
4
  # to avoid N+1 queries
5
5
  def scope_includes
6
- [:tax_category, :master, { taxons: :taxonomy }, { product_properties: [:property] }, { variants_including_master: variant_includes }]
6
+ includes = [:tax_category, :master, :option_types, { taxons: :taxonomy }, { variants_including_master: variant_includes }]
7
+ includes << { product_properties: [:property] } if Spree::Config[:product_properties_enabled]
8
+ includes << { metafields: :metafield_definition }
9
+ includes
7
10
  end
8
11
 
9
12
  def variant_includes
10
- [:images, :prices, :stock_items, { option_values: [:option_type] }]
13
+ [:images, :prices, :stock_items, :stock_locations, { option_values: [:option_type] }]
11
14
  end
12
15
 
13
16
  def multi_line_csv?
@@ -24,7 +27,10 @@ module Spree
24
27
  end
25
28
 
26
29
  def csv_headers
27
- @csv_headers ||= Spree::CSV::ProductVariantPresenter::CSV_HEADERS + properties_headers
30
+ headers = Spree::CSV::ProductVariantPresenter::CSV_HEADERS.dup
31
+ headers += properties_headers if Spree::Config[:product_properties_enabled]
32
+ headers += metafields_headers
33
+ @csv_headers ||= headers
28
34
  end
29
35
 
30
36
  def properties_headers
@@ -2,6 +2,7 @@ module Spree
2
2
  class GiftCard < Spree.base_class
3
3
  extend DisplayMoney
4
4
  include Spree::SingleStoreResource
5
+ include Spree::Metafields
5
6
  include Spree::Security::GiftCards if defined?(Spree::Security::GiftCards)
6
7
 
7
8
  #
@@ -55,8 +56,9 @@ module Spree
55
56
  #
56
57
  # Ransack
57
58
  #
58
- self.whitelisted_ransackable_attributes = %w[code user_id]
59
+ self.whitelisted_ransackable_attributes = %w[code user_id state]
59
60
  self.whitelisted_ransackable_associations = %w[users orders batch]
61
+ self.whitelisted_ransackable_scopes = %w[active expired redeemed partially_redeemed]
60
62
 
61
63
  auto_strip_attributes :code
62
64
 
@@ -131,6 +133,10 @@ module Spree
131
133
  end.to_s
132
134
  end
133
135
 
136
+ def to_csv(_store = nil)
137
+ Spree::CSV::GiftCardPresenter.new(self).call
138
+ end
139
+
134
140
  private
135
141
 
136
142
  def generate_code
@@ -10,7 +10,6 @@ module Spree
10
10
  belongs_to :created_by, class_name: Spree.admin_user_class.to_s, optional: true
11
11
  has_many :gift_cards, class_name: 'Spree::GiftCard', inverse_of: :batch, dependent: :destroy
12
12
 
13
-
14
13
  #
15
14
  # Validations
16
15
  #