spree_core 6.0.0.beta4 → 6.0.0.rc2
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 +5 -5
- 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/search_provider/index_job.rb +4 -0
- data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
- data/app/jobs/spree/uploads/purge_unattached_job.rb +49 -0
- data/app/mailers/spree/admin_user_mailer.rb +4 -19
- data/app/mailers/spree/base_mailer.rb +97 -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 +15 -1
- data/app/models/concerns/spree/api_typed.rb +91 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +41 -13
- data/app/models/concerns/spree/calculator/currency_amounts.rb +1 -1
- data/app/models/concerns/spree/calculator/tiers.rb +63 -0
- data/app/models/concerns/spree/canonical_decimals.rb +61 -0
- data/app/models/concerns/spree/customer_methods.rb +23 -18
- data/app/models/concerns/spree/derived_number.rb +11 -13
- 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/json_schema.rb +139 -0
- data/app/models/concerns/spree/preference_schema.rb +62 -32
- data/app/models/concerns/spree/product_scopes.rb +16 -2
- 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 +8 -4
- data/app/models/concerns/spree/purchase/purchase_order.rb +9 -4
- data/app/models/concerns/spree/purchase/store_credits.rb +36 -1
- data/app/models/concerns/spree/purchase/totals.rb +49 -8
- data/app/models/concerns/spree/ransackable_attributes.rb +46 -0
- 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/signed_id_attachments.rb +54 -0
- data/app/models/concerns/spree/single_store_resource.rb +1 -1
- data/app/models/concerns/spree/stores/email_branding.rb +32 -0
- data/app/models/concerns/spree/stores/markets.rb +5 -2
- data/app/models/concerns/spree/type_labels.rb +38 -0
- data/app/models/concerns/spree/typed_adjustment_line.rb +5 -6
- data/app/models/concerns/spree/typed_associations.rb +12 -18
- data/app/models/concerns/spree/vat_price_calculation.rb +5 -6
- data/app/models/spree/address.rb +12 -2
- data/app/models/spree/adjusters/largest_remainder.rb +14 -1
- data/app/models/spree/adjusters/promotion.rb +4 -19
- data/app/models/spree/allowed_origin.rb +3 -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 +99 -22
- data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
- data/app/models/spree/calculator/flat_rate.rb +3 -5
- data/app/models/spree/calculator/flexi_rate.rb +4 -6
- data/app/models/spree/calculator/percent_on_line_item.rb +2 -2
- data/app/models/spree/calculator/price_sack.rb +5 -7
- data/app/models/spree/calculator/shipping/digital_delivery.rb +3 -5
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +7 -7
- data/app/models/spree/calculator/shipping/flat_rate.rb +5 -7
- data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -6
- data/app/models/spree/calculator/shipping/per_item.rb +3 -5
- data/app/models/spree/calculator/shipping/price_sack.rb +5 -7
- data/app/models/spree/calculator/tiered_flat_rate.rb +7 -24
- data/app/models/spree/calculator/tiered_percent.rb +7 -25
- data/app/models/spree/cart.rb +78 -18
- data/app/models/spree/catalog.rb +1 -0
- data/app/models/spree/catalog_price.rb +0 -7
- data/app/models/spree/catalog_price_tier.rb +0 -8
- data/app/models/spree/category.rb +3 -1
- data/app/models/spree/channel/gating.rb +3 -2
- data/app/models/spree/channel.rb +6 -4
- data/app/models/spree/claim.rb +22 -1
- data/app/models/spree/claim_line_item.rb +41 -6
- data/app/models/spree/collection.rb +2 -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 +7 -4
- 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/category_rule.rb +2 -5
- data/app/models/spree/commission_rules/item_total_rule.rb +2 -2
- data/app/models/spree/commission_rules/product_rule.rb +1 -1
- data/app/models/spree/commission_rules/seller_rule.rb +2 -5
- data/app/models/spree/company.rb +1 -0
- 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 +20 -35
- data/app/models/spree/custom_fields/boolean.rb +1 -1
- data/app/models/spree/custom_fields/json.rb +1 -1
- data/app/models/spree/customer_group.rb +1 -0
- data/app/models/spree/data_feed/google.rb +0 -2
- data/app/models/spree/data_request.rb +2 -1
- data/app/models/spree/delivery_method.rb +36 -14
- data/app/models/spree/delivery_method_rule.rb +4 -11
- data/app/models/spree/delivery_method_rules/channel_rule.rb +2 -5
- data/app/models/spree/delivery_method_rules/item_total_rule.rb +2 -2
- data/app/models/spree/delivery_profile.rb +1 -0
- data/app/models/spree/delivery_rate.rb +7 -8
- data/app/models/spree/delivery_rate_provider/base.rb +1 -0
- data/app/models/spree/delivery_zone.rb +1 -2
- 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 +16 -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 +3 -3
- data/app/models/spree/fee.rb +4 -2
- data/app/models/spree/file_upload.rb +155 -0
- data/app/models/spree/fulfillment.rb +66 -30
- data/app/models/spree/fulfillment_changer.rb +2 -2
- data/app/models/spree/fulfillment_group.rb +6 -1
- 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 +6 -0
- data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
- data/app/models/spree/gateway.rb +6 -2
- data/app/models/spree/gift_card.rb +18 -8
- data/app/models/spree/gift_card_batch.rb +3 -5
- data/app/models/spree/import.rb +5 -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 +9 -19
- data/app/models/spree/invitation.rb +5 -5
- data/app/models/spree/line_item.rb +15 -4
- data/app/models/spree/locale.rb +11 -8
- data/app/models/spree/market.rb +8 -2
- data/app/models/spree/media.rb +1 -9
- data/app/models/spree/newsletter_subscriber.rb +4 -0
- data/app/models/spree/option_type.rb +2 -1
- data/app/models/spree/option_value.rb +2 -1
- data/app/models/spree/order.rb +60 -142
- data/app/models/spree/order_group.rb +18 -4
- 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 +2 -2
- data/app/models/spree/payment/gateway_options.rb +15 -53
- data/app/models/spree/payment/processing.rb +2 -4
- data/app/models/spree/payment.rb +21 -13
- data/app/models/spree/payment_method/check.rb +2 -0
- data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
- data/app/models/spree/payment_method/store_credit.rb +15 -13
- data/app/models/spree/payment_method.rb +78 -6
- 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/policy.rb +1 -0
- data/app/models/spree/price.rb +17 -14
- data/app/models/spree/price_adjustment_tier.rb +4 -0
- data/app/models/spree/price_list.rb +14 -9
- data/app/models/spree/price_rules/channel_rule.rb +3 -6
- data/app/models/spree/price_rules/customer_group_rule.rb +3 -6
- data/app/models/spree/price_rules/market_rule.rb +2 -5
- data/app/models/spree/price_rules/user_rule.rb +1 -3
- data/app/models/spree/product/channels.rb +9 -2
- data/app/models/spree/product.rb +54 -30
- data/app/models/spree/product_submission.rb +0 -12
- data/app/models/spree/product_type.rb +1 -1
- data/app/models/spree/promotion/actions/create_line_items.rb +28 -18
- data/app/models/spree/promotion/rules/category.rb +1 -5
- data/app/models/spree/promotion/rules/channel.rb +2 -9
- data/app/models/spree/promotion/rules/country.rb +3 -8
- data/app/models/spree/promotion/rules/currency.rb +1 -5
- data/app/models/spree/promotion/rules/customer_group.rb +2 -9
- data/app/models/spree/promotion/rules/first_order.rb +0 -4
- data/app/models/spree/promotion/rules/item_total.rb +6 -10
- data/app/models/spree/promotion/rules/market.rb +2 -9
- data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
- data/app/models/spree/promotion/rules/option_value.rb +2 -6
- data/app/models/spree/promotion/rules/product.rb +1 -5
- 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 +36 -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 +3 -0
- data/app/models/spree/quantity_rule.rb +1 -1
- data/app/models/spree/refund.rb +41 -18
- data/app/models/spree/return.rb +31 -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 +2 -1
- data/app/models/spree/search_provider/base.rb +4 -0
- data/app/models/spree/seller.rb +10 -2
- data/app/models/spree/seller_payout.rb +8 -0
- data/app/models/spree/seller_requirement.rb +5 -12
- data/app/models/spree/seller_requirement_custom_field.rb +1 -2
- data/app/models/spree/seller_requirement_submission.rb +3 -2
- data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
- data/app/models/spree/seller_transfer.rb +6 -0
- data/app/models/spree/shipping_label.rb +2 -0
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/estimator.rb +1 -1
- 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 +3 -1
- data/app/models/spree/stock_location.rb +1 -0
- 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 +6 -2
- data/app/models/spree/store.rb +70 -19
- data/app/models/spree/store_credit.rb +13 -13
- data/app/models/spree/store_credit_event.rb +5 -5
- data/app/models/spree/supplier.rb +1 -0
- data/app/models/spree/tax_category.rb +1 -0
- data/app/models/spree/tax_exemption_certificate.rb +3 -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 +70 -41
- data/app/models/spree/tax_provider/recorded_share.rb +114 -0
- data/app/models/spree/tax_rate.rb +30 -8
- data/app/models/spree/taxon.rb +1 -1
- data/app/models/spree/taxonomy.rb +0 -1
- data/app/models/spree/update_status.rb +25 -0
- data/app/models/spree/variant.rb +10 -12
- data/app/models/spree/variant_media.rb +1 -1
- data/app/models/spree/webhook_delivery.rb +1 -0
- data/app/models/spree/webhook_endpoint.rb +46 -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 +10 -6
- data/app/presenters/spree/csv/price_list_price_presenter.rb +3 -8
- data/app/presenters/spree/csv/product_variant_presenter.rb +59 -20
- data/app/presenters/spree/csv/purchase_order_item_presenter.rb +1 -3
- 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 +22 -8
- 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 +77 -34
- data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
- data/app/services/spree/locales.rb +43 -10
- 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 +5 -4
- data/app/services/spree/orders/discounts/create.rb +14 -11
- 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/prices/bulk_upsert.rb +32 -25
- 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/seeds/stores.rb +1 -1
- 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 +23 -25
- data/app/services/spree/update_check.rb +160 -0
- data/app/services/spree/uploads/assign_stores.rb +92 -0
- data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
- data/app/validators/spree/attachment_content_type_validator.rb +19 -2
- data/app/validators/spree/locale_codes_validator.rb +22 -0
- 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 +53 -49
- 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/capture.rb +20 -5
- data/app/workflows/spree/payments/process.rb +5 -3
- 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/price_lists/update.rb +4 -4
- 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/create.rb +3 -1
- 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 +7 -7
- 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/create.rb +3 -1
- 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/currency_exponents.json +180 -0
- 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/content_locales.rb +9 -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 +47 -505
- 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/db/migrate/20261009000001_change_money_columns_to_decimal_19_4.rb +116 -0
- data/db/migrate/20261010000001_add_store_id_to_active_storage_blobs.rb +12 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
- data/lib/generators/spree/dummy/templates/rails/test.rb +26 -0
- data/lib/spree/checkout/default_requirements.rb +37 -13
- data/lib/spree/core/configuration.rb +19 -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 +72 -165
- data/lib/spree/core/permissions/default_catalog.rb +5 -0
- data/lib/spree/core/preferences/invalid_preferences.rb +18 -0
- data/lib/spree/core/preferences/json_conversion.rb +186 -0
- data/lib/spree/core/preferences/masking.rb +55 -2
- data/lib/spree/core/preferences/preferable.rb +343 -103
- data/lib/spree/core/preferences/preferable_class_methods.rb +188 -136
- data/lib/spree/core/previews/import_preview.rb +10 -0
- data/lib/spree/core/ransack_configuration.rb +13 -2
- data/lib/spree/core/tax/provider_error.rb +37 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +91 -373
- 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/iso_data.rb +7 -0
- data/lib/spree/localized_number.rb +6 -0
- data/lib/spree/migrations.rb +4 -1
- data/lib/spree/money.rb +196 -15
- data/lib/spree/reporting/adapters/base.rb +7 -6
- 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/registry.rb +13 -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/product_factory.rb +9 -0
- 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 +67 -1
- data/lib/spree/uploads.rb +101 -0
- data/lib/tasks/categories_and_collections.rake +3 -3
- data/lib/tasks/core.rake +6 -4
- data/lib/tasks/markets.rake +2 -1
- data/lib/tasks/media.rake +3 -3
- data/lib/tasks/migrate_users_to_customers.rake +23 -18
- data/lib/tasks/money.rake +46 -0
- data/lib/tasks/package_types.rake +1 -1
- data/lib/tasks/preferences_json.rake +101 -0
- data/lib/tasks/seller_sample_data.rake +2 -2
- data/lib/tasks/store_settings.rake +13 -6
- data/lib/tasks/typed_adjustments_migration.rake +1 -1
- data/lib/tasks/uploads.rake +19 -0
- data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
- metadata +219 -46
- data/app/jobs/spree/products/touch_categories_job.rb +0 -15
- data/app/jobs/spree/products/touch_taxons_job.rb +0 -13
- 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
|
@@ -158,7 +158,7 @@ module Spree
|
|
|
158
158
|
next if value.blank? || Spree.actor_classes.include?(value)
|
|
159
159
|
|
|
160
160
|
errors.add(type_column, :unregistered_actor,
|
|
161
|
-
message:
|
|
161
|
+
message: I18n.t('spree.errors.messages.unregistered_actor'))
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
164
|
|
|
@@ -4,12 +4,21 @@ module Spree
|
|
|
4
4
|
|
|
5
5
|
include Spree::Actor
|
|
6
6
|
include Spree::PrefixedId
|
|
7
|
+
include Spree::SignedIdAttachments
|
|
7
8
|
include Spree::UserRoles
|
|
8
9
|
include Spree::RansackableAttributes
|
|
9
10
|
|
|
10
11
|
included do
|
|
11
12
|
has_prefix_id :adm
|
|
12
13
|
|
|
14
|
+
publishes_events 'admin_user.password_reset', 'seller_user.password_reset'
|
|
15
|
+
# Carry a live reset token and have no external consumer: core sends
|
|
16
|
+
# these emails itself, so no webhook endpoint ever receives them.
|
|
17
|
+
publishes_event 'admin_user.password_reset_requested',
|
|
18
|
+
serializer: 'Spree::Api::V3::PasswordResetRequestedEventSerializer', webhook: false
|
|
19
|
+
publishes_event 'seller_user.password_reset_requested',
|
|
20
|
+
serializer: 'Spree::Api::V3::PasswordResetRequestedEventSerializer', webhook: false
|
|
21
|
+
|
|
13
22
|
has_person_name
|
|
14
23
|
|
|
15
24
|
normalizes :email, :first_name, :last_name, with: ->(value) { value&.to_s&.squish&.presence }
|
|
@@ -58,6 +67,7 @@ module Spree
|
|
|
58
67
|
|
|
59
68
|
# Attachments
|
|
60
69
|
has_one_attached :avatar, service: Spree.public_storage_service_name
|
|
70
|
+
signed_id_attachments :avatar
|
|
61
71
|
|
|
62
72
|
validates :avatar, content_type: Rails.application.config.active_storage.web_image_content_types
|
|
63
73
|
|
|
@@ -128,10 +138,14 @@ module Spree
|
|
|
128
138
|
# @param model [Class]
|
|
129
139
|
# @param name [Symbol] the acted_by association
|
|
130
140
|
def clear_actor(model, name)
|
|
141
|
+
columns = Spree::ActedBy.columns_for(name, nil)
|
|
142
|
+
# Append-only tables (revisions) have no updated_at to stamp.
|
|
143
|
+
columns = columns.merge(updated_at: Time.current) if model.column_names.include?('updated_at')
|
|
144
|
+
|
|
131
145
|
model.
|
|
132
146
|
where(:"#{name}_id" => id).
|
|
133
147
|
where(:"#{name}_type" => [self.class.polymorphic_name, nil]).
|
|
134
|
-
update_all(
|
|
148
|
+
update_all(columns)
|
|
135
149
|
end
|
|
136
150
|
end
|
|
137
151
|
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Wire shorthand for the plain-Ruby strategy classes a merchant picks from a
|
|
3
|
+
# registry (fulfillment, delivery-rate, pickup-point, payout and digital
|
|
4
|
+
# asset providers, order routing strategies). Records keep storing the class
|
|
5
|
+
# name; the API reads and writes this shorthand, so the contract never
|
|
6
|
+
# carries a Ruby constant.
|
|
7
|
+
#
|
|
8
|
+
# class Spree::FulfillmentProvider::Base
|
|
9
|
+
# extend Spree::ApiTyped
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# Spree::FulfillmentProvider::Manual.api_type # => "manual"
|
|
13
|
+
# SpreeEasyPost::FulfillmentProvider.api_type # => "easy_post"
|
|
14
|
+
module ApiTyped
|
|
15
|
+
# The demodulized + underscored leaf. A gem names its class after the
|
|
16
|
+
# family (`SpreeEasyPost::FulfillmentProvider`, `SpreeAcmeOms::Strategy`),
|
|
17
|
+
# where the leaf would collapse every gem to one value — those derive it
|
|
18
|
+
# from the gem's module instead, matching {Spree::Integration.api_type}.
|
|
19
|
+
# Any other class keeps its leaf, so two providers nested in one module
|
|
20
|
+
# never share a value. Override to keep the value stable across a rename.
|
|
21
|
+
#
|
|
22
|
+
# @return [String]
|
|
23
|
+
def api_type
|
|
24
|
+
leaf = name.to_s.demodulize
|
|
25
|
+
outer = name.to_s.deconstantize.demodulize.delete_prefix('Spree')
|
|
26
|
+
return leaf.underscore unless leaf == api_typed_family && outer.present?
|
|
27
|
+
|
|
28
|
+
outer.underscore
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The family's own name — the module its base class sits in
|
|
32
|
+
# (`FulfillmentProvider` for `Spree::FulfillmentProvider::Base`).
|
|
33
|
+
#
|
|
34
|
+
# @return [String, nil]
|
|
35
|
+
def api_typed_family
|
|
36
|
+
base = ancestors.reverse.find { |ancestor| ancestor.is_a?(Class) && ancestor.singleton_class.include?(Spree::ApiTyped) }
|
|
37
|
+
base&.name.to_s.deconstantize.demodulize.presence
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The registered class answering to a wire shorthand, or nil.
|
|
41
|
+
#
|
|
42
|
+
# @param registry [Array<Class, String>]
|
|
43
|
+
# @param api_type [String, nil]
|
|
44
|
+
# @return [Class, nil]
|
|
45
|
+
def self.find(registry, api_type)
|
|
46
|
+
return nil if api_type.blank?
|
|
47
|
+
|
|
48
|
+
Array(registry).map { |entry| entry.is_a?(Class) ? entry : entry.to_s.safe_constantize }.
|
|
49
|
+
compact.find { |klass| klass.api_type == api_type.to_s }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The registered class a record's stored class name points at, or nil
|
|
53
|
+
# when it is no longer registered (a typo, an uninstalled gem) — so a
|
|
54
|
+
# stored string is never constantized outside the registry.
|
|
55
|
+
#
|
|
56
|
+
# @param registry [Array<Class>]
|
|
57
|
+
# @param class_name [String, nil]
|
|
58
|
+
# @return [Class, nil]
|
|
59
|
+
def self.registered_class(registry, class_name)
|
|
60
|
+
return nil if class_name.blank?
|
|
61
|
+
|
|
62
|
+
Array(registry).find { |klass| klass.to_s == class_name.to_s }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The class name a record stores for a value arriving from the API. A
|
|
66
|
+
# registered shorthand becomes its class name; anything else passes through
|
|
67
|
+
# unchanged so the record's own inclusion validation rejects it.
|
|
68
|
+
#
|
|
69
|
+
# @param registry [Array<Class, String>]
|
|
70
|
+
# @param value [String, nil]
|
|
71
|
+
# @return [String, nil]
|
|
72
|
+
def self.class_name_for(registry, value)
|
|
73
|
+
return value if value.blank?
|
|
74
|
+
|
|
75
|
+
find(registry, value)&.to_s || value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The shorthand for a stored class name. A class that is no longer
|
|
79
|
+
# registered (an uninstalled gem) is shortened the same way rather than
|
|
80
|
+
# constantized.
|
|
81
|
+
#
|
|
82
|
+
# @param class_name [String, nil]
|
|
83
|
+
# @return [String, nil]
|
|
84
|
+
def self.api_type_for(class_name)
|
|
85
|
+
return nil if class_name.blank?
|
|
86
|
+
|
|
87
|
+
klass = class_name.to_s.safe_constantize
|
|
88
|
+
klass.respond_to?(:api_type) ? klass.api_type : class_name.to_s.demodulize.underscore
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -26,32 +26,60 @@ module Spree
|
|
|
26
26
|
def calculator_type=(calculator_type)
|
|
27
27
|
return if calculator_type.blank?
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
klass = registry.find { |k| k.api_type == calculator_type.to_s }
|
|
29
|
+
klass = registered_calculator_class(calculator_type)
|
|
31
30
|
self.calculator = klass.new if klass && !calculator.instance_of?(klass)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
# API v3 writer for the
|
|
35
|
-
# payload.
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
|
|
33
|
+
# API v3 writer for the `calculator: { type:, preferences: {} }`
|
|
34
|
+
# payload. A type the registry does not list is refused like a setting
|
|
35
|
+
# that does not match (`/calculator/type`); preferences are checked
|
|
36
|
+
# against the calculator's schema and written through its typed writers.
|
|
37
|
+
#
|
|
38
|
+
# @param attrs [Hash, ActionController::Parameters, nil]
|
|
39
|
+
# @param pointer [String] where the calculator sits in the request
|
|
40
|
+
# @raise [Spree::Preferences::InvalidPreferences] for a type the registry does not list, or
|
|
41
|
+
# preferences that do not match its schema
|
|
42
|
+
# @return [void]
|
|
43
|
+
def assign_calculator_attributes(attrs, pointer: '/calculator')
|
|
39
44
|
return if attrs.nil?
|
|
40
45
|
|
|
41
46
|
attrs = attrs.to_h.with_indifferent_access
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
type = attrs[:type].to_s
|
|
48
|
+
target = calculator
|
|
49
|
+
if type.present? && calculator&.class&.api_type != type
|
|
50
|
+
klass = registered_calculator_class(type)
|
|
51
|
+
unless klass
|
|
52
|
+
raise Spree::Preferences::InvalidPreferences.new(
|
|
53
|
+
[{ pointer: '/type', message: I18n.t('spree.errors.messages.unknown_calculator_type', type: type) }], prefix: pointer
|
|
54
|
+
)
|
|
55
|
+
end
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
target = klass.new
|
|
58
|
+
end
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
if attrs[:preferences].present?
|
|
61
|
+
# Preferences sent without a type land on the default calculator
|
|
62
|
+
# rather than on nothing, for a model that has one.
|
|
63
|
+
target ||= default_calculator if respond_to?(:default_calculator, true)
|
|
64
|
+
target&.assign_preferences(attrs[:preferences], pointer: "#{pointer}/preferences")
|
|
50
65
|
end
|
|
66
|
+
|
|
67
|
+
# Attached only once its settings are accepted: on a saved owner,
|
|
68
|
+
# assigning a has_one replaces the stored calculator immediately.
|
|
69
|
+
self.calculator = target unless target.equal?(calculator)
|
|
51
70
|
end
|
|
52
71
|
|
|
53
72
|
private
|
|
54
73
|
|
|
74
|
+
# The registered calculator for an API shorthand, resolved from the
|
|
75
|
+
# registry entry's own name: nothing user-supplied reaches
|
|
76
|
+
# `constantize`, and in development the registry can hold a class from
|
|
77
|
+
# an earlier reload whose instances fail the association's type check.
|
|
78
|
+
def registered_calculator_class(type)
|
|
79
|
+
registry = self.class.respond_to?(:calculators) ? self.class.calculators : []
|
|
80
|
+
registry.find { |klass| klass.api_type == type.to_s }&.to_s&.constantize
|
|
81
|
+
end
|
|
82
|
+
|
|
55
83
|
def self.model_name_without_spree_namespace
|
|
56
84
|
to_s.tableize.tr('/', '_').sub('spree_', '')
|
|
57
85
|
end
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
extend ActiveSupport::Concern
|
|
14
14
|
|
|
15
15
|
included do
|
|
16
|
-
preference :amounts, :hash, default: {}
|
|
16
|
+
preference :amounts, :hash, keys: :currency, values: :money, default: {}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# The amount this calculator quotes for a currency: the per-currency
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class Calculator < Spree.base_class
|
|
3
|
+
# A tier ladder for the tiered calculators: a list of
|
|
4
|
+
# `{ "threshold" => "100.0", "value" => "10.0" }` objects, where the value
|
|
5
|
+
# is a percentage or an amount depending on the calculator. A list rather
|
|
6
|
+
# than a hash keyed by threshold, because JSON object keys are always
|
|
7
|
+
# strings and a number key would not survive storage.
|
|
8
|
+
module Tiers
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
preference :tiers, :array, of: :object, properties: { threshold: :money, value: :decimal }, default: []
|
|
13
|
+
|
|
14
|
+
validate :preferred_tiers_content
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @return [BigDecimal, nil] nil when the value is not a number
|
|
18
|
+
def self.decimal(value)
|
|
19
|
+
BigDecimal(value.to_s, exception: false)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The value of the highest tier whose threshold the amount reaches.
|
|
23
|
+
#
|
|
24
|
+
# @param amount [Numeric]
|
|
25
|
+
# @return [BigDecimal, nil] nil when the amount reaches no tier
|
|
26
|
+
def tier_value_for(amount)
|
|
27
|
+
tier_pairs.reverse_each.detect { |threshold, _| amount >= threshold }&.last
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
# Tiers that are not numbers are skipped: validation refuses them on
|
|
33
|
+
# save, but an unsaved calculator can still be asked to compute. Tiers
|
|
34
|
+
# still in the pre-6.0 hash keyed by threshold, left by an upgrade that
|
|
35
|
+
# could not convert them, keep discounting until they are converted.
|
|
36
|
+
#
|
|
37
|
+
# @return [Array<Array(BigDecimal, BigDecimal)>] threshold and value, lowest threshold first
|
|
38
|
+
def tier_pairs
|
|
39
|
+
tiers = preferred_tiers
|
|
40
|
+
pairs = case tiers
|
|
41
|
+
when Hash then tiers.to_a
|
|
42
|
+
when Array then tiers.grep(Hash).map { |tier| tier.values_at('threshold', 'value') }
|
|
43
|
+
else []
|
|
44
|
+
end
|
|
45
|
+
pairs.map { |threshold, value| [Tiers.decimal(threshold), Tiers.decimal(value)] }.select(&:all?).sort_by(&:first)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def preferred_tiers_content
|
|
49
|
+
tiers = preferred_tiers
|
|
50
|
+
unless tiers.is_a?(Array) && tiers.all? { |tier| tier.is_a?(Hash) }
|
|
51
|
+
return errors.add(:preferred_tiers, :should_be_list)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
thresholds = tiers.map { |tier| Tiers.decimal(tier['threshold']) }
|
|
55
|
+
values = tiers.map { |tier| Tiers.decimal(tier['value']) }
|
|
56
|
+
|
|
57
|
+
errors.add(:preferred_tiers, :thresholds_should_be_positive_number) unless thresholds.all? { |threshold| threshold&.positive? }
|
|
58
|
+
errors.add(:preferred_tiers, :thresholds_should_be_unique) unless thresholds.uniq.size == thresholds.size
|
|
59
|
+
validate_tier_values(values)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Decimal attributes written as a number or canonical decimal text
|
|
3
|
+
# ("1234.56"). Text in any other shape raises Spree::Money::InvalidFormat,
|
|
4
|
+
# which the API answers as `invalid_money_format`, instead of being read as
|
|
5
|
+
# a wrong number the way Rails' cast reads "1,599.99" as 1 and "$10" as 0.
|
|
6
|
+
#
|
|
7
|
+
# The columns hold four decimals, so a value with more decimals than it may
|
|
8
|
+
# carry is refused on validation rather than stored and left to drift: an
|
|
9
|
+
# amount that changes hands keeps its currency's decimals, a unit price four,
|
|
10
|
+
# a rate its column's scale.
|
|
11
|
+
module CanonicalDecimals
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
# nil when the record has no currency yet (a payment before its order),
|
|
17
|
+
# which leaves the check to the record's own presence validations.
|
|
18
|
+
def canonical_decimals_currency_places
|
|
19
|
+
currency = try(:currency)
|
|
20
|
+
Spree::Money::Rounding.precision(currency) if currency.present?
|
|
21
|
+
rescue ActiveSupport::DelegationError
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class_methods do
|
|
26
|
+
# @param names [Array<Symbol>]
|
|
27
|
+
# @param places [:currency, Integer, nil] decimals allowed: the record's
|
|
28
|
+
# currency's, a fixed count, or nil for no check
|
|
29
|
+
# @param ignore_blank [Boolean] leave the attribute as it was when given a blank
|
|
30
|
+
def canonical_decimals(*names, places: :currency, ignore_blank: false)
|
|
31
|
+
names.each do |name|
|
|
32
|
+
define_method("#{name}=") do |value|
|
|
33
|
+
next if ignore_blank && value.blank?
|
|
34
|
+
|
|
35
|
+
super(Spree::Money::Rounding.parse_decimal(value))
|
|
36
|
+
rescue Spree::Money::InvalidFormat => error
|
|
37
|
+
raise Spree::Money::InvalidFormat.new(error.message, field: name)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
return if places.nil?
|
|
42
|
+
|
|
43
|
+
validate do
|
|
44
|
+
allowed = places == :currency ? canonical_decimals_currency_places : places
|
|
45
|
+
next if allowed.nil?
|
|
46
|
+
|
|
47
|
+
names.each do |name|
|
|
48
|
+
next unless will_save_change_to_attribute?(name)
|
|
49
|
+
|
|
50
|
+
# Read before the column's scale rounds it away.
|
|
51
|
+
raw = read_attribute_before_type_cast(name)
|
|
52
|
+
value = raw.is_a?(Numeric) ? BigDecimal(raw.to_s) : self[name]
|
|
53
|
+
next if value.nil? || value.round(allowed) == value
|
|
54
|
+
|
|
55
|
+
errors.add(name, :too_many_decimals, count: allowed)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -3,6 +3,7 @@ module Spree
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
include Spree::PrefixedId
|
|
6
|
+
include Spree::SignedIdAttachments
|
|
6
7
|
include Spree::HasCustomFields
|
|
7
8
|
include Spree::UserAddress
|
|
8
9
|
include Spree::UserPaymentSource
|
|
@@ -20,8 +21,10 @@ module Spree
|
|
|
20
21
|
# 2026-03-16 "Consolidate metadata") — no public/private split.
|
|
21
22
|
attribute :metadata, default: -> { {} }
|
|
22
23
|
|
|
23
|
-
# Enable lifecycle events for user models
|
|
24
24
|
publishes_lifecycle_events
|
|
25
|
+
publishes_events 'customer.password_reset', 'customer.anonymized'
|
|
26
|
+
publishes_event 'customer.password_reset_requested',
|
|
27
|
+
serializer: 'Spree::Api::V3::PasswordResetRequestedEventSerializer', credential: 'write_customers'
|
|
25
28
|
|
|
26
29
|
# Password reset token (Rails 7.1+ signed token, no DB column needed)
|
|
27
30
|
# Token auto-invalidates when password changes (salt changes)
|
|
@@ -117,6 +120,7 @@ module Spree
|
|
|
117
120
|
# Attachments
|
|
118
121
|
#
|
|
119
122
|
has_one_attached :avatar, service: Spree.public_storage_service_name
|
|
123
|
+
signed_id_attachments :avatar
|
|
120
124
|
|
|
121
125
|
has_spree_rich_text :internal_note
|
|
122
126
|
|
|
@@ -184,14 +188,12 @@ module Spree
|
|
|
184
188
|
where(conditions.reduce(:or))
|
|
185
189
|
end
|
|
186
190
|
|
|
187
|
-
# Backward compatibility alias — remove in Spree 6.0
|
|
188
|
-
def self.multi_search(query) = search(query)
|
|
189
|
-
|
|
190
191
|
self.whitelisted_ransackable_associations = %w[bill_address ship_address addresses tags spree_roles orders customer_groups]
|
|
191
192
|
self.whitelisted_ransackable_attributes = %w[id email first_name last_name phone accepts_email_marketing
|
|
192
|
-
created_at updated_at
|
|
193
|
-
self.whitelisted_ransackable_scopes = %w[search
|
|
193
|
+
created_at updated_at]
|
|
194
|
+
self.whitelisted_ransackable_scopes = %w[search with_min_total_spent with_standing_for_company
|
|
194
195
|
anonymized]
|
|
196
|
+
self.ransackable_scope_types = { 'with_min_total_spent' => 'decimal', 'with_standing_for_company' => { list: 'id' }, 'anonymized' => 'boolean' }
|
|
195
197
|
|
|
196
198
|
# Two-state scope: see Spree::Base.ransack_flag? for why the cast is
|
|
197
199
|
# opted out of here and done inside the scope instead.
|
|
@@ -223,10 +225,11 @@ module Spree
|
|
|
223
225
|
}
|
|
224
226
|
|
|
225
227
|
# Customers with standing for a company: members of the node or of any
|
|
226
|
-
# ancestor (see #standing_for?). Accepts
|
|
227
|
-
|
|
228
|
+
# ancestor (see #standing_for?). Accepts records, ids, or an array; a
|
|
229
|
+
# splat because Ransack passes each value of a list as its own argument.
|
|
230
|
+
scope :with_standing_for_company, ->(*companies) {
|
|
228
231
|
scoped = Spree::Current.store&.companies || Spree::Company.none
|
|
229
|
-
nodes =
|
|
232
|
+
nodes = companies.flatten.filter_map do |company|
|
|
230
233
|
company.is_a?(Spree::Company) ? company : scoped.find_by_param(company)
|
|
231
234
|
end
|
|
232
235
|
next none if nodes.empty?
|
|
@@ -278,6 +281,16 @@ module Spree
|
|
|
278
281
|
end
|
|
279
282
|
end
|
|
280
283
|
|
|
284
|
+
class_methods do
|
|
285
|
+
# Customer lifecycle events are `user.*`; an installation whose staff
|
|
286
|
+
# class shares these methods publishes theirs as `admin.*`.
|
|
287
|
+
#
|
|
288
|
+
# @return [String]
|
|
289
|
+
def event_prefix
|
|
290
|
+
self == Spree.admin_user_class && Spree.admin_user_class != Spree.customer_class ? 'admin' : 'user'
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
281
294
|
# Returns the last incomplete spree order for the current store
|
|
282
295
|
# @deprecated Carts are {Spree::Cart} since 6.0, so an incomplete order is
|
|
283
296
|
# a backoffice draft rather than the customer's cart. Use
|
|
@@ -337,7 +350,7 @@ module Spree
|
|
|
337
350
|
# @return [Spree::Wishlist]
|
|
338
351
|
def default_wishlist_for_store(current_store)
|
|
339
352
|
wishlists.find_by(is_default: true, store_id: current_store.id) || ActiveRecord::Base.connected_to(role: :writing) do
|
|
340
|
-
wishlists.create!(store: current_store, is_default: true, name:
|
|
353
|
+
wishlists.create!(store: current_store, is_default: true, name: I18n.t('spree.default_wishlist_name'))
|
|
341
354
|
end
|
|
342
355
|
end
|
|
343
356
|
|
|
@@ -368,14 +381,6 @@ module Spree
|
|
|
368
381
|
'Spree::Api::V3::CustomerSerializer'.safe_constantize
|
|
369
382
|
end
|
|
370
383
|
|
|
371
|
-
def event_prefix
|
|
372
|
-
if self.class == Spree.admin_user_class && Spree.admin_user_class != Spree.customer_class
|
|
373
|
-
'admin'
|
|
374
|
-
else
|
|
375
|
-
'user'
|
|
376
|
-
end
|
|
377
|
-
end
|
|
378
|
-
|
|
379
384
|
# @param store [Spree::Store] store to scope the lookup to; defaults to the current store
|
|
380
385
|
# @return [Spree::NewsletterSubscriber, nil] this user's newsletter subscriber for the given store
|
|
381
386
|
def newsletter_subscriber(store = Spree::Current.store)
|
|
@@ -43,24 +43,22 @@ module Spree
|
|
|
43
43
|
# unsaved record sorts last, which is what a merchant watching a new row
|
|
44
44
|
# appear expects.
|
|
45
45
|
def position_among_siblings
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
parent = owner
|
|
47
|
+
association = sibling_association_name
|
|
48
|
+
return 1 if parent.nil? || !parent.respond_to?(association)
|
|
48
49
|
|
|
49
|
-
earlier = id.
|
|
50
|
+
earlier = if id.present? && parent.read_association_in_memory?(association)
|
|
51
|
+
parent.public_send(association).to_a.count { |sibling| sibling.id.present? && sibling.id < id }
|
|
52
|
+
else
|
|
53
|
+
scope = parent.public_send(association).unscope(:order)
|
|
54
|
+
id.nil? ? scope.count : scope.where(scope.arel_table[:id].lt(id)).count
|
|
55
|
+
end
|
|
50
56
|
|
|
51
57
|
earlier + 1
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def sibling_scope
|
|
57
|
-
parent = owner
|
|
58
|
-
return if parent.nil?
|
|
59
|
-
|
|
60
|
-
association = self.class.name.demodulize.underscore.pluralize
|
|
61
|
-
return unless parent.respond_to?(association)
|
|
62
|
-
|
|
63
|
-
parent.public_send(association).unscope(:order)
|
|
60
|
+
def sibling_association_name
|
|
61
|
+
self.class.name.demodulize.underscore.pluralize
|
|
64
62
|
end
|
|
65
63
|
end
|
|
66
64
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# What a store's published email template and its draft share: which
|
|
3
|
+
# editable template they are, in which language, and the default they
|
|
4
|
+
# started from.
|
|
5
|
+
module EmailTemplateIdentity
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
# The version every language uses. Never NULL, so the unique index on
|
|
9
|
+
# store, key and locale holds for it too.
|
|
10
|
+
ANY_LOCALE = 'any'.freeze
|
|
11
|
+
LOCALE_FORMAT = /\A(any|[a-z]{2,3}(-[A-Za-z0-9]{2,8})*)\z/
|
|
12
|
+
|
|
13
|
+
included do
|
|
14
|
+
include Spree::SingleStoreResource
|
|
15
|
+
|
|
16
|
+
normalizes :locale, with: ->(locale) { locale.to_s.strip.presence || ANY_LOCALE }, apply_to_nil: true
|
|
17
|
+
|
|
18
|
+
validates :key, presence: true, uniqueness: { scope: spree_base_uniqueness_scope + [:store_id, :locale] }
|
|
19
|
+
validates :locale, format: { with: LOCALE_FORMAT }
|
|
20
|
+
validates :body, :base_body, presence: true
|
|
21
|
+
validate :key_editable
|
|
22
|
+
validate :locale_supported
|
|
23
|
+
|
|
24
|
+
scope :for_key, ->(key, locale = ANY_LOCALE) { where(key: key, locale: locale) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [Spree::Emails::EditableTemplates::Definition, nil]
|
|
28
|
+
def definition
|
|
29
|
+
Spree.editable_email_templates[key]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Boolean] whether Spree's default changed since this version started from it
|
|
33
|
+
# @param default [Spree::Emails::Template, nil] the current default
|
|
34
|
+
def default_changed?(default)
|
|
35
|
+
return false unless default
|
|
36
|
+
|
|
37
|
+
default.body != base_body || default.subject.to_s != base_subject.to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def key_editable
|
|
43
|
+
errors.add(:key, :invalid) if key.present? && definition.nil?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def locale_supported
|
|
47
|
+
return if locale == ANY_LOCALE || store.nil?
|
|
48
|
+
|
|
49
|
+
errors.add(:locale, :inclusion) unless store.supported_locales_list.include?(locale)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -24,10 +24,16 @@ module Spree
|
|
|
24
24
|
pending?
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
%w[complete fail
|
|
27
|
+
%w[complete fail expire].each do |transition|
|
|
28
28
|
define_method(:"can_#{transition}?") { pending? || processing? }
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# A failed confirm can leave the provider intent payable. Cancel is how
|
|
32
|
+
# placement retires it; the other transitions stay closed.
|
|
33
|
+
def can_cancel?
|
|
34
|
+
pending? || processing? || failed?
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
def process
|
|
32
38
|
return false unless transition_to('processing', from: %w[pending])
|
|
33
39
|
|
|
@@ -50,7 +56,7 @@ module Spree
|
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
def cancel
|
|
53
|
-
return false unless transition_to('canceled', from: %w[pending processing])
|
|
59
|
+
return false unless transition_to('canceled', from: %w[pending processing failed])
|
|
54
60
|
|
|
55
61
|
publish_canceled_event
|
|
56
62
|
true
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PostSale
|
|
3
|
+
# Asks the order's tax provider what tax a return, claim or exchange gives
|
|
4
|
+
# back. Including models name their lines through +taxed_lines+.
|
|
5
|
+
#
|
|
6
|
+
# The provider is asked twice: when the record opens, for what was
|
|
7
|
+
# announced, and again before money moves, for what actually goes back.
|
|
8
|
+
# Only the first is copied onto the lines' columns — they keep what was
|
|
9
|
+
# announced, the way a return line's +pre_tax_amount+ does — while the rows
|
|
10
|
+
# always say what the customer is, or was, refunded.
|
|
11
|
+
module Taxation
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
# @return [void]
|
|
15
|
+
# @raise [Spree::Tax::ProviderError]
|
|
16
|
+
def calculate_tax!
|
|
17
|
+
lines = taxed_lines.to_a
|
|
18
|
+
estimate_tax(lines)
|
|
19
|
+
lines.each(&:fold_tax_lines!)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param amounts [Hash{Object => BigDecimal}, nil] the money refunded per
|
|
23
|
+
# line when it is less than the line is worth
|
|
24
|
+
# @return [void]
|
|
25
|
+
# @raise [Spree::Tax::ProviderError]
|
|
26
|
+
def settle_tax!(amounts: nil)
|
|
27
|
+
lines = taxed_lines.select(&:settles_tax?)
|
|
28
|
+
estimate_tax(lines, amounts: amounts) if lines.any?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Drops every row on the record's lines, for a record withdrawn before
|
|
32
|
+
# anything went back. Nothing is worked out, so the provider is not asked
|
|
33
|
+
# — a cancellation must not wait on, or fail with, a tax service.
|
|
34
|
+
#
|
|
35
|
+
# @return [void]
|
|
36
|
+
def clear_tax!
|
|
37
|
+
Spree::TaxLine.post_sale.where(Spree::TaxLine.adjustable_key_for(taxed_lines.klass) => taxed_lines.select(:id)).delete_all
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The tax the record's lines give back, as their credit rows stand.
|
|
41
|
+
#
|
|
42
|
+
# @return [BigDecimal]
|
|
43
|
+
def credited_tax_total
|
|
44
|
+
taxed_lines.sum(0.to_d, &:credited_tax_total)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def estimate_tax(lines, amounts: nil)
|
|
50
|
+
order.tax_provider.estimate_refund(order, lines, amounts: amounts, tax_date: order.completed_at)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|