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
|
@@ -1,36 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Typed model preferences, each a Rails store accessor on the `preferences`
|
|
2
|
+
# JSON column — `secret_preferences` for a `:password` one (see
|
|
3
|
+
# {Spree::SecretPreferences}). Included by `Spree::Base`.
|
|
2
4
|
#
|
|
3
|
-
# A class including Preferable must implement #preferences which should return
|
|
4
|
-
# an object responding to .fetch(key), []=(key, val), and .delete(key).
|
|
5
|
-
#
|
|
6
|
-
# The generated writer method performs typecasting before assignment into the
|
|
7
|
-
# preferences object.
|
|
8
|
-
#
|
|
9
|
-
# Examples:
|
|
10
|
-
#
|
|
11
|
-
# # Spree::Base includes Preferable and defines preferences as a serialized
|
|
12
|
-
# # column.
|
|
13
5
|
# class Settings < Spree::Base
|
|
14
6
|
# preference :color, :string, default: 'red'
|
|
15
7
|
# preference :temperature, :integer, default: 21
|
|
16
8
|
# end
|
|
17
9
|
#
|
|
18
10
|
# s = Settings.new
|
|
19
|
-
# s.preferred_color
|
|
20
|
-
# s.preferred_temperature #
|
|
21
|
-
#
|
|
22
|
-
# s.
|
|
23
|
-
# s.preferred_color # => 'blue'
|
|
24
|
-
#
|
|
25
|
-
# # Typecasting is performed on assignment
|
|
26
|
-
# s.preferred_temperature = '24'
|
|
27
|
-
# s.preferred_temperature # => 24
|
|
28
|
-
#
|
|
29
|
-
# # Modifications have been made to the .preferences hash
|
|
30
|
-
# s.preferences #=> {color: 'blue', temperature: 24}
|
|
31
|
-
#
|
|
32
|
-
# # Save the changes. All handled by activerecord
|
|
33
|
-
# s.save!
|
|
11
|
+
# s.preferred_color # => 'red'
|
|
12
|
+
# s.preferred_temperature = '24' # cast on assignment
|
|
13
|
+
# s.preferred_temperature # => 24
|
|
14
|
+
# s.preferences # => { 'color' => 'red', 'temperature' => 24 }
|
|
34
15
|
|
|
35
16
|
require 'spree/core/preferences/preferable_class_methods'
|
|
36
17
|
|
|
@@ -38,47 +19,56 @@ module Spree::Preferences::Preferable
|
|
|
38
19
|
extend ActiveSupport::Concern
|
|
39
20
|
|
|
40
21
|
included do
|
|
41
|
-
serialize :preferences,
|
|
22
|
+
serialize :preferences, coder: Spree::Metadata::HashSerializer
|
|
42
23
|
extend Spree::Preferences::PreferableClassMethods
|
|
43
24
|
end
|
|
44
25
|
|
|
26
|
+
# JSON keeps decimals and times as strings; the declared type turns them
|
|
27
|
+
# back into a BigDecimal and a time. Anything else, or a string that does
|
|
28
|
+
# not parse, comes back as stored.
|
|
29
|
+
#
|
|
30
|
+
# @param value [Object] a value as JSON stores it
|
|
31
|
+
# @param type [Symbol] the preference's declared type
|
|
32
|
+
# @return [Object]
|
|
33
|
+
def self.restore_value(value, type)
|
|
34
|
+
return value unless value.is_a?(String)
|
|
35
|
+
|
|
36
|
+
case type
|
|
37
|
+
when :decimal then BigDecimal(value, exception: false) || value
|
|
38
|
+
when :datetime then Time.zone.iso8601(value)
|
|
39
|
+
else value
|
|
40
|
+
end
|
|
41
|
+
rescue ArgumentError
|
|
42
|
+
value
|
|
43
|
+
end
|
|
44
|
+
|
|
45
45
|
def get_preference(name)
|
|
46
46
|
has_preference! name
|
|
47
|
-
|
|
47
|
+
public_send(:"preferred_#{name}")
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def set_preference(name, value)
|
|
51
51
|
has_preference! name
|
|
52
|
-
|
|
52
|
+
public_send(:"preferred_#{name}=", value)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def preference_type(name)
|
|
56
|
-
|
|
57
|
-
send self.class.preference_type_getter_method(name)
|
|
56
|
+
preference_definition(name)[:type]
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def preference_default(name)
|
|
61
|
-
|
|
62
|
-
send self.class.preference_default_getter_method(name)
|
|
60
|
+
instance_exec(&preference_definition(name)[:default])
|
|
63
61
|
end
|
|
64
62
|
|
|
65
63
|
def preference_deprecated(name)
|
|
66
|
-
|
|
67
|
-
send(self.class.preference_deprecated_getter_method(name))
|
|
64
|
+
preference_definition(name)[:deprecated]
|
|
68
65
|
end
|
|
69
66
|
|
|
70
67
|
# Whether this preference is written by Spree rather than supplied by the
|
|
71
68
|
# operator — a webhook secret a provider issues, say. Kept out of the
|
|
72
69
|
# schema, so nothing offers it as a field to fill in.
|
|
73
70
|
def preference_internal(name)
|
|
74
|
-
|
|
75
|
-
getter = self.class.preference_internal_getter_method(name)
|
|
76
|
-
# A preference declared before this option existed has no such reader.
|
|
77
|
-
# Treated as not internal rather than raising, so one old declaration
|
|
78
|
-
# cannot take a whole class's preference schema down with it.
|
|
79
|
-
return false unless respond_to?(getter)
|
|
80
|
-
|
|
81
|
-
send(getter)
|
|
71
|
+
preference_definition(name)[:internal]
|
|
82
72
|
end
|
|
83
73
|
|
|
84
74
|
# The fixed set this preference's value must come from, when it has one.
|
|
@@ -87,14 +77,8 @@ module Spree::Preferences::Preferable
|
|
|
87
77
|
#
|
|
88
78
|
# @return [Array, nil]
|
|
89
79
|
def preference_choices(name)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# A preference declared before this option existed has no such reader.
|
|
93
|
-
# Treated as unconstrained rather than raising, so one old declaration
|
|
94
|
-
# cannot take a whole class's preference schema down with it.
|
|
95
|
-
return unless respond_to?(getter)
|
|
96
|
-
|
|
97
|
-
send(getter)
|
|
80
|
+
choices = preference_definition(name)[:choices]
|
|
81
|
+
choices.respond_to?(:call) ? choices.call : choices
|
|
98
82
|
end
|
|
99
83
|
|
|
100
84
|
def has_preference!(name)
|
|
@@ -102,57 +86,125 @@ module Spree::Preferences::Preferable
|
|
|
102
86
|
end
|
|
103
87
|
|
|
104
88
|
def has_preference?(name)
|
|
105
|
-
|
|
89
|
+
self.class.preference_definitions.key?(name.to_sym)
|
|
106
90
|
end
|
|
107
91
|
|
|
108
92
|
def defined_preferences
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
93
|
+
self.class.preference_definitions.keys
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def default_preferences
|
|
97
|
+
defined_preferences.index_with { |name| preference_default(name) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Every preference value, secrets included — what `preferences` alone held
|
|
101
|
+
# before secrets moved to their own column. Declared preferences read
|
|
102
|
+
# through their `preferred_*` reader, so a default applies and decimals and
|
|
103
|
+
# times come back typed rather than in the string form JSON stores.
|
|
104
|
+
#
|
|
105
|
+
# @return [ActiveSupport::HashWithIndifferentAccess]
|
|
106
|
+
def preference_values
|
|
107
|
+
(preferences || {}).to_h.with_indifferent_access.merge(defined_preferences.index_with { |name| get_preference(name) })
|
|
116
108
|
end
|
|
117
109
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
110
|
+
# The stored value of a preference, before its default applies. A secret
|
|
111
|
+
# found in `preferences` wins over the encrypted column: it is either a
|
|
112
|
+
# newer value assigned as part of a whole hash, waiting for the save that
|
|
113
|
+
# moves it across, or one the upgrade has not moved yet — the writer always
|
|
114
|
+
# removes that copy, so it is never stale.
|
|
115
|
+
#
|
|
116
|
+
# @param name [Symbol, String]
|
|
117
|
+
# @return [Object] the stored value, or the block's result when nothing is stored
|
|
118
|
+
def stored_preference(name)
|
|
119
|
+
if secret_preference?(name)
|
|
120
|
+
(preferences || {}).fetch(name) { (secret_preferences || {}).fetch(name) { return yield } }
|
|
121
|
+
else
|
|
122
|
+
(preferences || {}).fetch(name) { yield }
|
|
122
123
|
end
|
|
123
124
|
end
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
# Fills in the declared default of every preference this record has no
|
|
127
|
+
# stored value for, so a preference added to the class after the row was
|
|
128
|
+
# saved never reads as missing and the raw `preferences` hash always holds
|
|
129
|
+
# every value. Secrets are left out: their defaults are read through the
|
|
130
|
+
# `preferred_*` reader, never copied into storage.
|
|
131
|
+
#
|
|
132
|
+
# Filling in a default is not a change to a stored record: a loaded record
|
|
133
|
+
# stays clean, so `with_lock` still accepts it, and the defaults are written
|
|
134
|
+
# with its next real save.
|
|
135
|
+
def backfill_default_preferences
|
|
136
|
+
secrets = self.class.secret_preference_names
|
|
137
|
+
missing = defined_preferences.reject { |name| preferences.key?(name) || secrets.include?(name) }
|
|
138
|
+
return if missing.empty?
|
|
139
|
+
|
|
140
|
+
already_changed = attribute_changed?(:preferences)
|
|
141
|
+
self.preferences = preferences.merge(missing.index_with { |name| stored_default(name) })
|
|
142
|
+
clear_attribute_change(:preferences) if persisted? && !already_changed
|
|
131
143
|
end
|
|
132
144
|
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
# Names of the preferences the last save changed, secrets included.
|
|
146
|
+
#
|
|
147
|
+
# @return [Array<Symbol>]
|
|
148
|
+
def previously_changed_preference_names
|
|
149
|
+
defined_preferences.select { |name| public_send(:"saved_change_to_preferred_#{name}?") }
|
|
135
150
|
end
|
|
136
151
|
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
private
|
|
153
|
+
|
|
154
|
+
def preference_definition(name)
|
|
155
|
+
has_preference! name
|
|
156
|
+
self.class.preference_definitions[name.to_sym]
|
|
139
157
|
end
|
|
140
158
|
|
|
141
|
-
|
|
142
|
-
|
|
159
|
+
# A declared default in the form JSON stores, as the writer would store it,
|
|
160
|
+
# so a decimal default reads back as a BigDecimal and setting the same
|
|
161
|
+
# amount is not a change.
|
|
162
|
+
def stored_default(name)
|
|
163
|
+
default = preference_default(name)
|
|
164
|
+
type = preference_type(name)
|
|
165
|
+
return default if default.nil? || %i[decimal datetime].exclude?(type)
|
|
166
|
+
|
|
167
|
+
convert_preference_value(default, type, nullable: true).as_json
|
|
143
168
|
end
|
|
144
169
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
170
|
+
# Only a class that stores secrets can declare one, so the names say it all.
|
|
171
|
+
def secret_preference?(name)
|
|
172
|
+
self.class.secret_preference_names.include?(name.to_sym)
|
|
173
|
+
end
|
|
149
174
|
|
|
150
|
-
|
|
175
|
+
def cast_preference(name, value)
|
|
176
|
+
definition = preference_definition(name)
|
|
177
|
+
parse_on_set = definition[:parse_on_set]
|
|
178
|
+
if parse_on_set.is_a?(Proc)
|
|
179
|
+
# Procs that accept more than one arg opt into receiving the owning
|
|
180
|
+
# record so they can scope (e.g. `normalize_id_preference` rejecting
|
|
181
|
+
# cross-store IDs). `arity.abs > 1` covers both the `(value, owner)` and
|
|
182
|
+
# `(value, owner = nil)` shapes.
|
|
183
|
+
value = parse_on_set.arity.abs > 1 ? parse_on_set.call(value, self) : parse_on_set.call(value)
|
|
184
|
+
end
|
|
185
|
+
value = convert_preference_value(value, definition[:type], nullable: definition[:nullable])
|
|
186
|
+
# Decimals and times are kept in the string form JSON stores, so a record
|
|
187
|
+
# read back from the database and one just written compare equal and
|
|
188
|
+
# setting the same value is not a change. The reader restores them.
|
|
189
|
+
value = value.as_json if %i[decimal datetime].include?(definition[:type])
|
|
151
190
|
|
|
152
|
-
[
|
|
191
|
+
Spree::Deprecation.warn("`#{name}` is deprecated. #{definition[:deprecated]}") if definition[:deprecated]
|
|
192
|
+
value
|
|
153
193
|
end
|
|
154
194
|
|
|
155
|
-
|
|
195
|
+
# Assigns a new hash rather than calling the store accessor's writer, which
|
|
196
|
+
# skips writing nil to a missing key — leaving the default in force when nil
|
|
197
|
+
# was meant. Rails still sees no change when the value is the one stored.
|
|
198
|
+
def write_preference(name, value)
|
|
199
|
+
if secret_preference?(name)
|
|
200
|
+
# A copy assigned as part of a whole hash would otherwise outlive the
|
|
201
|
+
# value written here and be moved over it on save.
|
|
202
|
+
self.preferences = preferences.except(name) if preferences&.key?(name)
|
|
203
|
+
self.secret_preferences = (secret_preferences || {}).with_indifferent_access.merge(name => value)
|
|
204
|
+
else
|
|
205
|
+
self.preferences = (preferences || {}).with_indifferent_access.merge(name => value)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
156
208
|
|
|
157
209
|
def convert_preference_value(value, type, nullable: false)
|
|
158
210
|
case type
|
|
@@ -195,6 +247,8 @@ module Spree::Preferences::Preferable
|
|
|
195
247
|
case value.class.to_s
|
|
196
248
|
when 'Hash'
|
|
197
249
|
value
|
|
250
|
+
when 'ActionController::Parameters'
|
|
251
|
+
value.to_h
|
|
198
252
|
when 'String'
|
|
199
253
|
# only works with hashes whose keys are strings
|
|
200
254
|
JSON.parse value.gsub('=>', ':')
|
|
@@ -221,7 +275,7 @@ module Spree::Preferences::Preferable
|
|
|
221
275
|
value.to_time
|
|
222
276
|
end
|
|
223
277
|
# Kept as the `yyyy-MM-dd` string it arrived as rather than coerced to a
|
|
224
|
-
# Time. Preferences are
|
|
278
|
+
# Time. Preferences are stored as JSON, and what a date *means* depends on
|
|
225
279
|
# the store's timezone, which this method has no access to: coercing here
|
|
226
280
|
# would freeze the value against the server's zone and move the operator's
|
|
227
281
|
# deadline by hours. The reader owns that (see
|
|
@@ -1,169 +1,104 @@
|
|
|
1
1
|
module Spree::Preferences
|
|
2
2
|
module PreferableClassMethods
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
# Bumped by every declaration anywhere, so a class's cached view of its
|
|
4
|
+
# preferences is rebuilt when an ancestor gains one after the class was
|
|
5
|
+
# loaded — a decorator adding a secret to Spree::PaymentMethod, say.
|
|
6
|
+
@declarations = 0
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :declarations
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Every preference the class declares, inherited ones included, in
|
|
13
|
+
# declaration order — so an admin form can present them the way their
|
|
14
|
+
# author grouped them.
|
|
15
|
+
#
|
|
16
|
+
# @return [Hash{Symbol => Hash}] name => `{ type:, default:, deprecated:, internal:, choices:, nullable:, parse_on_set: }`
|
|
17
|
+
def preference_definitions
|
|
18
|
+
preference_declarations_cache[:definitions] ||= begin
|
|
19
|
+
inherited = superclass.respond_to?(:preference_definitions) ? superclass.preference_definitions : {}
|
|
20
|
+
inherited.merge(own_preference_definitions).freeze
|
|
11
21
|
end
|
|
12
22
|
end
|
|
13
23
|
|
|
24
|
+
# Each declared preference's type, known from the declarations alone —
|
|
25
|
+
# without building a record.
|
|
26
|
+
#
|
|
27
|
+
# @return [Hash{Symbol => Symbol}]
|
|
28
|
+
def declared_preference_types
|
|
29
|
+
preference_declarations_cache[:types] ||= preference_definitions.transform_values { |definition| definition[:type] }.freeze
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Names of the `:password` preferences, which a model including
|
|
33
|
+
# {Spree::SecretPreferences} stores encrypted in `secret_preferences`.
|
|
34
|
+
#
|
|
35
|
+
# @return [Array<Symbol>]
|
|
36
|
+
def secret_preference_names
|
|
37
|
+
preference_declarations_cache[:secrets] ||= declared_preference_types.filter_map { |name, type| name if type == :password }.freeze
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Declares a typed preference with a `preferred_<name>` reader and writer
|
|
41
|
+
# and a `prefers_<name>?` query. On a model, the value lives in the
|
|
42
|
+
# `preferences` column (`secret_preferences` for a `:password` one) as a
|
|
43
|
+
# Rails store accessor, which brings the change methods with it:
|
|
44
|
+
# `preferred_<name>_changed?`, `_change`, `_was`,
|
|
45
|
+
# `saved_change_to_preferred_<name>?` and `preferred_<name>_before_last_save`.
|
|
14
46
|
def preference(name, type, *args)
|
|
15
|
-
|
|
47
|
+
name = name.to_sym
|
|
48
|
+
secret = type == :password
|
|
49
|
+
if secret && !include?(Spree::SecretPreferences)
|
|
50
|
+
raise ArgumentError, "#{self.name} declares the secret preference `#{name}` but cannot encrypt it. " \
|
|
51
|
+
'Include Spree::SecretPreferences and add a `secret_preferences` text column to its table.'
|
|
52
|
+
end
|
|
53
|
+
|
|
16
54
|
options = args.extract_options!
|
|
17
55
|
options.assert_valid_keys(:default, :deprecated, :in, :internal, :nullable, :parse_on_set)
|
|
18
56
|
default = options[:default]
|
|
19
57
|
default = -> { options[:default] } unless default.is_a?(Proc)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if parse_on_set.is_a?(Proc)
|
|
41
|
-
# Procs that accept more than one arg opt into receiving the
|
|
42
|
-
# owning record so they can scope (e.g. `normalize_id_preference`
|
|
43
|
-
# rejecting cross-store IDs). `arity.abs > 1` covers both the
|
|
44
|
-
# `(value, owner)` and `(value, owner = nil)` shapes.
|
|
45
|
-
value = parse_on_set.arity.abs > 1 ? parse_on_set.call(value, self) : parse_on_set.call(value)
|
|
46
|
-
end
|
|
47
|
-
value = convert_preference_value(value, type, nullable: nullable)
|
|
48
|
-
self.preferences = {} if preferences.nil?
|
|
49
|
-
preferences[name] = value
|
|
50
|
-
|
|
51
|
-
Spree::Deprecation.warn("`#{name}` is deprecated. #{deprecated}") if deprecated
|
|
52
|
-
|
|
53
|
-
# If this is an activerecord object, we need to inform
|
|
54
|
-
# ActiveRecord::Dirty that this value has changed, since this is an
|
|
55
|
-
# in-place update to the preferences hash.
|
|
56
|
-
preferences_will_change! if respond_to?(:preferences_will_change!)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
define_method preference_default_getter_method(name), &default
|
|
60
|
-
|
|
61
|
-
define_method preference_type_getter_method(name) do
|
|
62
|
-
type
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
define_method preference_deprecated_getter_method(name) do
|
|
66
|
-
deprecated
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Whether this preference is the system's to write rather than the
|
|
70
|
-
# operator's to supply — a value a provider hands back to us, not one
|
|
71
|
-
# anybody could know in advance.
|
|
72
|
-
define_method preference_internal_getter_method(name) do
|
|
73
|
-
internal
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
define_method preference_choices_getter_method(name) do
|
|
77
|
-
choices.respond_to?(:call) ? choices.call : choices
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
define_method prefers_query_method(name) do
|
|
81
|
-
preferences.fetch(name).to_b
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
define_method preference_change_method(name) do
|
|
85
|
-
preference_change(name, changes) if respond_to?(:changes)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
define_method preference_was_method(name) do
|
|
89
|
-
return unless respond_to?(:changes)
|
|
90
|
-
|
|
91
|
-
preference_change(name, changes)&.first || get_preference(name)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
define_method preference_changed_method(name) do
|
|
95
|
-
respond_to?(:changes) && preference_change(name, changes).present?
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
define_method preference_previous_change_method(name) do
|
|
99
|
-
preference_change(name, previous_changes) if respond_to?(:previous_changes)
|
|
58
|
+
own_preference_definitions[name] = {
|
|
59
|
+
type: type,
|
|
60
|
+
default: default,
|
|
61
|
+
deprecated: options[:deprecated],
|
|
62
|
+
# Whether the system writes the value rather than the operator — a
|
|
63
|
+
# value a provider hands back, kept out of every admin form.
|
|
64
|
+
internal: options[:internal],
|
|
65
|
+
# The fixed set a value must come from; turns a text box into a picker.
|
|
66
|
+
choices: options[:in],
|
|
67
|
+
nullable: options[:nullable],
|
|
68
|
+
parse_on_set: options[:parse_on_set]
|
|
69
|
+
}
|
|
70
|
+
PreferableClassMethods.declarations += 1
|
|
71
|
+
|
|
72
|
+
store_accessor(secret ? :secret_preferences : :preferences, name, prefix: :preferred)
|
|
73
|
+
|
|
74
|
+
# Overrides the store accessor's reader and writer, which treat a missing
|
|
75
|
+
# key as nil; here a missing key means the declared default.
|
|
76
|
+
define_method(:"preferred_#{name}") do
|
|
77
|
+
Spree::Preferences::Preferable.restore_value(stored_preference(name) { stored_default(name) }, type)
|
|
100
78
|
end
|
|
101
79
|
|
|
102
|
-
define_method
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
preference_change(name, previous_changes)&.first
|
|
80
|
+
define_method(:"preferred_#{name}=") do |value|
|
|
81
|
+
write_preference(name, cast_preference(name, value))
|
|
106
82
|
end
|
|
107
83
|
|
|
108
|
-
define_method
|
|
109
|
-
|
|
84
|
+
define_method(:"prefers_#{name}?") do
|
|
85
|
+
stored_preference(name) { raise KeyError, "key not found: #{name.inspect}" }.to_b
|
|
110
86
|
end
|
|
111
87
|
end
|
|
112
88
|
|
|
113
|
-
|
|
114
|
-
"preferred_#{name}".to_sym
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def preference_setter_method(name)
|
|
118
|
-
"preferred_#{name}=".to_sym
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def preference_default_getter_method(name)
|
|
122
|
-
"preferred_#{name}_default".to_sym
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def preference_deprecated_getter_method(name)
|
|
126
|
-
"preferred_#{name}_deprecated".to_sym
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def preference_internal_getter_method(name)
|
|
130
|
-
"preferred_#{name}_internal".to_sym
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def preference_choices_getter_method(name)
|
|
134
|
-
"preferred_#{name}_choices".to_sym
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def preference_type_getter_method(name)
|
|
138
|
-
"preferred_#{name}_type".to_sym
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def prefers_query_method(name)
|
|
142
|
-
"prefers_#{name}?".to_sym
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def preference_change_method(name)
|
|
146
|
-
"preferred_#{name}_change".to_sym
|
|
147
|
-
end
|
|
89
|
+
private
|
|
148
90
|
|
|
149
|
-
def
|
|
150
|
-
|
|
91
|
+
def own_preference_definitions
|
|
92
|
+
@own_preference_definitions ||= {}
|
|
151
93
|
end
|
|
152
94
|
|
|
153
|
-
def
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def preference_previous_was_method(name)
|
|
162
|
-
"preferred_#{name}_previously_was".to_sym
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def preference_previous_changed_method(name)
|
|
166
|
-
"preferred_#{name}_previously_changed?".to_sym
|
|
95
|
+
def preference_declarations_cache
|
|
96
|
+
generation = PreferableClassMethods.declarations
|
|
97
|
+
unless @preference_declarations_generation == generation
|
|
98
|
+
@preference_declarations_generation = generation
|
|
99
|
+
@preference_declarations_cache = {}
|
|
100
|
+
end
|
|
101
|
+
@preference_declarations_cache
|
|
167
102
|
end
|
|
168
103
|
end
|
|
169
104
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require_relative 'preview_data'
|
|
2
|
+
|
|
3
|
+
# Preview Spree import emails at /rails/mailers/spree/import
|
|
4
|
+
class Spree::ImportPreview < ActionMailer::Preview
|
|
5
|
+
include Spree::PreviewData::LocaleParam
|
|
6
|
+
|
|
7
|
+
def import_done
|
|
8
|
+
Spree::ImportMailer.import_done(Spree::Import.where.not(user_id: nil).last)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Tax
|
|
3
|
+
# A tax engine did not produce tax for a sale. Providers raise one of the
|
|
4
|
+
# two subclasses below; core rescues this and refuses the sale rather than
|
|
5
|
+
# completing one whose tax nobody computed — charging nothing because nobody
|
|
6
|
+
# could be asked is the error a merchant discovers in a filing.
|
|
7
|
+
#
|
|
8
|
+
# Split because a customer can only act on one of them. Retrying an outage
|
|
9
|
+
# may work; retrying a postcode the engine rejects never will, and telling
|
|
10
|
+
# someone to try again is worse than telling them nothing.
|
|
11
|
+
#
|
|
12
|
+
# The parallel is {Spree::Pricing::PriceResolution::ProviderUnavailable},
|
|
13
|
+
# which refuses a sale whose prices could not be confirmed. Neither is for a
|
|
14
|
+
# provider a merchant switched off: that is configuration, not failure, and
|
|
15
|
+
# an unconfigured engine simply has no opinion and writes no rows.
|
|
16
|
+
class ProviderError < StandardError
|
|
17
|
+
# @return [String, nil] which engine failed, for the report
|
|
18
|
+
attr_reader :provider_key
|
|
19
|
+
|
|
20
|
+
def initialize(message = nil, provider_key: nil)
|
|
21
|
+
@provider_key = provider_key
|
|
22
|
+
super(message)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The engine could not be asked — unreachable, timed out, or answering with
|
|
27
|
+
# a fault of its own. Nothing is known about the tax, and trying again
|
|
28
|
+
# later may well succeed, so the customer is told to retry.
|
|
29
|
+
class ProviderUnavailable < ProviderError; end
|
|
30
|
+
|
|
31
|
+
# The engine was asked and said no: an address it will not accept, a
|
|
32
|
+
# misconfigured company code, a request it rejects. The service is working,
|
|
33
|
+
# so retrying changes nothing — what the engine said is the useful part and
|
|
34
|
+
# core passes its message on rather than inventing one.
|
|
35
|
+
class CalculationRefused < ProviderError; end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/spree/core/version.rb
CHANGED