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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PostSale
|
|
3
|
+
# The tax side of a line on a return, claim or exchange: the rows the tax
|
|
4
|
+
# provider writes on it (see Spree::TaxProvider::Base#estimate_refund).
|
|
5
|
+
#
|
|
6
|
+
# Including models answer +credited_quantity+ and +credited_worth+, which
|
|
7
|
+
# the provider reads, and +fold_tax_lines!+, which copies the rows written
|
|
8
|
+
# at opening onto the line's own columns.
|
|
9
|
+
module TaxedLine
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
included do
|
|
13
|
+
has_many :tax_lines, class_name: 'Spree::TaxLine', dependent: :destroy
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Whether this line's tax goes through the provider when it is refunded.
|
|
17
|
+
# A line opened before refunds carried tax recorded none against a taxed
|
|
18
|
+
# sale line, and is refunded exactly as it was opened.
|
|
19
|
+
#
|
|
20
|
+
# Read from the columns, which are written once at opening, rather than
|
|
21
|
+
# the rows: settling rewrites or deletes the rows, and a settle whose
|
|
22
|
+
# money step then failed would otherwise leave the line looking like one
|
|
23
|
+
# that never carried tax.
|
|
24
|
+
#
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
def settles_tax?
|
|
27
|
+
recorded_tax_total.positive? || line_item.nil? || !line_item.tax_lines.exists?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The tax this line gives back, as its credit rows stand now.
|
|
31
|
+
#
|
|
32
|
+
# @return [BigDecimal]
|
|
33
|
+
def credited_tax_total
|
|
34
|
+
tax_lines.credits.sum(:amount)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# The tax copied onto the line's columns when it opened.
|
|
40
|
+
def recorded_tax_total
|
|
41
|
+
tax_total
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Array(BigDecimal, BigDecimal)] included and additional tax
|
|
45
|
+
def tax_totals_from(rows)
|
|
46
|
+
included, additional = rows.partition(&:included?)
|
|
47
|
+
[included.sum(0.to_d, &:amount), additional.sum(0.to_d, &:amount)]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PreferenceSchema
|
|
3
|
+
# Compiles a class's preference declarations into the JSON Schema of its
|
|
4
|
+
# `preferences` object — the contract the OpenAPI components, the SDK
|
|
5
|
+
# types, the `/…/types` endpoints and write validation all read, so none
|
|
6
|
+
# of them is written by hand.
|
|
7
|
+
module JsonSchema
|
|
8
|
+
DECIMAL_PATTERN = '^-?\d+(\.\d+)?$'.freeze
|
|
9
|
+
|
|
10
|
+
# Domain formats, with the pattern that makes each one checkable by any
|
|
11
|
+
# validator, not only those that know the format's name.
|
|
12
|
+
FORMATS = {
|
|
13
|
+
currency: { format: 'currency', pattern: '^[A-Z]{3}$' },
|
|
14
|
+
iso_country: { format: 'iso-country', pattern: '^[A-Z]{2}$' },
|
|
15
|
+
timezone: { format: 'timezone' },
|
|
16
|
+
locale: { format: 'locale' },
|
|
17
|
+
url: { format: 'uri' },
|
|
18
|
+
color: { format: 'color', pattern: '^#[0-9A-Fa-f]{6}$' }
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
# @param klass [Class] a class including {Spree::PreferenceSchema}
|
|
22
|
+
# @return [Hash] a JSON Schema object (string keys)
|
|
23
|
+
def self.for(klass)
|
|
24
|
+
fields = klass.preference_schema
|
|
25
|
+
definitions = klass.preference_definitions
|
|
26
|
+
properties = fields.to_h do |field|
|
|
27
|
+
[field[:key].to_s, property_schema(definitions.fetch(field[:key]), default: field[:default])]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
{ 'type' => 'object', 'properties' => properties, 'additionalProperties' => false }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The published schema plus the deprecated preferences, which a write
|
|
34
|
+
# still accepts until their removal although no read returns them.
|
|
35
|
+
#
|
|
36
|
+
# @param klass [Class]
|
|
37
|
+
# @return [Hash]
|
|
38
|
+
def self.writable(klass)
|
|
39
|
+
deprecated = klass.preference_definitions.filter_map do |name, definition|
|
|
40
|
+
[name.to_s, property_schema(definition)] if definition[:deprecated] && !definition[:internal]
|
|
41
|
+
end
|
|
42
|
+
schema = klass.preference_json_schema
|
|
43
|
+
schema.merge('properties' => schema['properties'].merge(deprecated.to_h))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The schema of one declared preference.
|
|
47
|
+
#
|
|
48
|
+
# @param definition [Hash] an entry of `preference_definitions`
|
|
49
|
+
# @param default [Object] the declared default, already evaluated
|
|
50
|
+
# @return [Hash]
|
|
51
|
+
def self.property_schema(definition, default: declared_default(definition))
|
|
52
|
+
schema = value_schema(definition)
|
|
53
|
+
choices = definition[:choices]
|
|
54
|
+
choices = choices.call if choices.respond_to?(:call)
|
|
55
|
+
schema['enum'] = choices.map { |choice| choice.is_a?(Symbol) ? choice.to_s : choice } if choices.present?
|
|
56
|
+
default = definition[:dynamic_default] ? nil : wire_default(default, definition)
|
|
57
|
+
unset_reads_null = default.nil? && !definition[:dynamic_default]
|
|
58
|
+
|
|
59
|
+
# A value nothing has set reads back as null, so a preference without
|
|
60
|
+
# a default is nullable on the wire whatever its declared type — and so
|
|
61
|
+
# is a secret, whose default is never sent.
|
|
62
|
+
if (definition[:nullable] || unset_reads_null || definition[:type] == :password) && schema['type']
|
|
63
|
+
schema['type'] = [schema['type'], 'null']
|
|
64
|
+
schema['enum'] += [nil] if schema['enum']
|
|
65
|
+
end
|
|
66
|
+
# A secret's default would leak alongside the masked value.
|
|
67
|
+
schema['default'] = default unless default.nil? || definition[:type] == :password
|
|
68
|
+
schema
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# A default can be a block that reads the database (the default store's
|
|
72
|
+
# currency); one that cannot be read here is left out.
|
|
73
|
+
def self.declared_default(definition)
|
|
74
|
+
definition[:default].call
|
|
75
|
+
rescue StandardError
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.value_schema(definition)
|
|
80
|
+
case definition[:type]
|
|
81
|
+
when :string then string_schema(definition[:format])
|
|
82
|
+
when :text then { 'type' => 'string', 'x-spree-widget' => 'textarea' }
|
|
83
|
+
when :boolean then { 'type' => 'boolean' }
|
|
84
|
+
when :integer then { 'type' => 'integer' }
|
|
85
|
+
when :decimal, :money then item_schema(definition[:type], definition)
|
|
86
|
+
when :password then { 'type' => 'string', 'x-spree-secret' => true }
|
|
87
|
+
when :date then { 'type' => 'string', 'format' => 'date' }
|
|
88
|
+
when :datetime then { 'type' => 'string', 'format' => 'date-time' }
|
|
89
|
+
when :array
|
|
90
|
+
schema = { 'type' => 'array' }
|
|
91
|
+
schema['items'] = item_schema(definition[:of], definition) if definition[:of]
|
|
92
|
+
schema
|
|
93
|
+
when :hash
|
|
94
|
+
schema = { 'type' => 'object' }
|
|
95
|
+
if definition[:keys] && definition[:values]
|
|
96
|
+
schema['propertyNames'] = definition[:keys] == :currency ? string_schema(:currency) : { 'type' => 'string' }
|
|
97
|
+
schema['additionalProperties'] = item_schema(definition[:values], definition)
|
|
98
|
+
end
|
|
99
|
+
schema
|
|
100
|
+
else
|
|
101
|
+
{}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.item_schema(item_type, definition)
|
|
106
|
+
case item_type
|
|
107
|
+
when :string then string_schema(definition[:format])
|
|
108
|
+
when :integer then { 'type' => 'integer' }
|
|
109
|
+
when :boolean then { 'type' => 'boolean' }
|
|
110
|
+
when :decimal then { 'type' => 'string', 'pattern' => DECIMAL_PATTERN }
|
|
111
|
+
when :money then { 'type' => 'string', 'format' => 'money', 'pattern' => DECIMAL_PATTERN }
|
|
112
|
+
when :id
|
|
113
|
+
prefix = Spree::Preferences::Preferable.preference_model(definition)._prefix_id_prefix
|
|
114
|
+
{ 'type' => 'string', 'format' => 'prefixed-id', 'pattern' => "^#{prefix}_[A-Za-z0-9]+$", 'x-spree-prefix' => prefix }
|
|
115
|
+
when :object
|
|
116
|
+
properties = definition[:properties].to_h { |name, type| [name.to_s, item_schema(type, {})] }
|
|
117
|
+
{ 'type' => 'object', 'properties' => properties, 'required' => properties.keys, 'additionalProperties' => false }
|
|
118
|
+
else
|
|
119
|
+
{}
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.string_schema(format)
|
|
124
|
+
{ 'type' => 'string' }.merge(FORMATS.fetch(format, {}).transform_keys(&:to_s))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The default in the form the API takes it back: a decimal as its exact
|
|
128
|
+
# string without trailing zeros ("100", "0.23"), as every API decimal is.
|
|
129
|
+
def self.wire_default(default, definition)
|
|
130
|
+
return default if default.nil?
|
|
131
|
+
return Spree::Money::Rounding.format_decimal(default) if %i[decimal money].include?(definition[:type])
|
|
132
|
+
|
|
133
|
+
default.as_json
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private_class_method :declared_default, :value_schema, :item_schema, :string_schema, :wire_default
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -50,12 +50,41 @@ module Spree
|
|
|
50
50
|
# runtime. Each entry also caches `key_string` (frozen) so hot-path
|
|
51
51
|
# serializers don't allocate `pref.to_s` per request.
|
|
52
52
|
def preference_schema
|
|
53
|
-
@preference_schema
|
|
53
|
+
return @preference_schema if @preference_schema
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
@preference_schema
|
|
55
|
+
fields, complete = compute_preference_schema
|
|
56
|
+
# Settled on only when every default could be read; one computed
|
|
57
|
+
# before the database was up is retried next time.
|
|
58
|
+
@preference_schema = fields if complete
|
|
59
|
+
fields
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The JSON Schema of this class's `preferences` object (see
|
|
63
|
+
# {Spree::PreferenceSchema::JsonSchema}). Memoized like
|
|
64
|
+
# {#preference_schema}.
|
|
65
|
+
#
|
|
66
|
+
# @return [Hash]
|
|
67
|
+
def preference_json_schema
|
|
68
|
+
return @preference_json_schema if @preference_json_schema
|
|
69
|
+
|
|
70
|
+
schema = Spree::PreferenceSchema::JsonSchema.for(self)
|
|
71
|
+
@preference_json_schema = schema if @preference_schema
|
|
72
|
+
schema
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Checks a `preferences` payload against {#preference_json_schema}, which
|
|
76
|
+
# also accepts deprecated preferences until their removal.
|
|
77
|
+
#
|
|
78
|
+
# @param values [Hash{String => Object}]
|
|
79
|
+
# @return [Array<Hash{Symbol => String}>] `{ pointer:, message: }` per failure, empty when it matches
|
|
80
|
+
def preference_failures(values)
|
|
81
|
+
# The published schema plus deprecated preferences, which a write
|
|
82
|
+
# still accepts until their removal. Memoized like the published one.
|
|
83
|
+
schemer = @preference_schemer || JSONSchemer.schema(Spree::PreferenceSchema::JsonSchema.writable(self))
|
|
84
|
+
@preference_schemer ||= schemer if @preference_schema
|
|
85
|
+
schemer.validate(values).map do |error|
|
|
86
|
+
{ pointer: error['data_pointer'], message: error['error'] }
|
|
87
|
+
end
|
|
59
88
|
end
|
|
60
89
|
|
|
61
90
|
# Wire-safe variant of `preference_schema` with `:password`
|
|
@@ -77,6 +106,10 @@ module Spree
|
|
|
77
106
|
wire = { key: field[:key], type: field[:type], default: field[:default] }
|
|
78
107
|
wire[:choices] = field[:choices] if field[:choices].present?
|
|
79
108
|
wire[:default] = nil if field[:type] == :password
|
|
109
|
+
# A decimal default goes out as the decimal string the API takes back.
|
|
110
|
+
if %i[decimal money].include?(field[:type]) && field[:default].is_a?(Numeric)
|
|
111
|
+
wire[:default] = Spree::Money::Rounding.format_decimal(field[:default])
|
|
112
|
+
end
|
|
80
113
|
wire.freeze
|
|
81
114
|
end.freeze
|
|
82
115
|
end
|
|
@@ -93,35 +126,26 @@ module Spree
|
|
|
93
126
|
.freeze
|
|
94
127
|
end
|
|
95
128
|
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
# looks like a preference) keeps its existing place at the end.
|
|
99
|
-
def ordered_preferences(instance)
|
|
100
|
-
defined = instance.defined_preferences
|
|
101
|
-
return defined unless respond_to?(:declared_preference_order)
|
|
102
|
-
|
|
103
|
-
declared = declared_preference_order & defined
|
|
104
|
-
declared + (defined - declared)
|
|
105
|
-
end
|
|
106
|
-
|
|
129
|
+
# @return [Array(Array<Hash>, Boolean)] the fields, and whether their
|
|
130
|
+
# defaults could all be read
|
|
107
131
|
def compute_preference_schema
|
|
108
132
|
# Only instantiation is guarded. `new` touches the database to read
|
|
109
133
|
# the column list, so it fails whenever the schema is asked for before
|
|
110
|
-
# a connection exists — at boot,
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
# before the database was up.
|
|
134
|
+
# a connection exists — at boot, in a rake task on an empty database,
|
|
135
|
+
# or while generating the SDK types. An uninitialized instance still
|
|
136
|
+
# describes every preference, since the declarations are pure Ruby;
|
|
137
|
+
# only a default read from the database is missing, so that result is
|
|
138
|
+
# not memoized. An error describing the preferences themselves is a
|
|
139
|
+
# real bug and is left to raise.
|
|
140
|
+
complete = true
|
|
118
141
|
instance = begin
|
|
119
142
|
new
|
|
120
143
|
rescue StandardError
|
|
121
|
-
|
|
144
|
+
complete = false
|
|
145
|
+
allocate
|
|
122
146
|
end
|
|
123
147
|
|
|
124
|
-
|
|
148
|
+
fields = instance.defined_preferences.filter_map do |pref|
|
|
125
149
|
next if instance.preference_deprecated(pref)
|
|
126
150
|
# Written by Spree, not supplied by the operator — a value a
|
|
127
151
|
# provider hands back after we register something with it. Offering
|
|
@@ -137,6 +161,7 @@ module Spree
|
|
|
137
161
|
choices: instance.preference_choices(pref)
|
|
138
162
|
}.compact.freeze
|
|
139
163
|
end
|
|
164
|
+
[fields, complete]
|
|
140
165
|
end
|
|
141
166
|
|
|
142
167
|
# Builds a `parse_on_set:` lambda for `preference :foo_ids, :array`
|
|
@@ -161,6 +186,9 @@ module Spree
|
|
|
161
186
|
# relation builder; defaults to the unscoped `klass`.
|
|
162
187
|
# @return [Proc] suitable for the `parse_on_set:` preference option.
|
|
163
188
|
def normalize_id_preference(klass: nil, scope: nil)
|
|
189
|
+
Spree::Deprecation.warn(
|
|
190
|
+
'`normalize_id_preference` is deprecated. Declare the list as `preference :name, :array, of: :id, model:, scope:`.'
|
|
191
|
+
)
|
|
164
192
|
lambda do |values, owner = nil|
|
|
165
193
|
raw = Array(values).flat_map { |v| v.to_s.split(',') }.compact_blank.map(&:strip)
|
|
166
194
|
next raw unless klass
|
|
@@ -189,11 +217,10 @@ module Spree
|
|
|
189
217
|
registered_subclasses.find { |klass| klass.api_type == shorthand.to_s }
|
|
190
218
|
end
|
|
191
219
|
|
|
192
|
-
# Returns a `[{ type:, label:, description:,
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
# discovery surface and must never leak gateway-shipped defaults.
|
|
220
|
+
# Returns a `[{ type:, label:, description:, schema: }]` array for every
|
|
221
|
+
# registered subclass, sorted by label for stable output. `schema` is
|
|
222
|
+
# the JSON Schema of the subclass's `preferences`, which never carries a
|
|
223
|
+
# secret's default.
|
|
197
224
|
#
|
|
198
225
|
# `label` and `description` are a fallback, not the admin UI's copy:
|
|
199
226
|
# they resolve in the request's locale, which is the store's rather
|
|
@@ -210,12 +237,15 @@ module Spree
|
|
|
210
237
|
# Asking for only one left promotion types with a null description
|
|
211
238
|
# and no fallback for a client without its own translation.
|
|
212
239
|
description: subclass_description(klass),
|
|
213
|
-
|
|
240
|
+
schema: klass.preference_json_schema
|
|
214
241
|
}
|
|
215
242
|
# Only present when true, so families without the concept keep
|
|
216
243
|
# their wire shape byte-identical. Pickers stop offering a
|
|
217
244
|
# superseded kind; existing rows still render and match.
|
|
218
245
|
entry[:superseded] = true if klass.respond_to?(:superseded?) && klass.superseded?
|
|
246
|
+
# Provider families (payment methods) add their gallery logo and
|
|
247
|
+
# setup guide; other families keep their wire shape unchanged.
|
|
248
|
+
entry.merge!(klass.provider_listing) if klass.respond_to?(:provider_listing)
|
|
219
249
|
entry
|
|
220
250
|
end.sort_by { |entry| entry[:label] }
|
|
221
251
|
end
|
|
@@ -20,7 +20,8 @@ module Spree
|
|
|
20
20
|
.group("#{Variant.table_name}.product_id")
|
|
21
21
|
.to_sql
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
# Above any price decimal(19,4) holds, so an unpriced product sorts last.
|
|
24
|
+
price_expr = Arel.sql('COALESCE(price_agg.agg_price, 1000000000000000)')
|
|
24
25
|
|
|
25
26
|
joins("LEFT JOIN (#{price_agg_sql}) AS price_agg ON price_agg.product_id = #{Product.table_name}.id").
|
|
26
27
|
select("#{Product.table_name}.*").
|
|
@@ -109,6 +110,9 @@ module Spree
|
|
|
109
110
|
#
|
|
110
111
|
# Spree::Product.in_taxon(taxon).count(distinct: true)
|
|
111
112
|
scope :in_taxon, ->(taxon) {
|
|
113
|
+
taxon = Spree::Category.find_by_prefix_id(taxon) if taxon.is_a?(String)
|
|
114
|
+
next none if taxon.nil?
|
|
115
|
+
|
|
112
116
|
joins(:product_categories).
|
|
113
117
|
where("#{Spree::ProductCategory.table_name}.category_id" => taxon.cached_self_and_descendants_ids).distinct
|
|
114
118
|
}
|
|
@@ -156,7 +160,8 @@ module Spree
|
|
|
156
160
|
where(Spree::ProductCategory.table_name => { category_id: taxon_ids }).
|
|
157
161
|
select("#{Product.table_name}.*", "#{min_position_sql} AS min_taxon_position").
|
|
158
162
|
group("#{Product.table_name}.id").
|
|
159
|
-
order
|
|
163
|
+
# Ties fall back to id, so the order holds across queries and pages.
|
|
164
|
+
order(Arel.sql("#{min_position_sql} ASC"), arel_table[:id].asc)
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
scope :with_option_value, ->(option, value) {
|
|
@@ -218,6 +223,15 @@ module Spree
|
|
|
218
223
|
channel = Spree::Current.channel
|
|
219
224
|
return where(discontinue_on: [nil, Time.current.beginning_of_minute..]) unless channel
|
|
220
225
|
|
|
226
|
+
not_discontinued_on(channel)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Published on the channel and not yet unpublished from it — a channel
|
|
230
|
+
# publication's end date is the product's discontinuation there.
|
|
231
|
+
#
|
|
232
|
+
# @param channel [Spree::Channel]
|
|
233
|
+
# @return [ActiveRecord::Relation<Spree::Product>]
|
|
234
|
+
def self.not_discontinued_on(channel)
|
|
221
235
|
for_channel(channel).where(Spree::ProductPublication.table_name => { unpublished_at: [nil, Time.current.beginning_of_minute..] })
|
|
222
236
|
end
|
|
223
237
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# What a provider shows on its gallery card in the dashboard: a logo and a
|
|
3
|
+
# link to its setup guide. Shared by integrations and payment methods so a
|
|
4
|
+
# provider gem declares both the same way whichever family it extends.
|
|
5
|
+
#
|
|
6
|
+
# class SpreeStripe::Gateway < Spree::Gateway
|
|
7
|
+
# def self.logo_url = 'https://example.com/stripe.png'
|
|
8
|
+
# def self.docs_url = 'https://spreecommerce.org/docs/integrations/payments/stripe'
|
|
9
|
+
# end
|
|
10
|
+
module ProviderListing
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
class_methods do
|
|
14
|
+
# Logo shown on the gallery card: an absolute URL to publicly hosted
|
|
15
|
+
# brand assets, or a `data:` URI for gems that want to be self-contained
|
|
16
|
+
# (works air-gapped, no CSP domain to allowlist). Anything an `<img src>`
|
|
17
|
+
# accepts. Deliberately not an asset-pipeline path — provider gems must
|
|
18
|
+
# not force an asset pipeline onto headless API hosts. Hosted logos are a
|
|
19
|
+
# courtesy, not a guarantee: the dashboard falls back to a letter avatar
|
|
20
|
+
# when unset or unreachable.
|
|
21
|
+
#
|
|
22
|
+
# @return [String, nil]
|
|
23
|
+
def logo_url
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Absolute URL of the provider's setup guide, linked from its gallery
|
|
28
|
+
# card. A third-party gem points at its own documentation.
|
|
29
|
+
#
|
|
30
|
+
# @return [String, nil]
|
|
31
|
+
def docs_url
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Hash] the listing attributes merged into a type-discovery entry
|
|
36
|
+
def provider_listing
|
|
37
|
+
{ logo_url: logo_url, docs_url: docs_url }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -36,9 +36,12 @@ module Spree
|
|
|
36
36
|
module Publishable
|
|
37
37
|
extend ActiveSupport::Concern
|
|
38
38
|
|
|
39
|
+
LIFECYCLE_ACTIONS = { create: :created, update: :updated, delete: :deleted }.freeze
|
|
40
|
+
|
|
39
41
|
included do
|
|
40
42
|
class_attribute :publish_events, default: true
|
|
41
43
|
class_attribute :lifecycle_events_enabled, default: false
|
|
44
|
+
class_attribute :lifecycle_event_actions, default: []
|
|
42
45
|
|
|
43
46
|
after_update :mark_real_update_for_events
|
|
44
47
|
end
|
|
@@ -76,6 +79,9 @@ module Spree
|
|
|
76
79
|
events &= Array(options[:only]) if options[:only]
|
|
77
80
|
events -= Array(options[:except]) if options[:except]
|
|
78
81
|
|
|
82
|
+
self.lifecycle_event_actions = events.map { |event| LIFECYCLE_ACTIONS.fetch(event) }
|
|
83
|
+
lifecycle_event_actions.each { |action| publishes_event(action) }
|
|
84
|
+
|
|
79
85
|
if events.include?(:create)
|
|
80
86
|
after_commit :publish_create_event, on: :create, if: :should_publish_events?
|
|
81
87
|
end
|
|
@@ -90,6 +96,51 @@ module Spree
|
|
|
90
96
|
end
|
|
91
97
|
end
|
|
92
98
|
|
|
99
|
+
# A subclass publishes its lifecycle events under its own prefix, which may
|
|
100
|
+
# differ from its parent's, so they are declared again for it.
|
|
101
|
+
def inherited(subclass)
|
|
102
|
+
super
|
|
103
|
+
subclass.lifecycle_event_actions.each { |action| subclass.publishes_event(action) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Declare events this model publishes, each carrying this model's Store
|
|
107
|
+
# serializer as its payload.
|
|
108
|
+
#
|
|
109
|
+
# @param names [Array<Symbol, String>] actions (`:placed`, prefixed with
|
|
110
|
+
# {event_prefix}) or full dotted names (`'customer.password_reset'`)
|
|
111
|
+
# @param options [Hash] see {publishes_event}
|
|
112
|
+
# @return [void]
|
|
113
|
+
#
|
|
114
|
+
# @example
|
|
115
|
+
# publishes_events :placed, :paid, :shipped
|
|
116
|
+
def publishes_events(*names, **options)
|
|
117
|
+
names.each { |name| publishes_event(name, **options) }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Declare one event this model publishes. Publishing a name no model
|
|
121
|
+
# declared raises in development and test.
|
|
122
|
+
#
|
|
123
|
+
# @param name [Symbol, String] an action or a full dotted name
|
|
124
|
+
# @param serializer [String, nil] payload serializer class name, when the
|
|
125
|
+
# payload is not this model's Store serializer
|
|
126
|
+
# @param credential [String, nil] for a payload carrying a live credential,
|
|
127
|
+
# the permission key needed to point a webhook endpoint at it; such an
|
|
128
|
+
# event reaches only endpoints that name it outright
|
|
129
|
+
# @param deprecated_alias_of [String, nil] the event this one duplicates
|
|
130
|
+
# @param webhook [Boolean] false for an event no webhook endpoint may
|
|
131
|
+
# receive, even one subscribed to `*`
|
|
132
|
+
# @return [void]
|
|
133
|
+
#
|
|
134
|
+
# @example
|
|
135
|
+
# publishes_event :canceled, serializer: 'Spree::Api::V3::OrderCanceledEventSerializer'
|
|
136
|
+
# publishes_event 'customer.password_reset_requested', serializer: '...', credential: 'write_customers'
|
|
137
|
+
def publishes_event(name, serializer: nil, credential: nil, deprecated_alias_of: nil, webhook: true)
|
|
138
|
+
Spree::Events.catalog.declare(
|
|
139
|
+
self, name,
|
|
140
|
+
serializer: serializer, credential: credential, deprecated_alias_of: deprecated_alias_of, webhook: webhook
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
93
144
|
# Disable lifecycle events for this model
|
|
94
145
|
#
|
|
95
146
|
# @example
|
|
@@ -145,10 +196,13 @@ module Spree
|
|
|
145
196
|
# order.publish_event('order.placed', metadata: { user_id: 1 })
|
|
146
197
|
#
|
|
147
198
|
def publish_event(event_name, payload = nil, metadata = {})
|
|
199
|
+
# Checked even while events are disabled, so every spec that reaches a
|
|
200
|
+
# publish also proves the event is declared.
|
|
201
|
+
Spree::Events.catalog.verify_declared!(event_name)
|
|
148
202
|
return unless Spree::Events.enabled?
|
|
149
203
|
|
|
150
204
|
@_current_event_name = event_name
|
|
151
|
-
payload ||=
|
|
205
|
+
payload ||= event_payload_for(event_name)
|
|
152
206
|
Spree::Events.publish(event_name, payload, metadata)
|
|
153
207
|
ensure
|
|
154
208
|
@_current_event_name = nil
|
|
@@ -161,20 +215,19 @@ module Spree
|
|
|
161
215
|
#
|
|
162
216
|
# @return [Hash]
|
|
163
217
|
def event_payload
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
unless serializer
|
|
167
|
-
return {
|
|
168
|
-
id: respond_to?(:prefixed_id) ? prefixed_id : id,
|
|
169
|
-
created_at: created_at&.iso8601,
|
|
170
|
-
updated_at: updated_at&.iso8601
|
|
171
|
-
}
|
|
172
|
-
end
|
|
218
|
+
build_event_payload(event_serializer_class)
|
|
219
|
+
end
|
|
173
220
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
221
|
+
# The payload for one event, built with the serializer the catalog declares
|
|
222
|
+
# for it, falling back to this model's Store serializer.
|
|
223
|
+
#
|
|
224
|
+
# @param event_name [String]
|
|
225
|
+
# @param params [Hash] extra serializer params the event serializer reads,
|
|
226
|
+
# e.g. the reset token of a password reset request
|
|
227
|
+
# @return [Hash]
|
|
228
|
+
def event_payload_for(event_name, **params)
|
|
229
|
+
serializer = Spree::Events.catalog.find(event_name)&.serializer_name&.safe_constantize
|
|
230
|
+
build_event_payload(serializer || event_serializer_class, params)
|
|
178
231
|
end
|
|
179
232
|
|
|
180
233
|
# Find the event serializer class for this model
|
|
@@ -223,6 +276,21 @@ module Spree
|
|
|
223
276
|
|
|
224
277
|
private
|
|
225
278
|
|
|
279
|
+
def build_event_payload(serializer, params = {})
|
|
280
|
+
unless serializer
|
|
281
|
+
return {
|
|
282
|
+
id: respond_to?(:prefixed_id) ? prefixed_id : id,
|
|
283
|
+
created_at: created_at&.iso8601,
|
|
284
|
+
updated_at: updated_at&.iso8601
|
|
285
|
+
}
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Use as_json to ensure all values are JSON-safe primitives.
|
|
289
|
+
# Alba's to_h can return raw Ruby objects (e.g., Spree::Money) which
|
|
290
|
+
# ActiveJob cannot serialize for async event subscribers.
|
|
291
|
+
serializer.new(self, params: event_serializer_params.merge(params)).to_h.as_json
|
|
292
|
+
end
|
|
293
|
+
|
|
226
294
|
# Build params for V3 serializers
|
|
227
295
|
#
|
|
228
296
|
# @return [Hash]
|
|
@@ -59,17 +59,17 @@ module Spree
|
|
|
59
59
|
|
|
60
60
|
return false if preferred_stock_location_id.present?
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
profile_requires_address = Hash.new do |cache, profile|
|
|
65
|
-
cache[profile] = profile.present? && profile.requires_shipping_address?
|
|
66
|
-
end
|
|
62
|
+
items_require_shipping_address?
|
|
63
|
+
end
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
# Whether delivery can be proposed yet: once there is a destination (a
|
|
66
|
+
# shipping address, or a pickup intent for a purchase that never gets
|
|
67
|
+
# one), or straight away when no item ships to an address. Quoted
|
|
68
|
+
# without a destination, only pickup qualifies.
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean]
|
|
71
|
+
def can_propose_deliveries?
|
|
72
|
+
ship_address.present? || preferred_stock_location_id.present? || !items_require_shipping_address?
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# @deprecated The shipping address is canonical — use +use_shipping+ to
|
|
@@ -174,6 +174,19 @@ module Spree
|
|
|
174
174
|
|
|
175
175
|
private
|
|
176
176
|
|
|
177
|
+
def items_require_shipping_address?
|
|
178
|
+
# Answered by the profile kind, resolved once per profile.
|
|
179
|
+
profile_requires_address = Hash.new do |cache, profile|
|
|
180
|
+
cache[profile] = profile.present? && profile.requires_shipping_address?
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Both profile paths are preloaded: the variant's own override, and the
|
|
184
|
+
# product's for the variants that inherit it.
|
|
185
|
+
line_items.includes(variant: [:delivery_profile, { product: :delivery_profile }]).any? do |line_item|
|
|
186
|
+
profile_requires_address[line_item.variant&.resolved_delivery_profile]
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
177
190
|
# The address behind an id a client may select, or nil when the id names
|
|
178
191
|
# one this purchase has no business shipping to.
|
|
179
192
|
#
|
|
@@ -15,7 +15,7 @@ module Spree
|
|
|
15
15
|
|
|
16
16
|
supported_codes = store.supported_currencies_list.map(&:iso_code)
|
|
17
17
|
unless supported_codes.include?(currency)
|
|
18
|
-
errors.add(:currency, :currency_not_supported_by_store, message:
|
|
18
|
+
errors.add(:currency, :currency_not_supported_by_store, message: I18n.t('spree.currency_not_supported_by_store'))
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|