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.
- checksums.yaml +4 -4
- data/app/assets/images/payment_icons/ach.svg +1 -0
- data/app/assets/images/payment_icons/achdirectdebit.svg +1 -0
- data/app/assets/images/payment_icons/ada.svg +1 -0
- data/app/assets/images/payment_icons/adyen.svg +1 -0
- data/app/assets/images/payment_icons/affinbank.svg +1 -0
- data/app/assets/images/payment_icons/affirm.svg +1 -0
- data/app/assets/images/payment_icons/afterpay.svg +1 -0
- data/app/assets/images/payment_icons/airtel_money.svg +1 -0
- data/app/assets/images/payment_icons/airteltigo_mobile_money.svg +1 -0
- data/app/assets/images/payment_icons/alandsbanken.svg +1 -0
- data/app/assets/images/payment_icons/alipay.svg +1 -0
- data/app/assets/images/payment_icons/alipay_hk.svg +1 -0
- data/app/assets/images/payment_icons/alliancebank.svg +1 -0
- data/app/assets/images/payment_icons/alma.svg +1 -0
- data/app/assets/images/payment_icons/amazon.svg +1 -0
- data/app/assets/images/payment_icons/ambank.svg +1 -0
- data/app/assets/images/payment_icons/american_express.svg +1 -0
- data/app/assets/images/payment_icons/apecoin.svg +1 -0
- data/app/assets/images/payment_icons/apple_pay.svg +1 -0
- data/app/assets/images/payment_icons/arbitrum.svg +1 -0
- data/app/assets/images/payment_icons/atome.svg +1 -0
- data/app/assets/images/payment_icons/authorizenet.svg +1 -0
- data/app/assets/images/payment_icons/avalanche.svg +1 -0
- data/app/assets/images/payment_icons/bacs.svg +1 -0
- data/app/assets/images/payment_icons/bancontact.svg +1 -0
- data/app/assets/images/payment_icons/bangkokbank.svg +1 -0
- data/app/assets/images/payment_icons/bankislam.svg +1 -0
- data/app/assets/images/payment_icons/bankmuamalat.svg +1 -0
- data/app/assets/images/payment_icons/bankofamerica.svg +1 -0
- data/app/assets/images/payment_icons/bankrakyat.svg +1 -0
- data/app/assets/images/payment_icons/base.svg +1 -0
- data/app/assets/images/payment_icons/becs.svg +1 -0
- data/app/assets/images/payment_icons/benefit.svg +1 -0
- data/app/assets/images/payment_icons/billie.svg +1 -0
- data/app/assets/images/payment_icons/bitcoin.svg +1 -0
- data/app/assets/images/payment_icons/bitcoin_cash.svg +1 -0
- data/app/assets/images/payment_icons/bizum.svg +1 -0
- data/app/assets/images/payment_icons/blik.svg +1 -0
- data/app/assets/images/payment_icons/bnbchain.svg +1 -0
- data/app/assets/images/payment_icons/bogus.svg +1 -0
- data/app/assets/images/payment_icons/bogus_app_coin.svg +1 -0
- data/app/assets/images/payment_icons/boleto.svg +1 -0
- data/app/assets/images/payment_icons/boost.svg +1 -0
- data/app/assets/images/payment_icons/braintree.svg +1 -0
- data/app/assets/images/payment_icons/buckaroopaybybank.svg +1 -0
- data/app/assets/images/payment_icons/busd.svg +1 -0
- data/app/assets/images/payment_icons/cartes_bancaires.svg +1 -0
- data/app/assets/images/payment_icons/cash.svg +1 -0
- data/app/assets/images/payment_icons/cashappafterpay.svg +1 -0
- data/app/assets/images/payment_icons/cashapppay.svg +1 -0
- data/app/assets/images/payment_icons/checkout_finance.svg +1 -0
- data/app/assets/images/payment_icons/chinabank.svg +1 -0
- data/app/assets/images/payment_icons/clearpay.svg +1 -0
- data/app/assets/images/payment_icons/collector_bank.svg +1 -0
- data/app/assets/images/payment_icons/dai.svg +1 -0
- data/app/assets/images/payment_icons/dana.svg +1 -0
- data/app/assets/images/payment_icons/dankort.svg +1 -0
- data/app/assets/images/payment_icons/danske_bank.svg +1 -0
- data/app/assets/images/payment_icons/deutschebank.svg +1 -0
- data/app/assets/images/payment_icons/diners_club.svg +1 -0
- data/app/assets/images/payment_icons/directbanktransferlatinamerica.svg +1 -0
- data/app/assets/images/payment_icons/discover.svg +1 -0
- data/app/assets/images/payment_icons/dogecoin.svg +1 -0
- data/app/assets/images/payment_icons/duitnow.svg +1 -0
- data/app/assets/images/payment_icons/eft_secure.svg +1 -0
- data/app/assets/images/payment_icons/eftpos_au.svg +1 -0
- data/app/assets/images/payment_icons/eftposau.svg +1 -0
- data/app/assets/images/payment_icons/elo.svg +1 -0
- data/app/assets/images/payment_icons/eos.svg +1 -0
- data/app/assets/images/payment_icons/eps.svg +1 -0
- data/app/assets/images/payment_icons/ethereum.svg +1 -0
- data/app/assets/images/payment_icons/eurobonuscheckout.svg +1 -0
- data/app/assets/images/payment_icons/ezcash.svg +1 -0
- data/app/assets/images/payment_icons/fantom.svg +1 -0
- data/app/assets/images/payment_icons/fpx.svg +1 -0
- data/app/assets/images/payment_icons/gcash.svg +1 -0
- data/app/assets/images/payment_icons/generic.svg +1 -0
- data/app/assets/images/payment_icons/genericbank.svg +1 -0
- data/app/assets/images/payment_icons/gift-card.svg +1 -0
- data/app/assets/images/payment_icons/giropay.svg +1 -0
- data/app/assets/images/payment_icons/glbepaypal.svg +1 -0
- data/app/assets/images/payment_icons/gmobanktransfer.svg +1 -0
- data/app/assets/images/payment_icons/gnosis.svg +1 -0
- data/app/assets/images/payment_icons/google_pay.svg +1 -0
- data/app/assets/images/payment_icons/google_wallet.svg +1 -0
- data/app/assets/images/payment_icons/gopay.svg +1 -0
- data/app/assets/images/payment_icons/grabpay.svg +1 -0
- data/app/assets/images/payment_icons/gusd.svg +1 -0
- data/app/assets/images/payment_icons/handelsbanken.svg +1 -0
- data/app/assets/images/payment_icons/hongleongbank.svg +1 -0
- data/app/assets/images/payment_icons/hypercard.svg +1 -0
- data/app/assets/images/payment_icons/hypercash.svg +1 -0
- data/app/assets/images/payment_icons/ideal.svg +1 -0
- data/app/assets/images/payment_icons/in3viaideal.svg +1 -0
- data/app/assets/images/payment_icons/inbank.svg +1 -0
- data/app/assets/images/payment_icons/jcb.svg +1 -0
- data/app/assets/images/payment_icons/kakao_pay.svg +1 -0
- data/app/assets/images/payment_icons/kasikornbank.svg +1 -0
- data/app/assets/images/payment_icons/klarna-pay-later.svg +1 -0
- data/app/assets/images/payment_icons/klarna-pay-now.svg +1 -0
- data/app/assets/images/payment_icons/klarna-slice-it.svg +1 -0
- data/app/assets/images/payment_icons/klarna.svg +1 -0
- data/app/assets/images/payment_icons/knet.svg +1 -0
- data/app/assets/images/payment_icons/krungthaibank.svg +1 -0
- data/app/assets/images/payment_icons/landbank.svg +1 -0
- data/app/assets/images/payment_icons/laybuybyklarna.svg +1 -0
- data/app/assets/images/payment_icons/litecoin.svg +1 -0
- data/app/assets/images/payment_icons/lunchcheck.svg +1 -0
- data/app/assets/images/payment_icons/mach.svg +1 -0
- data/app/assets/images/payment_icons/mada.svg +1 -0
- data/app/assets/images/payment_icons/maestro.svg +1 -0
- data/app/assets/images/payment_icons/master.svg +1 -0
- data/app/assets/images/payment_icons/mayabank.svg +1 -0
- data/app/assets/images/payment_icons/maybank.svg +1 -0
- data/app/assets/images/payment_icons/maybankm2u.svg +1 -0
- data/app/assets/images/payment_icons/maybankqrpay.svg +1 -0
- data/app/assets/images/payment_icons/mbway.svg +1 -0
- data/app/assets/images/payment_icons/mcash.svg +1 -0
- data/app/assets/images/payment_icons/medicinosbankas.svg +1 -0
- data/app/assets/images/payment_icons/mercadopago.svg +1 -0
- data/app/assets/images/payment_icons/metrobank.svg +1 -0
- data/app/assets/images/payment_icons/mobilepay.svg +1 -0
- data/app/assets/images/payment_icons/momopay.svg +1 -0
- data/app/assets/images/payment_icons/monero.svg +1 -0
- data/app/assets/images/payment_icons/mtn_mobile_money.svg +1 -0
- data/app/assets/images/payment_icons/mybank.svg +1 -0
- data/app/assets/images/payment_icons/naver_pay.svg +1 -0
- data/app/assets/images/payment_icons/nelo.svg +1 -0
- data/app/assets/images/payment_icons/netbanking.svg +1 -0
- data/app/assets/images/payment_icons/novalnetdirectdebitach.svg +1 -0
- data/app/assets/images/payment_icons/nubank.svg +1 -0
- data/app/assets/images/payment_icons/ocbcbank.svg +1 -0
- data/app/assets/images/payment_icons/offlinebanktransferlatinamerica.svg +1 -0
- data/app/assets/images/payment_icons/ola_money.svg +1 -0
- data/app/assets/images/payment_icons/oney.svg +1 -0
- data/app/assets/images/payment_icons/onlinebanking.svg +1 -0
- data/app/assets/images/payment_icons/onlinebanktransfer.svg +1 -0
- data/app/assets/images/payment_icons/orangemobilemoney.svg +1 -0
- data/app/assets/images/payment_icons/otpbank.svg +1 -0
- data/app/assets/images/payment_icons/oxxo.svg +1 -0
- data/app/assets/images/payment_icons/paybybank.svg +1 -0
- data/app/assets/images/payment_icons/paybybankmollie.svg +1 -0
- data/app/assets/images/payment_icons/paybybankus.svg +1 -0
- data/app/assets/images/payment_icons/paycash.svg +1 -0
- data/app/assets/images/payment_icons/payco.svg +1 -0
- data/app/assets/images/payment_icons/payeverpaybybank.svg +1 -0
- data/app/assets/images/payment_icons/payme.svg +1 -0
- data/app/assets/images/payment_icons/paynow.svg +1 -0
- data/app/assets/images/payment_icons/paynowmbank.svg +1 -0
- data/app/assets/images/payment_icons/paypal.svg +1 -0
- data/app/assets/images/payment_icons/paypo.svg +1 -0
- data/app/assets/images/payment_icons/payrexxbanktransfer.svg +1 -0
- data/app/assets/images/payment_icons/payrexxpaybybank.svg +1 -0
- data/app/assets/images/payment_icons/paysafecash.svg +1 -0
- data/app/assets/images/payment_icons/pix.svg +1 -0
- data/app/assets/images/payment_icons/polygon.svg +1 -0
- data/app/assets/images/payment_icons/przelewy24.svg +1 -0
- data/app/assets/images/payment_icons/publicbank.svg +1 -0
- data/app/assets/images/payment_icons/publicbank_pbe.svg +1 -0
- data/app/assets/images/payment_icons/qr_promptpay.svg +1 -0
- data/app/assets/images/payment_icons/rabobank.svg +1 -0
- data/app/assets/images/payment_icons/revolut.svg +1 -0
- data/app/assets/images/payment_icons/rhbbank.svg +1 -0
- data/app/assets/images/payment_icons/riverty.svg +1 -0
- data/app/assets/images/payment_icons/safetypaybanktransfer.svg +1 -0
- data/app/assets/images/payment_icons/samsung_pay.svg +1 -0
- data/app/assets/images/payment_icons/satispay.svg +1 -0
- data/app/assets/images/payment_icons/scalapay.svg +1 -0
- data/app/assets/images/payment_icons/scotiabank.svg +1 -0
- data/app/assets/images/payment_icons/seabankid.svg +1 -0
- data/app/assets/images/payment_icons/seabankph.svg +1 -0
- data/app/assets/images/payment_icons/sepa_bank_transfer.svg +1 -0
- data/app/assets/images/payment_icons/sepadirectdebit.svg +1 -0
- data/app/assets/images/payment_icons/seveneleven.svg +1 -0
- data/app/assets/images/payment_icons/shib.svg +1 -0
- data/app/assets/images/payment_icons/shopcash.svg +1 -0
- data/app/assets/images/payment_icons/siauliubankas.svg +1 -0
- data/app/assets/images/payment_icons/skeps.svg +1 -0
- data/app/assets/images/payment_icons/snap_checkout.svg +1 -0
- data/app/assets/images/payment_icons/sofort.svg +1 -0
- data/app/assets/images/payment_icons/softbank.svg +1 -0
- data/app/assets/images/payment_icons/spei.svg +1 -0
- data/app/assets/images/payment_icons/storecredit.svg +1 -0
- data/app/assets/images/payment_icons/stripe.svg +1 -0
- data/app/assets/images/payment_icons/sveacheckout.svg +1 -0
- data/app/assets/images/payment_icons/swedbank.svg +1 -0
- data/app/assets/images/payment_icons/swish.svg +1 -0
- data/app/assets/images/payment_icons/tbibank.svg +1 -0
- data/app/assets/images/payment_icons/thanachartbank.svg +1 -0
- data/app/assets/images/payment_icons/tnmmoney.svg +1 -0
- data/app/assets/images/payment_icons/touchngo.svg +1 -0
- data/app/assets/images/payment_icons/trustly.svg +1 -0
- data/app/assets/images/payment_icons/twint.svg +1 -0
- data/app/assets/images/payment_icons/uaevisa.svg +1 -0
- data/app/assets/images/payment_icons/unionpay.svg +1 -0
- data/app/assets/images/payment_icons/unzerbanktransfer.svg +1 -0
- data/app/assets/images/payment_icons/upi.svg +1 -0
- data/app/assets/images/payment_icons/usbank.svg +1 -0
- data/app/assets/images/payment_icons/usdc.svg +1 -0
- data/app/assets/images/payment_icons/usdt.svg +1 -0
- data/app/assets/images/payment_icons/venmo.svg +1 -0
- data/app/assets/images/payment_icons/vipps.svg +1 -0
- data/app/assets/images/payment_icons/visa.svg +1 -0
- data/app/assets/images/payment_icons/volksbank.svg +1 -0
- data/app/assets/images/payment_icons/waavepaybybank.svg +1 -0
- data/app/assets/images/payment_icons/walley.svg +1 -0
- data/app/assets/images/payment_icons/wbtc.svg +1 -0
- data/app/assets/images/payment_icons/wechatpay.svg +1 -0
- data/app/assets/images/payment_icons/whishcheckout.svg +1 -0
- data/app/assets/images/payment_icons/wingbank.svg +1 -0
- data/app/assets/images/payment_icons/xrp.svg +1 -0
- data/app/assets/images/payment_icons/zip.svg +1 -0
- data/app/finders/spree/posts/find.rb +137 -0
- data/app/helpers/spree/addresses_helper.rb +9 -3
- data/app/helpers/spree/base_helper.rb +8 -4
- data/app/jobs/spree/images/save_from_url_job.rb +70 -0
- data/app/jobs/spree/imports/create_rows_job.rb +72 -0
- data/app/jobs/spree/imports/process_rows_job.rb +19 -0
- data/app/models/concerns/spree/adjustment_source.rb +3 -3
- data/app/models/concerns/spree/admin_user_methods.rb +52 -0
- data/app/models/concerns/spree/display_on.rb +7 -1
- data/app/models/concerns/spree/has_image_alt_text.rb +18 -0
- data/app/models/concerns/spree/has_one_link.rb +1 -1
- data/app/models/concerns/spree/has_page_links.rb +1 -1
- data/app/models/concerns/spree/linkable.rb +9 -0
- data/app/models/concerns/spree/metadata.rb +2 -0
- data/app/models/concerns/spree/metafields.rb +94 -0
- data/app/models/concerns/spree/named_type.rb +1 -1
- data/app/models/concerns/spree/user_methods.rb +20 -7
- data/app/models/concerns/spree/user_payment_source.rb +1 -1
- data/app/models/concerns/spree/user_roles.rb +3 -2
- data/app/models/spree/ability.rb +4 -0
- data/app/models/spree/address.rb +3 -1
- data/app/models/spree/adjustable/adjuster/tax.rb +2 -2
- data/app/models/spree/adjustable/adjustments_updater.rb +26 -3
- data/app/models/spree/adjustment.rb +4 -1
- data/app/models/spree/asset.rb +16 -0
- data/app/models/spree/base.rb +1 -1
- data/app/models/spree/credit_card.rb +1 -0
- data/app/models/spree/current.rb +5 -1
- data/app/models/spree/custom_domain.rb +5 -0
- data/app/models/spree/customer_return.rb +1 -0
- data/app/models/spree/export.rb +39 -2
- data/app/models/spree/exports/customers.rb +17 -0
- data/app/models/spree/exports/gift_cards.rb +13 -0
- data/app/models/spree/exports/newsletter_subscribers.rb +13 -0
- data/app/models/spree/exports/orders.rb +6 -4
- data/app/models/spree/exports/products.rb +9 -3
- data/app/models/spree/gift_card.rb +7 -1
- data/app/models/spree/gift_card_batch.rb +0 -1
- data/app/models/spree/import.rb +279 -0
- data/app/models/spree/import_mapping.rb +46 -0
- data/app/models/spree/import_row.rb +108 -0
- data/app/models/spree/import_schema.rb +25 -0
- data/app/models/spree/import_schemas/products.rb +45 -0
- data/app/models/spree/imports/products.rb +9 -0
- data/app/models/spree/invitation.rb +1 -1
- data/app/models/spree/legacy_user.rb +0 -1
- data/app/models/spree/line_item.rb +2 -7
- data/app/models/spree/metafield.rb +53 -0
- data/app/models/spree/metafield_definition.rb +91 -0
- data/app/models/spree/metafields/boolean.rb +15 -0
- data/app/models/spree/metafields/json.rb +27 -0
- data/app/models/spree/metafields/long_text.rb +7 -0
- data/app/models/spree/metafields/number.rb +15 -0
- data/app/models/spree/metafields/rich_text.rb +18 -0
- data/app/models/spree/metafields/short_text.rb +7 -0
- data/app/models/spree/newsletter_subscriber/emails.rb +12 -0
- data/app/models/spree/newsletter_subscriber.rb +64 -0
- data/app/models/spree/option_type.rb +2 -0
- data/app/models/spree/option_value.rb +1 -0
- data/app/models/spree/order/checkout.rb +7 -0
- data/app/models/spree/order.rb +8 -3
- data/app/models/spree/order_merger.rb +2 -4
- data/app/models/spree/order_promotion.rb +3 -0
- data/app/models/spree/order_updater.rb +2 -2
- data/app/models/spree/page.rb +5 -1
- data/app/models/spree/page_block.rb +4 -0
- data/app/models/spree/page_blocks/image.rb +3 -0
- data/app/models/spree/page_blocks/metafields.rb +18 -0
- data/app/models/spree/page_blocks/products/brand.rb +15 -0
- data/app/models/spree/page_blocks/products/buy_buttons.rb +5 -1
- data/app/models/spree/page_blocks/products/description.rb +18 -0
- data/app/models/spree/page_blocks/products/price.rb +1 -1
- data/app/models/spree/page_blocks/products/quantity_selector.rb +6 -2
- data/app/models/spree/page_blocks/products/title.rb +5 -1
- data/app/models/spree/page_blocks/products/variant_picker.rb +1 -1
- data/app/models/spree/page_section.rb +2 -2
- data/app/models/spree/page_sections/breadcrumbs.rb +12 -0
- data/app/models/spree/page_sections/footer.rb +20 -13
- data/app/models/spree/page_sections/image_banner.rb +6 -1
- data/app/models/spree/page_sections/image_with_text.rb +5 -0
- data/app/models/spree/page_sections/newsletter.rb +7 -0
- data/app/models/spree/page_sections/product_details.rb +38 -1
- data/app/models/spree/pages/account.rb +4 -0
- data/app/models/spree/pages/cart.rb +10 -0
- data/app/models/spree/pages/checkout.rb +6 -0
- data/app/models/spree/pages/custom.rb +4 -0
- data/app/models/spree/pages/homepage.rb +4 -0
- data/app/models/spree/pages/login.rb +4 -0
- data/app/models/spree/pages/password.rb +2 -0
- data/app/models/spree/pages/post_list.rb +4 -0
- data/app/models/spree/pages/product_details.rb +1 -0
- data/app/models/spree/pages/shop_all.rb +4 -0
- data/app/models/spree/pages/wishlist.rb +4 -0
- data/app/models/spree/payment.rb +1 -0
- data/app/models/spree/payment_method.rb +4 -5
- data/app/models/spree/payment_source.rb +1 -0
- data/app/models/spree/policy.rb +70 -0
- data/app/models/spree/post.rb +4 -2
- data/app/models/spree/post_category.rb +1 -0
- data/app/models/spree/product.rb +16 -9
- data/app/models/spree/promotion/actions/free_shipping.rb +3 -3
- data/app/models/spree/promotion.rb +24 -8
- data/app/models/spree/promotion_action.rb +1 -1
- data/app/models/spree/promotion_handler/cart.rb +1 -1
- data/app/models/spree/promotion_handler/coupon.rb +1 -1
- data/app/models/spree/promotion_rule.rb +1 -1
- data/app/models/spree/property.rb +18 -0
- data/app/models/spree/refund.rb +2 -1
- data/app/models/spree/role_user.rb +1 -1
- data/app/models/spree/shipment.rb +1 -0
- data/app/models/spree/shipping_method.rb +1 -0
- data/app/models/spree/stock_item.rb +1 -0
- data/app/models/spree/stock_transfer.rb +1 -0
- data/app/models/spree/store.rb +45 -3
- data/app/models/spree/store_credit.rb +2 -1
- data/app/models/spree/tax_category.rb +1 -1
- data/app/models/spree/tax_rate.rb +18 -3
- data/app/models/spree/taxon.rb +12 -2
- data/app/models/spree/taxonomy.rb +11 -0
- data/app/models/spree/theme.rb +1 -1
- data/app/models/spree/variant.rb +17 -0
- data/app/presenters/spree/csv/customer_presenter.rb +59 -0
- data/app/presenters/spree/csv/gift_card_presenter.rb +49 -0
- data/app/presenters/spree/csv/metafields_helper.rb +17 -0
- data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +39 -0
- data/app/presenters/spree/csv/order_line_item_presenter.rb +12 -5
- data/app/presenters/spree/csv/product_variant_presenter.rb +9 -5
- data/app/services/spree/cart/recalculate.rb +6 -4
- data/app/services/spree/cart/remove_line_item.rb +1 -2
- data/app/services/spree/imports/row_processors/base.rb +19 -0
- data/app/services/spree/imports/row_processors/product_variant.rb +228 -0
- data/app/services/spree/line_items/destroy.rb +1 -1
- data/app/services/spree/newsletter/subscribe.rb +43 -0
- data/app/services/spree/newsletter/verify.rb +30 -0
- data/app/sorters/spree/posts/sort.rb +40 -0
- data/app/views/spree/shared/_payment.html.erb +3 -3
- data/config/locales/en.yml +56 -2
- data/db/migrate/20250605131334_add_missing_fields_to_users.rb +23 -13
- data/db/migrate/20250728095629_create_spree_metafields.rb +27 -0
- data/db/migrate/20250730102644_add_missing_unique_indexes_to_spree_option_types_and_values.rb +40 -0
- data/db/migrate/20250730154601_add_unique_index_on_spree_properties_name.rb +24 -0
- data/db/migrate/20250811112056_create_spree_policies.rb +22 -0
- data/db/migrate/20250821211705_fix_unique_index_on_spree_order_promotions.rb +20 -0
- data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +7 -0
- data/db/migrate/20250826093602_create_spree_newsletter_subscribers.rb +12 -0
- data/db/migrate/20250902143122_fix_policies_store_association.rb +18 -0
- data/db/migrate/20250911205659_add_missing_index_on_spree_products_promotionable.rb +5 -0
- data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +10 -0
- data/db/migrate/20250914101955_add_missing_indexes_on_spree_adjustments.rb +6 -0
- data/db/migrate/20250915093930_add_metadata_to_spree_newsletter_subscribers.rb +20 -0
- data/db/migrate/20250923141845_create_spree_imports.rb +47 -0
- data/lib/generators/spree/cursor_rules/cursor_rules_generator.rb +19 -0
- data/lib/generators/spree/cursor_rules/templates/spree_rules.mdc +387 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +0 -1
- data/lib/generators/spree/install/templates/config/initializers/spree.rb +5 -0
- data/lib/spree/core/configuration.rb +1 -0
- data/lib/spree/core/dependencies.rb +3 -0
- data/lib/spree/core/engine.rb +57 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +3 -1
- data/lib/spree/permitted_attributes.rb +18 -3
- data/lib/spree/testing_support/capybara_config.rb +9 -3
- data/lib/spree/testing_support/factories/export_factory.rb +5 -17
- data/lib/spree/testing_support/factories/import_factory.rb +20 -0
- data/lib/spree/testing_support/factories/import_mapping_factory.rb +7 -0
- data/lib/spree/testing_support/factories/import_row_factory.rb +7 -0
- data/lib/spree/testing_support/factories/metafield_definition_factory.rb +72 -0
- data/lib/spree/testing_support/factories/metafield_factory.rb +68 -0
- data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +18 -0
- data/lib/spree/testing_support/factories/policy_factory.rb +8 -0
- data/lib/spree/testing_support/factories/post_factory.rb +8 -0
- data/lib/spree/testing_support/store.rb +11 -0
- data/lib/tasks/core.rake +128 -0
- metadata +279 -6
- data/app/models/action_text/rich_text_decorator.rb +0 -11
- 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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
154
|
+
# Returns the full name of the user
|
|
155
|
+
# @return [String]
|
|
156
|
+
def full_name
|
|
157
|
+
name&.full
|
|
145
158
|
end
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
return unless Spree.admin_user_class != Spree.user_class
|
|
160
|
+
private
|
|
149
161
|
|
|
150
|
-
|
|
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: :
|
|
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',
|
|
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
|
|
data/app/models/spree/ability.rb
CHANGED
|
@@ -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
|
data/app/models/spree/address.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
8
|
-
additional_tax_total = tax.additional.
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
data/app/models/spree/asset.rb
CHANGED
|
@@ -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
|
data/app/models/spree/base.rb
CHANGED
|
@@ -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?(
|
|
90
|
+
if defined?(deleted_at) && deleted_at.present?
|
|
91
91
|
[
|
|
92
92
|
['deleted', :name],
|
|
93
93
|
['deleted', :name, :uuid_for_friendly_id]
|
data/app/models/spree/current.rb
CHANGED
|
@@ -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
|
data/app/models/spree/export.rb
CHANGED
|
@@ -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
|
-
|
|
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}".
|
|
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 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,
|
|
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
|
-
|
|
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
|