spree_core 6.0.0.beta4 → 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
|
@@ -59,17 +59,17 @@ module Spree
|
|
|
59
59
|
|
|
60
60
|
return false if preferred_stock_location_id.present?
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
profile_requires_address = Hash.new do |cache, profile|
|
|
65
|
-
cache[profile] = profile.present? && profile.requires_shipping_address?
|
|
66
|
-
end
|
|
62
|
+
items_require_shipping_address?
|
|
63
|
+
end
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
# Whether delivery can be proposed yet: once there is a destination (a
|
|
66
|
+
# shipping address, or a pickup intent for a purchase that never gets
|
|
67
|
+
# one), or straight away when no item ships to an address. Quoted
|
|
68
|
+
# without a destination, only pickup qualifies.
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean]
|
|
71
|
+
def can_propose_deliveries?
|
|
72
|
+
ship_address.present? || preferred_stock_location_id.present? || !items_require_shipping_address?
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# @deprecated The shipping address is canonical — use +use_shipping+ to
|
|
@@ -174,6 +174,19 @@ module Spree
|
|
|
174
174
|
|
|
175
175
|
private
|
|
176
176
|
|
|
177
|
+
def items_require_shipping_address?
|
|
178
|
+
# Answered by the profile kind, resolved once per profile.
|
|
179
|
+
profile_requires_address = Hash.new do |cache, profile|
|
|
180
|
+
cache[profile] = profile.present? && profile.requires_shipping_address?
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Both profile paths are preloaded: the variant's own override, and the
|
|
184
|
+
# product's for the variants that inherit it.
|
|
185
|
+
line_items.includes(variant: [:delivery_profile, { product: :delivery_profile }]).any? do |line_item|
|
|
186
|
+
profile_requires_address[line_item.variant&.resolved_delivery_profile]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
177
190
|
# The address behind an id a client may select, or nil when the id names
|
|
178
191
|
# one this purchase has no business shipping to.
|
|
179
192
|
#
|
|
@@ -15,7 +15,7 @@ module Spree
|
|
|
15
15
|
|
|
16
16
|
supported_codes = store.supported_currencies_list.map(&:iso_code)
|
|
17
17
|
unless supported_codes.include?(currency)
|
|
18
|
-
errors.add(:currency, :currency_not_supported_by_store, message:
|
|
18
|
+
errors.add(:currency, :currency_not_supported_by_store, message: I18n.t('spree.currency_not_supported_by_store'))
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Purchase
|
|
3
|
+
# One-release bridges for the names renamed in 6.0, shared by
|
|
4
|
+
# {Spree::Cart} and {Spree::Order}. Before 6.0 a cart was an incomplete
|
|
5
|
+
# order, so extensions written against 5.x (payment gateways, checkout
|
|
6
|
+
# plugins) call the legacy names on carts too. Every legacy name warns and
|
|
7
|
+
# forwards to its replacement. Removed in 6.1.
|
|
8
|
+
#
|
|
9
|
+
# Renamed columns are also registered with +alias_attribute+, so the legacy
|
|
10
|
+
# name keeps working in +where+ / +find_by+ (those paths do not warn).
|
|
11
|
+
module DeprecatedAliases
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
RENAMED_ATTRIBUTES = {
|
|
15
|
+
promo_total: :discount_total,
|
|
16
|
+
item_count: :total_quantity,
|
|
17
|
+
shipment_total: :delivery_total,
|
|
18
|
+
ship_total: :delivery_total,
|
|
19
|
+
special_instructions: :customer_note
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
RENAMED_METHODS = {
|
|
23
|
+
display_promo_total: :display_discount_total,
|
|
24
|
+
display_shipment_total: :display_delivery_total,
|
|
25
|
+
display_ship_total: :display_delivery_total,
|
|
26
|
+
shipping_discount: :fulfillment_discount,
|
|
27
|
+
delivery_required?: :delivery_step_required?,
|
|
28
|
+
requires_ship_address?: :shipping_address_required?,
|
|
29
|
+
set_shipments_cost: :set_fulfillments_cost,
|
|
30
|
+
ensure_updated_shipments: :ensure_updated_fulfillments,
|
|
31
|
+
create_proposed_fulfillments: :rebuild_fulfillments!,
|
|
32
|
+
create_proposed_shipments: :rebuild_fulfillments!,
|
|
33
|
+
associate_user!: :associate_customer!,
|
|
34
|
+
update_with_updater!: :recalculate_totals!,
|
|
35
|
+
all_line_items: :line_items
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# 5.x took a store argument; payment methods now always come from the
|
|
39
|
+
# purchase's own store.
|
|
40
|
+
RENAMED_PAYMENT_METHOD_READERS = %i[
|
|
41
|
+
available_payment_methods
|
|
42
|
+
collect_payment_methods
|
|
43
|
+
collect_frontend_payment_methods
|
|
44
|
+
].freeze
|
|
45
|
+
|
|
46
|
+
included do
|
|
47
|
+
include Spree::DeprecatedCustomerAlias
|
|
48
|
+
|
|
49
|
+
RENAMED_ATTRIBUTES.each do |legacy_name, current_name|
|
|
50
|
+
alias_attribute legacy_name, current_name
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
RENAMED_ATTRIBUTES.each do |legacy_name, current_name|
|
|
55
|
+
define_method(legacy_name) do
|
|
56
|
+
warn_deprecated_name(legacy_name, current_name)
|
|
57
|
+
public_send(current_name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
define_method(:"#{legacy_name}=") do |value|
|
|
61
|
+
warn_deprecated_name(:"#{legacy_name}=", :"#{current_name}=")
|
|
62
|
+
public_send(:"#{current_name}=", value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
RENAMED_METHODS.each do |legacy_name, current_name|
|
|
67
|
+
define_method(legacy_name) do |*args, **options, &block|
|
|
68
|
+
warn_deprecated_name(legacy_name, current_name)
|
|
69
|
+
public_send(current_name, *args, **options, &block)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
RENAMED_PAYMENT_METHOD_READERS.each do |legacy_name|
|
|
74
|
+
define_method(legacy_name) do |*|
|
|
75
|
+
warn_deprecated_name(legacy_name, :payment_methods)
|
|
76
|
+
payment_methods
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def warn_deprecated_name(legacy_name, current_name)
|
|
83
|
+
Spree::Deprecation.warn("#{self.class.name}##{legacy_name} is deprecated and will be removed in Spree 6.1. Use ##{current_name} instead.")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -51,16 +51,22 @@ module Spree
|
|
|
51
51
|
|
|
52
52
|
payment = payments.checkout.store_credits.where(source: gift_card.store_credits).first
|
|
53
53
|
return unless payment
|
|
54
|
+
# Nothing is left for the card to pay, and a store credit cannot hold
|
|
55
|
+
# a zero amount, so the card is released rather than resized.
|
|
56
|
+
return remove_gift_card unless total.positive?
|
|
54
57
|
|
|
55
58
|
gift_card.with_lock do
|
|
56
|
-
|
|
59
|
+
# A locking read: a plain reload can return the transaction's older
|
|
60
|
+
# snapshot, and a resize committed since would be applied again.
|
|
61
|
+
payment.lock!
|
|
62
|
+
new_amount = [gift_card.spendable_amount + payment.amount, total].min
|
|
57
63
|
next if payment.amount == new_amount
|
|
58
64
|
|
|
59
65
|
difference = new_amount - payment.amount
|
|
60
|
-
# update_column bypasses Payment#max_amount validation which can
|
|
61
|
-
# fail on stale in-memory state; bounds enforced via min() above.
|
|
62
|
-
payment.update_column(:amount, new_amount)
|
|
63
66
|
payment.source.update_column(:amount, new_amount)
|
|
67
|
+
# Skips Payment#max_amount, which can fail on stale in-memory state;
|
|
68
|
+
# min() above bounds the amount.
|
|
69
|
+
payment.update_store_credit_amount!(new_amount)
|
|
64
70
|
gift_card.amount_used += difference
|
|
65
71
|
gift_card.save!
|
|
66
72
|
end
|
|
@@ -22,7 +22,7 @@ module Spree
|
|
|
22
22
|
return if locale.blank? || store.blank?
|
|
23
23
|
|
|
24
24
|
unless store.supported_locales_list.include?(locale)
|
|
25
|
-
errors.add(:locale, :locale_not_supported_by_store, message:
|
|
25
|
+
errors.add(:locale, :locale_not_supported_by_store, message: I18n.t('spree.locale_not_supported_by_store'))
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
end
|
|
@@ -31,11 +31,15 @@ module Spree
|
|
|
31
31
|
payments.pending
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# Active front-end payment methods available for this record.
|
|
34
|
+
# Active front-end payment methods available for this record. Store
|
|
35
|
+
# credit is never one of them: it is applied through its own endpoint
|
|
36
|
+
# before the buyer chooses how to pay the rest.
|
|
35
37
|
#
|
|
36
38
|
# @return [Array<Spree::PaymentMethod>]
|
|
37
39
|
def payment_methods
|
|
38
|
-
store.payment_methods.active.storefront_visible.select
|
|
40
|
+
store.payment_methods.active.storefront_visible.select do |payment_method|
|
|
41
|
+
!payment_method.store_credit? && payment_method.available_for_order?(self)
|
|
42
|
+
end
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
# Free checkouts have no money to collect.
|
|
@@ -71,7 +75,7 @@ module Spree
|
|
|
71
75
|
# Don't run if there are authorized payments
|
|
72
76
|
return if pending_payments.any? && unprocessed_payments.empty?
|
|
73
77
|
# Never complete without a successfully processed payment.
|
|
74
|
-
raise Spree::Core::GatewayError,
|
|
78
|
+
raise Spree::Core::GatewayError, I18n.t('spree.no_payment_found') if unprocessed_payments.empty?
|
|
75
79
|
|
|
76
80
|
unprocessed_payments.each do |payment|
|
|
77
81
|
break if settled_payment_total >= total
|
|
@@ -83,8 +83,8 @@ module Spree
|
|
|
83
83
|
return if stored_size <= MAX_PO_DOCUMENT_SIZE
|
|
84
84
|
|
|
85
85
|
errors.add(
|
|
86
|
-
:po_document, :po_document_too_large, message:
|
|
87
|
-
|
|
86
|
+
:po_document, :po_document_too_large, message: I18n.t(
|
|
87
|
+
'spree.po_document_too_large',
|
|
88
88
|
size: ActiveSupport::NumberHelper.number_to_human_size(MAX_PO_DOCUMENT_SIZE)
|
|
89
89
|
)
|
|
90
90
|
)
|
|
@@ -10,9 +10,37 @@ module Spree
|
|
|
10
10
|
line_items.sum(:quantity)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# What promotion calculators take a share of: the line amounts less the
|
|
14
|
+
# free gifts, so a gift never enlarges a percentage off the order.
|
|
15
|
+
#
|
|
13
16
|
# @return [BigDecimal]
|
|
14
17
|
def amount
|
|
15
|
-
line_items.sum(BigDecimal('0'), &:amount)
|
|
18
|
+
line_items.sum(BigDecimal('0'), &:amount) - gift_amount
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# The list price of the units gift promotions give away. The gift keeps
|
|
22
|
+
# its price on its line and is paid for by a discount, so anything
|
|
23
|
+
# measuring what the shopper spends has to take it out.
|
|
24
|
+
#
|
|
25
|
+
# @param promotion [Spree::Promotion, nil] the promotion doing the
|
|
26
|
+
# measuring; its own gifts are always taken out, since a gift cannot
|
|
27
|
+
# be what qualifies the promotion giving it away
|
|
28
|
+
# @param own_gift_only [Boolean] true while deciding whether another
|
|
29
|
+
# promotion gives its gift, so two gift promotions with spend
|
|
30
|
+
# thresholds do not each wait on the other
|
|
31
|
+
# @return [BigDecimal]
|
|
32
|
+
def gift_amount(promotion: nil, own_gift_only: false)
|
|
33
|
+
return BigDecimal('0') if line_items.empty?
|
|
34
|
+
|
|
35
|
+
excluded_gift_promo_actions = gift_promo_actions(promotion, own_gift_only).select do |action|
|
|
36
|
+
action.promotion_id == promotion&.id || action.gives_away?(self)
|
|
37
|
+
end
|
|
38
|
+
return BigDecimal('0') if excluded_gift_promo_actions.empty?
|
|
39
|
+
|
|
40
|
+
line_items.sum(BigDecimal('0')) do |line_item|
|
|
41
|
+
gifted_quantity = excluded_gift_promo_actions.sum { |action| action.gifted_quantity_of(line_item) }
|
|
42
|
+
line_item.price * [gifted_quantity, line_item.quantity].min
|
|
43
|
+
end
|
|
16
44
|
end
|
|
17
45
|
|
|
18
46
|
# Re-sums what the customer has actually paid, and nothing else. A
|
|
@@ -78,6 +106,26 @@ module Spree
|
|
|
78
106
|
|
|
79
107
|
private
|
|
80
108
|
|
|
109
|
+
# Other promotions' gifts are looked for among the promotions a
|
|
110
|
+
# recalculation writes discounts for, so a gift leaves the measure
|
|
111
|
+
# exactly when its discount can be written.
|
|
112
|
+
def gift_promo_actions(promotion, own_gift_only)
|
|
113
|
+
own = promotion ? promotion.actions.grep(Spree::Promotion::Actions::CreateLineItems) : []
|
|
114
|
+
return own if own_gift_only
|
|
115
|
+
|
|
116
|
+
discounting_ids = promotion_ids + Spree::Promotion.held_by_saved_coupon_code(self).map(&:id)
|
|
117
|
+
return own if discounting_ids.empty?
|
|
118
|
+
|
|
119
|
+
gifting_cart_variants = Spree::PromotionActionLineItem.
|
|
120
|
+
where(variant_id: line_items.map(&:variant_id)).
|
|
121
|
+
select(:promotion_action_id)
|
|
122
|
+
others = Spree::Promotion::Actions::CreateLineItems.
|
|
123
|
+
where(promotion_id: discounting_ids, id: gifting_cart_variants).
|
|
124
|
+
includes(:promotion, :promotion_action_line_items)
|
|
125
|
+
|
|
126
|
+
(own + others.to_a).uniq(&:id)
|
|
127
|
+
end
|
|
128
|
+
|
|
81
129
|
# Completed payments less their refunds, as a scalar subquery so the
|
|
82
130
|
# sum and the write are one statement.
|
|
83
131
|
#
|
|
@@ -97,7 +97,7 @@ module Spree
|
|
|
97
97
|
variant_ids = items.reject(&:marked_for_destruction?).filter_map(&:variant_id)
|
|
98
98
|
return if variant_ids.uniq.size == variant_ids.size
|
|
99
99
|
|
|
100
|
-
errors.add(:items, :duplicate_variant, message:
|
|
100
|
+
errors.add(:items, :duplicate_variant, message: I18n.t('spree.errors.messages.duplicate_variant'))
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
end
|
|
@@ -25,10 +25,6 @@ module Spree
|
|
|
25
25
|
model_class.arel_table[attribute.to_sym].lower.matches("%#{escaped}%", '\\')
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
-
|
|
29
|
-
# Backward compatibility aliases — remove in Spree 6.0
|
|
30
|
-
def self.sanitize_query_for_multi_search(query) = sanitize_query_for_search(query)
|
|
31
|
-
def self.multi_search_condition(model_class, attribute, query) = search_condition(model_class, attribute, query)
|
|
32
28
|
end
|
|
33
29
|
end
|
|
34
30
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Stores a model's `:password` preferences in a separate `secret_preferences`
|
|
3
|
+
# text column, encrypted with Active Record encryption whenever keys are
|
|
4
|
+
# configured, so the `preferences` column never holds a secret. Declarations
|
|
5
|
+
# are unchanged: `preference :secret_key, :password` and
|
|
6
|
+
# `preferred_secret_key` read and write the encrypted column.
|
|
7
|
+
#
|
|
8
|
+
# Values written before encryption was enabled stay readable and are
|
|
9
|
+
# encrypted on the next save, or at once by
|
|
10
|
+
# `spree:upgrade:encrypt_secret_preferences`.
|
|
11
|
+
module SecretPreferences
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
included do
|
|
15
|
+
serialize :secret_preferences, coder: TextHashSerializer
|
|
16
|
+
|
|
17
|
+
if ActiveRecord::Encryption.config.has_primary_key?
|
|
18
|
+
encrypts :secret_preferences, support_unencrypted_data: true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
before_save :move_secret_preferences
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The column is `text`, since ciphertext cannot live in a JSON column. An
|
|
25
|
+
# empty column stays nil rather than `{}`: encrypted attributes compare the
|
|
26
|
+
# stored value with the loaded one, so `{}` would mark every record without
|
|
27
|
+
# secrets as changed.
|
|
28
|
+
class TextHashSerializer
|
|
29
|
+
def self.dump(hash)
|
|
30
|
+
ActiveSupport::JSON.encode(hash) unless hash.nil?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.load(json)
|
|
34
|
+
Spree::Metadata::HashSerializer.load(ActiveSupport::JSON.decode(json)) unless json.nil?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Both columns as they should be stored, when `preferences` holds a secret
|
|
39
|
+
# — one assigned as part of a whole hash, or one the upgrade has not moved
|
|
40
|
+
# yet. A secret found there is the newer value and wins.
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash{Symbol => Hash}, nil] `preferences:` and `secret_preferences:`, or nil when there is nothing to move
|
|
43
|
+
def separated_secret_preferences
|
|
44
|
+
secrets = (preferences || {}).slice(*self.class.secret_preference_names)
|
|
45
|
+
return if secrets.empty?
|
|
46
|
+
|
|
47
|
+
{ preferences: preferences.except(*secrets.keys), secret_preferences: (secret_preferences || {}).merge(secrets) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# Keeps a secret out of the plain `preferences` column when the record is
|
|
53
|
+
# saved.
|
|
54
|
+
def move_secret_preferences
|
|
55
|
+
separated = separated_secret_preferences
|
|
56
|
+
assign_attributes(separated) if separated
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -19,7 +19,7 @@ module Spree
|
|
|
19
19
|
|
|
20
20
|
def ensure_store_association_is_not_changed
|
|
21
21
|
if store_id_changed? && persisted?
|
|
22
|
-
errors.add(:store, :store_association_can_not_be_changed, message:
|
|
22
|
+
errors.add(:store, :store_association_can_not_be_changed, message: I18n.t('spree.errors.messages.store_association_can_not_be_changed'))
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Stores
|
|
3
|
+
# The colors and font of a store's customer emails (see Spree::Emails::Branding).
|
|
4
|
+
# All optional: a blank one keeps the shipped design.
|
|
5
|
+
module EmailBranding
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
Spree::Emails::Branding.attribute_names.each { |name| preference :"email_#{name}", :string }
|
|
10
|
+
|
|
11
|
+
validate :email_branding_valid
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param overrides [Hash] unsaved values, e.g. to preview them
|
|
15
|
+
# @return [Spree::Emails::Branding]
|
|
16
|
+
def email_branding(overrides = {})
|
|
17
|
+
saved = Spree::Emails::Branding.attribute_names.to_h { |name| [name, public_send(:"preferred_email_#{name}")] }
|
|
18
|
+
Spree::Emails::Branding.new(saved.merge(overrides.to_h.stringify_keys.slice(*saved.keys)))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def email_branding_valid
|
|
24
|
+
branding = email_branding
|
|
25
|
+
return if branding.valid?
|
|
26
|
+
|
|
27
|
+
branding.errors.each { |error| errors.add(:"preferred_email_#{error.attribute}", error.type) }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Names and describes each kind in a family of typed subclasses (promotion
|
|
3
|
+
# rules, collection rules, …) from one translation scope keyed by
|
|
4
|
+
# +api_type+, so the `/types` endpoints and dashboard pickers label
|
|
5
|
+
# extension kinds the same way as built-in ones.
|
|
6
|
+
#
|
|
7
|
+
# class Spree::PromotionRule < Spree.base_class
|
|
8
|
+
# include Spree::TypeLabels
|
|
9
|
+
# self.type_labels_scope = 'spree.promotion_rule_types'
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# Spree::Promotion::Rules::Currency.human_name # => spree.promotion_rule_types.currency.name
|
|
13
|
+
module TypeLabels
|
|
14
|
+
extend ActiveSupport::Concern
|
|
15
|
+
|
|
16
|
+
included do
|
|
17
|
+
class_attribute :type_labels_scope, instance_writer: false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class_methods do
|
|
21
|
+
# @return [String] the kind's name, falling back to its humanized +api_type+
|
|
22
|
+
def human_name
|
|
23
|
+
I18n.t("#{type_labels_scope}.#{api_type}.name", default: api_type.titleize)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [String] what the kind does, empty when it has no translation
|
|
27
|
+
def human_description
|
|
28
|
+
I18n.t("#{type_labels_scope}.#{api_type}.description", default: '')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String]
|
|
33
|
+
def human_name = self.class.human_name
|
|
34
|
+
|
|
35
|
+
# @return [String]
|
|
36
|
+
def human_description = self.class.human_description
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -49,7 +49,7 @@ module Spree
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def exactly_one_owner
|
|
52
|
-
errors.add(:base, :exactly_one_of_cart_or_order, message:
|
|
52
|
+
errors.add(:base, :exactly_one_of_cart_or_order, message: I18n.t('spree.errors.messages.exactly_one_of_cart_or_order')) unless [order, cart].compact.one?
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -93,6 +93,12 @@ module Spree
|
|
|
93
93
|
record.save! if record.changed?
|
|
94
94
|
|
|
95
95
|
record.id
|
|
96
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
97
|
+
# A row saved from the owner's `after_save` fails inside the owner's own
|
|
98
|
+
# save, and a plain `save` swallows the exception — so its errors are
|
|
99
|
+
# copied onto the owner, or the caller learns only that something failed.
|
|
100
|
+
e.record.errors.full_messages.each { |message| errors.add(:base, message) }
|
|
101
|
+
raise
|
|
96
102
|
end
|
|
97
103
|
|
|
98
104
|
# Decode `*_ids` array preferences (`customer_group_ids`, `user_ids`,
|
data/app/models/spree/address.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Spree
|
|
|
7
7
|
include Spree::HasCustomFields
|
|
8
8
|
include Spree::Metadata
|
|
9
9
|
|
|
10
|
-
serialize :preferences,
|
|
10
|
+
serialize :preferences, coder: Spree::Metadata::HashSerializer, default: {}
|
|
11
11
|
|
|
12
12
|
NO_ZIPCODE_ISO_CODES ||= [
|
|
13
13
|
'AO', 'AG', 'AW', 'BS', 'BZ', 'BJ', 'BM', 'BO', 'BW', 'BF', 'BI', 'CM', 'CF', 'KM', 'CG',
|
|
@@ -27,6 +27,10 @@ module Spree
|
|
|
27
27
|
# those attributes depending of the logic of their applications
|
|
28
28
|
ADDRESS_FIELDS = %w(first_name last_name company address1 address2 city state postal_code country phone)
|
|
29
29
|
|
|
30
|
+
# The columns a delivery quote reads. A change to anything else (a phone
|
|
31
|
+
# number, the recipient's name) leaves the parcel going where it was.
|
|
32
|
+
DESTINATION_FIELDS = %w(address1 address2 city state_code postal_code country_code).freeze
|
|
33
|
+
|
|
30
34
|
# Beyond identity and ownership, three groups are excluded because they
|
|
31
35
|
# cannot tell two addresses apart. Latitude and longitude are derived from
|
|
32
36
|
# the address, so an entry whose geocoding has already run would otherwise
|
|
@@ -379,6 +383,12 @@ module Spree
|
|
|
379
383
|
attributes.except(*EXCLUDED_KEYS_FOR_COMPARISON)
|
|
380
384
|
end
|
|
381
385
|
|
|
386
|
+
# @return [Boolean] whether the last save moved this address somewhere
|
|
387
|
+
# else, as opposed to correcting who receives the parcel
|
|
388
|
+
def saved_change_to_destination?
|
|
389
|
+
saved_changes.keys.intersect?(DESTINATION_FIELDS)
|
|
390
|
+
end
|
|
391
|
+
|
|
382
392
|
# A country alone doesn't make an address — a new one is pre-filled with the
|
|
383
393
|
# store's default, so both the code and the association are ignored here.
|
|
384
394
|
def empty?
|
|
@@ -65,22 +65,8 @@ module Spree
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
# The owner's PERSISTED coupon code keeps its promotion in candidacy
|
|
69
|
-
# even before it ever applied — the discount activates on the exact
|
|
70
|
-
# recalculation where the cart first qualifies, and deactivates the
|
|
71
|
-
# same way (Shopify-parity for cart-level discount codes). In-memory
|
|
72
|
-
# assignments deliberately don't participate: unsaved codes belong to
|
|
73
|
-
# the explicit PromotionHandler::Coupon path.
|
|
74
68
|
def coupon_promotions
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
code = order.read_attribute(:coupon_code)
|
|
78
|
-
return [] if code.blank?
|
|
79
|
-
|
|
80
|
-
promotion = order.store.promotions.active.with_coupon_code(code)
|
|
81
|
-
return [] if promotion.nil? || promotion.usage_limit_exceeded?(order)
|
|
82
|
-
|
|
83
|
-
[promotion]
|
|
69
|
+
Spree::Promotion.held_by_saved_coupon_code(order)
|
|
84
70
|
end
|
|
85
71
|
|
|
86
72
|
# A coupon promotion that produced rows becomes an applied promotion —
|
data/app/models/spree/api_key.rb
CHANGED
|
@@ -44,7 +44,6 @@ module Spree
|
|
|
44
44
|
publishable? ? token : @plaintext_token
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
belongs_to :store, class_name: 'Spree::Store'
|
|
48
47
|
# Optional single-channel binding for publishable keys. A bound key
|
|
49
48
|
# server-assigns the request's channel (see Api::V3::ChannelResolution),
|
|
50
49
|
# making channel identity unforgeable instead of client-asserted via the
|
|
@@ -162,23 +161,23 @@ module Spree
|
|
|
162
161
|
return if invalid.empty?
|
|
163
162
|
|
|
164
163
|
errors.add(:scopes, :unknown_scopes, scopes: invalid.join(', '),
|
|
165
|
-
message:
|
|
164
|
+
message: I18n.t('spree.errors.messages.unknown_scopes', scopes: invalid.join(', ')))
|
|
166
165
|
end
|
|
167
166
|
|
|
168
167
|
def scopes_immutable
|
|
169
|
-
errors.add(:scopes, :api_key_scopes_immutable, message:
|
|
168
|
+
errors.add(:scopes, :api_key_scopes_immutable, message: I18n.t('spree.api_key_scopes_immutable'))
|
|
170
169
|
end
|
|
171
170
|
|
|
172
171
|
def channel_binding_valid
|
|
173
172
|
if secret?
|
|
174
|
-
errors.add(:channel, :api_key_channel_publishable_only, message:
|
|
173
|
+
errors.add(:channel, :api_key_channel_publishable_only, message: I18n.t('spree.api_key_channel_publishable_only'))
|
|
175
174
|
elsif channel && channel.store_id != store_id
|
|
176
|
-
errors.add(:channel, :api_key_channel_must_belong_to_store, message:
|
|
175
|
+
errors.add(:channel, :api_key_channel_must_belong_to_store, message: I18n.t('spree.api_key_channel_must_belong_to_store'))
|
|
177
176
|
end
|
|
178
177
|
end
|
|
179
178
|
|
|
180
179
|
def channel_immutable
|
|
181
|
-
errors.add(:channel, :api_key_channel_immutable, message:
|
|
180
|
+
errors.add(:channel, :api_key_channel_immutable, message: I18n.t('spree.api_key_channel_immutable'))
|
|
182
181
|
end
|
|
183
182
|
|
|
184
183
|
# Generates the token on creation. For publishable keys, stores the raw token
|
|
@@ -129,7 +129,7 @@ module Spree
|
|
|
129
129
|
# Password login is not available through an OIDC provider — the browser
|
|
130
130
|
# redirect is the only entry point.
|
|
131
131
|
def authenticate
|
|
132
|
-
failure(
|
|
132
|
+
failure(I18n.t('spree.errors.messages.oidc_requires_redirect'))
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
# Completes the login started by +#authorization_url+: exchanges the
|
|
@@ -138,13 +138,13 @@ module Spree
|
|
|
138
138
|
# @return [Spree::ServiceModule::Result]
|
|
139
139
|
def callback
|
|
140
140
|
code = params[:code]
|
|
141
|
-
return authentication_failure(
|
|
141
|
+
return authentication_failure(I18n.t('spree.errors.messages.oidc_missing_code')) if code.blank?
|
|
142
142
|
|
|
143
143
|
claims = verify_id_token(exchange_code_for_tokens(code).fetch('id_token'))
|
|
144
144
|
resolve_user(claims)
|
|
145
145
|
rescue StandardError => e
|
|
146
146
|
Rails.logger.error("[Spree] OIDC callback failed for #{config[:label] || config[:issuer]}: #{e.message}")
|
|
147
|
-
authentication_failure(
|
|
147
|
+
authentication_failure(I18n.t('spree.errors.messages.oidc_authentication_failed'))
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
# True when the last +#callback+ failed because no account here is
|
|
@@ -168,7 +168,7 @@ module Spree
|
|
|
168
168
|
# every member of a corporate tenant would become store staff.
|
|
169
169
|
def resolve_user(claims)
|
|
170
170
|
subject = claims['sub']
|
|
171
|
-
return authentication_failure(
|
|
171
|
+
return authentication_failure(I18n.t('spree.errors.messages.oidc_authentication_failed')) if subject.blank?
|
|
172
172
|
|
|
173
173
|
identity = Spree::UserIdentity.find_by(
|
|
174
174
|
provider: registry_key,
|
|
@@ -191,7 +191,7 @@ module Spree
|
|
|
191
191
|
|
|
192
192
|
def not_provisioned_failure
|
|
193
193
|
@account_not_provisioned = true
|
|
194
|
-
failure(
|
|
194
|
+
failure(I18n.t('spree.errors.messages.oidc_account_not_provisioned'))
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
# Only an email the provider asserts as verified may claim an existing
|