spree_core 6.0.0.beta3 → 6.0.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/README.md +2 -2
- data/app/helpers/spree/images_helper.rb +0 -57
- data/app/jobs/spree/base_job.rb +21 -0
- data/app/jobs/spree/carts/reap_expired_job.rb +2 -2
- data/app/jobs/spree/collections/regenerate_time_based_job.rb +6 -3
- data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +8 -5
- data/app/jobs/spree/payments/cancel_unused_sessions_job.rb +61 -0
- data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
- data/app/mailers/spree/admin_user_mailer.rb +4 -19
- data/app/mailers/spree/base_mailer.rb +96 -13
- data/app/mailers/spree/email_template_mailer.rb +27 -0
- data/app/mailers/spree/export_mailer.rb +3 -4
- data/app/mailers/spree/import_mailer.rb +3 -4
- data/app/mailers/spree/invitation_mailer.rb +24 -7
- data/app/mailers/spree/seller_user_mailer.rb +4 -16
- data/app/mailers/spree/webhook_mailer.rb +2 -2
- data/app/models/concerns/spree/acted_by.rb +1 -1
- data/app/models/concerns/spree/admin_user_methods.rb +13 -1
- data/app/models/concerns/spree/api_typed.rb +91 -0
- data/app/models/concerns/spree/calculator/tiers.rb +76 -0
- data/app/models/concerns/spree/customer_methods.rb +15 -14
- data/app/models/concerns/spree/email_template_identity.rb +52 -0
- data/app/models/concerns/spree/parameterizable_name.rb +2 -0
- data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
- data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
- data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
- data/app/models/concerns/spree/preference_schema.rb +4 -12
- data/app/models/concerns/spree/product_scopes.rb +11 -1
- data/app/models/concerns/spree/provider_listing.rb +41 -0
- data/app/models/concerns/spree/publishable.rb +82 -14
- data/app/models/concerns/spree/purchase/addresses.rb +23 -10
- data/app/models/concerns/spree/purchase/currency.rb +1 -1
- data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
- data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
- data/app/models/concerns/spree/purchase/locale.rb +1 -1
- data/app/models/concerns/spree/purchase/payment_processing.rb +7 -3
- data/app/models/concerns/spree/purchase/purchase_order.rb +2 -2
- data/app/models/concerns/spree/purchase/totals.rb +49 -1
- data/app/models/concerns/spree/receivable.rb +1 -1
- data/app/models/concerns/spree/searchable.rb +0 -4
- data/app/models/concerns/spree/secret_preferences.rb +59 -0
- data/app/models/concerns/spree/single_store_resource.rb +1 -1
- data/app/models/concerns/spree/stores/email_branding.rb +31 -0
- data/app/models/concerns/spree/type_labels.rb +38 -0
- data/app/models/concerns/spree/typed_adjustment_line.rb +1 -1
- data/app/models/concerns/spree/typed_associations.rb +6 -0
- data/app/models/spree/address.rb +11 -1
- data/app/models/spree/adjusters/promotion.rb +1 -15
- data/app/models/spree/allowed_origin.rb +0 -1
- data/app/models/spree/api_key.rb +5 -6
- data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
- data/app/models/spree/base.rb +70 -22
- data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/flat_rate.rb +1 -3
- data/app/models/spree/calculator/flexi_rate.rb +1 -3
- data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
- data/app/models/spree/calculator/price_sack.rb +1 -3
- data/app/models/spree/calculator/shipping/digital_delivery.rb +1 -3
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/shipping/flat_rate.rb +1 -3
- data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -3
- data/app/models/spree/calculator/shipping/per_item.rb +1 -3
- data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
- data/app/models/spree/calculator/tiered_flat_rate.rb +6 -23
- data/app/models/spree/calculator/tiered_percent.rb +7 -25
- data/app/models/spree/cart.rb +78 -18
- data/app/models/spree/channel/gating.rb +1 -1
- data/app/models/spree/channel.rb +4 -4
- data/app/models/spree/claim.rb +21 -1
- data/app/models/spree/claim_line_item.rb +41 -6
- data/app/models/spree/collection.rb +0 -1
- data/app/models/spree/collection_rule.rb +5 -21
- data/app/models/spree/commission_line.rb +1 -1
- data/app/models/spree/commission_rate.rb +2 -3
- data/app/models/spree/commission_rate_value.rb +1 -1
- data/app/models/spree/commission_rule.rb +4 -11
- data/app/models/spree/commission_rules/product_rule.rb +1 -1
- data/app/models/spree/company_invitation.rb +1 -0
- data/app/models/spree/consent_record.rb +0 -1
- data/app/models/spree/credit_card.rb +1 -1
- data/app/models/spree/current.rb +23 -0
- data/app/models/spree/custom_field.rb +2 -2
- data/app/models/spree/custom_field_definition.rb +9 -23
- data/app/models/spree/custom_fields/boolean.rb +1 -1
- data/app/models/spree/custom_fields/json.rb +1 -1
- data/app/models/spree/data_feed/google.rb +0 -2
- data/app/models/spree/data_request.rb +1 -1
- data/app/models/spree/delivery_method.rb +30 -13
- data/app/models/spree/delivery_method_rule.rb +4 -11
- data/app/models/spree/delivery_rate.rb +4 -5
- data/app/models/spree/delivery_rate_provider/base.rb +1 -0
- data/app/models/spree/delivery_zone.rb +0 -1
- data/app/models/spree/delivery_zone_member.rb +4 -4
- data/app/models/spree/digital_asset.rb +7 -0
- data/app/models/spree/digital_asset_provider/base.rb +2 -0
- data/app/models/spree/digital_link.rb +2 -5
- data/app/models/spree/discount.rb +1 -1
- data/app/models/spree/email_template.rb +25 -0
- data/app/models/spree/email_template_draft.rb +13 -0
- data/app/models/spree/email_template_revision.rb +17 -0
- data/app/models/spree/email_templates/entry.rb +118 -0
- data/app/models/spree/email_templates/sample_record.rb +16 -0
- data/app/models/spree/emails/branding.rb +80 -0
- data/app/models/spree/exchange.rb +15 -1
- data/app/models/spree/exchange_line_item.rb +121 -5
- data/app/models/spree/export.rb +0 -1
- data/app/models/spree/exports/products.rb +10 -1
- data/app/models/spree/exports/report.rb +1 -1
- data/app/models/spree/fee.rb +4 -2
- data/app/models/spree/fulfillment.rb +53 -27
- data/app/models/spree/fulfillment_changer.rb +2 -2
- data/app/models/spree/fulfillment_item.rb +0 -4
- data/app/models/spree/fulfillment_provider/base.rb +1 -0
- data/app/models/spree/gateway/bogus.rb +4 -0
- data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
- data/app/models/spree/gateway.rb +5 -1
- data/app/models/spree/gift_card.rb +12 -1
- data/app/models/spree/gift_card_batch.rb +0 -1
- data/app/models/spree/import.rb +2 -7
- data/app/models/spree/import_mapping.rb +1 -2
- data/app/models/spree/import_row.rb +2 -2
- data/app/models/spree/import_schemas/products.rb +1 -0
- data/app/models/spree/integration.rb +8 -18
- data/app/models/spree/invitation.rb +5 -5
- data/app/models/spree/line_item.rb +13 -2
- data/app/models/spree/locale.rb +11 -8
- data/app/models/spree/market.rb +1 -0
- data/app/models/spree/newsletter_subscriber.rb +3 -0
- data/app/models/spree/order.rb +56 -138
- data/app/models/spree/order_group.rb +11 -2
- data/app/models/spree/order_inventory.rb +10 -5
- data/app/models/spree/order_promotion.rb +1 -1
- data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
- data/app/models/spree/order_routing/strategy/base.rb +4 -2
- data/app/models/spree/order_routing/strategy/rules.rb +1 -1
- data/app/models/spree/order_routing_rule.rb +6 -22
- data/app/models/spree/package_type.rb +1 -2
- data/app/models/spree/payment/gateway_options.rb +14 -52
- data/app/models/spree/payment/processing.rb +2 -4
- data/app/models/spree/payment.rb +20 -5
- data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
- data/app/models/spree/payment_method/store_credit.rb +4 -4
- data/app/models/spree/payment_method.rb +36 -4
- data/app/models/spree/payment_session.rb +5 -1
- data/app/models/spree/payment_setup_session.rb +1 -0
- data/app/models/spree/payout_provider/base.rb +3 -1
- data/app/models/spree/pickup_point_provider/base.rb +2 -0
- data/app/models/spree/price_list.rb +1 -2
- data/app/models/spree/price_rules/channel_rule.rb +1 -1
- data/app/models/spree/price_rules/customer_group_rule.rb +1 -1
- data/app/models/spree/product.rb +31 -7
- data/app/models/spree/product_submission.rb +0 -12
- data/app/models/spree/product_type.rb +0 -1
- data/app/models/spree/promotion/actions/create_line_items.rb +27 -17
- data/app/models/spree/promotion/rules/category.rb +0 -4
- data/app/models/spree/promotion/rules/channel.rb +0 -4
- data/app/models/spree/promotion/rules/country.rb +0 -4
- data/app/models/spree/promotion/rules/currency.rb +0 -4
- data/app/models/spree/promotion/rules/customer_group.rb +0 -4
- data/app/models/spree/promotion/rules/first_order.rb +0 -4
- data/app/models/spree/promotion/rules/item_total.rb +4 -8
- data/app/models/spree/promotion/rules/market.rb +0 -4
- data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
- data/app/models/spree/promotion/rules/option_value.rb +0 -3
- data/app/models/spree/promotion/rules/product.rb +0 -4
- data/app/models/spree/promotion/rules/user.rb +0 -4
- data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
- data/app/models/spree/promotion.rb +35 -6
- data/app/models/spree/promotion_action.rb +7 -14
- data/app/models/spree/promotion_action_line_item.rb +1 -1
- data/app/models/spree/promotion_handler/coupon.rb +59 -27
- data/app/models/spree/promotion_rule.rb +7 -14
- data/app/models/spree/purchase_order.rb +1 -0
- data/app/models/spree/quantity_rule.rb +1 -1
- data/app/models/spree/refund.rb +28 -3
- data/app/models/spree/return.rb +30 -2
- data/app/models/spree/return_line_item.rb +65 -14
- data/app/models/spree/role.rb +7 -7
- data/app/models/spree/saved_report.rb +1 -1
- data/app/models/spree/seller.rb +2 -1
- data/app/models/spree/seller_payout.rb +7 -0
- data/app/models/spree/seller_requirement.rb +4 -12
- data/app/models/spree/seller_requirement_custom_field.rb +1 -2
- data/app/models/spree/seller_requirement_submission.rb +2 -2
- data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
- data/app/models/spree/seller_transfer.rb +5 -0
- data/app/models/spree/shipping_label.rb +1 -0
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
- data/app/models/spree/stock/packer.rb +2 -2
- data/app/models/spree/stock/splitter/weight.rb +1 -1
- data/app/models/spree/stock_level/custom_events.rb +6 -0
- data/app/models/spree/stock_level.rb +1 -1
- data/app/models/spree/stock_movement.rb +1 -1
- data/app/models/spree/stock_receipt_item.rb +1 -1
- data/app/models/spree/stock_reservation.rb +2 -2
- data/app/models/spree/stock_transfer.rb +4 -2
- data/app/models/spree/store.rb +33 -7
- data/app/models/spree/store_credit.rb +8 -9
- data/app/models/spree/store_credit_event.rb +5 -5
- data/app/models/spree/tax_exemption_certificate.rb +2 -0
- data/app/models/spree/tax_identifier.rb +3 -1
- data/app/models/spree/tax_line.rb +53 -3
- data/app/models/spree/tax_provider/base.rb +71 -14
- data/app/models/spree/tax_provider/internal.rb +59 -33
- data/app/models/spree/tax_provider/recorded_share.rb +114 -0
- data/app/models/spree/tax_rate.rb +1 -2
- data/app/models/spree/taxonomy.rb +0 -1
- data/app/models/spree/variant.rb +4 -3
- data/app/models/spree/variant_media.rb +1 -1
- data/app/models/spree/webhook_endpoint.rb +10 -13
- data/app/models/spree/wishlist.rb +1 -6
- data/app/models/spree/wishlist_item/custom_events.rb +6 -0
- data/app/presenters/spree/csv/customer_presenter.rb +1 -1
- data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
- data/app/presenters/spree/csv/order_line_item_presenter.rb +1 -1
- data/app/presenters/spree/csv/product_variant_presenter.rb +42 -12
- data/app/serializers/spree/emails/base_serializer.rb +14 -0
- data/app/serializers/spree/emails/export_serializer.rb +13 -0
- data/app/serializers/spree/emails/import_serializer.rb +19 -0
- data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
- data/app/serializers/spree/emails/store_serializer.rb +31 -0
- data/app/serializers/spree/emails/user_serializer.rb +12 -0
- data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
- data/app/services/spree/carts/create.rb +8 -2
- data/app/services/spree/carts/destroy.rb +2 -2
- data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
- data/app/services/spree/carts/split_by_seller.rb +1 -1
- data/app/services/spree/carts/update.rb +19 -5
- data/app/services/spree/catalogs/set_product_terms.rb +1 -1
- data/app/services/spree/categories/add_products.rb +13 -31
- data/app/services/spree/categories/remove_products.rb +12 -51
- data/app/services/spree/checkout/advance.rb +1 -4
- data/app/services/spree/collections/add_products.rb +10 -23
- data/app/services/spree/collections/remove_products.rb +7 -43
- data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
- data/app/services/spree/data_requests/create.rb +1 -1
- data/app/services/spree/deliveries/destroy.rb +2 -2
- data/app/services/spree/email_templates/check.rb +58 -0
- data/app/services/spree/email_templates/no_sample_record.rb +7 -0
- data/app/services/spree/email_templates/preview.rb +99 -0
- data/app/services/spree/emails/file_system.rb +23 -0
- data/app/services/spree/emails/filters.rb +84 -0
- data/app/services/spree/emails/liquid_context.rb +48 -0
- data/app/services/spree/emails/rendered_email.rb +14 -0
- data/app/services/spree/emails/renderer.rb +125 -0
- data/app/services/spree/emails/template.rb +51 -0
- data/app/services/spree/emails/template_data.rb +23 -0
- data/app/services/spree/emails/template_resolver.rb +109 -0
- data/app/services/spree/emails/text_converter.rb +72 -0
- data/app/services/spree/emails/translation_inliner.rb +134 -0
- data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
- data/app/services/spree/imports/row_processors/product_variant.rb +65 -34
- data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
- data/app/services/spree/locales.rb +4 -5
- data/app/services/spree/newsletter/subscribe.rb +8 -12
- data/app/services/spree/orders/build_fulfillments.rb +3 -2
- data/app/services/spree/orders/create.rb +2 -1
- data/app/services/spree/orders/discounts/create.rb +7 -4
- data/app/services/spree/orders/discounts/destroy.rb +1 -0
- data/app/services/spree/orders/discounts/update.rb +1 -0
- data/app/services/spree/orders/fees/create.rb +1 -0
- data/app/services/spree/orders/fees/destroy.rb +1 -0
- data/app/services/spree/orders/fees/update.rb +1 -0
- data/app/services/spree/orders/remove_line_item.rb +13 -3
- data/app/services/spree/orders/update.rb +10 -6
- data/app/services/spree/orders/update_item.rb +6 -3
- data/app/services/spree/orders/update_statuses.rb +3 -1
- data/app/services/spree/product_memberships.rb +61 -0
- data/app/services/spree/returns/eligibility_validator.rb +1 -1
- data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
- data/app/services/spree/seeds/commission_rates.rb +1 -1
- data/app/services/spree/seeds/digital_delivery.rb +1 -1
- data/app/services/spree/seeds/payment_methods.rb +2 -2
- data/app/services/spree/seeds/saved_reports.rb +3 -3
- data/app/services/spree/sellers/requirements.rb +1 -2
- data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
- data/app/services/spree/shipping_labels/store_file.rb +3 -3
- data/app/services/spree/stock_levels/correct.rb +3 -3
- data/app/services/spree/stock_reservations/reserve.rb +2 -3
- data/app/services/spree/store_credits/apply.rb +15 -13
- data/app/services/spree/stores/provision_defaults.rb +15 -14
- data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
- data/app/views/layouts/spree/base_mailer.html.erb +2 -43
- data/app/views/layouts/spree/base_mailer.liquid +79 -0
- data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/export_mailer/export_done.liquid +12 -0
- data/app/views/spree/import_mailer/import_done.liquid +15 -0
- data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
- data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
- data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/shared/_mailer_button.html.erb +4 -5
- data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
- data/app/views/spree/shared/_summary_row.liquid +8 -0
- data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
- data/app/workflows/spree/carts/add_item.rb +4 -4
- data/app/workflows/spree/carts/complete.rb +13 -3
- data/app/workflows/spree/carts/merge.rb +1 -1
- data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
- data/app/workflows/spree/carts/upsert_items.rb +18 -9
- data/app/workflows/spree/claims/approve.rb +5 -22
- data/app/workflows/spree/claims/cancel.rb +5 -21
- data/app/workflows/spree/claims/create.rb +15 -0
- data/app/workflows/spree/claims/deny.rb +7 -0
- data/app/workflows/spree/claims/resolve.rb +34 -31
- data/app/workflows/spree/customers/anonymize.rb +3 -4
- data/app/workflows/spree/customers/create.rb +1 -1
- data/app/workflows/spree/data_requests/fulfill.rb +2 -2
- data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
- data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
- data/app/workflows/spree/email_templates/publish.rb +70 -0
- data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
- data/app/workflows/spree/email_templates/revert.rb +29 -0
- data/app/workflows/spree/email_templates/save_draft.rb +85 -0
- data/app/workflows/spree/exchanges/approve.rb +5 -22
- data/app/workflows/spree/exchanges/cancel.rb +5 -21
- data/app/workflows/spree/exchanges/create.rb +6 -0
- data/app/workflows/spree/exchanges/fulfill.rb +75 -51
- data/app/workflows/spree/exchanges/receive.rb +6 -65
- data/app/workflows/spree/fulfillments/cancel.rb +1 -1
- data/app/workflows/spree/fulfillments/create.rb +51 -47
- data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
- data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
- data/app/workflows/spree/fulfillments/replacements.rb +59 -0
- data/app/workflows/spree/gift_cards/apply.rb +2 -2
- data/app/workflows/spree/orders/add_item.rb +5 -1
- data/app/workflows/spree/orders/cancel.rb +3 -3
- data/app/workflows/spree/orders/complete.rb +16 -8
- data/app/workflows/spree/payments/process.rb +2 -2
- data/app/workflows/spree/post_sale/approval.rb +35 -0
- data/app/workflows/spree/post_sale/cancellation.rb +44 -0
- data/app/workflows/spree/post_sale/receipt.rb +92 -0
- data/app/workflows/spree/products/nested_attributes.rb +1 -1
- data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
- data/app/workflows/spree/purchase_orders/close.rb +3 -31
- data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
- data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
- data/app/workflows/spree/purchase_orders/receive.rb +1 -1
- data/app/workflows/spree/purchase_orders/update.rb +5 -34
- data/app/workflows/spree/receivables/closing_short.rb +46 -0
- data/app/workflows/spree/receivables/draft_editing.rb +48 -0
- data/app/workflows/spree/refunds/create.rb +5 -2
- data/app/workflows/spree/refunds/order_payments.rb +14 -2
- data/app/workflows/spree/refunds/tax_credit.rb +67 -0
- data/app/workflows/spree/returns/approve.rb +5 -22
- data/app/workflows/spree/returns/cancel.rb +5 -22
- data/app/workflows/spree/returns/create.rb +8 -0
- data/app/workflows/spree/returns/receive.rb +5 -76
- data/app/workflows/spree/returns/refund.rb +31 -8
- data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
- data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
- data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
- data/app/workflows/spree/shipping_labels/record.rb +5 -5
- data/app/workflows/spree/shipping_labels/refund.rb +2 -2
- data/app/workflows/spree/stock_receipts/recording.rb +4 -4
- data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
- data/app/workflows/spree/stock_transfers/close.rb +3 -30
- data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
- data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
- data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
- data/app/workflows/spree/stock_transfers/receive.rb +1 -1
- data/app/workflows/spree/stock_transfers/update.rb +5 -33
- data/config/i18n-tasks.yml +32 -18
- data/config/initializers/alba.rb +7 -0
- data/config/language_names.csv +264 -0
- data/config/locales/ar.yml +811 -0
- data/config/locales/be.yml +855 -0
- data/config/locales/bg.yml +723 -0
- data/config/locales/ca.yml +689 -0
- data/config/locales/cs.yml +855 -0
- data/config/locales/da.yml +771 -0
- data/config/locales/de-CH.yml +248 -0
- data/config/locales/de.yml +835 -0
- data/config/locales/el.yml +769 -0
- data/config/locales/en.yml +37 -503
- data/config/locales/es-CL.yml +301 -0
- data/config/locales/es-EC.yml +186 -0
- data/config/locales/es-MX.yml +334 -0
- data/config/locales/es.yml +831 -0
- data/config/locales/et.yml +644 -0
- data/config/locales/fa.yml +709 -0
- data/config/locales/fi.yml +771 -0
- data/config/locales/fr.yml +830 -0
- data/config/locales/hu.yml +751 -0
- data/config/locales/id.yml +756 -0
- data/config/locales/it.yml +833 -0
- data/config/locales/ja.yml +726 -0
- data/config/locales/km.yml +759 -0
- data/config/locales/ko.yml +575 -0
- data/config/locales/ku.yml +753 -0
- data/config/locales/lt.yml +821 -0
- data/config/locales/lv.yml +685 -0
- data/config/locales/nb.yml +824 -0
- data/config/locales/ne.yml +823 -0
- data/config/locales/nl.yml +826 -0
- data/config/locales/pl.yml +835 -0
- data/config/locales/pt-BR.yml +547 -0
- data/config/locales/pt.yml +636 -0
- data/config/locales/ro.yml +750 -0
- data/config/locales/ru.yml +834 -0
- data/config/locales/sk.yml +680 -0
- data/config/locales/sl-SI.yml +600 -0
- data/config/locales/sv.yml +827 -0
- data/config/locales/th.yml +745 -0
- data/config/locales/tr.yml +777 -0
- data/config/locales/uk.yml +836 -0
- data/config/locales/vi.yml +768 -0
- data/config/locales/zh-CN.yml +786 -0
- data/config/locales/zh-TW.yml +834 -0
- data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
- data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
- data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
- data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
- data/lib/generators/spree/dummy/templates/rails/test.rb +7 -0
- data/lib/spree/checkout/default_requirements.rb +37 -13
- data/lib/spree/core/configuration.rb +1 -1
- data/lib/spree/core/controller_helpers/store.rb +1 -1
- data/lib/spree/core/dependencies.rb +5 -4
- data/lib/spree/core/emails/escaped_output.rb +60 -0
- data/lib/spree/core/engine.rb +48 -167
- data/lib/spree/core/permissions/default_catalog.rb +5 -0
- data/lib/spree/core/preferences/json_conversion.rb +182 -0
- data/lib/spree/core/preferences/masking.rb +4 -1
- data/lib/spree/core/preferences/preferable.rb +137 -83
- data/lib/spree/core/preferences/preferable_class_methods.rb +81 -146
- data/lib/spree/core/previews/import_preview.rb +10 -0
- data/lib/spree/core/tax/provider_error.rb +37 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +85 -370
- data/lib/spree/emails/editable_templates.rb +82 -0
- data/lib/spree/events/catalog.rb +189 -0
- data/lib/spree/events.rb +21 -1
- data/lib/spree/hooks.rb +0 -17
- data/lib/spree/i18n.rb +32 -15
- data/lib/spree/reporting/adapters/live.rb +6 -6
- data/lib/spree/reporting/default_vocabulary.rb +22 -10
- data/lib/spree/reporting/hydration.rb +1 -1
- data/lib/spree/reporting/query.rb +8 -0
- data/lib/spree/reporting/schema.rb +10 -10
- data/lib/spree/testing_support/editable_email_templates.rb +61 -0
- data/lib/spree/testing_support/emails.rb +22 -0
- data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
- data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
- data/lib/spree/testing_support/factories/order_factory.rb +2 -2
- data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
- data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
- data/lib/spree/testing_support/i18n.rb +3 -94
- data/lib/spree/testing_support/lifecycle_events.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/spree/testing_support/preferences.rb +0 -5
- data/lib/spree/testing_support/store.rb +9 -1
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +45 -0
- data/lib/tasks/categories_and_collections.rake +3 -3
- data/lib/tasks/core.rake +6 -4
- data/lib/tasks/media.rake +3 -3
- data/lib/tasks/package_types.rake +1 -1
- data/lib/tasks/preferences_json.rake +101 -0
- data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
- metadata +188 -44
- data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
- data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
- data/app/models/spree/base_analytics_event_handler.rb +0 -54
- data/app/models/spree/invitations/store.rb +0 -6
- data/app/models/spree/preference.rb +0 -14
- data/app/models/spree/promotion_handler/page.rb +0 -25
- data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
- data/app/models/spree/stock/differentiator.rb +0 -45
- data/app/presenters/spree/filters/options_presenter.rb +0 -47
- data/app/services/spree/carts/empty.rb +0 -42
- data/app/services/spree/country_to_timezone.rb +0 -273
- data/app/services/spree/payments/create.rb +0 -92
- data/app/validators/spree/url_validator.rb +0 -23
- data/app/views/active_storage/blobs/_blob.html.erb +0 -14
- data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/export_mailer/export_done.html.erb +0 -7
- data/app/views/spree/import_mailer/import_done.html.erb +0 -12
- data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
- data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
- data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
- data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
- data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
- data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
- data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
- data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
- data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
- data/app/workflows/spree/returns/purchase_label.rb +0 -32
- data/lib/spree/analytics.rb +0 -11
- data/lib/spree/core/preferences/scoped_store.rb +0 -34
- data/lib/spree/core/preferences/store.rb +0 -105
- data/lib/spree/translation_migrations.rb +0 -52
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 16e068be2852f0ba21dcbb6c34792612f42956a1fb72fbfe6fd8356103df4fea
|
|
4
|
+
data.tar.gz: e0c952c2895b904f00ff5b3a3fd99a5b06a9cd56f751ab7972a31a36eb58d8b7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16656693e4a5bbfde9ea1c53c86a7fb7605fbb689d92d28c261c62bfb2e8862e12357ff181c2568d0e2d847de0db122a2c62a7d8d936a4cae54082e4a078b49c
|
|
7
|
+
data.tar.gz: 602c2b586f08e4332edb18714dbc0e1d97bc4dbe33705ece95259e3f8d2de7bfc0acab52f1150c0154fcab9517c06b47978e872b07f89eaefca1ed7f99d5c58f
|
data/README.md
CHANGED
|
@@ -28,8 +28,8 @@ All models are namespaced under `Spree::` and include:
|
|
|
28
28
|
- `Spree::Product` / `Spree::Variant` - Product catalog
|
|
29
29
|
- `Spree::Order` / `Spree::LineItem` - Order management
|
|
30
30
|
- `Spree::Payment` / `Spree::PaymentMethod` - Payment processing
|
|
31
|
-
- `Spree::
|
|
32
|
-
- `Spree::
|
|
31
|
+
- `Spree::Fulfillment` / `Spree::DeliveryMethod` - Shipping and fulfillment
|
|
32
|
+
- `Spree::Category` / `Spree::Collection` - Product categorization
|
|
33
33
|
- `Spree::Store` - Multi-store support
|
|
34
34
|
- `Spree::Promotion` - Promotions and discounts
|
|
35
35
|
- `Spree::GiftCard` - Gift card functionality
|
|
@@ -1,30 +1,5 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module ImagesHelper
|
|
3
|
-
# render an image tag with a spree image variant
|
|
4
|
-
# it also automatically scales the width and height by 2x to look great on retina screens
|
|
5
|
-
#
|
|
6
|
-
# @param image [ActiveStorage::Attachment] the image to render
|
|
7
|
-
# @param options [Hash] options for the image tag
|
|
8
|
-
# @option options [Integer] :width the width of the image
|
|
9
|
-
# @option options [Integer] :height the height of the image
|
|
10
|
-
# @option options [Symbol] :variant use a preprocessed named variant (e.g., :mini, :small, :medium, :large, :xlarge)
|
|
11
|
-
def spree_image_tag(image, options = {})
|
|
12
|
-
return unless image
|
|
13
|
-
return unless image.variable?
|
|
14
|
-
return if image.respond_to?(:attached?) && !image.attached?
|
|
15
|
-
|
|
16
|
-
url_options = if options[:variant].present?
|
|
17
|
-
{ variant: options[:variant] }
|
|
18
|
-
else
|
|
19
|
-
{ width: options[:width], height: options[:height], format: options[:format] }
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
image_tag(
|
|
23
|
-
spree_image_url(image, url_options),
|
|
24
|
-
options.except(:variant, :format)
|
|
25
|
-
)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
3
|
def spree_image_url(image, options = {})
|
|
29
4
|
return unless image
|
|
30
5
|
return unless image.variable?
|
|
@@ -56,38 +31,6 @@ module Spree
|
|
|
56
31
|
end
|
|
57
32
|
end
|
|
58
33
|
|
|
59
|
-
def spree_asset_aspect_ratio(attachment)
|
|
60
|
-
return unless attachment.present?
|
|
61
|
-
return unless attachment.analyzed?
|
|
62
|
-
|
|
63
|
-
metadata = attachment.metadata
|
|
64
|
-
aspect_ratio = metadata['aspect_ratio'].presence
|
|
65
|
-
|
|
66
|
-
return aspect_ratio if aspect_ratio
|
|
67
|
-
|
|
68
|
-
width = metadata['width']&.to_f
|
|
69
|
-
return unless width
|
|
70
|
-
|
|
71
|
-
height = metadata['height']&.to_f
|
|
72
|
-
return unless height
|
|
73
|
-
return if height.zero?
|
|
74
|
-
|
|
75
|
-
w = width.to_f
|
|
76
|
-
h = height.to_f
|
|
77
|
-
|
|
78
|
-
# Always return width / height, flipping if needed
|
|
79
|
-
ratio = if h > w
|
|
80
|
-
h / w
|
|
81
|
-
elsif h < w
|
|
82
|
-
w / h
|
|
83
|
-
else
|
|
84
|
-
# h == w, square image
|
|
85
|
-
1.0
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
ratio.round(3)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
34
|
# convert images to webp with some sane optimization defaults
|
|
92
35
|
# it also automatically scales the width and height by 2x to look great on retina screens
|
|
93
36
|
#
|
data/app/jobs/spree/base_job.rb
CHANGED
|
@@ -19,5 +19,26 @@ module Spree
|
|
|
19
19
|
retry_on ActiveRecord::RecordNotFound, wait: 2.seconds, attempts: 3
|
|
20
20
|
|
|
21
21
|
discard_on ActiveJob::DeserializationError
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Runs the block once per store owning a record in +records+, inside that
|
|
26
|
+
# store and with +records+ narrowed to it.
|
|
27
|
+
#
|
|
28
|
+
# For installation-wide sweeps. A job has no store of its own, so the work
|
|
29
|
+
# done for a record — its emails, events, store settings — would otherwise
|
|
30
|
+
# run in the default store whichever store the record belongs to.
|
|
31
|
+
# Narrowing the relation keeps each query store-scoped once the store
|
|
32
|
+
# context arms Spree::StoreScopeGuard.
|
|
33
|
+
#
|
|
34
|
+
# @param records [ActiveRecord::Relation] records of a store-owned model
|
|
35
|
+
# @yieldparam store_records [ActiveRecord::Relation] +records+ of one store
|
|
36
|
+
# @return [void]
|
|
37
|
+
def each_store_of(records)
|
|
38
|
+
# Deleted stores too — their records were swept before this was per store.
|
|
39
|
+
Spree::Store.with_deleted.where(id: records.select(:store_id)).find_each do |store|
|
|
40
|
+
Spree::Current.with_store(store) { yield records.where(store_id: store.id) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
22
43
|
end
|
|
23
44
|
end
|
|
@@ -20,7 +20,7 @@ module Spree
|
|
|
20
20
|
def base_scope
|
|
21
21
|
Spree::Cart.incomplete.
|
|
22
22
|
where.not(id: Spree::PaymentSession.where(status: %w[pending processing]).select(:cart_id)).
|
|
23
|
-
where.not(id: Spree::Payment.
|
|
23
|
+
where.not(id: Spree::Payment.valid.where.not(cart_id: nil).select(:cart_id))
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def empty_scope
|
|
@@ -36,7 +36,7 @@ module Spree
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def reap(scope)
|
|
39
|
-
scope.in_batches(of: 200, &:destroy_all)
|
|
39
|
+
each_store_of(scope) { |carts| carts.in_batches(of: 200, &:destroy_all) }
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
end
|
|
@@ -9,9 +9,12 @@ module Spree
|
|
|
9
9
|
def perform
|
|
10
10
|
types = Rails.application.config.spree.time_based_collection_rules.map(&:to_s)
|
|
11
11
|
|
|
12
|
-
Spree::Collection.automatic.
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
time_based = Spree::Collection.automatic.
|
|
13
|
+
where(id: Spree::CollectionRule.where(type: types).select(:collection_id))
|
|
14
|
+
|
|
15
|
+
each_store_of(time_based) do |collections|
|
|
16
|
+
collections.find_each { |collection| Spree::Collections::RegenerateProducts.call(collection: collection) }
|
|
17
|
+
end
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
end
|
|
@@ -10,11 +10,13 @@ module Spree
|
|
|
10
10
|
STALE_AFTER = 10.minutes
|
|
11
11
|
|
|
12
12
|
def perform
|
|
13
|
-
Spree::Order.drafts.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
stale_drafts = Spree::Order.drafts.
|
|
14
|
+
where.not(cart_id: nil).
|
|
15
|
+
where(completed_at: nil).
|
|
16
|
+
where(created_at: ...STALE_AFTER.ago)
|
|
17
|
+
|
|
18
|
+
each_store_of(stale_drafts) do |orders|
|
|
19
|
+
orders.find_each do |order|
|
|
18
20
|
cart = order.cart
|
|
19
21
|
next if cart.nil? || cart.completed?
|
|
20
22
|
|
|
@@ -23,6 +25,7 @@ module Spree
|
|
|
23
25
|
rescue StandardError => e
|
|
24
26
|
Rails.logger.error("[Spree] Completion recovery raised for order #{order.number}: #{e.message}")
|
|
25
27
|
end
|
|
28
|
+
end
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Payments
|
|
3
|
+
# Cancels the payment sessions an order was placed without, so none of
|
|
4
|
+
# them can still be paid. The cursor is the last session handled. A
|
|
5
|
+
# refusal is handled: asking again cannot change it. A session the
|
|
6
|
+
# provider could not be reached about is not, so a later run asks
|
|
7
|
+
# again. The buyer already holds that session's client secret.
|
|
8
|
+
class CancelUnusedSessionsJob < Spree::BaseJob
|
|
9
|
+
include ActiveJob::Continuable
|
|
10
|
+
|
|
11
|
+
# Continuable resumes only after a cursor is saved. The first session
|
|
12
|
+
# has none, so an outage there would drop the job and leave the intent
|
|
13
|
+
# payable. A refusal never reaches this retry: #cancel reports it.
|
|
14
|
+
retry_on StandardError, wait: :polynomially_longer, attempts: 5
|
|
15
|
+
# Must come after `retry_on StandardError` so DeserializationError lands in discard.
|
|
16
|
+
discard_on ActiveJob::DeserializationError
|
|
17
|
+
|
|
18
|
+
# Placement can run inside a webhook's transaction; enqueued before it
|
|
19
|
+
# commits, the job could read the order before its sessions moved onto it.
|
|
20
|
+
self.enqueue_after_transaction_commit = true
|
|
21
|
+
|
|
22
|
+
def perform(order_id)
|
|
23
|
+
# Outside the step on purpose: runs on every execution, resumes included.
|
|
24
|
+
@order = Spree::Order.find_by(id: order_id)
|
|
25
|
+
return if @order.nil?
|
|
26
|
+
|
|
27
|
+
Spree::Current.store = @order.store
|
|
28
|
+
|
|
29
|
+
step :cancel_sessions
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def cancel_sessions(step)
|
|
35
|
+
payment_sessions = @order.payment_sessions.unused.order(:id)
|
|
36
|
+
payment_sessions = payment_sessions.where(Spree::PaymentSession.arel_table[:id].gt(step.cursor)) if step.cursor
|
|
37
|
+
|
|
38
|
+
payment_sessions.each do |payment_session|
|
|
39
|
+
cancel(payment_session)
|
|
40
|
+
step.set!(payment_session.id)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cancel(payment_session)
|
|
45
|
+
payment_session.payment_method.cancel_payment_session(payment_session: payment_session)
|
|
46
|
+
rescue Spree::Core::GatewayError => error
|
|
47
|
+
# An unknown outcome is not a refusal. Cancel can be asked again;
|
|
48
|
+
# counting it handled leaves the intent payable.
|
|
49
|
+
raise if error.is_a?(Spree::Core::AmbiguousGatewayError)
|
|
50
|
+
|
|
51
|
+
# A refusal means the session was paid after all; retrying
|
|
52
|
+
# cannot change that, and the order is placed either way.
|
|
53
|
+
Rails.error.report(
|
|
54
|
+
error,
|
|
55
|
+
context: { payment_session_id: payment_session.id, order_id: @order.id },
|
|
56
|
+
source: 'spree.payments.cancel_unused_sessions'
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -17,6 +17,8 @@ module Spree
|
|
|
17
17
|
def perform(seller_id)
|
|
18
18
|
seller = Spree::Seller.find_by(id: seller_id)
|
|
19
19
|
return if seller.nil?
|
|
20
|
+
|
|
21
|
+
Spree::Current.store = seller.store
|
|
20
22
|
return if seller.resolved_payouts_schedule_interval == 'manual'
|
|
21
23
|
|
|
22
24
|
currencies_owed(seller).each do |currency|
|
|
@@ -8,31 +8,16 @@ module Spree
|
|
|
8
8
|
@current_store = store
|
|
9
9
|
@reset_url = password_reset_url(token, store, redirect_url)
|
|
10
10
|
|
|
11
|
-
with_store_locale(store,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
store_url: store.formatted_url
|
|
11
|
+
with_store_locale(store, staff_locale(admin_user, store)) do
|
|
12
|
+
mail_template(
|
|
13
|
+
{ user: email_data(admin_user, Spree::Emails::UserSerializer), reset_url: @reset_url },
|
|
14
|
+
to: admin_user.email, store_url: store.formatted_url
|
|
16
15
|
)
|
|
17
16
|
end
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
private
|
|
21
20
|
|
|
22
|
-
# Locale chain for admin auth emails:
|
|
23
|
-
# the admin's own dashboard language (persisted by the profile/language
|
|
24
|
-
# switcher via `PATCH /api/v3/admin/me`) → the store's configured admin
|
|
25
|
-
# locale → nil, which lets with_store_locale fall back to the store's
|
|
26
|
-
# default (storefront) locale. Blank or unavailable values fall through.
|
|
27
|
-
def preferred_locale(admin_user, store)
|
|
28
|
-
[admin_user.try(:selected_locale), store&.preferred_admin_locale]
|
|
29
|
-
.find { |locale| available_locale?(locale) }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def available_locale?(locale)
|
|
33
|
-
locale.present? && I18n.available_locales.map(&:to_s).include?(locale.to_s)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
21
|
# The dashboard SPA passes a validated redirect URL; the token is appended as
|
|
37
22
|
# a query param, falling back to the dashboard's own reset page.
|
|
38
23
|
def password_reset_url(token, store, redirect_url)
|
|
@@ -41,7 +41,7 @@ module Spree
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def reply_to_address
|
|
44
|
-
current_store.
|
|
44
|
+
current_store.support_email_address
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def money(amount, currency = nil)
|
|
@@ -50,25 +50,72 @@ module Spree
|
|
|
50
50
|
end
|
|
51
51
|
helper_method :money
|
|
52
52
|
|
|
53
|
+
# Wraps HTML a mailer rendered from its own ERB views in the Liquid email
|
|
54
|
+
# layout, so every email a Spree mailer sends carries the store's logo,
|
|
55
|
+
# header and footer. Called by `layouts/spree/base_mailer.html.erb`.
|
|
56
|
+
#
|
|
57
|
+
# @param html [String] the rendered view
|
|
58
|
+
# @return [ActiveSupport::SafeBuffer]
|
|
59
|
+
def render_in_email_layout(html)
|
|
60
|
+
email_renderer.wrap(html, subject: message.subject).html_safe
|
|
61
|
+
end
|
|
62
|
+
helper_method :render_in_email_layout
|
|
63
|
+
|
|
53
64
|
def mail(headers = {}, &block)
|
|
54
65
|
ensure_default_action_mailer_url_host(headers[:store_url])
|
|
66
|
+
# Mailers that don't wrap their action in `with_store_locale` still get
|
|
67
|
+
# the store's default locale, as `mail` applied before Spree 5.6.
|
|
68
|
+
in_store_locale { super }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
protected
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
# Locale for an email to a staff member or seller: their own dashboard
|
|
74
|
+
# language, then the store's admin locale, then nil — which lets
|
|
75
|
+
# with_store_locale fall back to the store's default. Blank or unavailable
|
|
76
|
+
# values fall through.
|
|
77
|
+
def staff_locale(user, store)
|
|
78
|
+
[user.try(:selected_locale), store&.preferred_admin_locale].find { |locale| I18n.locale_available?(locale) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Renders the current action's email from its Liquid template and builds
|
|
82
|
+
# the message. The template's front matter supplies the subject, and the
|
|
83
|
+
# plain-text part is generated from the HTML unless a `.text.liquid` sits
|
|
84
|
+
# next to the template.
|
|
85
|
+
#
|
|
86
|
+
# @param assigns [Hash] the template's variables — serializer output and
|
|
87
|
+
# mailer-built values such as token-carrying URLs, never models
|
|
88
|
+
# @param template [String] the template key, defaults to the action's view path
|
|
89
|
+
# @param headers [Hash] mail headers (`to:`, `store_url:`, ...)
|
|
90
|
+
# @return [Mail::Message]
|
|
91
|
+
def mail_template(assigns = {}, template: "#{mailer_name}/#{action_name}", **headers)
|
|
92
|
+
in_store_locale do
|
|
93
|
+
email = render_email_template(template, assigns)
|
|
94
|
+
|
|
95
|
+
mail(headers.merge(subject: email.subject)) do |format|
|
|
96
|
+
format.text { render plain: email.text, layout: false }
|
|
97
|
+
format.html { render html: email.html.html_safe, layout: false }
|
|
98
|
+
end
|
|
63
99
|
end
|
|
64
100
|
end
|
|
65
101
|
|
|
66
|
-
|
|
102
|
+
# The currency an email's amounts are in: its order's, else the store's.
|
|
103
|
+
#
|
|
104
|
+
# @return [String]
|
|
105
|
+
def email_currency
|
|
106
|
+
(@order || @order_group)&.currency || current_store.default_currency
|
|
107
|
+
end
|
|
67
108
|
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
109
|
+
# A record as its template reads it: the serializer's JSON, the same data
|
|
110
|
+
# a renderer outside Ruby would receive. Download links and other bearer
|
|
111
|
+
# tokens stay out; the mailer builds any URL that carries one.
|
|
112
|
+
#
|
|
113
|
+
# @param object [Object, nil] the record
|
|
114
|
+
# @param serializer [Class] an email serializer
|
|
115
|
+
# @param params [Hash] serializer params, over the email's store, currency and locale
|
|
116
|
+
# @return [Hash, nil]
|
|
117
|
+
def email_data(object, serializer, **params)
|
|
118
|
+
Spree::Emails::TemplateData.call(object, serializer, store: current_store, currency: email_currency, **params)
|
|
72
119
|
end
|
|
73
120
|
|
|
74
121
|
# URI-based merge preserves existing query params and fragments so the token
|
|
@@ -86,6 +133,42 @@ module Spree
|
|
|
86
133
|
|
|
87
134
|
private
|
|
88
135
|
|
|
136
|
+
# A store's published template passed its checks with sample data, but a
|
|
137
|
+
# real record can still trip it. The customer then gets the email from the
|
|
138
|
+
# files rather than none, and the error is reported.
|
|
139
|
+
def render_email_template(template, assigns)
|
|
140
|
+
resolver = email_resolver(template)
|
|
141
|
+
email_renderer(resolver).render(find_email_template(resolver, template), assigns)
|
|
142
|
+
rescue Liquid::Error, MRML::Error => e
|
|
143
|
+
raise unless resolver&.stored_templates_used?
|
|
144
|
+
|
|
145
|
+
Rails.error.report(e, context: { email_template: template, store_id: current_store&.id })
|
|
146
|
+
fallback = email_resolver
|
|
147
|
+
email_renderer(fallback).render(find_email_template(fallback, template), assigns)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def find_email_template(resolver, template)
|
|
151
|
+
resolver.find(template) || raise(ArgumentError, "Missing email template #{template}.liquid")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# The store's published templates apply only to the emails merchants may
|
|
155
|
+
# edit; every other email, and the layout and partials it uses, comes
|
|
156
|
+
# from files.
|
|
157
|
+
def email_resolver(template = nil)
|
|
158
|
+
view_paths = self.class.view_paths.paths.map(&:path)
|
|
159
|
+
return Spree::Emails::TemplateResolver.new(view_paths) unless Spree.editable_email_templates.include?(template)
|
|
160
|
+
|
|
161
|
+
Spree::Emails::TemplateResolver.new(view_paths, store: current_store, locale: I18n.locale)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def email_renderer(resolver = email_resolver)
|
|
165
|
+
Spree::Emails::Renderer.new(resolver: resolver, store: current_store, currency: email_currency)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def in_store_locale(&block)
|
|
169
|
+
@_store_locale_active ? yield : with_store_locale(current_store, &block)
|
|
170
|
+
end
|
|
171
|
+
|
|
89
172
|
# this ensures that ActionMailer::Base.default_url_options[:host] is always set
|
|
90
173
|
# this is only a fail-safe solution if developer didn't set this in environment files
|
|
91
174
|
# http://guides.rubyonrails.org/action_mailer_basics.html#generating-urls-in-action-mailer-views
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Sends an editable email template, rendered with sample data as the editor
|
|
3
|
+
# previews it, so a merchant can see it in a real inbox before publishing.
|
|
4
|
+
class EmailTemplateMailer < BaseMailer
|
|
5
|
+
# @param store [Spree::Store]
|
|
6
|
+
# @param recipient [String] the email address of the admin who asked for it
|
|
7
|
+
# @param key [String] the editable template
|
|
8
|
+
# @param locale [String] a language code, or "any"
|
|
9
|
+
# @param subject [String, nil] an unsaved subject
|
|
10
|
+
# @param body [String, nil] an unsaved body
|
|
11
|
+
# @param record_id [String, nil] the record to build sample data from
|
|
12
|
+
# @param email_key [String, nil] for the layout or a partial, the email to show it in
|
|
13
|
+
# @param branding [Hash] unsaved branding values
|
|
14
|
+
def test_email(store, recipient, key, locale: nil, subject: nil, body: nil, record_id: nil, email_key: nil, branding: {})
|
|
15
|
+
@current_store = store
|
|
16
|
+
email = Spree::EmailTemplates::Preview.new(
|
|
17
|
+
store: store, key: key, locale: locale, subject: subject, body: body, record_id: record_id, email_key: email_key,
|
|
18
|
+
branding: branding
|
|
19
|
+
).call
|
|
20
|
+
|
|
21
|
+
mail(to: recipient, subject: I18n.t('spree.email_templates.test_email_subject', subject: email.subject)) do |format|
|
|
22
|
+
format.text { render plain: email.text, layout: false }
|
|
23
|
+
format.html { render html: email.html.html_safe, layout: false }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -4,10 +4,9 @@ module Spree
|
|
|
4
4
|
@export = export
|
|
5
5
|
|
|
6
6
|
with_store_locale(@export.store) do
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
store_url: current_store.url
|
|
7
|
+
mail_template(
|
|
8
|
+
{ export: email_data(@export, Spree::Emails::ExportSerializer) },
|
|
9
|
+
to: @export.user.email, store_url: current_store.url
|
|
11
10
|
)
|
|
12
11
|
end
|
|
13
12
|
end
|
|
@@ -4,10 +4,9 @@ module Spree
|
|
|
4
4
|
@import = import
|
|
5
5
|
|
|
6
6
|
with_store_locale(@import.store) do
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
store_url: current_store.url
|
|
7
|
+
mail_template(
|
|
8
|
+
{ import: email_data(@import, Spree::Emails::ImportSerializer) },
|
|
9
|
+
to: @import.user.email, store_url: current_store.url
|
|
11
10
|
)
|
|
12
11
|
end
|
|
13
12
|
end
|
|
@@ -8,9 +8,10 @@ module Spree
|
|
|
8
8
|
# default store's branding on multi-store installs.
|
|
9
9
|
@current_store = invitation.store
|
|
10
10
|
with_store_locale(invitation.store) do
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
mail_template(
|
|
12
|
+
{ invitation: email_data(invitation, Spree::Emails::InvitationSerializer), accept_url: accept_url(invitation) },
|
|
13
|
+
to: invitation.email
|
|
14
|
+
)
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
|
|
@@ -19,10 +20,26 @@ module Spree
|
|
|
19
20
|
@invitation = invitation
|
|
20
21
|
@current_store = invitation.store
|
|
21
22
|
with_store_locale(invitation.store) do
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
mail_template(
|
|
24
|
+
{ invitation: email_data(invitation, Spree::Emails::InvitationSerializer) },
|
|
25
|
+
to: invitation.inviter.email
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# The legacy `spree_admin` gem's own acceptance page when it is installed,
|
|
33
|
+
# otherwise the dashboard's.
|
|
34
|
+
def accept_url(invitation)
|
|
35
|
+
spree_routes = Spree::Core::Engine.routes.url_helpers
|
|
36
|
+
|
|
37
|
+
if spree_routes.respond_to?(:admin_invitation_url)
|
|
38
|
+
spree_routes.admin_invitation_url(invitation, token: invitation.token, host: invitation.store.formatted_url)
|
|
39
|
+
else
|
|
40
|
+
# Built with the mailer's own URL options, as the view built it before.
|
|
41
|
+
ensure_default_action_mailer_url_host
|
|
42
|
+
main_app.admin_invitation_acceptance_url(invitation)
|
|
26
43
|
end
|
|
27
44
|
end
|
|
28
45
|
end
|
|
@@ -12,28 +12,16 @@ module Spree
|
|
|
12
12
|
@current_store = store
|
|
13
13
|
@reset_url = password_reset_url(token, store, redirect_url)
|
|
14
14
|
|
|
15
|
-
with_store_locale(store,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
store_url: store.formatted_url
|
|
15
|
+
with_store_locale(store, staff_locale(seller_user, store)) do
|
|
16
|
+
mail_template(
|
|
17
|
+
{ user: email_data(seller_user, Spree::Emails::UserSerializer), reset_url: @reset_url },
|
|
18
|
+
to: seller_user.email, store_url: store.formatted_url
|
|
20
19
|
)
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
private
|
|
25
24
|
|
|
26
|
-
# The seller's own panel language, then the store's admin locale, then nil —
|
|
27
|
-
# which lets with_store_locale fall back to the store's default.
|
|
28
|
-
def preferred_locale(seller_user, store)
|
|
29
|
-
[seller_user.try(:selected_locale), store&.preferred_admin_locale]
|
|
30
|
-
.find { |locale| available_locale?(locale) }
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def available_locale?(locale)
|
|
34
|
-
locale.present? && I18n.available_locales.map(&:to_s).include?(locale.to_s)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
25
|
# The panel passes a validated redirect URL when it has one. Without it the
|
|
38
26
|
# panel origin is resolved server-side rather than falling back to the store
|
|
39
27
|
# URL: a storefront link cannot reset a seller password, and the marketplace
|
|
@@ -7,9 +7,9 @@ module Spree
|
|
|
7
7
|
@current_store = webhook_endpoint.store
|
|
8
8
|
|
|
9
9
|
with_store_locale(webhook_endpoint.store) do
|
|
10
|
-
|
|
10
|
+
mail_template(
|
|
11
|
+
{ endpoint: email_data(@endpoint, Spree::Emails::WebhookEndpointSerializer) },
|
|
11
12
|
to: @current_store.new_order_notifications_email.presence || @current_store.mail_from_address,
|
|
12
|
-
subject: Spree.t('webhook_mailer.endpoint_disabled.subject', endpoint_name: @endpoint.name || @endpoint.url),
|
|
13
13
|
store_url: @current_store.formatted_url
|
|
14
14
|
)
|
|
15
15
|
end
|
|
@@ -158,7 +158,7 @@ module Spree
|
|
|
158
158
|
next if value.blank? || Spree.actor_classes.include?(value)
|
|
159
159
|
|
|
160
160
|
errors.add(type_column, :unregistered_actor,
|
|
161
|
-
message:
|
|
161
|
+
message: I18n.t('spree.errors.messages.unregistered_actor'))
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
164
|
|
|
@@ -10,6 +10,14 @@ module Spree
|
|
|
10
10
|
included do
|
|
11
11
|
has_prefix_id :adm
|
|
12
12
|
|
|
13
|
+
publishes_events 'admin_user.password_reset', 'seller_user.password_reset'
|
|
14
|
+
# Carry a live reset token and have no external consumer: core sends
|
|
15
|
+
# these emails itself, so no webhook endpoint ever receives them.
|
|
16
|
+
publishes_event 'admin_user.password_reset_requested',
|
|
17
|
+
serializer: 'Spree::Api::V3::PasswordResetRequestedEventSerializer', webhook: false
|
|
18
|
+
publishes_event 'seller_user.password_reset_requested',
|
|
19
|
+
serializer: 'Spree::Api::V3::PasswordResetRequestedEventSerializer', webhook: false
|
|
20
|
+
|
|
13
21
|
has_person_name
|
|
14
22
|
|
|
15
23
|
normalizes :email, :first_name, :last_name, with: ->(value) { value&.to_s&.squish&.presence }
|
|
@@ -128,10 +136,14 @@ module Spree
|
|
|
128
136
|
# @param model [Class]
|
|
129
137
|
# @param name [Symbol] the acted_by association
|
|
130
138
|
def clear_actor(model, name)
|
|
139
|
+
columns = Spree::ActedBy.columns_for(name, nil)
|
|
140
|
+
# Append-only tables (revisions) have no updated_at to stamp.
|
|
141
|
+
columns = columns.merge(updated_at: Time.current) if model.column_names.include?('updated_at')
|
|
142
|
+
|
|
131
143
|
model.
|
|
132
144
|
where(:"#{name}_id" => id).
|
|
133
145
|
where(:"#{name}_type" => [self.class.polymorphic_name, nil]).
|
|
134
|
-
update_all(
|
|
146
|
+
update_all(columns)
|
|
135
147
|
end
|
|
136
148
|
end
|
|
137
149
|
end
|