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
|
@@ -18,7 +18,7 @@ module Spree
|
|
|
18
18
|
|
|
19
19
|
def process!
|
|
20
20
|
reference = attributes['reference'].to_s.strip
|
|
21
|
-
raise ArgumentError,
|
|
21
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_reference_required') if reference.blank?
|
|
22
22
|
|
|
23
23
|
line = {
|
|
24
24
|
variant: find_variant,
|
|
@@ -78,31 +78,31 @@ module Spree
|
|
|
78
78
|
|
|
79
79
|
def find_supplier
|
|
80
80
|
name = attributes['supplier'].to_s.strip
|
|
81
|
-
raise ArgumentError,
|
|
81
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_supplier_required') if name.blank?
|
|
82
82
|
|
|
83
83
|
cached_lookup(:purchase_order_import_supplier, name.downcase) do
|
|
84
84
|
store.suppliers.find_by(Spree::Supplier.arel_table[:name].lower.eq(name.downcase))
|
|
85
|
-
end || raise(ArgumentError,
|
|
85
|
+
end || raise(ArgumentError, I18n.t('spree.purchase_order_import_unknown_supplier', name: name))
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def find_destination
|
|
89
89
|
name = attributes['destination'].to_s.strip
|
|
90
|
-
raise ArgumentError,
|
|
90
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_destination_required') if name.blank?
|
|
91
91
|
|
|
92
92
|
cached_lookup(:purchase_order_import_destination, name.downcase) do
|
|
93
93
|
store.stock_locations.find_by(Spree::StockLocation.arel_table[:name].lower.eq(name.downcase))
|
|
94
|
-
end || raise(ArgumentError,
|
|
94
|
+
end || raise(ArgumentError, I18n.t('spree.purchase_order_import_unknown_destination', name: name))
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def find_variant
|
|
98
98
|
sku = attributes['sku'].to_s.strip
|
|
99
|
-
raise ArgumentError,
|
|
99
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_sku_required') if sku.blank?
|
|
100
100
|
|
|
101
101
|
variants = cached_lookup(:purchase_order_import_variant, sku.downcase) do
|
|
102
102
|
store.variants.where(Spree::Variant.arel_table[:sku].lower.eq(sku.downcase)).limit(2).to_a
|
|
103
103
|
end
|
|
104
|
-
raise ArgumentError,
|
|
105
|
-
raise ArgumentError,
|
|
104
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_unknown_sku', sku: sku) if variants.empty?
|
|
105
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_ambiguous_sku', sku: sku) if variants.many?
|
|
106
106
|
|
|
107
107
|
variants.first
|
|
108
108
|
end
|
|
@@ -110,7 +110,7 @@ module Spree
|
|
|
110
110
|
def quantity_for(value)
|
|
111
111
|
text = value.to_s.strip
|
|
112
112
|
unless text.match?(QUANTITY_FORMAT) && text.to_i.positive?
|
|
113
|
-
raise ArgumentError,
|
|
113
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_invalid_quantity', value: text)
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
text.to_i
|
|
@@ -118,7 +118,7 @@ module Spree
|
|
|
118
118
|
|
|
119
119
|
def amount_for(value)
|
|
120
120
|
text = value.to_s.strip
|
|
121
|
-
raise ArgumentError,
|
|
121
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_invalid_cost', value: text) unless text.match?(AMOUNT_FORMAT)
|
|
122
122
|
|
|
123
123
|
BigDecimal(text)
|
|
124
124
|
end
|
|
@@ -127,7 +127,7 @@ module Spree
|
|
|
127
127
|
currency = value.to_s.strip.upcase.presence || store.default_currency
|
|
128
128
|
return currency if supported_currencies.include?(currency)
|
|
129
129
|
|
|
130
|
-
raise ArgumentError,
|
|
130
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_unsupported_currency', currency: currency)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def supported_currencies
|
|
@@ -142,14 +142,14 @@ module Spree
|
|
|
142
142
|
|
|
143
143
|
Date.iso8601(text)
|
|
144
144
|
rescue Date::Error
|
|
145
|
-
raise ArgumentError,
|
|
145
|
+
raise ArgumentError, I18n.t('spree.purchase_order_import_invalid_date', column: column, value: text)
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def failure_message(result)
|
|
149
149
|
error = result.error
|
|
150
150
|
return error.full_messages.to_sentence if error.respond_to?(:full_messages)
|
|
151
151
|
|
|
152
|
-
error.to_s.presence ||
|
|
152
|
+
error.to_s.presence || I18n.t('spree.purchase_order_import_failed')
|
|
153
153
|
end
|
|
154
154
|
end
|
|
155
155
|
end
|
|
@@ -4,11 +4,10 @@ module Spree
|
|
|
4
4
|
# (Mobility-backed content such as product names, descriptions, taxons,
|
|
5
5
|
# option values, etc.).
|
|
6
6
|
#
|
|
7
|
-
# This is deliberately **independent of the
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# must not be constrained by which admin chrome translations exist.
|
|
7
|
+
# This is deliberately **independent of the languages Spree ships
|
|
8
|
+
# translations for** (`Spree.available_locales`). In a headless setup the
|
|
9
|
+
# storefront ships its own UI translations, so the languages a merchant can
|
|
10
|
+
# store content in must not be constrained by Spree's own translations.
|
|
12
11
|
#
|
|
13
12
|
# Codes follow BCP-47 casing (lowercase language, uppercase region —
|
|
14
13
|
# `pt-BR`, `zh-CN`) so the browser's `Intl.DisplayNames` resolves a clean
|
|
@@ -17,15 +16,15 @@ module Spree
|
|
|
17
16
|
# English, Brazilian vs. European Portuguese, Simplified vs. Traditional
|
|
18
17
|
# Chinese, etc.).
|
|
19
18
|
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
19
|
+
# Markets and stores only accept codes from this list when their locales
|
|
20
|
+
# change, so an order's or cart's locale (which must be one its store
|
|
21
|
+
# supports) is one too, unless the store kept a value saved before that.
|
|
23
22
|
ALL = %w[
|
|
24
23
|
aa ab ae af ak am an ar as av ay az
|
|
25
24
|
ba be bg bh bi bm bn bo br bs
|
|
26
25
|
ca ce ch co cr cs cu cv cy
|
|
27
26
|
da de de-AT de-CH dv dz
|
|
28
|
-
ee el en en-AU en-CA en-GB en-IN en-NZ eo es es-419 es-MX et eu
|
|
27
|
+
ee el en en-AU en-CA en-GB en-IN en-NZ en-US eo es es-419 es-CL es-EC es-MX et eu
|
|
29
28
|
fa ff fi fj fo fr fr-CA fy
|
|
30
29
|
ga gd gl gn gu gv
|
|
31
30
|
ha he hi ho hr ht hu hy hz
|
|
@@ -39,7 +38,7 @@ module Spree
|
|
|
39
38
|
pa pi pl ps pt pt-BR pt-PT
|
|
40
39
|
qu
|
|
41
40
|
rm rn ro ru rw
|
|
42
|
-
sa sc sd se sg si sk sl sm sn so sq sr ss st su sv sw
|
|
41
|
+
sa sc sd se sg si sk sl sl-SI sm sn so sq sr ss st su sv sw
|
|
43
42
|
ta te tg th ti tk tl tn to tr ts tt tw ty
|
|
44
43
|
ug uk ur uz
|
|
45
44
|
ve vi vo
|
|
@@ -48,5 +47,39 @@ module Spree
|
|
|
48
47
|
yi yo
|
|
49
48
|
za zh-CN zh-HK zh-TW zu
|
|
50
49
|
].freeze
|
|
50
|
+
|
|
51
|
+
# The codes in a comma-separated list that are not listed, for a
|
|
52
|
+
# validation to name.
|
|
53
|
+
#
|
|
54
|
+
# @param codes [String, nil] e.g. "en,pt-BR"
|
|
55
|
+
# @return [Array<String>]
|
|
56
|
+
def self.unlisted(codes)
|
|
57
|
+
codes.to_s.split(',').map(&:strip).reject(&:empty?) - ALL
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# A listed locale for a code from elsewhere (a host app's I18n config, a
|
|
61
|
+
# country's official language, a legacy column): the listed spelling of
|
|
62
|
+
# the code itself (`en_US` and `pt-br` give `en-US` and `pt-BR`),
|
|
63
|
+
# otherwise the listed one for its language (`en-IE` gives `en`, `zh`
|
|
64
|
+
# gives `zh-CN`), or nil when Spree knows neither.
|
|
65
|
+
#
|
|
66
|
+
# @param code [String, Symbol, nil]
|
|
67
|
+
# @return [String, nil]
|
|
68
|
+
def self.listed(code)
|
|
69
|
+
code = code.to_s.strip.tr('_', '-')
|
|
70
|
+
return if code.empty?
|
|
71
|
+
|
|
72
|
+
ALL.find { |listed| listed.casecmp?(code) } || for_language(code.split('-').first)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The listed locale for an ISO 639-1 language: the language itself, or
|
|
76
|
+
# its first regional variant when only those are listed (`zh` gives
|
|
77
|
+
# `zh-CN`).
|
|
78
|
+
#
|
|
79
|
+
# @param language [String]
|
|
80
|
+
# @return [String, nil]
|
|
81
|
+
def self.for_language(language)
|
|
82
|
+
Spree.locale_for_language(language.to_s.downcase, ALL)
|
|
83
|
+
end
|
|
51
84
|
end
|
|
52
85
|
end
|
|
@@ -26,7 +26,7 @@ module Spree
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
publish_subscription_requested unless subscriber.verified?
|
|
30
30
|
subscriber
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -34,17 +34,13 @@ module Spree
|
|
|
34
34
|
|
|
35
35
|
attr_reader :email, :current_user, :current_store, :redirect_url
|
|
36
36
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
customer_id: subscriber.customer&.prefixed_id
|
|
45
|
-
}
|
|
46
|
-
payload[:redirect_url] = redirect_url if redirect_url.present?
|
|
47
|
-
payload
|
|
37
|
+
def publish_subscription_requested
|
|
38
|
+
event_name = 'newsletter_subscriber.subscription_requested'
|
|
39
|
+
payload = subscriber.event_payload_for(
|
|
40
|
+
event_name,
|
|
41
|
+
include_verification_token: true, store: current_store, redirect_url: redirect_url.presence
|
|
42
|
+
)
|
|
43
|
+
subscriber.publish_event(event_name, payload)
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
def upsert_subscriber
|
|
@@ -17,13 +17,14 @@ module Spree
|
|
|
17
17
|
class BuildFulfillments
|
|
18
18
|
prepend Spree::ServiceModule::Base
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# @param keep_selection [Boolean] false when the destination changed
|
|
21
|
+
def call(order:, keep_selection: true)
|
|
21
22
|
return success(order) if order.completed?
|
|
22
23
|
return success(order) unless order.ship_address_id.present?
|
|
23
24
|
return success(order) unless order.line_items.any?
|
|
24
25
|
return success(order) unless order.delivery_step_required?
|
|
25
26
|
|
|
26
|
-
order.rebuild_fulfillments!
|
|
27
|
+
order.rebuild_fulfillments!(keep_selection: keep_selection)
|
|
27
28
|
order.create_shipment_tax_charge!
|
|
28
29
|
order.set_fulfillments_cost
|
|
29
30
|
order.apply_free_shipping_promotions
|
|
@@ -45,11 +45,11 @@ module Spree
|
|
|
45
45
|
rescue ActiveRecord::RecordInvalid => e
|
|
46
46
|
failure(e.record, e.record.errors.full_messages.to_sentence)
|
|
47
47
|
rescue ActiveStorage::FileNotFoundError
|
|
48
|
-
# An abandoned
|
|
48
|
+
# An abandoned presigned upload leaves the blob row without its bytes.
|
|
49
49
|
# ActiveStorage raises a message-less error for that, which would
|
|
50
50
|
# otherwise be an unhandled 500 on the Admin API.
|
|
51
51
|
order ||= @store.orders.new
|
|
52
|
-
order.errors.add(:po_document, :po_document_upload_incomplete, message:
|
|
52
|
+
order.errors.add(:po_document, :po_document_upload_incomplete, message: I18n.t('spree.po_document_upload_incomplete'))
|
|
53
53
|
failure(order, order.errors.full_messages.to_sentence)
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -71,8 +71,8 @@ module Spree
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
# The buyer's purchase order, when it arrived with the paperwork. A
|
|
74
|
-
# signed
|
|
75
|
-
attrs[:
|
|
74
|
+
# signed id, so it assigns like any other attribute.
|
|
75
|
+
attrs[:po_document_signed_id] = @params[:po_document_signed_id] if @params[:po_document_signed_id].present?
|
|
76
76
|
|
|
77
77
|
attrs[:market] = resolve_market if @params[:market_id].present?
|
|
78
78
|
attrs[:channel] = resolve_channel if @params[:channel_id].present?
|
|
@@ -170,6 +170,7 @@ module Spree
|
|
|
170
170
|
coupon_code: @params[:coupon_code]
|
|
171
171
|
}
|
|
172
172
|
order.coupon_code = nil
|
|
173
|
+
handler.release_stale_coupon_codes
|
|
173
174
|
end
|
|
174
175
|
end
|
|
175
176
|
end
|
|
@@ -17,17 +17,20 @@ module Spree
|
|
|
17
17
|
# @param line_item [Spree::LineItem, nil] nil distributes order-level
|
|
18
18
|
# @return [Spree::ServiceModule::Result] value is the created rows
|
|
19
19
|
def call(order:, label:, value:, value_type: 'flat', line_item: nil)
|
|
20
|
-
value =
|
|
21
|
-
return failure(nil,
|
|
22
|
-
return failure(nil,
|
|
20
|
+
value = Spree::Money::Rounding.parse_decimal(value)
|
|
21
|
+
return failure(nil, I18n.t('spree.errors.messages.discount_value_must_be_positive')) unless value&.positive?
|
|
22
|
+
return failure(nil, I18n.t('spree.errors.messages.discount_value_type_invalid')) unless %w[flat percent].include?(value_type)
|
|
23
23
|
|
|
24
24
|
rows = order.with_lock do
|
|
25
25
|
created = line_item ? [line_item_row(order, line_item, label, value, value_type)].compact : distributed_rows(order, label, value, value_type)
|
|
26
|
-
|
|
26
|
+
if created.any?
|
|
27
|
+
Spree.order_recalculate_totals_workflow.call(order: order)
|
|
28
|
+
order.update_statuses!
|
|
29
|
+
end
|
|
27
30
|
created
|
|
28
31
|
end
|
|
29
32
|
|
|
30
|
-
return failure(nil,
|
|
33
|
+
return failure(nil, I18n.t('spree.errors.messages.discount_has_no_effect')) if rows.empty?
|
|
31
34
|
|
|
32
35
|
success(rows)
|
|
33
36
|
end
|
|
@@ -36,7 +39,7 @@ module Spree
|
|
|
36
39
|
|
|
37
40
|
def line_item_row(order, line_item, label, value, value_type)
|
|
38
41
|
base = discountable_base(line_item)
|
|
39
|
-
amount = -[amount_for(base, value, value_type), base].min
|
|
42
|
+
amount = -[amount_for(base, value, value_type, order.currency), base].min
|
|
40
43
|
return if amount.zero?
|
|
41
44
|
|
|
42
45
|
create_row(order, line_item, label, amount, value, value_type)
|
|
@@ -48,19 +51,19 @@ module Spree
|
|
|
48
51
|
bases_sum = bases.sum
|
|
49
52
|
return [] if bases_sum <= 0
|
|
50
53
|
|
|
51
|
-
total = [amount_for(bases_sum, value, value_type), bases_sum].min
|
|
52
|
-
shares = Spree::Adjusters::LargestRemainder.
|
|
54
|
+
total = [amount_for(bases_sum, value, value_type, order.currency), bases_sum].min
|
|
55
|
+
shares = Spree::Adjusters::LargestRemainder.apportion(total, bases, order.currency)
|
|
53
56
|
|
|
54
57
|
line_items.each_with_index.filter_map do |line_item, index|
|
|
55
|
-
amount = -
|
|
58
|
+
amount = -shares[index]
|
|
56
59
|
next if amount.zero?
|
|
57
60
|
|
|
58
61
|
create_row(order, line_item, label, amount, value, value_type)
|
|
59
62
|
end
|
|
60
63
|
end
|
|
61
64
|
|
|
62
|
-
def amount_for(base, value, value_type)
|
|
63
|
-
value_type == 'percent' ?
|
|
65
|
+
def amount_for(base, value, value_type, currency)
|
|
66
|
+
Spree::Money::Rounding.to_currency(value_type == 'percent' ? base * value / 100 : value, currency)
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
# Remaining discountable base: amount net of already-applied discounts.
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Orders
|
|
3
|
-
# Draft-order twin of the cart service
|
|
4
|
-
#
|
|
3
|
+
# Draft-order twin of the cart service. Removing an item is an upsert with
|
|
4
|
+
# quantity zero, so the removal passes the same 'orders.upsert_items.validate'
|
|
5
|
+
# hook every other draft-order item edit does.
|
|
5
6
|
class RemoveLineItem
|
|
6
7
|
prepend Spree::ServiceModule::Base
|
|
7
8
|
|
|
9
|
+
# @param order [Spree::Order]
|
|
10
|
+
# @param line_item [Spree::LineItem]
|
|
11
|
+
# @param options [Hash] accepted for signature compatibility and ignored
|
|
12
|
+
# @return [Spree::ServiceModule::Result] value is the removed line item
|
|
8
13
|
def call(order:, line_item:, options: {})
|
|
9
|
-
Spree
|
|
14
|
+
result = Spree.order_upsert_items_workflow.call(
|
|
15
|
+
order: order,
|
|
16
|
+
items: [{ variant_id: line_item.variant_id, quantity: 0 }]
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
result.success? ? success(line_item) : failure(line_item, result.error)
|
|
10
20
|
end
|
|
11
21
|
end
|
|
12
22
|
end
|
|
@@ -15,7 +15,10 @@ module Spree
|
|
|
15
15
|
items_param = @params.delete(:items)
|
|
16
16
|
address_params = extract_address_params
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# requires_new: the rescue below sits outside the block, so joining a
|
|
19
|
+
# caller's open transaction (the API's order lock) would let it commit
|
|
20
|
+
# the half-applied edit.
|
|
21
|
+
ApplicationRecord.transaction(requires_new: true) do
|
|
19
22
|
ship_address_id_before = @order.ship_address_id
|
|
20
23
|
assign_addresses(address_params)
|
|
21
24
|
|
|
@@ -25,11 +28,12 @@ module Spree
|
|
|
25
28
|
|
|
26
29
|
process_items(items_param) if items_param
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
# An address row edited in place keeps its id, so an edit to the fields a quote reads counts too.
|
|
32
|
+
destination_changed = @order.ship_address_id != ship_address_id_before || @order.ship_address&.saved_change_to_destination?
|
|
33
|
+
build_fulfillments(keep_selection: !destination_changed) if items_param || destination_changed
|
|
31
34
|
|
|
32
35
|
@order.recalculate_totals!
|
|
36
|
+
@order.update_statuses!
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
success(@order.reload)
|
|
@@ -70,8 +74,8 @@ module Spree
|
|
|
70
74
|
propagate_step_failure!(result, fallback: 'Failed to update items on order') if result.failure?
|
|
71
75
|
end
|
|
72
76
|
|
|
73
|
-
def build_fulfillments
|
|
74
|
-
result = Spree::Orders::BuildFulfillments.call(order: @order)
|
|
77
|
+
def build_fulfillments(keep_selection:)
|
|
78
|
+
result = Spree::Orders::BuildFulfillments.call(order: @order, keep_selection: keep_selection)
|
|
75
79
|
propagate_step_failure!(result, fallback: 'Failed to build shipments') if result.failure?
|
|
76
80
|
end
|
|
77
81
|
|
|
@@ -21,7 +21,7 @@ module Spree
|
|
|
21
21
|
price_provided = !price.equal?(PRICE_NOT_PROVIDED)
|
|
22
22
|
|
|
23
23
|
if price_provided && order.completed?
|
|
24
|
-
return reject_price(line_item, :price_override_not_allowed,
|
|
24
|
+
return reject_price(line_item, :price_override_not_allowed, I18n.t('spree.cart_line_item.price_override_not_allowed'))
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
attributes = {}
|
|
@@ -30,7 +30,7 @@ module Spree
|
|
|
30
30
|
|
|
31
31
|
if price_provided && !price.nil?
|
|
32
32
|
manual_price = parse_manual_price(price)
|
|
33
|
-
return reject_price(line_item, :invalid_price,
|
|
33
|
+
return reject_price(line_item, :invalid_price, I18n.t('spree.cart_line_item.invalid_price')) if manual_price.nil?
|
|
34
34
|
|
|
35
35
|
attributes[:price] = manual_price
|
|
36
36
|
attributes[:price_source] = Spree::LineItem::MANUAL_PRICE_SOURCE
|
|
@@ -47,7 +47,10 @@ module Spree
|
|
|
47
47
|
ActiveRecord::Base.transaction do
|
|
48
48
|
return failure(line_item) unless line_item.update(attributes)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
if quantity_changing || price_provided
|
|
51
|
+
Spree::Orders::Recalculate.call(order: order, line_item: line_item)
|
|
52
|
+
order.update_statuses!
|
|
53
|
+
end
|
|
51
54
|
end
|
|
52
55
|
|
|
53
56
|
success(line_item)
|
|
@@ -4,7 +4,9 @@ module Spree
|
|
|
4
4
|
# Derive-then-persist: statuses are recomputed from payment/refund/
|
|
5
5
|
# fulfillment records and stored in indexed columns so admin filtering
|
|
6
6
|
# keeps working. Triggered from payment/refund/fulfillment/return event
|
|
7
|
-
# subscribers
|
|
7
|
+
# subscribers, and by each edit service that moves an order's total
|
|
8
|
+
# (fees, discounts, items) once it has re-summed — no event announces
|
|
9
|
+
# that change. Never inline from controllers.
|
|
8
10
|
class UpdateStatuses
|
|
9
11
|
prepend Spree::ServiceModule::Base
|
|
10
12
|
|
|
@@ -44,9 +44,12 @@ module Spree
|
|
|
44
44
|
# (docs/plans/6.0-volume-pricing.md).
|
|
45
45
|
def call(rows:)
|
|
46
46
|
rows = Array(rows).map { |r| r.with_indifferent_access }
|
|
47
|
-
# Amounts are parsed once, here; everything below reads numbers.
|
|
48
47
|
keyed = rows.select { |r| r[:variant_id].present? && r[:currency].present? }
|
|
49
|
-
|
|
48
|
+
unreadable = rows_with_malformed_amount(keyed)
|
|
49
|
+
return failure(nil, malformed_amounts: unreadable) if unreadable.any?
|
|
50
|
+
|
|
51
|
+
# Amounts are parsed once, here; everything below reads numbers.
|
|
52
|
+
keyed = keyed.map { |r| r.merge(amount: Spree::Money::Rounding.parse_decimal(r[:amount]), compare_at_amount: Spree::Money::Rounding.parse_decimal(r[:compare_at_amount])) }
|
|
50
53
|
# Checked before anything is deduped: `row_key` coerces the quantity,
|
|
51
54
|
# so two malformed rows would collapse into one and the batch would be
|
|
52
55
|
# judged on a shape the caller never sent.
|
|
@@ -98,12 +101,13 @@ module Spree
|
|
|
98
101
|
upsert_batch(base_rows, BASE_UNIQUE_BY)
|
|
99
102
|
end
|
|
100
103
|
upsert_batch(override_rows, OVERRIDE_UNIQUE_BY)
|
|
101
|
-
sweep(affected_keys, clear_rows)
|
|
104
|
+
cleared_variant_ids = sweep(affected_keys, clear_rows)
|
|
102
105
|
# `upsert_all` and `delete_all` both skip AR callbacks, so the
|
|
103
106
|
# `Price -> Variant -> Product` `touch:` chain never fires —
|
|
104
107
|
# downstream caches (`cache_key_with_version`) would stay stale.
|
|
105
|
-
#
|
|
106
|
-
|
|
108
|
+
# A blank row clearing a rung that had no amount changes nothing,
|
|
109
|
+
# so only priced rows and cleared amounts touch their variant.
|
|
110
|
+
touch_variants((upsert_rows.map { |r| r[:variant_id].to_s } + cleared_variant_ids).uniq)
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
success(price_count: payload.length)
|
|
@@ -167,6 +171,18 @@ module Spree
|
|
|
167
171
|
Integer(raw.to_s.strip, 10, exception: false)
|
|
168
172
|
end
|
|
169
173
|
|
|
174
|
+
# Rows whose amount is not a number or plain decimal text ("1,599.99",
|
|
175
|
+
# `false`), refused before a malformed value can read as "clear".
|
|
176
|
+
def rows_with_malformed_amount(rows)
|
|
177
|
+
rows.each_with_index.filter_map do |row, index|
|
|
178
|
+
Spree::Money::Rounding.parse_decimal(row[:amount])
|
|
179
|
+
Spree::Money::Rounding.parse_decimal(row[:compare_at_amount])
|
|
180
|
+
nil
|
|
181
|
+
rescue Spree::Money::InvalidFormat
|
|
182
|
+
{ index: index }
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
170
186
|
# Rows pricing below zero. This path runs no model validations, so the
|
|
171
187
|
# sign is checked here for every caller rather than in each of them.
|
|
172
188
|
def rows_with_negative_amount(rows)
|
|
@@ -370,15 +386,6 @@ module Spree
|
|
|
370
386
|
[row[:variant_id].to_s, row[:currency], row[:price_list_id].to_s]
|
|
371
387
|
end
|
|
372
388
|
|
|
373
|
-
# Parses locale-aware decimal input ("1.234,56" in DE, "1,234.56"
|
|
374
|
-
# in en-US). Numeric values pass through; blank values become nil.
|
|
375
|
-
def parse_amount(value)
|
|
376
|
-
return nil if value.blank?
|
|
377
|
-
return value if value.is_a?(Numeric)
|
|
378
|
-
|
|
379
|
-
Spree::LocalizedNumber.parse(value)
|
|
380
|
-
end
|
|
381
|
-
|
|
382
389
|
def sweep(affected_keys, clear_rows)
|
|
383
390
|
cleared_keys = clear_rows.map { |r| row_key(r) }.to_set
|
|
384
391
|
affected_set = affected_keys.to_set
|
|
@@ -392,16 +399,14 @@ module Spree
|
|
|
392
399
|
)
|
|
393
400
|
.pluck(:id, :variant_id, :currency, :price_list_id, :min_quantity, :amount)
|
|
394
401
|
|
|
395
|
-
|
|
402
|
+
doomed = candidates.select do |_, variant_id, currency, price_list_id, min_quantity, amount|
|
|
396
403
|
key = [variant_id.to_s, currency, price_list_id&.to_s, min_quantity]
|
|
397
|
-
|
|
398
|
-
next id if amount.nil?
|
|
399
|
-
next id if cleared_keys.include?(key)
|
|
400
|
-
|
|
401
|
-
nil
|
|
404
|
+
affected_set.include?(key) && (amount.nil? || cleared_keys.include?(key))
|
|
402
405
|
end
|
|
403
406
|
|
|
404
|
-
Spree::Price.where(id:
|
|
407
|
+
Spree::Price.where(id: doomed.map(&:first)).delete_all if doomed.any?
|
|
408
|
+
|
|
409
|
+
doomed.filter_map { |_, variant_id, *, amount| variant_id.to_s if amount }
|
|
405
410
|
end
|
|
406
411
|
|
|
407
412
|
# Bumps `updated_at` on the affected variants and their parent
|
|
@@ -410,15 +415,17 @@ module Spree
|
|
|
410
415
|
# `Variant -> Product` `touch:` chains otherwise.
|
|
411
416
|
#
|
|
412
417
|
# We use `touch_all` rather than per-record `Variant#touch`: it bumps
|
|
413
|
-
# every affected row in a single UPDATE
|
|
414
|
-
# the
|
|
418
|
+
# every affected row in a single UPDATE. It also skips the product's
|
|
419
|
+
# commit callbacks, so the search index, which stores prices, is
|
|
420
|
+
# refreshed here.
|
|
415
421
|
def touch_variants(variant_ids)
|
|
416
422
|
return if variant_ids.empty?
|
|
417
423
|
|
|
418
424
|
variants = Spree::Variant.where(id: variant_ids)
|
|
419
|
-
|
|
425
|
+
products = Spree::Product.where(id: variants.select(:product_id))
|
|
420
426
|
variants.touch_all
|
|
421
|
-
|
|
427
|
+
products.touch_all
|
|
428
|
+
products.find_each(&:enqueue_search_index)
|
|
422
429
|
end
|
|
423
430
|
|
|
424
431
|
# MySQL infers conflict targets from its own unique indexes and
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Bulk adding and removing products on categories or collections. The
|
|
3
|
+
# including service names the join model (`membership_class`), the grouping
|
|
4
|
+
# it belongs to (`group_class`, `group_key`) and refreshes the grouping's
|
|
5
|
+
# own counters in `refresh_group_counters`.
|
|
6
|
+
module ProductMemberships
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def add_memberships(groups, products)
|
|
10
|
+
rows = groups.pluck(:id).flat_map do |group_id|
|
|
11
|
+
position = membership_class.where(group_key => group_id).count
|
|
12
|
+
products.pluck(:id).map do |product_id|
|
|
13
|
+
{
|
|
14
|
+
group_key => group_id, product_id: product_id, position: (position += 1),
|
|
15
|
+
created_at: Time.current, updated_at: Time.current
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
membership_class.insert_all(rows)
|
|
20
|
+
|
|
21
|
+
refresh_after_membership_change(groups.pluck(:id), products)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Removes the products, then re-packs the remaining positions from 1.
|
|
25
|
+
def remove_memberships(groups, products)
|
|
26
|
+
group_ids = groups.pluck(:id)
|
|
27
|
+
|
|
28
|
+
ApplicationRecord.transaction do
|
|
29
|
+
membership_class.where(group_key => group_ids, product_id: products.pluck(:id)).delete_all
|
|
30
|
+
|
|
31
|
+
rows = group_ids.flat_map do |group_id|
|
|
32
|
+
membership_class.where(group_key => group_id).order(:position).pluck(:product_id).map.with_index(1) do |product_id, position|
|
|
33
|
+
{
|
|
34
|
+
group_key => group_id, product_id: product_id, position: position,
|
|
35
|
+
created_at: Time.current, updated_at: Time.current
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
membership_class.upsert_all(rows, unique_by: ([group_key, :product_id] unless Spree.mysql?)) if rows.any?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
refresh_after_membership_change(group_ids, products)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Bulk writes skip the join model's callbacks, so counters, caches and the
|
|
46
|
+
# search index are refreshed here.
|
|
47
|
+
def refresh_after_membership_change(group_ids, products)
|
|
48
|
+
product_ids = products.pluck(:id)
|
|
49
|
+
product_ids.each { |id| Spree::Product.reset_counters(id, membership_class.model_name.element.pluralize.to_sym) }
|
|
50
|
+
refresh_group_counters(group_ids)
|
|
51
|
+
|
|
52
|
+
Spree::Product.where(id: product_ids).touch_all
|
|
53
|
+
products.each(&:enqueue_search_index)
|
|
54
|
+
group_class.where(id: group_ids).touch_all
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def group_key
|
|
58
|
+
group_class.model_name.element.foreign_key.to_sym
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|