spree_core 5.6.0 → 6.0.0.beta1
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/LICENSE +30 -0
- data/README.md +2 -3
- data/app/finders/spree/line_items/find_by_variant.rb +5 -3
- data/app/finders/spree/sellers/panel_url.rb +47 -0
- data/app/finders/spree/stores/dashboard_url.rb +76 -0
- data/app/helpers/spree/images_helper.rb +1 -1
- data/app/jobs/spree/carts/reap_expired_job.rb +43 -0
- data/app/jobs/spree/collections/regenerate_time_based_job.rb +18 -0
- data/app/jobs/spree/data_requests/process_job.rb +42 -0
- data/app/jobs/spree/events/subscriber_job.rb +1 -1
- data/app/jobs/spree/images/migrate_product_media_job.rb +80 -0
- data/app/jobs/spree/images/save_from_url_job.rb +70 -13
- data/app/jobs/spree/imports/create_categories_job.rb +23 -14
- data/app/jobs/spree/imports/create_rows_job.rb +3 -62
- data/app/jobs/spree/imports/process_group_job.rb +46 -27
- data/app/jobs/spree/imports/process_job.rb +164 -0
- data/app/jobs/spree/imports/process_rows_job.rb +4 -63
- data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +29 -0
- data/app/jobs/spree/payments/handle_webhook_job.rb +1 -1
- data/app/jobs/spree/price_lists/sync_currencies_job.rb +42 -0
- data/app/jobs/spree/product_types/apply_to_products_job.rb +74 -0
- data/app/jobs/spree/products/auto_match_collections_job.rb +14 -0
- data/app/jobs/spree/products/auto_match_taxons_job.rb +6 -7
- data/app/jobs/spree/products/refresh_metrics_job.rb +1 -1
- data/app/jobs/spree/products/touch_categories_job.rb +15 -0
- data/app/jobs/spree/products/touch_taxons_job.rb +7 -6
- data/app/jobs/spree/seller_payouts/sweep_due_job.rb +57 -0
- data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +70 -0
- data/app/jobs/spree/seller_transfers/execute_pending_due_job.rb +56 -0
- data/app/jobs/spree/seller_transfers/execute_pending_job.rb +52 -0
- data/app/jobs/spree/shipping_labels/store_file_job.rb +17 -0
- data/app/jobs/spree/stock_locations/stock_items/create_job.rb +7 -5
- data/app/jobs/spree/stock_locations/stock_levels/create_job.rb +13 -0
- data/app/jobs/spree/stock_reservations/expire_job.rb +1 -1
- data/app/jobs/spree/tax_identifiers/validate_job.rb +14 -0
- data/app/jobs/spree/variants/remove_line_item_job.rb +29 -1
- data/app/mailers/spree/admin_user_mailer.rb +6 -35
- data/app/mailers/spree/base_mailer.rb +1 -20
- data/app/mailers/spree/invitation_mailer.rb +1 -1
- data/app/mailers/spree/seller_user_mailer.rb +47 -0
- data/app/models/concerns/spree/account_lockout.rb +38 -0
- data/app/models/concerns/spree/admin_user_methods.rb +14 -34
- data/app/models/concerns/spree/calculated_adjustments.rb +8 -14
- data/app/models/concerns/spree/calculator/currency_amounts.rb +43 -0
- data/app/models/concerns/spree/capture_method.rb +33 -0
- data/app/models/concerns/spree/custom_field_filterable.rb +163 -0
- data/app/models/concerns/spree/customer_methods.rb +421 -0
- data/app/models/concerns/spree/deprecated_customer_alias.rb +22 -0
- data/app/models/concerns/spree/derived_number.rb +66 -0
- data/app/models/concerns/spree/has_address_book.rb +89 -0
- data/app/models/concerns/spree/has_custom_fields.rb +360 -0
- data/app/models/concerns/spree/has_external_references.rb +95 -0
- data/app/models/concerns/spree/has_iso_geography.rb +71 -0
- data/app/models/concerns/spree/has_library_media.rb +94 -0
- data/app/models/concerns/spree/has_list_position.rb +15 -0
- data/app/models/concerns/spree/has_number.rb +139 -0
- data/app/models/concerns/spree/has_status.rb +76 -0
- data/app/models/concerns/spree/has_tracking_carrier.rb +18 -0
- data/app/models/concerns/spree/instruments_gateway_calls.rb +20 -0
- data/app/models/concerns/spree/integration_backed_provider.rb +89 -0
- data/app/models/concerns/spree/integrations_concern.rb +15 -2
- data/app/models/concerns/spree/label_translatable.rb +37 -0
- data/app/models/concerns/spree/marketplace/order_group.rb +35 -0
- data/app/models/concerns/spree/metadata.rb +18 -22
- data/app/models/concerns/spree/named_type.rb +11 -1
- data/app/models/concerns/spree/parameterizable_name.rb +5 -5
- data/app/models/concerns/spree/password_policy.rb +37 -0
- data/app/models/concerns/spree/payment_session_transitions.rb +93 -0
- data/app/models/concerns/spree/payment_source_concern.rb +1 -1
- data/app/models/concerns/spree/personal_data_matching.rb +120 -0
- data/app/models/concerns/spree/preference_schema.rb +120 -19
- data/app/models/concerns/spree/prefixed_id.rb +25 -3
- data/app/models/concerns/spree/presentation_translatable.rb +4 -37
- data/app/models/concerns/spree/product_scopes.rb +51 -191
- data/app/models/concerns/spree/provider_failure_policy.rb +44 -0
- data/app/models/concerns/spree/publishable.rb +7 -7
- data/app/models/concerns/spree/purchase/addresses.rb +258 -0
- data/app/models/concerns/spree/purchase/channel.rb +19 -0
- data/app/models/concerns/spree/purchase/checkout_steps.rb +68 -0
- data/app/models/concerns/spree/purchase/company.rb +96 -0
- data/app/models/concerns/spree/purchase/currency.rb +29 -0
- data/app/models/concerns/spree/purchase/digital_items.rb +60 -0
- data/app/models/concerns/spree/purchase/freight.rb +52 -0
- data/app/models/concerns/spree/purchase/gift_cards.rb +70 -0
- data/app/models/concerns/spree/purchase/lifecycle.rb +53 -0
- data/app/models/concerns/spree/purchase/line_item_currencies.rb +38 -0
- data/app/models/concerns/spree/purchase/line_item_lookup.rb +25 -0
- data/app/models/concerns/spree/purchase/locale.rb +30 -0
- data/app/models/concerns/spree/purchase/market.rb +29 -0
- data/app/models/concerns/spree/purchase/payment_processing.rb +93 -0
- data/app/models/concerns/spree/purchase/purchase_order.rb +94 -0
- data/app/models/concerns/spree/purchase/quantity_rules.rb +136 -0
- data/app/models/concerns/spree/purchase/store_credits.rb +100 -0
- data/app/models/concerns/spree/purchase/taxation.rb +146 -0
- data/app/models/concerns/spree/purchase/totals.rb +116 -0
- data/app/models/concerns/spree/purchase/validations.rb +45 -0
- data/app/models/concerns/spree/receivable.rb +103 -0
- data/app/models/concerns/spree/receivable_item.rb +78 -0
- data/app/models/concerns/spree/refund_methods.rb +19 -0
- data/app/models/concerns/spree/sanitizable_rich_text.rb +109 -0
- data/app/models/concerns/spree/search_indexable.rb +5 -2
- data/app/models/concerns/spree/single_store_resource.rb +3 -1
- data/app/models/concerns/spree/store_data_sources.rb +75 -0
- data/app/models/concerns/spree/store_preferences.rb +49 -0
- data/app/models/concerns/spree/stores/markets.rb +75 -28
- data/app/models/concerns/spree/stores/setup.rb +16 -1
- data/app/models/concerns/spree/translatable_resource.rb +25 -6
- data/app/models/concerns/spree/typed_adjustment_line.rb +55 -0
- data/app/models/concerns/spree/user_address.rb +13 -3
- data/app/models/concerns/spree/user_management.rb +18 -6
- data/app/models/concerns/spree/user_methods.rb +5 -330
- data/app/models/concerns/spree/user_payment_source.rb +1 -1
- data/app/models/concerns/spree/user_roles.rb +18 -11
- data/app/models/concerns/spree/validates_store_uniqueness.rb +53 -0
- data/app/models/concerns/spree/vat_price_calculation.rb +58 -6
- data/app/models/concerns/spree/webhook_payload_redaction.rb +1 -1
- data/app/models/spree/ability.rb +147 -43
- data/app/models/spree/address.rb +387 -118
- data/app/models/spree/adjusters/base.rb +26 -0
- data/app/models/spree/adjusters/largest_remainder.rb +24 -0
- data/app/models/spree/adjusters/promotion.rb +253 -0
- data/app/models/spree/admin_user.rb +26 -0
- data/app/models/spree/allowed_origin.rb +1 -1
- data/app/models/spree/api_key.rb +29 -28
- data/app/models/spree/asset.rb +14 -211
- data/app/models/spree/authentication/strategies/base_strategy.rb +54 -3
- data/app/models/spree/authentication/strategies/email_password_strategy.rb +11 -3
- data/app/models/spree/authentication/strategies/oidc_strategy.rb +330 -0
- data/app/models/spree/authentication/strategy_registry.rb +83 -0
- data/app/models/spree/base.rb +119 -13
- data/app/models/spree/base_analytics_event_handler.rb +1 -1
- data/app/models/spree/calculator/shipping/digital_delivery.rb +5 -2
- data/app/models/spree/calculator/shipping/flat_rate.rb +10 -5
- data/app/models/spree/calculator/shipping/flexi_rate.rb +6 -0
- data/app/models/spree/calculator/shipping/per_item.rb +8 -3
- data/app/models/spree/calculator/shipping/price_sack.rb +6 -0
- data/app/models/spree/calculator/tiered_flat_rate.rb +5 -1
- data/app/models/spree/calculator.rb +10 -0
- data/app/models/spree/cart.rb +285 -0
- data/app/models/spree/carts/item_warning.rb +28 -0
- data/app/models/spree/carts/resolved_item.rb +37 -0
- data/app/models/spree/catalog.rb +466 -0
- data/app/models/spree/catalog_assignment.rb +41 -0
- data/app/models/spree/catalog_order_minimum.rb +26 -0
- data/app/models/spree/catalog_price.rb +100 -0
- data/app/models/spree/catalog_price_tier.rb +36 -0
- data/app/models/spree/catalog_product.rb +23 -0
- data/app/models/spree/catalog_quantity_rule.rb +46 -0
- data/app/models/spree/catalogs/product_term.rb +43 -0
- data/app/models/spree/category.rb +430 -12
- data/app/models/spree/channel/gating.rb +5 -6
- data/app/models/spree/channel.rb +38 -4
- data/app/models/spree/channel_stock_location.rb +8 -0
- data/app/models/spree/checkout/default_requirements.rb +105 -22
- data/app/models/spree/checkout/registry.rb +64 -12
- data/app/models/spree/checkout/requirement.rb +9 -9
- data/app/models/spree/checkout/requirements.rb +25 -20
- data/app/models/spree/checkout/step.rb +12 -12
- data/app/models/spree/claim.rb +68 -0
- data/app/models/spree/claim_line_item.rb +46 -0
- data/app/models/spree/claim_reason.rb +24 -0
- data/app/models/spree/classification.rb +7 -49
- data/app/models/spree/collection.rb +269 -0
- data/app/models/spree/collection_rule.rb +58 -0
- data/app/models/spree/collection_rules/available_on.rb +21 -0
- data/app/models/spree/collection_rules/sale.rb +18 -0
- data/app/models/spree/collection_rules/tag.rb +21 -0
- data/app/models/spree/commission_line.rb +85 -0
- data/app/models/spree/commission_rate.rb +370 -0
- data/app/models/spree/commission_rate_value.rb +47 -0
- data/app/models/spree/commission_rule.rb +77 -0
- data/app/models/spree/commission_rule_product.rb +19 -0
- data/app/models/spree/commission_rules/category_rule.rb +33 -0
- data/app/models/spree/commission_rules/item_total_rule.rb +31 -0
- data/app/models/spree/commission_rules/product_rule.rb +55 -0
- data/app/models/spree/commission_rules/seller_rule.rb +33 -0
- data/app/models/spree/commission_tax.rb +51 -0
- data/app/models/spree/commissions/context.rb +71 -0
- data/app/models/spree/company.rb +347 -0
- data/app/models/spree/company_invitation.rb +81 -0
- data/app/models/spree/company_membership.rb +25 -0
- data/app/models/spree/consent_record.rb +125 -0
- data/app/models/spree/country.rb +128 -52
- data/app/models/spree/coupon_code.rb +18 -5
- data/app/models/spree/credit_card.rb +10 -9
- data/app/models/spree/current.rb +124 -18
- data/app/models/spree/custom_field.rb +160 -7
- data/app/models/spree/custom_field_definition/search_capabilities.rb +98 -0
- data/app/models/spree/custom_field_definition.rb +219 -5
- data/app/models/spree/custom_fields/boolean.rb +15 -0
- data/app/models/spree/custom_fields/json.rb +38 -0
- data/app/models/spree/custom_fields/long_text.rb +11 -0
- data/app/models/spree/custom_fields/number.rb +23 -0
- data/app/models/spree/custom_fields/rich_text.rb +23 -0
- data/app/models/spree/custom_fields/short_text.rb +15 -0
- data/app/models/spree/customer.rb +34 -0
- data/app/models/spree/customer_group.rb +12 -12
- data/app/models/spree/customer_group_user.rb +6 -4
- data/app/models/spree/data_feed.rb +1 -1
- data/app/models/spree/data_request.rb +98 -0
- data/app/models/spree/delivery.rb +169 -0
- data/app/models/spree/delivery_method.rb +566 -0
- data/app/models/spree/delivery_method_rule.rb +55 -0
- data/app/models/spree/delivery_method_rule_product.rb +8 -0
- data/app/models/spree/delivery_method_rules/channel_rule.rb +41 -0
- data/app/models/spree/delivery_method_rules/company_rule.rb +24 -0
- data/app/models/spree/delivery_method_rules/excluded_products_rule.rb +48 -0
- data/app/models/spree/delivery_method_rules/item_total_rule.rb +19 -0
- data/app/models/spree/delivery_method_rules/volume_rule.rb +35 -0
- data/app/models/spree/delivery_method_rules/weight_rule.rb +19 -0
- data/app/models/spree/delivery_method_service.rb +24 -0
- data/app/models/spree/delivery_method_stock_location.rb +11 -0
- data/app/models/spree/delivery_origin_group.rb +73 -0
- data/app/models/spree/delivery_origin_group_location.rb +9 -0
- data/app/models/spree/delivery_profile.rb +141 -0
- data/app/models/spree/delivery_profiles/digital.rb +20 -0
- data/app/models/spree/delivery_profiles/shipping.rb +12 -0
- data/app/models/spree/delivery_rate.rb +164 -0
- data/app/models/spree/delivery_rate_provider/base.rb +120 -0
- data/app/models/spree/delivery_rate_provider/estimate.rb +44 -0
- data/app/models/spree/delivery_rate_provider/freight.rb +41 -0
- data/app/models/spree/delivery_rate_provider/internal.rb +28 -0
- data/app/models/spree/delivery_rate_provider/service_catalog.rb +48 -0
- data/app/models/spree/delivery_zone.rb +64 -0
- data/app/models/spree/delivery_zone_member.rb +98 -0
- data/app/models/spree/digital.rb +12 -16
- data/app/models/spree/digital_asset.rb +144 -0
- data/app/models/spree/digital_asset_provider/base.rb +95 -0
- data/app/models/spree/digital_asset_provider/file.rb +20 -0
- data/app/models/spree/digital_delivery.rb +28 -0
- data/app/models/spree/digital_link.rb +31 -11
- data/app/models/spree/discount.rb +51 -0
- data/app/models/spree/event.rb +6 -6
- data/app/models/spree/exchange.rb +49 -44
- data/app/models/spree/exchange_line_item.rb +52 -0
- data/app/models/spree/export.rb +138 -23
- data/app/models/spree/exports/customers.rb +2 -2
- data/app/models/spree/exports/gift_cards.rb +2 -2
- data/app/models/spree/exports/newsletter_subscribers.rb +2 -2
- data/app/models/spree/exports/orders.rb +35 -5
- data/app/models/spree/exports/price_list_prices.rb +107 -0
- data/app/models/spree/exports/products.rb +4 -4
- data/app/models/spree/exports/purchase_orders.rb +39 -0
- data/app/models/spree/exports/report.rb +95 -0
- data/app/models/spree/external_reference.rb +77 -0
- data/app/models/spree/external_video.rb +117 -0
- data/app/models/spree/fee.rb +40 -0
- data/app/models/spree/freight_summary/line.rb +257 -0
- data/app/models/spree/freight_summary.rb +180 -0
- data/app/models/spree/fulfillment/custom_events.rb +96 -0
- data/app/models/spree/fulfillment.rb +973 -0
- data/app/models/spree/fulfillment_changer.rb +170 -0
- data/app/models/spree/fulfillment_item.rb +148 -0
- data/app/models/spree/fulfillment_provider/base.rb +130 -0
- data/app/models/spree/fulfillment_provider/digital.rb +44 -0
- data/app/models/spree/fulfillment_provider/manual.rb +15 -0
- data/app/models/spree/fulfillment_provider/pickup.rb +38 -0
- data/app/models/spree/fulfillment_provider/pickup_point.rb +24 -0
- data/app/models/spree/fulfilment_changer.rb +3 -164
- data/app/models/spree/gateway/bogus.rb +5 -4
- data/app/models/spree/gateway/custom_payment_source_method.rb +5 -5
- data/app/models/spree/gateway.rb +2 -2
- data/app/models/spree/gateway_customer.rb +12 -4
- data/app/models/spree/gift_card.rb +140 -48
- data/app/models/spree/gift_card_batch.rb +2 -2
- data/app/models/spree/import.rb +222 -92
- data/app/models/spree/import_mapping.rb +1 -1
- data/app/models/spree/import_row.rb +35 -31
- data/app/models/spree/import_schemas/price_list_prices.rb +13 -0
- data/app/models/spree/import_schemas/products.rb +8 -1
- data/app/models/spree/import_schemas/purchase_orders.rb +19 -0
- data/app/models/spree/imports/price_list_prices.rb +72 -0
- data/app/models/spree/imports/purchase_orders.rb +28 -0
- data/app/models/spree/integration.rb +128 -5
- data/app/models/spree/inventory_provider/base.rb +30 -0
- data/app/models/spree/inventory_provider/internal.rb +22 -0
- data/app/models/spree/inventory_unit.rb +3 -139
- data/app/models/spree/invitation.rb +53 -47
- data/app/models/spree/label_purchase.rb +29 -0
- data/app/models/spree/line_item.rb +239 -105
- data/app/models/spree/market.rb +106 -15
- data/app/models/spree/market_country.rb +10 -5
- data/app/models/spree/media.rb +497 -0
- data/app/models/spree/media_usage_reference.rb +27 -0
- data/app/models/spree/metafield.rb +14 -91
- data/app/models/spree/metafield_definition.rb +11 -127
- data/app/models/spree/metafields.rb +32 -0
- data/app/models/spree/newsletter_subscriber.rb +10 -9
- data/app/models/spree/number_generators/base.rb +56 -0
- data/app/models/spree/number_generators/random.rb +23 -0
- data/app/models/spree/number_generators/sequential.rb +88 -0
- data/app/models/spree/number_sequence.rb +86 -0
- data/app/models/spree/option_type.rb +18 -32
- data/app/models/spree/option_type_product_type.rb +8 -0
- data/app/models/spree/option_value.rb +19 -13
- data/app/models/spree/option_value_variant.rb +0 -1
- data/app/models/spree/order.rb +628 -571
- data/app/models/spree/order_cancellation_reason.rb +25 -0
- data/app/models/spree/order_group.rb +244 -0
- data/app/models/spree/order_inventory.rb +42 -33
- data/app/models/spree/order_merger.rb +11 -6
- data/app/models/spree/order_promotion.rb +32 -6
- data/app/models/spree/order_routing/has_strategy_preference.rb +1 -2
- data/app/models/spree/order_routing/rules/minimize_splits.rb +6 -6
- data/app/models/spree/order_routing/strategy/rules.rb +12 -8
- data/app/models/spree/order_routing_rule.rb +5 -15
- data/app/models/spree/order_updater.rb +131 -163
- data/app/models/spree/package_type.rb +191 -0
- data/app/models/spree/payment/custom_events.rb +52 -5
- data/app/models/spree/payment/gateway_options.rb +20 -5
- data/app/models/spree/payment/processing.rb +95 -116
- data/app/models/spree/payment.rb +299 -123
- data/app/models/spree/payment_method/check.rb +4 -3
- data/app/models/spree/payment_method/session_instrumentation.rb +34 -0
- data/app/models/spree/payment_method/store_credit.rb +4 -2
- data/app/models/spree/payment_method.rb +106 -9
- data/app/models/spree/payment_session.rb +150 -53
- data/app/models/spree/payment_setup_session.rb +4 -37
- data/app/models/spree/payment_source.rb +5 -4
- data/app/models/spree/payment_split.rb +90 -0
- data/app/models/spree/payout_provider/base.rb +218 -0
- data/app/models/spree/payout_provider/system.rb +36 -0
- data/app/models/spree/permission_sets.rb +17 -0
- data/app/models/spree/pickup_point_option.rb +28 -0
- data/app/models/spree/pickup_point_provider/base.rb +24 -0
- data/app/models/spree/policy.rb +12 -11
- data/app/models/spree/price.rb +91 -5
- data/app/models/spree/price_adjustment_tier.rb +63 -0
- data/app/models/spree/price_history.rb +1 -1
- data/app/models/spree/price_list.rb +266 -142
- data/app/models/spree/price_rule.rb +51 -7
- data/app/models/spree/price_rules/channel_rule.rb +3 -0
- data/app/models/spree/price_rules/customer_group_rule.rb +10 -1
- data/app/models/spree/price_rules/market_rule.rb +6 -0
- data/app/models/spree/price_rules/user_rule.rb +12 -3
- data/app/models/spree/price_rules/volume_rule.rb +6 -0
- data/app/models/spree/price_rules/zone_rule.rb +5 -13
- data/app/models/spree/pricing_provider/base.rb +51 -0
- data/app/models/spree/pricing_provider/internal.rb +255 -0
- data/app/models/spree/pricing_provider.rb +28 -0
- data/app/models/spree/product.rb +539 -537
- data/app/models/spree/product_category.rb +65 -0
- data/app/models/spree/product_collection.rb +20 -0
- data/app/models/spree/product_option_type.rb +0 -1
- data/app/models/spree/product_promotion_rule.rb +0 -1
- data/app/models/spree/product_publication.rb +0 -1
- data/app/models/spree/product_submission.rb +72 -0
- data/app/models/spree/product_type.rb +116 -0
- data/app/models/spree/product_type_category.rb +8 -0
- data/app/models/spree/product_type_custom_field_definition.rb +39 -0
- data/app/models/spree/promotion/actions/create_adjustment.rb +9 -7
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +11 -16
- data/app/models/spree/promotion/actions/create_line_items.rb +19 -8
- data/app/models/spree/promotion/actions/free_shipping.rb +12 -18
- data/app/models/spree/promotion/rules/category.rb +181 -0
- data/app/models/spree/promotion/rules/channel.rb +3 -3
- data/app/models/spree/promotion/rules/country.rb +43 -17
- data/app/models/spree/promotion/rules/currency.rb +2 -2
- data/app/models/spree/promotion/rules/customer_group.rb +13 -5
- data/app/models/spree/promotion/rules/first_order.rb +3 -3
- data/app/models/spree/promotion/rules/item_total.rb +2 -2
- data/app/models/spree/promotion/rules/market.rb +3 -3
- data/app/models/spree/promotion/rules/one_use_per_user.rb +4 -4
- data/app/models/spree/promotion/rules/option_value.rb +34 -11
- data/app/models/spree/promotion/rules/product.rb +3 -5
- data/app/models/spree/promotion/rules/taxon.rb +10 -131
- data/app/models/spree/promotion/rules/user.rb +14 -22
- data/app/models/spree/promotion/rules/user_logged_in.rb +3 -3
- data/app/models/spree/promotion.rb +59 -21
- data/app/models/spree/promotion_action.rb +52 -11
- data/app/models/spree/promotion_action_line_item.rb +1 -1
- data/app/models/spree/promotion_handler/coupon.rb +44 -16
- data/app/models/spree/promotion_handler/free_shipping.rb +1 -1
- data/app/models/spree/promotion_handler/promotion_duplicator.rb +1 -1
- data/app/models/spree/promotion_rule.rb +2 -9
- data/app/models/spree/promotion_rule_category.rb +22 -0
- data/app/models/spree/promotion_rule_taxon.rb +7 -6
- data/app/models/spree/promotion_rule_user.rb +2 -2
- data/app/models/spree/prototype.rb +3 -13
- data/app/models/spree/purchase_order.rb +95 -0
- data/app/models/spree/purchase_order_item.rb +40 -0
- data/app/models/spree/quantity_rule.rb +114 -0
- data/app/models/spree/refresh_token.rb +20 -2
- data/app/models/spree/refund.rb +94 -41
- data/app/models/spree/refund_reason.rb +32 -11
- data/app/models/spree/return.rb +150 -0
- data/app/models/spree/return_authorization_reason.rb +11 -6
- data/app/models/spree/return_line_item.rb +42 -0
- data/app/models/spree/return_reason.rb +26 -0
- data/app/models/spree/rich_text_sanitizer.rb +99 -0
- data/app/models/spree/role.rb +179 -4
- data/app/models/spree/role_user.rb +5 -21
- data/app/models/spree/saved_report.rb +56 -0
- data/app/models/spree/search_provider/base.rb +11 -0
- data/app/models/spree/search_provider/database.rb +106 -9
- data/app/models/spree/search_provider.rb +29 -0
- data/app/models/spree/seller.rb +475 -0
- data/app/models/spree/seller_balance.rb +86 -0
- data/app/models/spree/seller_payout.rb +116 -0
- data/app/models/spree/seller_requirement.rb +274 -0
- data/app/models/spree/seller_requirement_custom_field.rb +38 -0
- data/app/models/spree/seller_requirement_status.rb +144 -0
- data/app/models/spree/seller_requirement_submission.rb +91 -0
- data/app/models/spree/seller_requirements/accept_terms.rb +104 -0
- data/app/models/spree/seller_requirements/attestation.rb +22 -0
- data/app/models/spree/seller_requirements/billing_address.rb +13 -0
- data/app/models/spree/seller_requirements/complete_profile.rb +26 -0
- data/app/models/spree/seller_requirements/delivery_method.rb +20 -0
- data/app/models/spree/seller_requirements/document.rb +62 -0
- data/app/models/spree/seller_requirements/minimum_products.rb +20 -0
- data/app/models/spree/seller_requirements/operator_review.rb +23 -0
- data/app/models/spree/seller_requirements/package_type.rb +40 -0
- data/app/models/spree/seller_requirements/payout_account.rb +55 -0
- data/app/models/spree/seller_requirements/policy.rb +43 -0
- data/app/models/spree/seller_requirements/required_custom_fields.rb +56 -0
- data/app/models/spree/seller_requirements/returns_address.rb +19 -0
- data/app/models/spree/seller_transfer.rb +166 -0
- data/app/models/spree/setup_tasks.rb +3 -6
- data/app/models/spree/shipment.rb +3 -560
- data/app/models/spree/shipping_calculator.rb +7 -0
- data/app/models/spree/shipping_document.rb +32 -0
- data/app/models/spree/shipping_label.rb +146 -0
- data/app/models/spree/shipping_method.rb +3 -146
- data/app/models/spree/shipping_rate.rb +3 -97
- data/app/models/spree/state.rb +57 -23
- data/app/models/spree/stock/availability_validator.rb +1 -1
- data/app/models/spree/stock/content_item.rb +1 -0
- data/app/models/spree/stock/coordinator.rb +69 -9
- data/app/models/spree/stock/estimator.rb +198 -33
- data/app/models/spree/stock/inventory_unit_builder.rb +6 -1
- data/app/models/spree/stock/package.rb +145 -14
- data/app/models/spree/stock/packer.rb +20 -3
- data/app/models/spree/stock/prioritizer.rb +27 -4
- data/app/models/spree/stock/quantifier.rb +63 -38
- data/app/models/spree/stock/splitter/base.rb +3 -1
- data/app/models/spree/stock/splitter/delivery_profile.rb +41 -0
- data/app/models/spree/stock_item.rb +3 -147
- data/app/models/spree/stock_level/custom_events.rb +48 -0
- data/app/models/spree/stock_level.rb +305 -0
- data/app/models/spree/stock_location.rb +165 -87
- data/app/models/spree/stock_movement/custom_events.rb +1 -1
- data/app/models/spree/stock_movement.rb +166 -22
- data/app/models/spree/stock_receipt.rb +56 -0
- data/app/models/spree/stock_receipt_item.rb +35 -0
- data/app/models/spree/stock_reservation.rb +114 -11
- data/app/models/spree/stock_transfer.rb +84 -59
- data/app/models/spree/stock_transfer_item.rb +25 -0
- data/app/models/spree/store.rb +427 -107
- data/app/models/spree/store_credit.rb +88 -21
- data/app/models/spree/store_credit_category.rb +21 -28
- data/app/models/spree/store_credit_event.rb +1 -1
- data/app/models/spree/store_credit_type.rb +10 -2
- data/app/models/spree/store_payment_method.rb +0 -1
- data/app/models/spree/store_product.rb +2 -2
- data/app/models/spree/store_promotion.rb +0 -1
- data/app/models/spree/storefront/access_denied.rb +22 -0
- data/app/models/spree/storefront/access_policy.rb +180 -0
- data/app/models/spree/subscriber.rb +6 -6
- data/app/models/spree/supplier.rb +76 -0
- data/app/models/spree/tax_category.rb +15 -5
- data/app/models/spree/tax_exemption/item_override.rb +22 -0
- data/app/models/spree/tax_exemption.rb +81 -0
- data/app/models/spree/tax_exemption_certificate.rb +95 -0
- data/app/models/spree/tax_identifier.rb +209 -0
- data/app/models/spree/tax_identifiers/validation_result.rb +41 -0
- data/app/models/spree/tax_identifiers/validator/base.rb +53 -0
- data/app/models/spree/tax_identifiers/validator/eu_vat.rb +76 -0
- data/app/models/spree/tax_line.rb +79 -0
- data/app/models/spree/tax_provider/base.rb +150 -0
- data/app/models/spree/tax_provider/internal.rb +227 -0
- data/app/models/spree/tax_rate.rb +63 -100
- data/app/models/spree/taxon.rb +13 -514
- data/app/models/spree/taxon_rule.rb +8 -9
- data/app/models/spree/taxonomy.rb +8 -42
- data/app/models/spree/user_identity.rb +2 -2
- data/app/models/spree/variant.rb +629 -150
- data/app/models/spree/variant_media.rb +6 -7
- data/app/models/spree/webhook_endpoint.rb +2 -1
- data/app/models/spree/wished_item.rb +3 -31
- data/app/models/spree/wishlist.rb +17 -10
- data/app/models/spree/wishlist_item/custom_events.rb +42 -0
- data/app/models/spree/wishlist_item.rb +37 -0
- data/app/models/spree/zone.rb +24 -182
- data/app/models/spree/zone_member.rb +5 -8
- data/app/presenters/spree/csv/custom_fields_helper.rb +25 -0
- data/app/presenters/spree/csv/customer_presenter.rb +7 -6
- data/app/presenters/spree/csv/gift_card_presenter.rb +9 -8
- data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +7 -6
- data/app/presenters/spree/csv/order_line_item_presenter.rb +53 -21
- data/app/presenters/spree/csv/price_list_price_presenter.rb +41 -0
- data/app/presenters/spree/csv/product_variant_presenter.rb +22 -21
- data/app/presenters/spree/csv/purchase_order_item_presenter.rb +50 -0
- data/app/presenters/spree/data_feeds/google_presenter.rb +24 -10
- data/app/presenters/spree/filters/options_presenter.rb +2 -2
- data/app/services/spree/addresses/create.rb +70 -27
- data/app/services/spree/addresses/helper.rb +17 -54
- data/app/services/spree/addresses/update.rb +53 -31
- data/app/services/spree/carts/associate.rb +46 -0
- data/app/services/spree/carts/check_availability.rb +72 -0
- data/app/services/spree/carts/create.rb +7 -1
- data/app/services/spree/carts/destroy.rb +71 -0
- data/app/services/spree/carts/empty.rb +42 -0
- data/app/services/spree/carts/partition_by_seller.rb +71 -0
- data/app/services/spree/carts/price_items.rb +119 -0
- data/app/services/spree/carts/remove_item.rb +42 -0
- data/app/services/spree/carts/remove_line_item.rb +42 -0
- data/app/services/spree/carts/remove_out_of_stock_items.rb +104 -0
- data/app/services/spree/carts/set_quantity.rb +39 -0
- data/app/services/spree/carts/split_by_seller.rb +475 -0
- data/app/services/spree/carts/update.rb +157 -51
- data/app/services/spree/catalogs/resolve_prices.rb +192 -0
- data/app/services/spree/catalogs/resolve_quantity_rules.rb +115 -0
- data/app/services/spree/catalogs/set_assignments.rb +59 -0
- data/app/services/spree/catalogs/set_order_minimums.rb +44 -0
- data/app/services/spree/catalogs/set_product_terms.rb +83 -0
- data/app/services/spree/categories/add_products.rb +51 -0
- data/app/services/spree/categories/remove_products.rb +72 -0
- data/app/services/spree/checkout/add_store_credit.rb +11 -79
- data/app/services/spree/checkout/advance.rb +38 -38
- data/app/services/spree/checkout/remove_store_credit.rb +7 -12
- data/app/services/spree/collections/add_products.rb +42 -0
- data/app/services/spree/collections/regenerate_products.rb +67 -0
- data/app/services/spree/collections/remove_products.rb +65 -0
- data/app/services/spree/commissions/calculate_line.rb +132 -0
- data/app/services/spree/commissions/commission_order.rb +185 -0
- data/app/services/spree/commissions/resolve_rate.rb +114 -0
- data/app/services/spree/commissions/resolve_tax_rate.rb +78 -0
- data/app/services/spree/companies/add_member.rb +38 -0
- data/app/services/spree/company_invitations/accept.rb +60 -0
- data/app/services/spree/coupon_codes/coupon_codes_handler.rb +14 -2
- data/app/services/spree/customers/data_export.rb +372 -0
- data/app/services/spree/data_requests/create.rb +77 -0
- data/app/services/spree/deliveries/create.rb +56 -0
- data/app/services/spree/deliveries/destroy.rb +39 -0
- data/app/services/spree/deliveries/upsert_primary.rb +43 -0
- data/app/services/spree/fulfillments/recalculate_delivery.rb +62 -0
- data/app/services/spree/fulfillments/stand_down_provider.rb +47 -0
- data/app/services/spree/imports/row_processors/customer.rb +8 -8
- data/app/services/spree/imports/row_processors/price_list_price.rb +164 -0
- data/app/services/spree/imports/row_processors/product_variant.rb +199 -58
- data/app/services/spree/imports/row_processors/purchase_order.rb +157 -0
- data/app/services/spree/media/destroy.rb +88 -0
- data/app/services/spree/media/usage.rb +175 -0
- data/app/services/spree/newsletter/link_user.rb +4 -3
- data/app/services/spree/newsletter/subscribe.rb +3 -3
- data/app/services/spree/newsletter/verify.rb +6 -2
- data/app/services/spree/order_groups/allocate_payments.rb +101 -0
- data/app/services/spree/orders/approve.rb +9 -23
- data/app/services/spree/orders/build_fulfillments.rb +29 -0
- data/app/services/spree/orders/create.rb +38 -17
- data/app/services/spree/orders/create_from_cart.rb +88 -0
- data/app/services/spree/orders/create_user_account.rb +14 -67
- data/app/services/spree/orders/discounts/create.rb +84 -0
- data/app/services/spree/orders/discounts/destroy.rb +25 -0
- data/app/services/spree/orders/discounts/update.rb +29 -0
- data/app/services/spree/orders/fees/create.rb +28 -0
- data/app/services/spree/orders/fees/destroy.rb +22 -0
- data/app/services/spree/orders/fees/update.rb +25 -0
- data/app/services/spree/orders/remove_item.rb +13 -0
- data/app/services/spree/orders/remove_line_item.rb +13 -0
- data/app/services/spree/orders/update.rb +56 -11
- data/app/services/spree/orders/update_item.rb +105 -0
- data/app/services/spree/orders/update_statuses.rb +109 -0
- data/app/services/spree/payments/create.rb +31 -5
- data/app/services/spree/prices/bulk_upsert.rb +156 -30
- data/app/services/spree/product_submissions/close.rb +46 -0
- data/app/services/spree/product_types/apply_to_products.rb +167 -0
- data/app/services/spree/products/auto_match_collections.rb +75 -0
- data/app/services/spree/products/auto_match_taxons.rb +7 -54
- data/app/services/spree/products/duplicator.rb +16 -22
- data/app/services/spree/products/for_context.rb +54 -0
- data/app/services/spree/products/select_buy_box.rb +98 -0
- data/app/services/spree/purchases/amount_due_at_checkout.rb +33 -0
- data/app/services/spree/returns/eligibility_validator.rb +53 -0
- data/app/services/spree/sample_data/helper.rb +55 -0
- data/app/services/spree/sample_data/import_builder.rb +36 -0
- data/app/services/spree/sample_data/import_runner.rb +25 -41
- data/app/services/spree/sample_data/loader.rb +21 -63
- data/app/services/spree/search_provider/custom_field_schema.rb +153 -0
- data/app/services/spree/seeds/admin_user.rb +72 -12
- data/app/services/spree/seeds/all.rb +24 -13
- data/app/services/spree/seeds/allowed_origins.rb +3 -4
- data/app/services/spree/seeds/commission_rates.rb +53 -0
- data/app/services/spree/seeds/digital_delivery.rb +18 -8
- data/app/services/spree/seeds/product_types.rb +23 -0
- data/app/services/spree/seeds/returns_environment.rb +44 -16
- data/app/services/spree/seeds/roles.rb +6 -1
- data/app/services/spree/seeds/saved_reports.rb +107 -0
- data/app/services/spree/seeds/seller_requirements.rb +19 -0
- data/app/services/spree/seeds/stores.rb +1 -1
- data/app/services/spree/seeds/tax_categories.rb +9 -2
- data/app/services/spree/sellers/requirements.rb +129 -0
- data/app/services/spree/shipments/update.rb +4 -33
- data/app/services/spree/shipping_labels/confirm_refund.rb +47 -0
- data/app/services/spree/shipping_labels/store_file.rb +68 -0
- data/app/services/spree/stock_levels/bulk_upsert.rb +67 -0
- data/app/services/spree/stock_levels/correct.rb +69 -0
- data/app/services/spree/stock_levels/recount.rb +103 -0
- data/app/services/spree/stock_locations/stock_items/create.rb +8 -13
- data/app/services/spree/stock_locations/stock_levels/create.rb +25 -0
- data/app/services/spree/stock_reservations/extend.rb +9 -5
- data/app/services/spree/stock_reservations/release.rb +4 -3
- data/app/services/spree/stock_reservations/reserve.rb +43 -32
- data/app/services/spree/store_credits/apply.rb +93 -0
- data/app/services/spree/store_credits/remove.rb +17 -0
- data/app/services/spree/stores/provision_defaults.rb +269 -0
- data/app/services/spree/tax/resolve_exemptions.rb +44 -0
- data/app/services/spree/tax/unusable_exemption_error.rb +8 -0
- data/app/services/spree/tax_identifiers/validate.rb +77 -0
- data/app/services/spree/taxons/add_products.rb +6 -39
- data/app/services/spree/taxons/regenerate_products.rb +8 -53
- data/app/services/spree/taxons/remove_products.rb +6 -60
- data/app/services/spree/variants/remove_line_items.rb +6 -1
- data/app/subscribers/spree/event_log_subscriber.rb +1 -1
- data/app/subscribers/spree/order_commission_subscriber.rb +23 -0
- data/app/subscribers/spree/order_placed_subscriber.rb +58 -0
- data/app/subscribers/spree/order_status_subscriber.rb +82 -0
- data/app/subscribers/spree/payment_split_subscriber.rb +71 -0
- data/app/subscribers/spree/product_metrics_subscriber.rb +2 -2
- data/app/subscribers/spree/seller_onboarding_subscriber.rb +32 -0
- data/app/subscribers/spree/seller_transfer_reversal_subscriber.rb +26 -0
- data/app/subscribers/spree/seller_transfer_subscriber.rb +26 -0
- data/app/subscribers/spree/seller_user_email_subscriber.rb +27 -0
- data/app/subscribers/spree/tax_identifier_validation_subscriber.rb +26 -0
- data/app/validators/spree/addresses/phone_validator.rb +2 -2
- data/app/validators/spree/attachment_content_type_validator.rb +183 -0
- data/app/validators/spree/password_length_validator.rb +23 -0
- data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +8 -0
- data/app/views/spree/shared/_purchased_items_styles.html.erb +7 -0
- data/app/workflows/spree/carts/add_item.rb +234 -0
- data/app/workflows/spree/carts/complete.rb +516 -0
- data/app/workflows/spree/carts/merge.rb +90 -0
- data/app/workflows/spree/carts/recalculate.rb +54 -0
- data/app/workflows/spree/carts/recalculate_totals.rb +233 -0
- data/app/workflows/spree/carts/upsert_items.rb +368 -0
- data/app/workflows/spree/catalogs/activate.rb +51 -0
- data/app/workflows/spree/catalogs/create.rb +83 -0
- data/app/workflows/spree/catalogs/deactivate.rb +35 -0
- data/app/workflows/spree/catalogs/set_price_list.rb +130 -0
- data/app/workflows/spree/catalogs/update.rb +85 -0
- data/app/workflows/spree/claims/approve.rb +38 -0
- data/app/workflows/spree/claims/cancel.rb +37 -0
- data/app/workflows/spree/claims/create.rb +82 -0
- data/app/workflows/spree/claims/deny.rb +35 -0
- data/app/workflows/spree/claims/resolve.rb +166 -0
- data/app/workflows/spree/customers/anonymize.rb +502 -0
- data/app/workflows/spree/customers/create.rb +247 -0
- data/app/workflows/spree/data_requests/fulfill.rb +165 -0
- data/app/workflows/spree/deliveries/update_tracking.rb +96 -0
- data/app/workflows/spree/exchanges/approve.rb +38 -0
- data/app/workflows/spree/exchanges/cancel.rb +40 -0
- data/app/workflows/spree/exchanges/create.rb +90 -0
- data/app/workflows/spree/exchanges/fulfill.rb +146 -0
- data/app/workflows/spree/exchanges/receive.rb +86 -0
- data/app/workflows/spree/fulfillments/cancel.rb +87 -0
- data/app/workflows/spree/fulfillments/create.rb +346 -0
- data/app/workflows/spree/fulfillments/fulfill.rb +354 -0
- data/app/workflows/spree/fulfillments/mark_delivered.rb +67 -0
- data/app/workflows/spree/fulfillments/purchase_label.rb +40 -0
- data/app/workflows/spree/fulfillments/update.rb +123 -0
- data/app/workflows/spree/gift_cards/apply.rb +235 -0
- data/app/workflows/spree/gift_cards/cancel.rb +43 -0
- data/app/workflows/spree/gift_cards/redeem.rb +59 -0
- data/app/workflows/spree/gift_cards/remove.rb +78 -0
- data/app/workflows/spree/imports/complete.rb +45 -0
- data/app/workflows/spree/imports/complete_mapping.rb +50 -0
- data/app/workflows/spree/imports/retry_failed_rows.rb +46 -0
- data/app/workflows/spree/imports/start_mapping.rb +48 -0
- data/app/workflows/spree/imports/start_processing.rb +36 -0
- data/app/workflows/spree/invitations/accept.rb +50 -0
- data/app/workflows/spree/orders/add_item.rb +12 -0
- data/app/workflows/spree/orders/cancel.rb +260 -0
- data/app/workflows/spree/orders/complete.rb +169 -0
- data/app/workflows/spree/orders/recalculate.rb +11 -0
- data/app/workflows/spree/orders/recalculate_totals.rb +12 -0
- data/app/workflows/spree/orders/upsert_items.rb +28 -0
- data/app/workflows/spree/payment_sessions/complete.rb +44 -0
- data/app/workflows/spree/payments/capture.rb +109 -0
- data/app/workflows/spree/payments/handle_webhook.rb +100 -0
- data/app/workflows/spree/payments/process.rb +73 -0
- data/app/workflows/spree/payments/void.rb +72 -0
- data/app/workflows/spree/price_lists/activate.rb +49 -0
- data/app/workflows/spree/price_lists/create.rb +57 -0
- data/app/workflows/spree/price_lists/deactivate.rb +30 -0
- data/app/workflows/spree/price_lists/update.rb +117 -0
- data/app/workflows/spree/products/activate.rb +33 -0
- data/app/workflows/spree/products/approve.rb +52 -0
- data/app/workflows/spree/products/archive.rb +44 -0
- data/app/workflows/spree/products/create.rb +82 -0
- data/app/workflows/spree/products/destroy.rb +35 -0
- data/app/workflows/spree/products/draft.rb +49 -0
- data/app/workflows/spree/products/nested_attributes.rb +242 -0
- data/app/workflows/spree/products/propose.rb +62 -0
- data/app/workflows/spree/products/reject.rb +54 -0
- data/app/workflows/spree/products/update.rb +83 -0
- data/app/workflows/spree/purchase_orders/cancel.rb +57 -0
- data/app/workflows/spree/purchase_orders/close.rb +57 -0
- data/app/workflows/spree/purchase_orders/create.rb +68 -0
- data/app/workflows/spree/purchase_orders/mark_draft.rb +49 -0
- data/app/workflows/spree/purchase_orders/mark_ordered.rb +50 -0
- data/app/workflows/spree/purchase_orders/receive.rb +65 -0
- data/app/workflows/spree/purchase_orders/update.rb +56 -0
- data/app/workflows/spree/receivables/incoming_counter.rb +50 -0
- data/app/workflows/spree/refunds/create.rb +136 -0
- data/app/workflows/spree/refunds/order_payments.rb +129 -0
- data/app/workflows/spree/returns/approve.rb +47 -0
- data/app/workflows/spree/returns/cancel.rb +52 -0
- data/app/workflows/spree/returns/create.rb +135 -0
- data/app/workflows/spree/returns/purchase_label.rb +32 -0
- data/app/workflows/spree/returns/receive.rb +94 -0
- data/app/workflows/spree/returns/refund.rb +134 -0
- data/app/workflows/spree/seller_payouts/complete.rb +80 -0
- data/app/workflows/spree/seller_payouts/sweep.rb +192 -0
- data/app/workflows/spree/seller_requirement_submissions/accept.rb +40 -0
- data/app/workflows/spree/seller_requirement_submissions/create.rb +104 -0
- data/app/workflows/spree/seller_requirement_submissions/reject.rb +45 -0
- data/app/workflows/spree/seller_requirement_submissions/reviewable.rb +19 -0
- data/app/workflows/spree/seller_requirement_submissions/waive.rb +64 -0
- data/app/workflows/spree/seller_transfers/create.rb +131 -0
- data/app/workflows/spree/seller_transfers/reverse.rb +241 -0
- data/app/workflows/spree/sellers/approve.rb +88 -0
- data/app/workflows/spree/sellers/create.rb +72 -0
- data/app/workflows/spree/sellers/invite.rb +61 -0
- data/app/workflows/spree/sellers/reject.rb +52 -0
- data/app/workflows/spree/sellers/reopen_onboarding.rb +55 -0
- data/app/workflows/spree/sellers/start_onboarding.rb +46 -0
- data/app/workflows/spree/sellers/submit_for_review.rb +76 -0
- data/app/workflows/spree/sellers/suspend.rb +55 -0
- data/app/workflows/spree/shipping_labels/purchase.rb +154 -0
- data/app/workflows/spree/shipping_labels/record.rb +102 -0
- data/app/workflows/spree/shipping_labels/refund.rb +77 -0
- data/app/workflows/spree/stock_receipts/recording.rb +165 -0
- data/app/workflows/spree/stock_transfers/cancel.rb +103 -0
- data/app/workflows/spree/stock_transfers/close.rb +56 -0
- data/app/workflows/spree/stock_transfers/create.rb +63 -0
- data/app/workflows/spree/stock_transfers/mark_draft.rb +41 -0
- data/app/workflows/spree/stock_transfers/mark_in_transit.rb +91 -0
- data/app/workflows/spree/stock_transfers/mark_ready.rb +36 -0
- data/app/workflows/spree/stock_transfers/receive.rb +62 -0
- data/app/workflows/spree/stock_transfers/update.rb +55 -0
- data/app/workflows/spree/tax_exemption_certificates/verify.rb +48 -0
- data/app/workflows/spree/variants/create.rb +66 -0
- data/app/workflows/spree/variants/nested_attributes.rb +90 -0
- data/app/workflows/spree/variants/update.rb +55 -0
- data/config/brakeman.ignore +79 -79
- data/config/i18n-tasks.yml +4 -3
- data/config/initializers/mobility.rb +0 -4
- data/config/initializers/spree_store_setup_tasks.rb +4 -0
- data/config/locales/en.yml +955 -187
- data/config/routes.rb +11 -4
- data/db/migrate/20220201103922_add_first_name_and_last_name_to_spree_users.rb +7 -5
- data/db/migrate/20221215151408_add_selected_locale_to_spree_users.rb +6 -4
- data/db/migrate/20250127151258_add_phone_to_spree_users.rb +3 -1
- data/db/migrate/20250207084000_add_accepts_email_marketing_field_to_spree_users_table.rb +4 -2
- data/db/migrate/20250410061306_create_spree_invitations.rb +1 -1
- data/db/migrate/20250605131334_add_missing_fields_to_users.rb +24 -20
- data/db/migrate/20260315100000_add_product_media_support.rb +4 -1
- data/db/migrate/20260613000001_add_store_id_to_spree_role_users.rb +6 -3
- data/db/migrate/20260630000001_add_default_variant_id_to_spree_products.rb +9 -0
- data/db/migrate/20260708160054_create_spree_collections.rb +69 -0
- data/db/migrate/20260710000001_rename_taxons_to_categories.rb +28 -0
- data/db/migrate/20260722000001_add_searchable_sortable_to_spree_metafield_definitions.rb +6 -0
- data/db/migrate/20260727000001_add_unique_country_abbr_index_to_spree_states.rb +53 -0
- data/db/migrate/20260728000000_create_spree_customers_and_admin_users.rb +59 -0
- data/db/migrate/20260728000001_rename_prototypes_to_product_types.rb +59 -0
- data/db/migrate/20260728000002_create_spree_delivery_zones.rb +29 -0
- data/db/migrate/20260728000003_create_spree_carts.rb +58 -0
- data/db/migrate/20260728000003_rename_user_id_to_customer_id.rb +30 -0
- data/db/migrate/20260728000004_rename_shipping_to_fulfillment_and_delivery.rb +83 -0
- data/db/migrate/20260728000005_add_fulfillment_type_columns.rb +33 -0
- data/db/migrate/20260729000001_create_typed_adjustment_tables.rb +87 -0
- data/db/migrate/20260729100001_add_checkout_columns_to_spree_carts.rb +15 -0
- data/db/migrate/20260729100002_relax_owner_null_constraints.rb +7 -0
- data/db/migrate/20260729120001_rename_order_totals_to_standard_names.rb +7 -0
- data/db/migrate/20260729130001_create_spree_delivery_method_rules.rb +19 -0
- data/db/migrate/20260729140001_add_coupon_code_to_spree_orders.rb +6 -0
- data/db/migrate/20260729150001_add_store_to_delivery_methods_and_stock_locations.rb +6 -0
- data/db/migrate/20260802190001_create_spree_returns.rb +49 -0
- data/db/migrate/20260802210001_create_spree_exchanges_and_claims.rb +80 -0
- data/db/migrate/20260805100001_rename_return_authorization_reasons_to_return_reasons.rb +109 -0
- data/db/migrate/20260806000001_add_store_id_to_spree_tax_rates_and_categories.rb +16 -0
- data/db/migrate/20260806000001_create_spree_delivery_method_rule_products.rb +22 -0
- data/db/migrate/20260806000002_add_treatment_columns_to_spree_tax_lines.rb +19 -0
- data/db/migrate/20260806000003_create_spree_tax_identifiers.rb +41 -0
- data/db/migrate/20260806000004_add_tax_provider_to_spree_markets.rb +9 -0
- data/db/migrate/20260806000005_add_country_and_state_to_spree_tax_rates.rb +20 -0
- data/db/migrate/20260806100001_create_spree_product_type_custom_field_definitions.rb +17 -0
- data/db/migrate/20260806120001_add_rate_provider_to_delivery_methods.rb +7 -0
- data/db/migrate/20260806120002_add_carrier_columns_to_delivery_rates.rb +13 -0
- data/db/migrate/20260806130001_add_unique_store_type_index_to_spree_integrations.rb +9 -0
- data/db/migrate/20260807000001_add_permissions_to_spree_roles.rb +14 -0
- data/db/migrate/20260807000001_create_spree_companies.rb +66 -0
- data/db/migrate/20260807000003_create_spree_tax_exemption_certificates.rb +31 -0
- data/db/migrate/20260807120001_remove_claim_type_from_spree_claims.rb +5 -0
- data/db/migrate/20260807130001_add_country_state_codes_to_consumers.rb +28 -0
- data/db/migrate/20260807150001_reindex_spree_categories_by_store.rb +42 -0
- data/db/migrate/20260809000001_rename_metafields_to_custom_fields.rb +126 -0
- data/db/migrate/20260809100001_support_multi_rate_delivery_methods.rb +35 -0
- data/db/migrate/20260809120001_consolidate_metadata_columns.rb +120 -0
- data/db/migrate/20260809150001_promote_shipping_categories_to_delivery_profiles.rb +153 -0
- data/db/migrate/20260809170001_create_spree_channel_stock_locations.rb +17 -0
- data/db/migrate/20260809200001_add_rich_text_columns_for_action_text_removal.rb +11 -0
- data/db/migrate/20260810000001_rename_digitals_to_digital_assets.rb +38 -0
- data/db/migrate/20260811000001_add_delivery_tracking_to_fulfillments.rb +17 -0
- data/db/migrate/20260812000001_add_provider_type_to_spree_digital_assets.rb +7 -0
- data/db/migrate/20260812000002_add_metadata_to_spree_digital_assets.rb +11 -0
- data/db/migrate/20260813000001_create_spree_number_sequences.rb +13 -0
- data/db/migrate/20260813120001_add_setup_token_to_spree_stores.rb +11 -0
- data/db/migrate/20260813130001_add_capture_method_to_payment_methods.rb +17 -0
- data/db/migrate/20260813130001_add_unique_indexes_to_spree_data_feeds.rb +10 -0
- data/db/migrate/20260814000001_add_customs_fields_to_spree_variants.rb +7 -0
- data/db/migrate/20260814000001_rename_stock_items_to_stock_levels.rb +72 -0
- data/db/migrate/20260814000002_add_typed_stock_movements.rb +25 -0
- data/db/migrate/20260815000001_add_audience_to_spree_refresh_tokens.rb +14 -0
- data/db/migrate/20260815000002_add_resource_to_spree_roles.rb +190 -0
- data/db/migrate/20260816000001_create_spree_sellers.rb +59 -0
- data/db/migrate/20260816000002_add_seller_to_spree_products.rb +10 -0
- data/db/migrate/20260816210001_create_spree_commission_rates.rb +229 -0
- data/db/migrate/20260816210002_create_spree_commission_lines.rb +72 -0
- data/db/migrate/20260817000001_add_seller_to_spree_line_items.rb +15 -0
- data/db/migrate/20260817000002_add_seller_and_delivery_profile_to_spree_variants.rb +17 -0
- data/db/migrate/20260818000001_create_spree_external_references.rb +33 -0
- data/db/migrate/20260818000001_create_spree_seller_requirements.rb +60 -0
- data/db/migrate/20260818000001_rename_spree_payments_state_to_status.rb +5 -0
- data/db/migrate/20260818000002_add_price_source_to_spree_line_items.rb +9 -0
- data/db/migrate/20260818090001_rename_assets_to_media.rb +10 -0
- data/db/migrate/20260819000001_create_spree_seller_requirement_custom_fields.rb +15 -0
- data/db/migrate/20260819000001_replace_payment_method_display_on_with_storefront_visible.rb +25 -0
- data/db/migrate/20260819000002_rename_spree_gift_cards_state_to_status.rb +5 -0
- data/db/migrate/20260820000001_add_seller_business_identity_and_stock_locations.rb +17 -0
- data/db/migrate/20260820000001_create_spree_order_groups.rb +33 -0
- data/db/migrate/20260820000002_add_order_group_to_spree_orders_and_payments.rb +14 -0
- data/db/migrate/20260820000003_create_spree_payment_splits.rb +21 -0
- data/db/migrate/20260820000004_add_order_to_spree_refunds.rb +32 -0
- data/db/migrate/20260821000001_add_claimed_amount_to_spree_payment_splits.rb +14 -0
- data/db/migrate/20260822000001_create_spree_seller_transfers.rb +94 -0
- data/db/migrate/20260822000002_create_spree_seller_payouts.rb +48 -0
- data/db/migrate/20260822000003_add_payout_account_to_spree_sellers.rb +11 -0
- data/db/migrate/20260824000001_add_store_to_spree_media.rb +16 -0
- data/db/migrate/20260825000001_create_spree_catalogs.rb +40 -0
- data/db/migrate/20260826000001_add_owner_to_spree_addresses.rb +30 -0
- data/db/migrate/20260826000002_create_spree_product_submissions.rb +28 -0
- data/db/migrate/20260827000001_add_seller_to_spree_exports.rb +5 -0
- data/db/migrate/20260828100001_unify_import_and_export_tenancy.rb +79 -0
- data/db/migrate/20260830000001_add_catalog_to_spree_price_lists.rb +47 -0
- data/db/migrate/20260830000002_add_po_number_to_purchases.rb +16 -0
- data/db/migrate/20260830000002_add_price_adjustment_to_spree_price_lists.rb +14 -0
- data/db/migrate/20260831000001_add_description_to_spree_catalogs.rb +7 -0
- data/db/migrate/20260831000002_add_quantity_rules_to_spree_variants.rb +18 -0
- data/db/migrate/20260831000003_create_spree_catalog_terms.rb +35 -0
- data/db/migrate/20260831100001_create_spree_package_types.rb +38 -0
- data/db/migrate/20260831100002_add_carton_details_to_spree_variants.rb +10 -0
- data/db/migrate/20260831100003_add_unpriced_to_spree_delivery_rates.rb +8 -0
- data/db/migrate/20260901000001_add_seller_to_spree_delivery_methods.rb +11 -0
- data/db/migrate/20260902000001_add_store_and_filter_key_to_custom_field_definitions.rb +143 -0
- data/db/migrate/20260902000002_add_eu_legal_compliance.rb +68 -0
- data/db/migrate/20260902000002_replace_order_cancellation_tables_with_cancel_columns.rb +88 -0
- data/db/migrate/20260902100001_rename_wished_items_to_wishlist_items.rb +8 -0
- data/db/migrate/20260902100002_rename_promo_total_to_discount_total.rb +9 -0
- data/db/migrate/20260902100003_rename_address_name_and_postal_code_columns.rb +24 -0
- data/db/migrate/20260902100004_rename_option_presentation_to_label.rb +13 -0
- data/db/migrate/20260903000001_create_spree_shipping_labels_and_deliveries.rb +73 -0
- data/db/migrate/20260903120000_create_spree_saved_reports.rb +57 -0
- data/db/migrate/20260904000001_add_returns_enabled_to_stock_locations.rb +14 -0
- data/db/migrate/20260906000001_add_commission_total_to_orders.rb +22 -0
- data/db/migrate/20260907000001_add_inventory_operations.rb +113 -0
- data/db/migrate/20260907000001_add_min_quantity_to_spree_prices.rb +28 -0
- data/db/migrate/20260907000002_create_spree_price_adjustment_tiers.rb +19 -0
- data/db/migrate/20260908000001_add_seller_to_spree_package_types.rb +78 -0
- data/db/migrate/20260910000001_add_stock_receipts.rb +44 -0
- data/db/migrate/20260910000002_add_counters_to_spree_stock_levels.rb +6 -0
- data/db/migrate/20260910120000_index_reporting_time_columns.rb +12 -0
- data/db/migrate/20260912120000_index_reporting_lifetime_and_promotion_columns.rb +19 -0
- data/db/migrate/20260914000001_add_settlement_to_spree_seller_transfers.rb +18 -0
- data/db/migrate/20260914230000_add_store_to_spree_option_types.rb +46 -0
- data/db/sample_data/collections.rb +58 -0
- data/db/sample_data/custom_field_definitions.rb +9 -0
- data/db/sample_data/digital_products.rb +26 -0
- data/db/sample_data/fulfillment.rb +88 -0
- data/db/sample_data/options.rb +25 -1
- data/db/sample_data/orders.rb +53 -30
- data/db/sample_data/payment_methods.rb +2 -2
- data/db/sample_data/price_list_prices.csv +25 -0
- data/db/sample_data/product_type_categories.rb +27 -0
- data/db/sample_data/product_types.rb +83 -0
- data/db/sample_data/products.csv +39 -38
- data/db/sample_data/wholesale.rb +42 -3
- data/lib/generators/spree/api_resource/api_resource_generator.rb +1 -1
- data/lib/generators/spree/authentication/dummy/templates/create_spree_admin_users.rb.tt +1 -0
- data/lib/generators/spree/controller_decorator/controller_decorator_generator.rb +4 -4
- data/lib/generators/spree/dummy/dummy_generator.rb +5 -12
- data/lib/generators/spree/dummy/templates/rails/test.rb +5 -0
- data/lib/generators/spree/model/model_generator.rb +6 -6
- data/lib/generators/spree/model/templates/model.rb.tt +7 -3
- data/lib/spree/category_permalink_deduplicator.rb +328 -0
- data/lib/spree/core/configuration.rb +96 -47
- data/lib/spree/core/controller_helpers/store.rb +8 -9
- data/lib/spree/core/dependencies.rb +324 -71
- data/lib/spree/core/dependencies_helper.rb +4 -0
- data/lib/spree/core/engine.rb +333 -75
- data/lib/spree/core/number_generator.rb +37 -33
- data/lib/spree/core/permission_configuration.rb +183 -91
- data/lib/spree/core/permissions/default_catalog.rb +236 -0
- data/lib/spree/core/permissions/entry.rb +15 -0
- data/lib/spree/core/permissions/scope.rb +76 -0
- data/lib/spree/core/preferences/masking.rb +24 -2
- data/lib/spree/core/preferences/preferable.rb +59 -3
- data/lib/spree/core/preferences/preferable_class_methods.rb +41 -2
- data/lib/spree/core/preferences/runtime_configuration.rb +68 -9
- data/lib/spree/core/previews/admin_user_preview.rb +0 -8
- data/lib/spree/core/previews/invitation_preview.rb +1 -1
- data/lib/spree/core/previews/preview_data.rb +2 -2
- data/lib/spree/core/previews/seller_user_preview.rb +14 -0
- data/lib/spree/core/pricing/context.rb +28 -8
- data/lib/spree/core/pricing/price_resolution.rb +150 -0
- data/lib/spree/core/pricing/resolver.rb +17 -107
- data/lib/spree/core/ransack_configuration.rb +3 -3
- data/lib/spree/core/routes.rb +4 -6
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +422 -62
- data/lib/spree/events/adapters/base.rb +42 -15
- data/lib/spree/hooks.rb +146 -0
- data/lib/spree/iso_data.rb +188 -0
- data/lib/spree/measurement.rb +113 -0
- data/lib/spree/money.rb +70 -0
- data/lib/spree/number_generators/registry.rb +80 -0
- data/lib/spree/reporting/adapters/base.rb +71 -0
- data/lib/spree/reporting/adapters/live.rb +677 -0
- data/lib/spree/reporting/counter_result.rb +19 -0
- data/lib/spree/reporting/counters.rb +33 -0
- data/lib/spree/reporting/default_vocabulary.rb +705 -0
- data/lib/spree/reporting/hydration.rb +64 -0
- data/lib/spree/reporting/query.rb +487 -0
- data/lib/spree/reporting/registry.rb +294 -0
- data/lib/spree/reporting/result.rb +10 -0
- data/lib/spree/reporting/schema.rb +124 -0
- data/lib/spree/reporting.rb +49 -0
- data/lib/spree/service_module.rb +6 -0
- data/lib/spree/store_scope_guard.rb +180 -0
- data/lib/spree/testing_support/action_text.rb +33 -0
- data/lib/spree/testing_support/common_rake.rb +4 -66
- data/lib/spree/testing_support/country_pool.rb +50 -0
- data/lib/spree/testing_support/extension_rake.rb +2 -6
- data/lib/spree/testing_support/factories/address_factory.rb +45 -15
- data/lib/spree/testing_support/factories/api_key_factory.rb +1 -1
- data/lib/spree/testing_support/factories/calculator_factory.rb +0 -3
- data/lib/spree/testing_support/factories/cart_factory.rb +60 -0
- data/lib/spree/testing_support/factories/catalog_factory.rb +37 -0
- data/lib/spree/testing_support/factories/category_factory.rb +21 -0
- data/lib/spree/testing_support/factories/claim_factory.rb +33 -0
- data/lib/spree/testing_support/factories/classification_factory.rb +3 -2
- data/lib/spree/testing_support/factories/collection_factory.rb +25 -0
- data/lib/spree/testing_support/factories/collection_rule_factory.rb +47 -0
- data/lib/spree/testing_support/factories/commission_rate_factory.rb +75 -0
- data/lib/spree/testing_support/factories/company_factory.rb +31 -0
- data/lib/spree/testing_support/factories/consent_record_factory.rb +10 -0
- data/lib/spree/testing_support/factories/country_factory.rb +28 -9
- data/lib/spree/testing_support/factories/custom_field_definition_factory.rb +79 -0
- data/lib/spree/testing_support/factories/custom_field_factory.rb +64 -0
- data/lib/spree/testing_support/factories/customer_group_user_factory.rb +1 -1
- data/lib/spree/testing_support/factories/data_request_factory.rb +17 -0
- data/lib/spree/testing_support/factories/delivery_factory.rb +13 -0
- data/lib/spree/testing_support/factories/delivery_method_factory.rb +47 -0
- data/lib/spree/testing_support/factories/delivery_origin_group_factory.rb +5 -0
- data/lib/spree/testing_support/factories/delivery_profile_factory.rb +9 -0
- data/lib/spree/testing_support/factories/delivery_rate_factory.rb +7 -0
- data/lib/spree/testing_support/factories/delivery_zone_factory.rb +34 -0
- data/lib/spree/testing_support/factories/digital_asset_factory.rb +13 -0
- data/lib/spree/testing_support/factories/digital_link_factory.rb +1 -1
- data/lib/spree/testing_support/factories/discount_factory.rb +9 -0
- data/lib/spree/testing_support/factories/exchange_factory.rb +50 -0
- data/lib/spree/testing_support/factories/external_reference_factory.rb +8 -0
- data/lib/spree/testing_support/factories/fee_factory.rb +8 -0
- data/lib/spree/testing_support/factories/fulfillment_factory.rb +32 -0
- data/lib/spree/testing_support/factories/fulfillment_item_factory.rb +17 -0
- data/lib/spree/testing_support/factories/gateway_customer_factory.rb +1 -1
- data/lib/spree/testing_support/factories/gift_card_factory.rb +2 -2
- data/lib/spree/testing_support/factories/import_factory.rb +33 -1
- data/lib/spree/testing_support/factories/line_item_factory.rb +10 -5
- data/lib/spree/testing_support/factories/market_factory.rb +5 -12
- data/lib/spree/testing_support/factories/media_factory.rb +36 -0
- data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +1 -1
- data/lib/spree/testing_support/factories/number_sequence_factory.rb +7 -0
- data/lib/spree/testing_support/factories/options_factory.rb +5 -5
- data/lib/spree/testing_support/factories/order_factory.rb +39 -23
- data/lib/spree/testing_support/factories/order_group_factory.rb +29 -0
- data/lib/spree/testing_support/factories/package_type_factory.rb +36 -0
- data/lib/spree/testing_support/factories/payment_factory.rb +7 -7
- data/lib/spree/testing_support/factories/payment_method_factory.rb +5 -5
- data/lib/spree/testing_support/factories/payment_session_factory.rb +10 -6
- data/lib/spree/testing_support/factories/payment_setup_session_factory.rb +2 -2
- data/lib/spree/testing_support/factories/payment_split_factory.rb +10 -0
- data/lib/spree/testing_support/factories/price_adjustment_tier_factory.rb +7 -0
- data/lib/spree/testing_support/factories/price_factory.rb +6 -0
- data/lib/spree/testing_support/factories/price_rule_factory.rb +0 -10
- data/lib/spree/testing_support/factories/product_collection_factory.rb +8 -0
- data/lib/spree/testing_support/factories/product_factory.rb +35 -7
- data/lib/spree/testing_support/factories/product_type_custom_field_definition_factory.rb +10 -0
- data/lib/spree/testing_support/factories/product_type_factory.rb +12 -0
- data/lib/spree/testing_support/factories/promotion_factory.rb +4 -0
- data/lib/spree/testing_support/factories/promotion_rule_factory.rb +1 -1
- data/lib/spree/testing_support/factories/purchase_order_factory.rb +49 -0
- data/lib/spree/testing_support/factories/refresh_token_factory.rb +2 -0
- data/lib/spree/testing_support/factories/refund_factory.rb +1 -2
- data/lib/spree/testing_support/factories/return_factory.rb +52 -0
- data/lib/spree/testing_support/factories/return_reason_factory.rb +19 -0
- data/lib/spree/testing_support/factories/role_factory.rb +1 -0
- data/lib/spree/testing_support/factories/saved_report_factory.rb +7 -0
- data/lib/spree/testing_support/factories/seller_factory.rb +34 -0
- data/lib/spree/testing_support/factories/seller_payout_factory.rb +14 -0
- data/lib/spree/testing_support/factories/seller_requirement_factory.rb +61 -0
- data/lib/spree/testing_support/factories/seller_requirement_submission_factory.rb +32 -0
- data/lib/spree/testing_support/factories/seller_transfer_factory.rb +21 -0
- data/lib/spree/testing_support/factories/shipping_label_factory.rb +44 -0
- data/lib/spree/testing_support/factories/state_factory.rb +18 -5
- data/lib/spree/testing_support/factories/stock_level_factory.rb +24 -0
- data/lib/spree/testing_support/factories/stock_location_factory.rb +12 -6
- data/lib/spree/testing_support/factories/stock_movement_factory.rb +20 -3
- data/lib/spree/testing_support/factories/stock_receipt_factory.rb +37 -0
- data/lib/spree/testing_support/factories/stock_reservation_factory.rb +11 -8
- data/lib/spree/testing_support/factories/stock_transfer_factory.rb +53 -6
- data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -3
- data/lib/spree/testing_support/factories/supplier_factory.rb +17 -0
- data/lib/spree/testing_support/factories/tax_exemption_certificate_factory.rb +18 -0
- data/lib/spree/testing_support/factories/tax_identifier_factory.rb +33 -0
- data/lib/spree/testing_support/factories/tax_line_factory.rb +19 -0
- data/lib/spree/testing_support/factories/tax_rate_factory.rb +5 -2
- data/lib/spree/testing_support/factories/taxon_factory.rb +6 -23
- data/lib/spree/testing_support/factories/taxonomy_factory.rb +8 -0
- data/lib/spree/testing_support/factories/user_factory.rb +8 -12
- data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
- data/lib/spree/testing_support/factories/wishlist_factory.rb +1 -1
- data/lib/spree/testing_support/factories/wishlist_item_factory.rb +10 -0
- data/lib/spree/testing_support/factories/zone_factory.rb +2 -25
- data/lib/spree/testing_support/factories/zone_member_factory.rb +2 -1
- data/lib/spree/testing_support/factories.rb +1 -6
- data/lib/spree/testing_support/imports.rb +16 -0
- data/lib/spree/testing_support/label_provider.rb +49 -0
- data/lib/spree/testing_support/lifecycle_events.rb +6 -1
- data/lib/spree/testing_support/metadata.rb +38 -36
- data/lib/spree/testing_support/order_walkthrough.rb +46 -36
- data/lib/spree/testing_support/preferences.rb +39 -0
- data/lib/spree/testing_support/purchase_order.rb +111 -0
- data/lib/spree/testing_support/store.rb +6 -2
- data/lib/spree/testing_support/tax_identifier_validators.rb +68 -0
- data/lib/spree/translations.rb +139 -5
- data/lib/spree/upgrades/5_4_to_5_5/manifest.yml +1 -1
- data/lib/spree/upgrades/5_5_to_5_6/manifest.yml +17 -9
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +683 -0
- data/lib/spree/validators.rb +74 -0
- data/lib/spree/workflow.rb +335 -0
- data/lib/tasks/backfill_custom_field_definition_stores.rake +82 -0
- data/lib/tasks/backfill_import_export_tenancy.rake +93 -0
- data/lib/tasks/backfill_library_media_placements.rake +29 -0
- data/lib/tasks/backfill_media_store_ids.rake +70 -0
- data/lib/tasks/backfill_reason_store_ids.rake +28 -0
- data/lib/tasks/capture_methods_migration.rake +30 -0
- data/lib/tasks/carts_migration.rake +113 -0
- data/lib/tasks/categories_and_collections.rake +188 -0
- data/lib/tasks/cli.rake +22 -3
- data/lib/tasks/consolidate_metadata.rake +73 -0
- data/lib/tasks/core.rake +1 -11
- data/lib/tasks/deliveries_migration.rake +52 -0
- data/lib/tasks/delivery_method_rules_migration.rake +51 -0
- data/lib/tasks/delivery_migration.rake +213 -0
- data/lib/tasks/delivery_profiles_migration.rake +109 -0
- data/lib/tasks/fold_store_credit_categories.rake +40 -0
- data/lib/tasks/fulfillment_statuses_migration.rake +59 -0
- data/lib/tasks/inventory_operations_migration.rake +223 -0
- data/lib/tasks/markets.rake +7 -2
- data/lib/tasks/media.rake +2 -2
- data/lib/tasks/media_class_rename.rake +56 -0
- data/lib/tasks/migrate_country_state_codes.rake +228 -0
- data/lib/tasks/migrate_promotion_option_value_rules.rake +124 -0
- data/lib/tasks/migrate_returns.rake +322 -0
- data/lib/tasks/migrate_rich_text_to_columns.rake +128 -0
- data/lib/tasks/migrate_users_to_customers.rake +161 -0
- data/lib/tasks/order_coupon_code_backfill.rake +32 -0
- data/lib/tasks/order_market_backfill.rake +27 -0
- data/lib/tasks/package_types.rake +62 -0
- data/lib/tasks/price_history.rake +49 -6
- data/lib/tasks/product_tag_tenants.rake +1 -3
- data/lib/tasks/products.rake +21 -2
- data/lib/tasks/remove_master_variant.rake +58 -0
- data/lib/tasks/rich_text.rake +44 -0
- data/lib/tasks/seller_sample_data.rake +142 -0
- data/lib/tasks/setup.rake +18 -0
- data/lib/tasks/single_store_associations.rake +11 -0
- data/lib/tasks/stock.rake +15 -0
- data/lib/tasks/store_binding_migration.rake +33 -0
- data/lib/tasks/store_settings.rake +104 -0
- data/lib/tasks/tax_zones_migration.rake +147 -0
- data/lib/tasks/taxons.rake +9 -10
- data/lib/tasks/typed_adjustments_migration.rake +189 -0
- data/lib/tasks/typed_stock_movements_migration.rake +250 -0
- data/lib/tasks/upgrade.rake +13 -8
- data/spec/fixtures/files/eu_vat_numbers.txt +56 -0
- data/spec/fixtures/files/price_list_prices_import.csv +4 -0
- data/spec/fixtures/files/products_import.csv +1 -1
- data/spec/fixtures/tiny-video.mp4 +0 -0
- metadata +792 -538
- data/app/assets/config/spree_core_manifest.js +0 -4
- data/app/assets/images/google_on_white_hdpi.png +0 -0
- data/app/assets/images/logo.png +0 -0
- data/app/assets/images/payment_icons/ach.svg +0 -1
- data/app/assets/images/payment_icons/achdirectdebit.svg +0 -1
- data/app/assets/images/payment_icons/ada.svg +0 -1
- data/app/assets/images/payment_icons/adyen.svg +0 -1
- data/app/assets/images/payment_icons/affinbank.svg +0 -1
- data/app/assets/images/payment_icons/affirm.svg +0 -1
- data/app/assets/images/payment_icons/afterpay.svg +0 -1
- data/app/assets/images/payment_icons/airtel_money.svg +0 -1
- data/app/assets/images/payment_icons/airteltigo_mobile_money.svg +0 -1
- data/app/assets/images/payment_icons/alandsbanken.svg +0 -1
- data/app/assets/images/payment_icons/alipay.svg +0 -1
- data/app/assets/images/payment_icons/alipay_hk.svg +0 -1
- data/app/assets/images/payment_icons/alliancebank.svg +0 -1
- data/app/assets/images/payment_icons/alma.svg +0 -1
- data/app/assets/images/payment_icons/amazon.svg +0 -1
- data/app/assets/images/payment_icons/ambank.svg +0 -1
- data/app/assets/images/payment_icons/american_express.svg +0 -1
- data/app/assets/images/payment_icons/apecoin.svg +0 -1
- data/app/assets/images/payment_icons/apple_pay.svg +0 -1
- data/app/assets/images/payment_icons/arbitrum.svg +0 -1
- data/app/assets/images/payment_icons/atome.svg +0 -1
- data/app/assets/images/payment_icons/authorizenet.svg +0 -1
- data/app/assets/images/payment_icons/avalanche.svg +0 -1
- data/app/assets/images/payment_icons/bacs.svg +0 -1
- data/app/assets/images/payment_icons/bancontact.svg +0 -1
- data/app/assets/images/payment_icons/bangkokbank.svg +0 -1
- data/app/assets/images/payment_icons/bankislam.svg +0 -1
- data/app/assets/images/payment_icons/bankmuamalat.svg +0 -1
- data/app/assets/images/payment_icons/bankofamerica.svg +0 -1
- data/app/assets/images/payment_icons/bankrakyat.svg +0 -1
- data/app/assets/images/payment_icons/banktransfer.svg +0 -1
- data/app/assets/images/payment_icons/base.svg +0 -1
- data/app/assets/images/payment_icons/becs.svg +0 -1
- data/app/assets/images/payment_icons/benefit.svg +0 -1
- data/app/assets/images/payment_icons/billie.svg +0 -1
- data/app/assets/images/payment_icons/bitcoin.svg +0 -1
- data/app/assets/images/payment_icons/bitcoin_cash.svg +0 -1
- data/app/assets/images/payment_icons/bizum.svg +0 -1
- data/app/assets/images/payment_icons/blik.svg +0 -1
- data/app/assets/images/payment_icons/bnbchain.svg +0 -1
- data/app/assets/images/payment_icons/bogus.svg +0 -1
- data/app/assets/images/payment_icons/bogus_app_coin.svg +0 -1
- data/app/assets/images/payment_icons/boleto.svg +0 -1
- data/app/assets/images/payment_icons/boost.svg +0 -1
- data/app/assets/images/payment_icons/braintree.svg +0 -1
- data/app/assets/images/payment_icons/buckaroopaybybank.svg +0 -1
- data/app/assets/images/payment_icons/busd.svg +0 -1
- data/app/assets/images/payment_icons/cartes_bancaires.svg +0 -1
- data/app/assets/images/payment_icons/cash.svg +0 -1
- data/app/assets/images/payment_icons/cashappafterpay.svg +0 -1
- data/app/assets/images/payment_icons/cashapppay.svg +0 -1
- data/app/assets/images/payment_icons/checkout_finance.svg +0 -1
- data/app/assets/images/payment_icons/chinabank.svg +0 -1
- data/app/assets/images/payment_icons/clearpay.svg +0 -1
- data/app/assets/images/payment_icons/collector_bank.svg +0 -1
- data/app/assets/images/payment_icons/dai.svg +0 -1
- data/app/assets/images/payment_icons/dana.svg +0 -1
- data/app/assets/images/payment_icons/dankort.svg +0 -1
- data/app/assets/images/payment_icons/danske_bank.svg +0 -1
- data/app/assets/images/payment_icons/deutschebank.svg +0 -1
- data/app/assets/images/payment_icons/diners_club.svg +0 -1
- data/app/assets/images/payment_icons/directbanktransferlatinamerica.svg +0 -1
- data/app/assets/images/payment_icons/discover.svg +0 -1
- data/app/assets/images/payment_icons/dogecoin.svg +0 -1
- data/app/assets/images/payment_icons/duitnow.svg +0 -1
- data/app/assets/images/payment_icons/eft_secure.svg +0 -1
- data/app/assets/images/payment_icons/eftpos_au.svg +0 -1
- data/app/assets/images/payment_icons/eftposau.svg +0 -1
- data/app/assets/images/payment_icons/elo.svg +0 -1
- data/app/assets/images/payment_icons/eos.svg +0 -1
- data/app/assets/images/payment_icons/eps.svg +0 -1
- data/app/assets/images/payment_icons/ethereum.svg +0 -1
- data/app/assets/images/payment_icons/eurobonuscheckout.svg +0 -1
- data/app/assets/images/payment_icons/ezcash.svg +0 -1
- data/app/assets/images/payment_icons/fantom.svg +0 -1
- data/app/assets/images/payment_icons/fpx.svg +0 -1
- data/app/assets/images/payment_icons/gcash.svg +0 -1
- data/app/assets/images/payment_icons/generic.svg +0 -1
- data/app/assets/images/payment_icons/genericbank.svg +0 -1
- data/app/assets/images/payment_icons/gift-card.svg +0 -1
- data/app/assets/images/payment_icons/giropay.svg +0 -1
- data/app/assets/images/payment_icons/glbepaypal.svg +0 -1
- data/app/assets/images/payment_icons/gmobanktransfer.svg +0 -1
- data/app/assets/images/payment_icons/gnosis.svg +0 -1
- data/app/assets/images/payment_icons/google_pay.svg +0 -1
- data/app/assets/images/payment_icons/google_wallet.svg +0 -1
- data/app/assets/images/payment_icons/gopay.svg +0 -1
- data/app/assets/images/payment_icons/grabpay.svg +0 -1
- data/app/assets/images/payment_icons/gusd.svg +0 -1
- data/app/assets/images/payment_icons/handelsbanken.svg +0 -1
- data/app/assets/images/payment_icons/hongleongbank.svg +0 -1
- data/app/assets/images/payment_icons/hypercard.svg +0 -1
- data/app/assets/images/payment_icons/hypercash.svg +0 -1
- data/app/assets/images/payment_icons/ideal.svg +0 -1
- data/app/assets/images/payment_icons/in3viaideal.svg +0 -1
- data/app/assets/images/payment_icons/inbank.svg +0 -1
- data/app/assets/images/payment_icons/jcb.svg +0 -1
- data/app/assets/images/payment_icons/kakao_pay.svg +0 -1
- data/app/assets/images/payment_icons/kasikornbank.svg +0 -1
- data/app/assets/images/payment_icons/klarna-pay-later.svg +0 -1
- data/app/assets/images/payment_icons/klarna-pay-now.svg +0 -1
- data/app/assets/images/payment_icons/klarna-slice-it.svg +0 -1
- data/app/assets/images/payment_icons/klarna.svg +0 -1
- data/app/assets/images/payment_icons/knet.svg +0 -1
- data/app/assets/images/payment_icons/krungthaibank.svg +0 -1
- data/app/assets/images/payment_icons/landbank.svg +0 -1
- data/app/assets/images/payment_icons/laybuybyklarna.svg +0 -1
- data/app/assets/images/payment_icons/litecoin.svg +0 -1
- data/app/assets/images/payment_icons/lunchcheck.svg +0 -1
- data/app/assets/images/payment_icons/mach.svg +0 -1
- data/app/assets/images/payment_icons/mada.svg +0 -1
- data/app/assets/images/payment_icons/maestro.svg +0 -1
- data/app/assets/images/payment_icons/master.svg +0 -1
- data/app/assets/images/payment_icons/mayabank.svg +0 -1
- data/app/assets/images/payment_icons/maybank.svg +0 -1
- data/app/assets/images/payment_icons/maybankm2u.svg +0 -1
- data/app/assets/images/payment_icons/maybankqrpay.svg +0 -1
- data/app/assets/images/payment_icons/mbway.svg +0 -1
- data/app/assets/images/payment_icons/mcash.svg +0 -1
- data/app/assets/images/payment_icons/medicinosbankas.svg +0 -1
- data/app/assets/images/payment_icons/mercadopago.svg +0 -1
- data/app/assets/images/payment_icons/metrobank.svg +0 -1
- data/app/assets/images/payment_icons/mobilepay.svg +0 -1
- data/app/assets/images/payment_icons/momopay.svg +0 -1
- data/app/assets/images/payment_icons/monero.svg +0 -1
- data/app/assets/images/payment_icons/mtn_mobile_money.svg +0 -1
- data/app/assets/images/payment_icons/mybank.svg +0 -1
- data/app/assets/images/payment_icons/naver_pay.svg +0 -1
- data/app/assets/images/payment_icons/nelo.svg +0 -1
- data/app/assets/images/payment_icons/netbanking.svg +0 -1
- data/app/assets/images/payment_icons/novalnetdirectdebitach.svg +0 -1
- data/app/assets/images/payment_icons/nubank.svg +0 -1
- data/app/assets/images/payment_icons/ocbcbank.svg +0 -1
- data/app/assets/images/payment_icons/offlinebanktransferlatinamerica.svg +0 -1
- data/app/assets/images/payment_icons/ola_money.svg +0 -1
- data/app/assets/images/payment_icons/oney.svg +0 -1
- data/app/assets/images/payment_icons/onlinebanking.svg +0 -1
- data/app/assets/images/payment_icons/onlinebanktransfer.svg +0 -1
- data/app/assets/images/payment_icons/orangemobilemoney.svg +0 -1
- data/app/assets/images/payment_icons/otpbank.svg +0 -1
- data/app/assets/images/payment_icons/oxxo.svg +0 -1
- data/app/assets/images/payment_icons/paybybank.svg +0 -1
- data/app/assets/images/payment_icons/paybybankmollie.svg +0 -1
- data/app/assets/images/payment_icons/paybybankus.svg +0 -1
- data/app/assets/images/payment_icons/paycash.svg +0 -1
- data/app/assets/images/payment_icons/payco.svg +0 -1
- data/app/assets/images/payment_icons/payeverpaybybank.svg +0 -1
- data/app/assets/images/payment_icons/payme.svg +0 -1
- data/app/assets/images/payment_icons/paynow.svg +0 -1
- data/app/assets/images/payment_icons/paynowmbank.svg +0 -1
- data/app/assets/images/payment_icons/paypal.svg +0 -1
- data/app/assets/images/payment_icons/paypo.svg +0 -1
- data/app/assets/images/payment_icons/payrexxbanktransfer.svg +0 -1
- data/app/assets/images/payment_icons/payrexxpaybybank.svg +0 -1
- data/app/assets/images/payment_icons/paysafecash.svg +0 -1
- data/app/assets/images/payment_icons/pix.svg +0 -1
- data/app/assets/images/payment_icons/polygon.svg +0 -1
- data/app/assets/images/payment_icons/przelewy24.svg +0 -1
- data/app/assets/images/payment_icons/publicbank.svg +0 -1
- data/app/assets/images/payment_icons/publicbank_pbe.svg +0 -1
- data/app/assets/images/payment_icons/qr_promptpay.svg +0 -1
- data/app/assets/images/payment_icons/rabobank.svg +0 -1
- data/app/assets/images/payment_icons/revolut.svg +0 -1
- data/app/assets/images/payment_icons/rhbbank.svg +0 -1
- data/app/assets/images/payment_icons/riverty.svg +0 -1
- data/app/assets/images/payment_icons/safetypaybanktransfer.svg +0 -1
- data/app/assets/images/payment_icons/samsung_pay.svg +0 -1
- data/app/assets/images/payment_icons/satispay.svg +0 -1
- data/app/assets/images/payment_icons/scalapay.svg +0 -1
- data/app/assets/images/payment_icons/scotiabank.svg +0 -1
- data/app/assets/images/payment_icons/seabankid.svg +0 -1
- data/app/assets/images/payment_icons/seabankph.svg +0 -1
- data/app/assets/images/payment_icons/sepa_bank_transfer.svg +0 -1
- data/app/assets/images/payment_icons/sepadebit.svg +0 -22
- data/app/assets/images/payment_icons/sepadirectdebit.svg +0 -1
- data/app/assets/images/payment_icons/seveneleven.svg +0 -1
- data/app/assets/images/payment_icons/shib.svg +0 -1
- data/app/assets/images/payment_icons/shopcash.svg +0 -1
- data/app/assets/images/payment_icons/siauliubankas.svg +0 -1
- data/app/assets/images/payment_icons/skeps.svg +0 -1
- data/app/assets/images/payment_icons/snap_checkout.svg +0 -1
- data/app/assets/images/payment_icons/sofort.svg +0 -1
- data/app/assets/images/payment_icons/softbank.svg +0 -1
- data/app/assets/images/payment_icons/spei.svg +0 -1
- data/app/assets/images/payment_icons/storecredit.svg +0 -1
- data/app/assets/images/payment_icons/stripe.svg +0 -1
- data/app/assets/images/payment_icons/sveacheckout.svg +0 -1
- data/app/assets/images/payment_icons/swedbank.svg +0 -1
- data/app/assets/images/payment_icons/swish.svg +0 -1
- data/app/assets/images/payment_icons/tbibank.svg +0 -1
- data/app/assets/images/payment_icons/thanachartbank.svg +0 -1
- data/app/assets/images/payment_icons/tnmmoney.svg +0 -1
- data/app/assets/images/payment_icons/touchngo.svg +0 -1
- data/app/assets/images/payment_icons/trustly.svg +0 -1
- data/app/assets/images/payment_icons/twint.svg +0 -1
- data/app/assets/images/payment_icons/uaevisa.svg +0 -1
- data/app/assets/images/payment_icons/unionpay.svg +0 -1
- data/app/assets/images/payment_icons/unzerbanktransfer.svg +0 -1
- data/app/assets/images/payment_icons/upi.svg +0 -1
- data/app/assets/images/payment_icons/usbank.svg +0 -1
- data/app/assets/images/payment_icons/usdc.svg +0 -1
- data/app/assets/images/payment_icons/usdt.svg +0 -1
- data/app/assets/images/payment_icons/venmo.svg +0 -1
- data/app/assets/images/payment_icons/vipps.svg +0 -1
- data/app/assets/images/payment_icons/visa.svg +0 -1
- data/app/assets/images/payment_icons/volksbank.svg +0 -1
- data/app/assets/images/payment_icons/waavepaybybank.svg +0 -1
- data/app/assets/images/payment_icons/walley.svg +0 -1
- data/app/assets/images/payment_icons/wbtc.svg +0 -1
- data/app/assets/images/payment_icons/wechatpay.svg +0 -1
- data/app/assets/images/payment_icons/whishcheckout.svg +0 -1
- data/app/assets/images/payment_icons/wingbank.svg +0 -1
- data/app/assets/images/payment_icons/xrp.svg +0 -1
- data/app/assets/images/payment_icons/zip.svg +0 -1
- data/app/controllers/spree/base_controller.rb +0 -11
- data/app/finders/concerns/spree/product_filterable.rb +0 -9
- data/app/finders/spree/addresses/find.rb +0 -27
- data/app/finders/spree/base_finder.rb +0 -14
- data/app/finders/spree/countries/find.rb +0 -31
- data/app/finders/spree/credit_cards/find.rb +0 -12
- data/app/finders/spree/option_values/find_available.rb +0 -28
- data/app/finders/spree/orders/find_complete.rb +0 -114
- data/app/finders/spree/orders/find_current.rb +0 -24
- data/app/finders/spree/orders/finder_helper.rb +0 -17
- data/app/finders/spree/products/find.rb +0 -328
- data/app/finders/spree/taxons/find.rb +0 -99
- data/app/finders/spree/variants/find.rb +0 -30
- data/app/finders/spree/variants/option_types_finder.rb +0 -21
- data/app/helpers/spree/addresses_helper.rb +0 -97
- data/app/helpers/spree/base_helper.rb +0 -132
- data/app/helpers/spree/currency_helper.rb +0 -46
- data/app/helpers/spree/integrations_helper.rb +0 -15
- data/app/helpers/spree/locale_helper.rb +0 -44
- data/app/helpers/spree/localized_names_helper.rb +0 -25
- data/app/helpers/spree/products_helper.rb +0 -147
- data/app/helpers/spree/shipment_helper.rb +0 -12
- data/app/javascript/spree/core/controllers/address_autocomplete_controller.js +0 -230
- data/app/javascript/spree/core/controllers/address_form_controller.js +0 -98
- data/app/javascript/spree/core/controllers/disable_submit_button_controller.js +0 -19
- data/app/javascript/spree/core/controllers/enable_button_controller.js +0 -29
- data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js +0 -161
- data/app/javascript/spree/core/helpers/debounce.js +0 -9
- data/app/javascript/spree/core/helpers/scroll_lock.js +0 -25
- data/app/jobs/spree/media/migrate_product_assets_job.rb +0 -83
- data/app/jobs/spree/reports/generate_job.rb +0 -23
- data/app/mailers/spree/report_mailer.rb +0 -20
- data/app/models/concerns/spree/adjustment_source.rb +0 -58
- data/app/models/concerns/spree/default_price.rb +0 -138
- data/app/models/concerns/spree/display_on.rb +0 -50
- data/app/models/concerns/spree/image_methods.rb +0 -40
- data/app/models/concerns/spree/legacy_multi_store_support.rb +0 -48
- data/app/models/concerns/spree/metafields.rb +0 -256
- data/app/models/concerns/spree/number_as_param.rb +0 -13
- data/app/models/concerns/spree/store_scoped_resource.rb +0 -37
- data/app/models/concerns/spree/unique_name.rb +0 -12
- data/app/models/spree/adjustable/adjuster/base.rb +0 -25
- data/app/models/spree/adjustable/adjuster/promotion.rb +0 -41
- data/app/models/spree/adjustable/adjuster/tax.rb +0 -26
- data/app/models/spree/adjustable/adjustments_updater.rb +0 -74
- data/app/models/spree/adjustable/promotion_accumulator.rb +0 -78
- data/app/models/spree/adjustment.rb +0 -130
- data/app/models/spree/asset/support/active_storage.rb +0 -26
- data/app/models/spree/calculator/default_tax.rb +0 -87
- data/app/models/spree/calculator/returns/default_refund_amount.rb +0 -40
- data/app/models/spree/customer_return.rb +0 -94
- data/app/models/spree/image/configuration/active_storage.rb +0 -11
- data/app/models/spree/image.rb +0 -6
- data/app/models/spree/legacy_admin_user.rb +0 -31
- data/app/models/spree/legacy_user.rb +0 -33
- data/app/models/spree/log_entry.rb +0 -30
- data/app/models/spree/metafields/boolean.rb +0 -15
- data/app/models/spree/metafields/json.rb +0 -37
- data/app/models/spree/metafields/long_text.rb +0 -7
- data/app/models/spree/metafields/number.rb +0 -15
- data/app/models/spree/metafields/rich_text.rb +0 -18
- data/app/models/spree/metafields/short_text.rb +0 -7
- data/app/models/spree/option_type_prototype.rb +0 -9
- data/app/models/spree/order/address_book.rb +0 -100
- data/app/models/spree/order/checkout.rb +0 -350
- data/app/models/spree/order/currency_updater.rb +0 -37
- data/app/models/spree/order/digital.rb +0 -60
- data/app/models/spree/order/gift_card.rb +0 -71
- data/app/models/spree/order/payments.rb +0 -64
- data/app/models/spree/order/store_credit.rb +0 -110
- data/app/models/spree/order/webhooks.rb +0 -19
- data/app/models/spree/order_approval.rb +0 -19
- data/app/models/spree/order_cancellation.rb +0 -19
- data/app/models/spree/order_contents.rb +0 -31
- data/app/models/spree/order_routing/strategy/legacy.rb +0 -33
- data/app/models/spree/payment/webhooks.rb +0 -15
- data/app/models/spree/permission_sets/base.rb +0 -81
- data/app/models/spree/permission_sets/configuration_management.rb +0 -64
- data/app/models/spree/permission_sets/dashboard_display.rb +0 -17
- data/app/models/spree/permission_sets/default_customer.rb +0 -83
- data/app/models/spree/permission_sets/order_display.rb +0 -27
- data/app/models/spree/permission_sets/order_management.rb +0 -33
- data/app/models/spree/permission_sets/product_display.rb +0 -27
- data/app/models/spree/permission_sets/product_management.rb +0 -28
- data/app/models/spree/permission_sets/promotion_management.rb +0 -22
- data/app/models/spree/permission_sets/role_management.rb +0 -21
- data/app/models/spree/permission_sets/stock_display.rb +0 -19
- data/app/models/spree/permission_sets/stock_management.rb +0 -19
- data/app/models/spree/permission_sets/super_user.rb +0 -28
- data/app/models/spree/permission_sets/user_display.rb +0 -19
- data/app/models/spree/permission_sets/user_management.rb +0 -20
- data/app/models/spree/product/webhooks.rb +0 -17
- data/app/models/spree/prototype_taxon.rb +0 -9
- data/app/models/spree/reimbursement/credit.rb +0 -27
- data/app/models/spree/reimbursement/reimbursement_type_engine.rb +0 -44
- data/app/models/spree/reimbursement/reimbursement_type_validator.rb +0 -15
- data/app/models/spree/reimbursement.rb +0 -167
- data/app/models/spree/reimbursement_performer.rb +0 -34
- data/app/models/spree/reimbursement_tax_calculator.rb +0 -37
- data/app/models/spree/reimbursement_type/credit.rb +0 -13
- data/app/models/spree/reimbursement_type/exchange.rb +0 -9
- data/app/models/spree/reimbursement_type/original_payment.rb +0 -13
- data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +0 -64
- data/app/models/spree/reimbursement_type/store_credit.rb +0 -20
- data/app/models/spree/reimbursement_type.rb +0 -22
- data/app/models/spree/report.rb +0 -130
- data/app/models/spree/report_line_item.rb +0 -27
- data/app/models/spree/report_line_items/products_performance.rb +0 -76
- data/app/models/spree/report_line_items/sales_total.rb +0 -49
- data/app/models/spree/reports/products_performance.rb +0 -44
- data/app/models/spree/reports/sales_total.rb +0 -18
- data/app/models/spree/return_authorization.rb +0 -109
- data/app/models/spree/return_item/eligibility_validator/base_validator.rb +0 -24
- data/app/models/spree/return_item/eligibility_validator/default.rb +0 -30
- data/app/models/spree/return_item/eligibility_validator/inventory_shipped.rb +0 -16
- data/app/models/spree/return_item/eligibility_validator/no_reimbursements.rb +0 -16
- data/app/models/spree/return_item/eligibility_validator/order_completed.rb +0 -16
- data/app/models/spree/return_item/eligibility_validator/rma_required.rb +0 -16
- data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +0 -16
- data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +0 -34
- data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +0 -9
- data/app/models/spree/return_item.rb +0 -287
- data/app/models/spree/returns_calculator.rb +0 -7
- data/app/models/spree/search_provider/meilisearch.rb +0 -496
- data/app/models/spree/shipment/custom_events.rb +0 -47
- data/app/models/spree/shipment/webhooks.rb +0 -11
- data/app/models/spree/shipment_handler.rb +0 -38
- data/app/models/spree/shipping_category.rb +0 -25
- data/app/models/spree/shipping_method_category.rb +0 -8
- data/app/models/spree/shipping_method_zone.rb +0 -8
- data/app/models/spree/state_change.rb +0 -12
- data/app/models/spree/stock/splitter/digital.rb +0 -35
- data/app/models/spree/stock/splitter/shipping_category.rb +0 -31
- data/app/models/spree/stock_item/webhooks.rb +0 -6
- data/app/models/spree/stock_movement/webhooks.rb +0 -6
- data/app/models/spree/variant/webhooks.rb +0 -6
- data/app/presenters/spree/csv/metafields_helper.rb +0 -17
- data/app/presenters/spree/search_provider/product_presenter.rb +0 -138
- data/app/presenters/spree/variant_presenter.rb +0 -81
- data/app/presenters/spree/variants/option_types_presenter.rb +0 -87
- data/app/services/spree/account/addresses/create.rb +0 -8
- data/app/services/spree/account/addresses/update.rb +0 -8
- data/app/services/spree/account/create.rb +0 -17
- data/app/services/spree/account/update.rb +0 -15
- data/app/services/spree/cart/add_item.rb +0 -63
- data/app/services/spree/cart/associate.rb +0 -29
- data/app/services/spree/cart/change_currency.rb +0 -27
- data/app/services/spree/cart/create.rb +0 -26
- data/app/services/spree/cart/destroy.rb +0 -50
- data/app/services/spree/cart/empty.rb +0 -38
- data/app/services/spree/cart/estimate_shipping_rates.rb +0 -39
- data/app/services/spree/cart/recalculate.rb +0 -35
- data/app/services/spree/cart/remove_item.rb +0 -39
- data/app/services/spree/cart/remove_line_item.rb +0 -28
- data/app/services/spree/cart/remove_out_of_stock_items.rb +0 -65
- data/app/services/spree/cart/set_quantity.rb +0 -32
- data/app/services/spree/cart/update.rb +0 -37
- data/app/services/spree/carts/complete.rb +0 -50
- data/app/services/spree/carts/upsert_items.rb +0 -76
- data/app/services/spree/checkout/complete.rb +0 -23
- data/app/services/spree/checkout/get_shipping_rates.rb +0 -52
- data/app/services/spree/checkout/next.rb +0 -13
- data/app/services/spree/checkout/select_shipping_method.rb +0 -63
- data/app/services/spree/checkout/update.rb +0 -72
- data/app/services/spree/credit_cards/destroy.rb +0 -25
- data/app/services/spree/fulfillments/create.rb +0 -259
- data/app/services/spree/gift_cards/apply.rb +0 -78
- data/app/services/spree/gift_cards/redeem.rb +0 -17
- data/app/services/spree/gift_cards/remove.rb +0 -39
- data/app/services/spree/line_items/create.rb +0 -20
- data/app/services/spree/line_items/destroy.rb +0 -18
- data/app/services/spree/line_items/helper.rb +0 -11
- data/app/services/spree/line_items/update.rb +0 -17
- data/app/services/spree/orders/build_shipments.rb +0 -29
- data/app/services/spree/orders/cancel.rb +0 -54
- data/app/services/spree/orders/complete.rb +0 -53
- data/app/services/spree/orders/update_contact_information.rb +0 -19
- data/app/services/spree/orders/upsert_items.rb +0 -70
- data/app/services/spree/payments/handle_webhook.rb +0 -57
- data/app/services/spree/seeds/countries.rb +0 -35
- data/app/services/spree/seeds/default_reimbursement_types.rb +0 -12
- data/app/services/spree/seeds/shipping_categories.rb +0 -12
- data/app/services/spree/seeds/states.rb +0 -63
- data/app/services/spree/seeds/stock_locations.rb +0 -18
- data/app/services/spree/seeds/store_credit_categories.rb +0 -14
- data/app/services/spree/seeds/zones.rb +0 -52
- data/app/services/spree/shipments/add_item.rb +0 -32
- data/app/services/spree/shipments/change_state.rb +0 -14
- data/app/services/spree/shipments/create.rb +0 -48
- data/app/services/spree/shipments/helper.rb +0 -23
- data/app/services/spree/shipments/remove_item.rb +0 -55
- data/app/services/spree/stores/settings_defaults_by_country.rb +0 -38
- data/app/services/spree/themes/duplicator.rb +0 -40
- data/app/sorters/spree/base_sorter.rb +0 -47
- data/app/sorters/spree/classifications/sort.rb +0 -68
- data/app/sorters/spree/products/sort.rb +0 -73
- data/app/subscribers/spree/report_subscriber.rb +0 -26
- data/app/views/spree/addresses/_form.html.erb +0 -59
- data/app/views/spree/addresses/_suggestions_box.erb +0 -14
- data/app/views/spree/admin_user_mailer/confirmation_email.html.erb +0 -7
- data/app/views/spree/report_mailer/report_done.html.erb +0 -7
- data/app/views/spree/shared/_google_places_api_loader.html.erb +0 -7
- data/app/views/spree/shared/_mailer_line_item.html.erb +0 -12
- data/app/views/spree/shared/_payment.html.erb +0 -52
- data/config/importmap.rb +0 -15
- data/config/initializers/carmen.rb +0 -23
- data/config/initializers/rails61_fixes.rb +0 -1
- data/db/migrate/20250120152208_add_default_locale_to_action_text_rich_texts.rb +0 -7
- data/db/migrate/20250122113708_add_first_and_last_name_to_spree_admin_class.rb +0 -9
- data/db/migrate/20250204162453_create_spree_reports.rb +0 -13
- data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
- data/db/migrate/20250508060800_add_selected_locale_to_spree_admin_users.rb +0 -8
- data/db/migrate/20260429000001_create_spree_order_cancellations.rb +0 -25
- data/db/migrate/20260429000002_create_spree_order_approvals.rb +0 -22
- data/db/sample_data/markets.rb +0 -64
- data/db/sample_data/metafield_definitions.rb +0 -7
- data/db/sample_data/shipping_methods.rb +0 -61
- data/lib/generators/spree/authentication/custom/custom_generator.rb +0 -49
- data/lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt +0 -50
- data/lib/generators/spree/authentication/devise/devise_generator.rb +0 -84
- data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +0 -50
- data/lib/generators/spree/dummy/templates/app/assets/config/manifest.js +0 -0
- data/lib/spree/core/components.rb +0 -31
- data/lib/spree/core/controller_helpers/auth.rb +0 -52
- data/lib/spree/core/controller_helpers/currency.rb +0 -59
- data/lib/spree/core/controller_helpers/locale.rb +0 -99
- data/lib/spree/core/controller_helpers/order.rb +0 -172
- data/lib/spree/core/controller_helpers/strong_parameters.rb +0 -48
- data/lib/spree/core/controller_helpers/turbo.rb +0 -19
- data/lib/spree/core/partials.rb +0 -42
- data/lib/spree/core/previews/report_preview.rb +0 -40
- data/lib/spree/permitted_attributes.rb +0 -333
- data/lib/spree/testing_support/authorization_helpers.rb +0 -72
- data/lib/spree/testing_support/caching.rb +0 -47
- data/lib/spree/testing_support/capybara_config.rb +0 -45
- data/lib/spree/testing_support/capybara_ext.rb +0 -83
- data/lib/spree/testing_support/controller_requests.rb +0 -11
- data/lib/spree/testing_support/factories/adjustment_factory.rb +0 -28
- data/lib/spree/testing_support/factories/asset_factory.rb +0 -12
- data/lib/spree/testing_support/factories/customer_return_factory.rb +0 -36
- data/lib/spree/testing_support/factories/digital_factory.rb +0 -10
- data/lib/spree/testing_support/factories/image_factory.rb +0 -11
- data/lib/spree/testing_support/factories/inventory_unit_factory.rb +0 -18
- data/lib/spree/testing_support/factories/log_entry_factory.rb +0 -6
- data/lib/spree/testing_support/factories/metafield_definition_factory.rb +0 -72
- data/lib/spree/testing_support/factories/metafield_factory.rb +0 -68
- data/lib/spree/testing_support/factories/prototype_factory.rb +0 -9
- data/lib/spree/testing_support/factories/reimbursement_credit_factory.rb +0 -7
- data/lib/spree/testing_support/factories/reimbursement_factory.rb +0 -16
- data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +0 -7
- data/lib/spree/testing_support/factories/report_factory.rb +0 -33
- data/lib/spree/testing_support/factories/return_authorization_factory.rb +0 -21
- data/lib/spree/testing_support/factories/return_item_factory.rb +0 -14
- data/lib/spree/testing_support/factories/shipment_factory.rb +0 -21
- data/lib/spree/testing_support/factories/shipping_category_factory.rb +0 -9
- data/lib/spree/testing_support/factories/shipping_method_factory.rb +0 -31
- data/lib/spree/testing_support/factories/shipping_rate_factory.rb +0 -7
- data/lib/spree/testing_support/factories/state_change_factory.rb +0 -8
- data/lib/spree/testing_support/factories/stock_item_factory.rb +0 -22
- data/lib/spree/testing_support/factories/store_credit_category_factory.rb +0 -9
- data/lib/spree/testing_support/factories/store_credit_type_factory.rb +0 -16
- data/lib/spree/testing_support/factories/wished_variant_factory.rb +0 -6
- data/lib/spree/webhooks.rb +0 -22
- data/lib/tasks/role_users.rake +0 -21
- data/vendor/javascript/@rails--request.js.js +0 -4
- data/vendor/javascript/@stimulus-components--auto-submit.js +0 -4
- data/vendor/javascript/stimulus-reveal-controller.js +0 -4
- data/vendor/javascript/stimulus-textarea-autogrow.js +0 -4
- data/vendor/javascript/tailwindcss-stimulus-components.js +0 -4
data/app/models/spree/product.rb
CHANGED
|
@@ -4,22 +4,18 @@
|
|
|
4
4
|
# Products properties include description, permalink, availability,
|
|
5
5
|
# shipping category, etc. that do not change by variant.
|
|
6
6
|
#
|
|
7
|
-
# MASTER VARIANT
|
|
8
|
-
# Every product has one master variant, which stores master price and sku, size and weight, etc.
|
|
9
|
-
# The master variant does not have option values associated with it.
|
|
10
|
-
# Price, SKU, size, weight, etc. are all delegated to the master variant.
|
|
11
|
-
# Contains on_hand inventory levels only when there are no variants for the product.
|
|
12
|
-
#
|
|
13
7
|
# VARIANTS
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
8
|
+
# Every product has at least one variant. Simple products have a single variant
|
|
9
|
+
# with no option values; products with options have one variant per combination.
|
|
10
|
+
# `default_variant` is the "face" of the product — price, SKU, size, weight, etc.
|
|
11
|
+
# are delegated to it. Inventory units are tied to Variant; the sum of each
|
|
12
|
+
# variant's on_hand determines the product's "on_hand" level.
|
|
19
13
|
#
|
|
20
14
|
|
|
21
15
|
module Spree
|
|
22
16
|
class Product < Spree.base_class
|
|
17
|
+
include Spree::CSV::CustomFieldsHelper
|
|
18
|
+
|
|
23
19
|
has_prefix_id :prod # Stripe: prod_
|
|
24
20
|
|
|
25
21
|
acts_as_paranoid
|
|
@@ -30,38 +26,52 @@ module Spree
|
|
|
30
26
|
include Spree::ProductScopes
|
|
31
27
|
include Spree::TranslatableResource
|
|
32
28
|
include Spree::MemoizedData
|
|
33
|
-
include Spree::
|
|
29
|
+
include Spree::SanitizableRichText
|
|
30
|
+
include Spree::HasCustomFields
|
|
31
|
+
include Spree::CustomFieldFilterable
|
|
34
32
|
include Spree::Metadata
|
|
35
|
-
include Spree::
|
|
33
|
+
include Spree::HasExternalReferences
|
|
34
|
+
include Spree::Searchable
|
|
36
35
|
include Spree::Product::Slugs
|
|
37
36
|
include Spree::Product::Channels
|
|
38
|
-
include Spree::LegacyMultiStoreSupport unless defined?(SpreeMultiStore)
|
|
39
37
|
include Spree::SearchIndexable
|
|
40
|
-
|
|
41
|
-
include Spree::VendorConcern
|
|
42
|
-
end
|
|
38
|
+
include Spree::StorePreferences
|
|
43
39
|
|
|
44
40
|
publishes_lifecycle_events
|
|
45
41
|
|
|
46
|
-
MEMOIZED_METHODS = %w[total_on_hand
|
|
42
|
+
MEMOIZED_METHODS = %w[total_on_hand category_and_ancestors
|
|
47
43
|
default_variant_id tax_category default_variant variant_for_images
|
|
48
|
-
|
|
44
|
+
primary_category buy_box_variants resolved_delivery_profile
|
|
49
45
|
purchasable? in_stock? backorderable? digital?]
|
|
50
46
|
|
|
47
|
+
# Statuses an operator sets directly. `bulk_status_update` validates
|
|
48
|
+
# against this, which is why the review statuses are not in it: reaching
|
|
49
|
+
# `proposed` is a seller submitting and reaching `rejected` is an operator
|
|
50
|
+
# deciding, and neither is a value to assign in bulk.
|
|
51
51
|
STATUSES = %w[draft active archived].freeze
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
# Where a product sits in the marketplace review lifecycle. Only the
|
|
54
|
+
# workflows (Products::Propose, ::Approve, ::Reject) move a product in or
|
|
55
|
+
# out of these; see docs/plans/6.0-seller-product-submission.md.
|
|
56
|
+
REVIEW_STATUSES = %w[proposed rejected].freeze
|
|
57
|
+
|
|
58
|
+
ALL_STATUSES = (STATUSES + REVIEW_STATUSES).freeze
|
|
58
59
|
|
|
59
60
|
TRANSLATABLE_FIELDS = %i[name description slug meta_description meta_title].freeze
|
|
60
61
|
RICH_TEXT_TRANSLATABLE_FIELDS = %i[description].freeze
|
|
61
62
|
translates(*TRANSLATABLE_FIELDS, column_fallback: Spree.mobility_column_fallback)
|
|
62
63
|
|
|
64
|
+
has_spree_rich_text :description
|
|
65
|
+
|
|
66
|
+
# Translated descriptions live on the Mobility translation table, so writes
|
|
67
|
+
# through locale accessors or +upsert_translations+ never touch the base
|
|
68
|
+
# record's +description+ attribute — the translation model needs its own
|
|
69
|
+
# sanitization pass.
|
|
63
70
|
self::Translation.class_eval do
|
|
71
|
+
include Spree::SanitizableRichText
|
|
72
|
+
|
|
64
73
|
normalizes :name, :meta_title, with: ->(value) { value&.to_s&.squish&.presence }
|
|
74
|
+
sanitizes_rich_text :description
|
|
65
75
|
end
|
|
66
76
|
|
|
67
77
|
# we need to have this callback before any dependent: :destroy associations
|
|
@@ -70,10 +80,76 @@ module Spree
|
|
|
70
80
|
|
|
71
81
|
has_many :product_option_types, -> { order(:position) }, dependent: :destroy, inverse_of: :product
|
|
72
82
|
has_many :option_types, through: :product_option_types
|
|
73
|
-
has_many :
|
|
74
|
-
has_many :
|
|
75
|
-
has_many :
|
|
76
|
-
|
|
83
|
+
has_many :product_categories, -> { order(created_at: :asc) }, class_name: 'Spree::ProductCategory', dependent: :delete_all, inverse_of: :product
|
|
84
|
+
has_many :categories, through: :product_categories, class_name: 'Spree::Category', source: :category, before_remove: :remove_category
|
|
85
|
+
has_many :taxonomies, through: :categories, deprecated: true
|
|
86
|
+
|
|
87
|
+
# @deprecated The classification_count column was renamed to categories_count in 6.0.
|
|
88
|
+
alias_attribute :classification_count, :categories_count
|
|
89
|
+
|
|
90
|
+
# @deprecated Use #product_categories; removed in 6.1.
|
|
91
|
+
# The live row in the review trail, or nil for a product nobody submitted.
|
|
92
|
+
#
|
|
93
|
+
# @return [Spree::ProductSubmission, nil]
|
|
94
|
+
def latest_submission
|
|
95
|
+
submissions.latest_first.first
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Why the marketplace turned this listing down, for the seller to read.
|
|
99
|
+
# Answers nil once the product moves on, so a stale note never shows
|
|
100
|
+
# against a product that is no longer rejected.
|
|
101
|
+
#
|
|
102
|
+
# @return [String, nil]
|
|
103
|
+
def rejection_reason
|
|
104
|
+
return unless rejected?
|
|
105
|
+
|
|
106
|
+
latest_submission&.review_note
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Whether this product is waiting on, or has had, a marketplace decision.
|
|
110
|
+
#
|
|
111
|
+
# @return [Boolean]
|
|
112
|
+
def in_review?
|
|
113
|
+
REVIEW_STATUSES.include?(status)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def classifications
|
|
117
|
+
Spree::Deprecation.warn('Spree::Product#classifications is deprecated and will be removed in Spree 6.1. Use #product_categories instead.')
|
|
118
|
+
product_categories
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @deprecated Use #categories / #category_ids; removed in 6.1.
|
|
122
|
+
def taxons
|
|
123
|
+
Spree::Deprecation.warn('Spree::Product#taxons is deprecated and will be removed in Spree 6.1. Use #categories instead.')
|
|
124
|
+
categories
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def taxons=(value)
|
|
128
|
+
Spree::Deprecation.warn('Spree::Product#taxons= is deprecated and will be removed in Spree 6.1. Use #categories= instead.')
|
|
129
|
+
self.categories = value
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def taxon_ids
|
|
133
|
+
Spree::Deprecation.warn('Spree::Product#taxon_ids is deprecated and will be removed in Spree 6.1. Use #category_ids instead.')
|
|
134
|
+
category_ids
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def taxon_ids=(ids)
|
|
138
|
+
Spree::Deprecation.warn('Spree::Product#taxon_ids= is deprecated and will be removed in Spree 6.1. Use #category_ids= instead.')
|
|
139
|
+
self.category_ids = ids
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
has_many :product_collections, class_name: 'Spree::ProductCollection', dependent: :destroy_async, inverse_of: :product
|
|
143
|
+
has_many :collections, through: :product_collections
|
|
144
|
+
has_many :catalog_products, class_name: 'Spree::CatalogProduct', dependent: :destroy_async, inverse_of: :product
|
|
145
|
+
has_many :catalogs, through: :catalog_products
|
|
146
|
+
|
|
147
|
+
# Dropped with the product so a deleted product stops excluding itself from
|
|
148
|
+
# delivery methods. Destroyed inline rather than async — a product sits in
|
|
149
|
+
# at most a handful of exclusion rules, and deferring it would leave the
|
|
150
|
+
# rule reporting an id the API can no longer resolve.
|
|
151
|
+
has_many :delivery_method_rule_products, class_name: 'Spree::DeliveryMethodRuleProduct',
|
|
152
|
+
dependent: :destroy, inverse_of: :product
|
|
77
153
|
|
|
78
154
|
has_many :product_promotion_rules, class_name: 'Spree::ProductPromotionRule'
|
|
79
155
|
has_many :promotion_rules, through: :product_promotion_rules, class_name: 'Spree::PromotionRule'
|
|
@@ -84,65 +160,92 @@ module Spree
|
|
|
84
160
|
class_name: 'Spree::Promotion',
|
|
85
161
|
source: :promotion
|
|
86
162
|
|
|
87
|
-
belongs_to :tax_category, class_name: 'Spree::TaxCategory'
|
|
88
|
-
belongs_to :
|
|
89
|
-
|
|
163
|
+
belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
|
|
164
|
+
belongs_to :product_type, class_name: 'Spree::ProductType', optional: true, counter_cache: :products_count
|
|
165
|
+
# The seller this product belongs to on a marketplace. Nil is the
|
|
166
|
+
# operator's own catalog — which is everything, on a store selling only
|
|
167
|
+
# its own goods.
|
|
168
|
+
belongs_to :seller, class_name: 'Spree::Seller', optional: true, inverse_of: :products
|
|
169
|
+
|
|
170
|
+
# How this product ships: origins, zones and methods all hang off the
|
|
171
|
+
# profile. Required — a product without one could not be fulfilled at
|
|
172
|
+
# all — and auto-assigned on create (type template, else the store's
|
|
173
|
+
# default profile), so callers never have to think about it. The FK is
|
|
174
|
+
# the renamed 5.x shipping_category_id, so migrated catalogs arrive
|
|
175
|
+
# already assigned.
|
|
176
|
+
belongs_to :delivery_profile, class_name: 'Spree::DeliveryProfile', inverse_of: :products
|
|
177
|
+
|
|
178
|
+
# Every one of the product's variants now resolves its profile through
|
|
179
|
+
# +resolved_delivery_profile+, which memoizes — so a reassignment has to
|
|
180
|
+
# drop the memo, or the whole fleet keeps shipping on the old profile
|
|
181
|
+
# until the product is saved.
|
|
182
|
+
%i[delivery_profile delivery_profile_id].each do |name|
|
|
183
|
+
define_method(:"#{name}=") do |value|
|
|
184
|
+
@resolved_delivery_profile = nil
|
|
185
|
+
@digital = nil
|
|
186
|
+
super(value)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
90
189
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
190
|
+
# Guards every write path (including raw prefixed-id assignment) against
|
|
191
|
+
# linking another store's profile.
|
|
192
|
+
validate :delivery_profile_must_belong_to_store, if: :delivery_profile_id_changed?
|
|
193
|
+
# Either side moving breaks the pair, and a product's store is not frozen
|
|
194
|
+
# after create, so watching only the seller would let a move carry the old
|
|
195
|
+
# store's seller along.
|
|
196
|
+
validate :seller_must_belong_to_store, if: -> { seller_id_changed? || store_id_changed? }
|
|
95
197
|
|
|
198
|
+
# Every product has at least one variant. `default_variant` is the "face" of
|
|
199
|
+
# the product (price display, default add-to-cart, property delegation).
|
|
96
200
|
has_many :variants,
|
|
97
|
-
-> { where(is_master: false).order(:position) },
|
|
98
|
-
inverse_of: :product,
|
|
99
|
-
class_name: 'Spree::Variant'
|
|
100
|
-
|
|
101
|
-
has_many :variants_including_master,
|
|
102
201
|
-> { order(:position) },
|
|
103
202
|
inverse_of: :product,
|
|
104
203
|
class_name: 'Spree::Variant',
|
|
105
204
|
dependent: :destroy
|
|
106
205
|
|
|
107
|
-
|
|
206
|
+
belongs_to :default_variant, class_name: 'Spree::Variant', optional: true, autosave: true
|
|
207
|
+
|
|
208
|
+
has_many :prices, through: :variants
|
|
108
209
|
|
|
109
|
-
has_many :
|
|
210
|
+
has_many :stock_levels, through: :variants
|
|
110
211
|
|
|
111
|
-
has_many :line_items, through: :
|
|
212
|
+
has_many :line_items, through: :variants
|
|
112
213
|
has_many :orders, through: :line_items
|
|
113
214
|
has_many :completed_orders, -> { reorder(nil).distinct.complete }, through: :line_items, source: :order
|
|
114
215
|
|
|
115
|
-
has_many :media, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::
|
|
216
|
+
has_many :media, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Media'
|
|
116
217
|
|
|
117
|
-
|
|
118
|
-
|
|
218
|
+
# The review trail for a seller's listing. Append-only: the latest row is
|
|
219
|
+
# the live one (docs/plans/6.0-seller-product-submission.md).
|
|
220
|
+
has_many :submissions, class_name: 'Spree::ProductSubmission', dependent: :destroy, inverse_of: :product
|
|
119
221
|
|
|
120
|
-
|
|
222
|
+
has_many :variant_images, -> { order(:position) }, source: :images, through: :variants
|
|
223
|
+
|
|
224
|
+
belongs_to :primary_media, class_name: 'Spree::Media', optional: true, foreign_key: :primary_media_id
|
|
121
225
|
|
|
122
226
|
has_many :option_value_variants, class_name: 'Spree::OptionValueVariant', through: :variants
|
|
123
227
|
has_many :option_values, class_name: 'Spree::OptionValue', through: :variants
|
|
124
228
|
|
|
125
|
-
has_many :
|
|
126
|
-
|
|
127
|
-
has_many :digitals, through: :variants_including_master
|
|
229
|
+
has_many :digital_assets, through: :variants, class_name: 'Spree::DigitalAsset'
|
|
230
|
+
has_many :digitals, through: :variants, class_name: 'Spree::DigitalAsset', source: :digital_assets, deprecated: true
|
|
128
231
|
|
|
129
|
-
after_initialize :ensure_master
|
|
130
232
|
after_initialize :assign_default_tax_category
|
|
233
|
+
# The type's profile is a creation-time template: stamped here, never
|
|
234
|
+
# synced afterwards — reassigning the type later leaves the product's
|
|
235
|
+
# profile alone. Falls back to the store's default profile, keeping the
|
|
236
|
+
# required association satisfiable without the caller naming one.
|
|
237
|
+
before_validation :stamp_template_delivery_profile, on: :create
|
|
131
238
|
|
|
132
|
-
|
|
133
|
-
|
|
239
|
+
after_create :sync_associations_from_product_type
|
|
240
|
+
after_update :sync_associations_from_product_type, if: :saved_change_to_product_type_id?
|
|
241
|
+
after_create :ensure_default_variant
|
|
242
|
+
after_create :set_default_variant
|
|
134
243
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
after_save :apply_pending_media, if: :pending_media?
|
|
244
|
+
after_save :auto_promote_default_variant
|
|
245
|
+
after_save :run_touch_callbacks, if: :saved_changes?
|
|
246
|
+
after_touch :touch_categories
|
|
139
247
|
|
|
140
|
-
|
|
141
|
-
after_save :run_touch_callbacks, if: :anything_changed?
|
|
142
|
-
after_save :reset_nested_changes
|
|
143
|
-
after_touch :touch_taxons
|
|
144
|
-
|
|
145
|
-
after_commit :auto_match_taxons, if: :eligible_for_taxon_matching?
|
|
248
|
+
after_commit :auto_match_collections, if: :eligible_for_collection_matching?
|
|
146
249
|
|
|
147
250
|
with_options length: { maximum: 255 }, allow_blank: true do
|
|
148
251
|
validates :meta_keywords
|
|
@@ -150,28 +253,53 @@ module Spree
|
|
|
150
253
|
end
|
|
151
254
|
with_options presence: true do
|
|
152
255
|
validates :name
|
|
153
|
-
validates :shipping_category, if: :requires_shipping_category?
|
|
154
|
-
validates :price, if: :requires_price?
|
|
155
256
|
end
|
|
156
257
|
|
|
258
|
+
# Every persisted product must keep a default variant. It's assigned on
|
|
259
|
+
# create by +set_default_variant+, so only enforce it on update.
|
|
260
|
+
validates :default_variant, presence: true, on: :update
|
|
261
|
+
|
|
157
262
|
validate :discontinue_on_must_be_later_than_make_active_at, if: -> { make_active_at && discontinue_on }
|
|
158
263
|
|
|
159
264
|
scope :for_store, ->(store) { where(store_id: store.id) }
|
|
160
|
-
|
|
161
|
-
|
|
265
|
+
# Products belonging to the given seller. Passing nil selects the
|
|
266
|
+
# operator's own catalog, which on a store with no sellers is everything.
|
|
267
|
+
# The same answer `Seller#products` gives, as a scope a bulk read can chain.
|
|
268
|
+
scope :for_seller, ->(seller) { where(seller_id: seller.respond_to?(:id) ? seller.id : seller) }
|
|
162
269
|
scope :not_archived, -> { where.not(status: 'archived') }
|
|
163
270
|
scope :on_sale, lambda { |currency = nil|
|
|
164
271
|
currency ||= Spree::Store.default.default_currency
|
|
165
|
-
|
|
272
|
+
with_currency(currency).
|
|
273
|
+
where.not(spree_prices: { amount: 0 }).
|
|
166
274
|
where.not(spree_prices: { compare_at_amount: [nil, 0] }).
|
|
167
275
|
where("#{Spree::Price.table_name}.compare_at_amount > #{Spree::Price.table_name}.amount")
|
|
168
276
|
}
|
|
169
277
|
|
|
170
|
-
|
|
278
|
+
# @param store [Spree::Store] whose custom-field definitions take part in
|
|
279
|
+
# the search. Definitions are store-owned, so the caller says which store
|
|
280
|
+
# is searching; `Spree::Current.store` is the request's answer and a
|
|
281
|
+
# background caller (an export, say) passes its own.
|
|
282
|
+
scope :search, ->(query, store = Spree::Current.store) {
|
|
171
283
|
next none if query.blank?
|
|
172
284
|
|
|
173
285
|
product_ids = Spree::Variant.search_by_product_name_or_sku(query).pluck(:product_id)
|
|
174
|
-
|
|
286
|
+
|
|
287
|
+
searchable_definitions = store.custom_field_definitions.
|
|
288
|
+
for_resource_type('Spree::Product').
|
|
289
|
+
searchable
|
|
290
|
+
|
|
291
|
+
# Only pay for the custom_field scan when some definition opts into search;
|
|
292
|
+
# the leading-wildcard LIKE over spree_custom_fields can't use an index.
|
|
293
|
+
custom_field_ids = if searchable_definitions.exists?
|
|
294
|
+
joins(custom_fields: :custom_field_definition).
|
|
295
|
+
merge(searchable_definitions).
|
|
296
|
+
where(search_condition(Spree::CustomField, :value, query)).
|
|
297
|
+
unscope(:order).distinct.pluck(:id)
|
|
298
|
+
else
|
|
299
|
+
[]
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
where(id: (product_ids + custom_field_ids).uniq.compact)
|
|
175
303
|
}
|
|
176
304
|
|
|
177
305
|
# Backward compatibility alias — remove in Spree 6.0
|
|
@@ -179,19 +307,15 @@ module Spree
|
|
|
179
307
|
|
|
180
308
|
scope :archivable, -> { where(status: %w[active draft]) }
|
|
181
309
|
scope :by_source, ->(source) { send(source) }
|
|
182
|
-
scope :paused, -> { where(status: 'paused') }
|
|
183
310
|
scope :published, -> { where(status: 'active') }
|
|
184
311
|
|
|
185
|
-
attr_accessor :option_values_hash
|
|
186
|
-
|
|
187
312
|
accepts_nested_attributes_for(
|
|
188
313
|
:variants,
|
|
189
314
|
allow_destroy: true,
|
|
190
315
|
reject_if: lambda do |v|
|
|
191
|
-
v[:option_value_variants_attributes].blank? && v[:
|
|
316
|
+
v[:option_value_variants_attributes].blank? && v[:stock_levels_attributes].blank? && v[:prices_attributes].blank?
|
|
192
317
|
end
|
|
193
318
|
)
|
|
194
|
-
accepts_nested_attributes_for :master, reject_if: :all_blank
|
|
195
319
|
accepts_nested_attributes_for(
|
|
196
320
|
:product_option_types,
|
|
197
321
|
allow_destroy: true,
|
|
@@ -206,179 +330,203 @@ module Spree
|
|
|
206
330
|
self.tag_list = tags
|
|
207
331
|
end
|
|
208
332
|
|
|
209
|
-
# Sets prices on the
|
|
333
|
+
# Sets prices on the default variant.
|
|
210
334
|
# Accepts array of { currency:, amount:, compare_at_amount: } hashes.
|
|
211
335
|
def prices=(prices_params)
|
|
212
|
-
|
|
336
|
+
find_or_build_default_variant.prices = prices_params
|
|
213
337
|
end
|
|
214
338
|
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
#
|
|
339
|
+
# Accepts both prefixed IDs and raw integer IDs. Only categories belonging to
|
|
340
|
+
# the product's own store are assigned — ids from another store are dropped,
|
|
341
|
+
# preventing cross-store category attachment. Decodes prefixed IDs, then calls
|
|
342
|
+
# the generated setter via super.
|
|
219
343
|
def category_ids=(ids)
|
|
220
344
|
decoded_ids = Array(ids).filter_map do |id|
|
|
221
|
-
id.to_s.include?('_') ? Spree::
|
|
345
|
+
id.to_s.include?('_') ? Spree::Category.decode_prefixed_id(id) : id
|
|
222
346
|
end
|
|
223
|
-
|
|
347
|
+
super(Spree::Category.for_store(assignable_store).where(id: decoded_ids).ids)
|
|
224
348
|
end
|
|
225
349
|
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
# the dedicated DELETE /media endpoint to avoid accidental data loss when
|
|
230
|
-
# a form ships stale state).
|
|
350
|
+
# Accepts both prefixed IDs and raw integer IDs. Only collections belonging
|
|
351
|
+
# to the product's own store are assigned; this overrides the generated
|
|
352
|
+
# setter and calls super.
|
|
231
353
|
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return super if media_params.first.is_a?(Spree::Asset)
|
|
243
|
-
|
|
244
|
-
if new_record?
|
|
245
|
-
@pending_media_params = media_params
|
|
246
|
-
return
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
apply_media(media_params)
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# Syncs variants from an array of hashes.
|
|
253
|
-
# Creates new variants, updates existing ones (matched by :id), and removes unlisted ones.
|
|
254
|
-
# Must be called on a persisted product (use after_save or call explicitly after create).
|
|
255
|
-
# @param variants_params [Array<Hash>] array of variant attribute hashes
|
|
256
|
-
# @return [void]
|
|
257
|
-
def variants=(variants_params)
|
|
258
|
-
return super if variants_params.blank? || variants_params.first.is_a?(Spree::Variant)
|
|
259
|
-
|
|
260
|
-
# Store for deferred processing if product is not yet persisted
|
|
261
|
-
if new_record?
|
|
262
|
-
@pending_variants_params = variants_params
|
|
263
|
-
return
|
|
354
|
+
# This is a replace setter, so it governs manual membership only. Automatic
|
|
355
|
+
# collections are materialized from their rules, which means they must
|
|
356
|
+
# survive a write that doesn't mention them (otherwise saving a product
|
|
357
|
+
# would wipe rule-derived membership until the next regeneration) and must
|
|
358
|
+
# not be assignable by hand (a hand-set member would vanish at that
|
|
359
|
+
# regeneration). So incoming automatic ids are ignored, and the product's
|
|
360
|
+
# existing automatic memberships are carried over untouched.
|
|
361
|
+
def collection_ids=(ids)
|
|
362
|
+
decoded_ids = Array(ids).filter_map do |id|
|
|
363
|
+
id.to_s.include?('_') ? Spree::Collection.decode_prefixed_id(id) : id
|
|
264
364
|
end
|
|
265
|
-
|
|
266
|
-
|
|
365
|
+
manual_ids = Spree::Collection.for_store(assignable_store).manual.where(id: decoded_ids).ids
|
|
366
|
+
super(manual_ids | collections.automatic.ids)
|
|
267
367
|
end
|
|
268
368
|
|
|
269
|
-
self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status available_on created_at updated_at]
|
|
270
|
-
self.whitelisted_ransackable_associations = %w[
|
|
271
|
-
|
|
272
|
-
self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon in_category in_categories price_between
|
|
369
|
+
self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status available_on created_at updated_at seller_id]
|
|
370
|
+
self.whitelisted_ransackable_associations = %w[categories collections store channels variants default_variant tags labels
|
|
371
|
+
product_type product_categories option_types seller]
|
|
372
|
+
self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon in_category in_categories in_collection price_between
|
|
273
373
|
price_lte price_gte
|
|
274
374
|
search multi_search in_stock out_of_stock with_option_value_ids
|
|
275
375
|
|
|
276
376
|
ascend_by_price descend_by_price]
|
|
277
377
|
|
|
378
|
+
# All product-level convenience attributes delegate to the default variant —
|
|
379
|
+
# one target, no branching. Reads resolve the existing default variant
|
|
380
|
+
# (nil-safe) and never build one as a side effect; only setters build on
|
|
381
|
+
# demand, so `product.sku = ...` still works before the product is persisted.
|
|
278
382
|
[
|
|
279
|
-
:sku, :barcode, :weight, :height, :width, :depth, :
|
|
383
|
+
:sku, :barcode, :weight, :height, :width, :depth, :dimensions_unit, :weight_unit,
|
|
384
|
+
:price_in, :amount_in, :compare_at_amount_in,
|
|
385
|
+
:cost_currency, :cost_price, :track_inventory
|
|
280
386
|
].each do |method_name|
|
|
281
|
-
delegate method_name, :
|
|
387
|
+
delegate method_name, to: :default_variant, allow_nil: true
|
|
388
|
+
delegate :"#{method_name}=", to: :find_or_build_default_variant
|
|
282
389
|
end
|
|
283
390
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
391
|
+
# Read-only delegations: these have no product-level writer. Prices are
|
|
392
|
+
# written per currency through the default variant's +set_price+.
|
|
393
|
+
delegate :compare_at_price, :track_inventory?, :images, to: :default_variant, allow_nil: true
|
|
394
|
+
|
|
395
|
+
# No state machine — status moves through the Spree::Products workflows
|
|
396
|
+
# (docs/plans/6.0-service-workflows.md). has_status generates the `draft`
|
|
397
|
+
# and `archived` scopes and every predicate, but never redefines what the
|
|
398
|
+
# model already owns: `Product.active` stays the currency-aware scope from
|
|
399
|
+
# Spree::ProductScopes rather than a plain status lookup.
|
|
400
|
+
include Spree::HasStatus
|
|
401
|
+
has_status(*ALL_STATUSES, default: :draft)
|
|
402
|
+
|
|
403
|
+
# @deprecated Call Spree.product_activate_workflow — removed in 6.1.
|
|
404
|
+
def activate!
|
|
405
|
+
Spree::Deprecation.warn('Spree::Product#activate! is deprecated and will be removed in Spree 6.1. Call Spree.product_activate_workflow instead.')
|
|
406
|
+
run_status_workflow(Spree.product_activate_workflow)
|
|
289
407
|
end
|
|
290
408
|
|
|
291
|
-
|
|
292
|
-
|
|
409
|
+
# @deprecated Call Spree.product_archive_workflow — removed in 6.1.
|
|
410
|
+
def archive!
|
|
411
|
+
Spree::Deprecation.warn('Spree::Product#archive! is deprecated and will be removed in Spree 6.1. Call Spree.product_archive_workflow instead.')
|
|
412
|
+
run_status_workflow(Spree.product_archive_workflow)
|
|
413
|
+
end
|
|
293
414
|
|
|
294
|
-
#
|
|
295
|
-
|
|
415
|
+
# @deprecated Call Spree.product_draft_workflow — removed in 6.1.
|
|
416
|
+
def draft!
|
|
417
|
+
Spree::Deprecation.warn('Spree::Product#draft! is deprecated and will be removed in Spree 6.1. Call Spree.product_draft_workflow instead.')
|
|
418
|
+
run_status_workflow(Spree.product_draft_workflow)
|
|
419
|
+
end
|
|
296
420
|
|
|
297
|
-
|
|
421
|
+
def self.bulk_auto_match_collections(store, product_ids)
|
|
422
|
+
return if store.collections.automatic.none?
|
|
298
423
|
|
|
299
|
-
|
|
300
|
-
event :activate do
|
|
301
|
-
transition to: :active
|
|
302
|
-
end
|
|
303
|
-
after_transition to: :active, do: [:after_activate, :send_product_activated_webhook, :publish_product_activated_event]
|
|
424
|
+
products_to_auto_match_ids = store.products.not_deleted.not_archived.where(id: product_ids).ids
|
|
304
425
|
|
|
305
|
-
|
|
306
|
-
|
|
426
|
+
auto_match_collections_jobs = products_to_auto_match_ids.map do |product_id|
|
|
427
|
+
Spree::Products::AutoMatchCollectionsJob.new(product_id).tap { |job| job.scheduled_at = 30.seconds.from_now }
|
|
307
428
|
end
|
|
308
|
-
after_transition to: :archived, do: [:after_archive, :send_product_archived_webhook, :publish_product_archived_event]
|
|
309
429
|
|
|
310
|
-
|
|
311
|
-
transition to: :draft
|
|
312
|
-
end
|
|
313
|
-
after_transition to: :draft, do: [:after_draft, :send_product_drafted_webhook]
|
|
430
|
+
ActiveJob.perform_all_later(auto_match_collections_jobs)
|
|
314
431
|
end
|
|
315
432
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
433
|
+
# The variant this product leads with: its price, its stock, and what the
|
|
434
|
+
# add-to-cart button buys. On a product only the operator sells this is
|
|
435
|
+
# simply the cheapest available variant; once sellers share the listing it
|
|
436
|
+
# is whichever of their offers wins the buy box.
|
|
437
|
+
#
|
|
438
|
+
# Computed per read and never stored — it turns on price, stock and who is
|
|
439
|
+
# currently selling, none of which hold still
|
|
440
|
+
# (docs/plans/6.0-multi-vendor-marketplace.md, Decision 11).
|
|
441
|
+
#
|
|
442
|
+
# @param currency [String, nil]
|
|
443
|
+
# @param option_value_ids [Array<String, Integer>, nil] narrows to one option
|
|
444
|
+
# combination, so a `Condition` option gives a new buy box and a used one
|
|
445
|
+
# @return [Spree::Variant, nil]
|
|
446
|
+
def buy_box_variant(currency: nil, option_value_ids: nil)
|
|
447
|
+
currency ||= Spree::Current.currency
|
|
448
|
+
key = [currency, option_value_ids]
|
|
449
|
+
@buy_box_variants ||= {}
|
|
450
|
+
return @buy_box_variants[key] if @buy_box_variants.key?(key)
|
|
320
451
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
452
|
+
@buy_box_variants[key] = Spree::Dependencies.product_buy_box_service.constantize.call(
|
|
453
|
+
product: self, currency: currency, option_value_ids: option_value_ids
|
|
454
|
+
).value
|
|
455
|
+
end
|
|
324
456
|
|
|
325
|
-
|
|
457
|
+
# The variant this product leads with in a currency: the buy-box winner,
|
|
458
|
+
# else the default. One definition, because a caller that prices a page and
|
|
459
|
+
# a caller that preloads for it must land on the same variant — otherwise
|
|
460
|
+
# the preload misses and every row falls back to a query of its own.
|
|
461
|
+
#
|
|
462
|
+
# @param currency [String, nil]
|
|
463
|
+
# @return [Spree::Variant, nil]
|
|
464
|
+
def featured_variant(currency: nil)
|
|
465
|
+
buy_box_variant(currency: currency) || default_variant
|
|
326
466
|
end
|
|
327
467
|
|
|
328
|
-
#
|
|
468
|
+
# Availability asks whether *anyone who is selling today* has this on the
|
|
469
|
+
# shelf. It deliberately does not ask the buy box: the winner is picked
|
|
470
|
+
# by price in one currency, and stock is a fact about the shelf, not about
|
|
471
|
+
# pricing — a variant stocked but priced only in another currency is still
|
|
472
|
+
# in stock. What it does share with the buy box is the seller test: a
|
|
473
|
+
# variant from a seller who is suspended, still onboarding or away is not
|
|
474
|
+
# for sale, so it cannot make the product available.
|
|
329
475
|
def purchasable?
|
|
330
|
-
@purchasable
|
|
476
|
+
return @purchasable unless @purchasable.nil?
|
|
477
|
+
|
|
478
|
+
@purchasable = sellable_variants.any?(&:purchasable?)
|
|
331
479
|
end
|
|
332
480
|
|
|
333
|
-
# Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
|
|
334
481
|
def in_stock?
|
|
335
|
-
@in_stock
|
|
336
|
-
end
|
|
482
|
+
return @in_stock unless @in_stock.nil?
|
|
337
483
|
|
|
338
|
-
|
|
339
|
-
def backorderable?
|
|
340
|
-
default_variant.backorderable? || variants.any?(&:backorderable?)
|
|
484
|
+
@in_stock = sellable_variants.any?(&:in_stock?)
|
|
341
485
|
end
|
|
342
486
|
|
|
343
|
-
def
|
|
344
|
-
|
|
487
|
+
def backorderable?
|
|
488
|
+
sellable_variants.any?(&:backorderable?)
|
|
345
489
|
end
|
|
346
490
|
|
|
347
|
-
|
|
348
|
-
|
|
491
|
+
# The variants a shopper could actually buy from: every one whose seller
|
|
492
|
+
# is selling today, first-party included. Reads the loaded association so
|
|
493
|
+
# a serialized list resolves it from variants it already holds.
|
|
494
|
+
#
|
|
495
|
+
# @return [Array<Spree::Variant>]
|
|
496
|
+
def sellable_variants
|
|
497
|
+
variants.select do |variant|
|
|
498
|
+
seller = variant.resolved_seller
|
|
499
|
+
seller.nil? || seller.sellable?
|
|
500
|
+
end
|
|
349
501
|
end
|
|
350
502
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
# @return [Boolean]
|
|
354
|
-
def has_variants?
|
|
355
|
-
return variants.size.positive? if variants.loaded?
|
|
356
|
-
|
|
357
|
-
variant_count.positive?
|
|
503
|
+
def on_sale?(currency)
|
|
504
|
+
prices.find_all { |p| p.currency == currency && p.non_zero? }.any?(&:discounted?)
|
|
358
505
|
end
|
|
359
506
|
|
|
360
|
-
#
|
|
361
|
-
#
|
|
362
|
-
#
|
|
363
|
-
# by `position` attribute
|
|
364
|
-
# If `track_inventory_levels` is disabled it will return first Variant sorted
|
|
365
|
-
# by `position` attribute
|
|
366
|
-
#
|
|
507
|
+
# The default variant is a persisted FK, but before the product is saved (or
|
|
508
|
+
# if it somehow has no default set) fall back to the first variant —
|
|
509
|
+
# including one built in memory via the delegated setters.
|
|
367
510
|
# @return [Spree::Variant]
|
|
368
511
|
def default_variant
|
|
369
|
-
|
|
370
|
-
available_variant
|
|
371
|
-
else
|
|
372
|
-
has_variants? ? variants.first : find_or_build_master
|
|
373
|
-
end
|
|
512
|
+
super || variants.first
|
|
374
513
|
end
|
|
375
514
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
def default_variant_id
|
|
379
|
-
@default_variant_id ||= default_variant.id
|
|
515
|
+
def find_or_build_default_variant
|
|
516
|
+
default_variant || variants.build
|
|
380
517
|
end
|
|
381
518
|
|
|
519
|
+
# "Does this product have more than one variant (i.e. options to choose from)?"
|
|
520
|
+
# Uses the variant_count counter cache for performance.
|
|
521
|
+
# @return [Boolean]
|
|
522
|
+
def has_multiple_variants?
|
|
523
|
+
return variants.size > 1 if variants.loaded?
|
|
524
|
+
|
|
525
|
+
variant_count > 1
|
|
526
|
+
end
|
|
527
|
+
# @deprecated Use #has_multiple_variants? — will be removed in Spree 6.1.
|
|
528
|
+
alias has_variants? has_multiple_variants?
|
|
529
|
+
|
|
382
530
|
# Returns the product's media gallery.
|
|
383
531
|
# Uses product-level media if present, otherwise falls back to variant images.
|
|
384
532
|
# @return [ActiveRecord::Relation]
|
|
@@ -401,34 +549,19 @@ module Spree
|
|
|
401
549
|
alias has_variant_images? has_media?
|
|
402
550
|
|
|
403
551
|
# Returns the variant that should be used for displaying images.
|
|
404
|
-
# Priority:
|
|
552
|
+
# Priority: default_variant > first variant with images
|
|
405
553
|
# @return [Spree::Variant, nil]
|
|
406
554
|
def variant_for_images
|
|
407
555
|
@variant_for_images ||= find_variant_for_images
|
|
408
556
|
end
|
|
409
557
|
|
|
410
|
-
# @deprecated
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
# @deprecated Use #primary_media instead.
|
|
417
|
-
def featured_image
|
|
418
|
-
Spree::Deprecation.warn('Spree::Product#featured_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
|
|
419
|
-
primary_media
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
# @deprecated Use #primary_media instead.
|
|
423
|
-
def primary_image
|
|
424
|
-
Spree::Deprecation.warn('Spree::Product#primary_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
|
|
425
|
-
primary_media
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
# Returns secondary media for Product (for hover effects).
|
|
429
|
-
# @return [Spree::Asset, nil]
|
|
558
|
+
# @deprecated Read #gallery_media directly; removed in 6.1. Nothing in
|
|
559
|
+
# Spree calls this — a hover image is a storefront presentation choice,
|
|
560
|
+
# not something core should name.
|
|
561
|
+
# @return [Spree::Media, nil]
|
|
430
562
|
def secondary_image
|
|
431
|
-
|
|
563
|
+
Spree::Deprecation.warn('Spree::Product#secondary_image is deprecated and will be removed in Spree 6.1. Use #gallery_media instead.')
|
|
564
|
+
gallery_media.second
|
|
432
565
|
end
|
|
433
566
|
|
|
434
567
|
# @deprecated Use media_count instead
|
|
@@ -440,7 +573,14 @@ module Spree
|
|
|
440
573
|
# Checks product-level media first, then falls back to variant images.
|
|
441
574
|
# Called when media is added, removed, or reordered.
|
|
442
575
|
def update_thumbnail!
|
|
443
|
-
|
|
576
|
+
# Fresh scopes, never the cached associations — this runs from a media
|
|
577
|
+
# row's after_commit, where a loaded association is missing its siblings.
|
|
578
|
+
candidates = media.reload.order(:position).presence || variant_images.reload.order(:position)
|
|
579
|
+
# Only media that can draw as an image: `thumbnail_url` is rendered in an
|
|
580
|
+
# <img> everywhere it's used, so a video with no still leaves no
|
|
581
|
+
# thumbnail at all rather than a broken one.
|
|
582
|
+
first_media = candidates.find(&:renderable_as_image?)
|
|
583
|
+
|
|
444
584
|
update_column(:primary_media_id, first_media&.id)
|
|
445
585
|
end
|
|
446
586
|
|
|
@@ -461,12 +601,9 @@ module Spree
|
|
|
461
601
|
# Returns tax category for Product
|
|
462
602
|
# @return [Spree::TaxCategory, nil]
|
|
463
603
|
def tax_category
|
|
464
|
-
@tax_category ||= super || TaxCategory.default
|
|
604
|
+
@tax_category ||= super || TaxCategory.default(assignable_store)
|
|
465
605
|
end
|
|
466
606
|
|
|
467
|
-
# Adding properties and option types on creation based on a chosen prototype
|
|
468
|
-
attr_accessor :prototype_id
|
|
469
|
-
|
|
470
607
|
def first_or_default_variant(currency)
|
|
471
608
|
if !has_variants?
|
|
472
609
|
default_variant
|
|
@@ -477,38 +614,50 @@ module Spree
|
|
|
477
614
|
end
|
|
478
615
|
end
|
|
479
616
|
|
|
617
|
+
# The first variant a shopper can actually buy, in this currency.
|
|
618
|
+
#
|
|
619
|
+
# Position order, deliberately — this is "the first one that works", not
|
|
620
|
+
# the buy box's ranked winner. What it does borrow from the buy box is the
|
|
621
|
+
# seller check: a variant from a seller who is suspended, still onboarding
|
|
622
|
+
# or away is not for sale, so it cannot be what the product displays.
|
|
480
623
|
def first_available_variant(currency)
|
|
481
|
-
|
|
624
|
+
sellable_variants.find do |variant|
|
|
625
|
+
variant.purchasable? && variant.price_in(currency).amount.present?
|
|
626
|
+
end
|
|
482
627
|
end
|
|
483
628
|
|
|
629
|
+
# Whether the shop price differs between this product's variants. Reads
|
|
630
|
+
# base prices only: a price list's rows are what one audience pays under an
|
|
631
|
+
# agreement, and a ladder holds several rows for one variant, so counting
|
|
632
|
+
# them would report a product as varying when it does not
|
|
633
|
+
# (docs/plans/6.0-volume-pricing.md).
|
|
484
634
|
def price_varies?(currency)
|
|
485
|
-
|
|
635
|
+
shop_prices(currency).map(&:amount).uniq.count > 1
|
|
486
636
|
end
|
|
487
637
|
|
|
488
638
|
def any_variant_available?(currency)
|
|
489
|
-
|
|
490
|
-
first_available_variant(currency).present?
|
|
491
|
-
else
|
|
492
|
-
master.purchasable? && master.price_in(currency).amount.present?
|
|
493
|
-
end
|
|
639
|
+
first_available_variant(currency).present?
|
|
494
640
|
end
|
|
495
641
|
|
|
496
642
|
# returns the lowest price for the product in the given currency
|
|
497
|
-
#
|
|
643
|
+
# prices are usually already loaded, so this should not trigger an extra query
|
|
498
644
|
def lowest_price(currency)
|
|
499
|
-
|
|
645
|
+
shop_prices(currency).min_by(&:amount)
|
|
500
646
|
end
|
|
501
647
|
|
|
502
|
-
#
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
648
|
+
# The prices this product is offered at publicly — one per variant. A price
|
|
649
|
+
# list's rows are excluded: they are what one audience pays under an
|
|
650
|
+
# agreement, so surfacing the cheapest of them as the product's "from"
|
|
651
|
+
# price would quote every shopper a contract they are not party to.
|
|
652
|
+
#
|
|
653
|
+
# Filtered in memory rather than through a scope because callers have
|
|
654
|
+
# already loaded the association, and a scope would re-query per product
|
|
655
|
+
# in a listing.
|
|
656
|
+
#
|
|
657
|
+
# @param currency [String]
|
|
658
|
+
# @return [Array<Spree::Price>]
|
|
659
|
+
def shop_prices(currency)
|
|
660
|
+
prices.find_all { |p| p.currency == currency && p.price_list_id.nil? && p.non_zero? }
|
|
512
661
|
end
|
|
513
662
|
|
|
514
663
|
# for adding products which are closely related to existing ones
|
|
@@ -554,21 +703,14 @@ module Spree
|
|
|
554
703
|
!!discontinue_on && discontinue_on <= Time.current
|
|
555
704
|
end
|
|
556
705
|
|
|
557
|
-
# determine if any variant
|
|
706
|
+
# determine if any variant can be supplied
|
|
558
707
|
def can_supply?
|
|
559
|
-
|
|
708
|
+
variants.any?(&:can_supply?)
|
|
560
709
|
end
|
|
561
710
|
|
|
562
|
-
# determine if any variant
|
|
711
|
+
# determine if any variant is out of stock and backorderable
|
|
563
712
|
def backordered?
|
|
564
|
-
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
def self.like_any(fields, values)
|
|
568
|
-
conditions = fields.product(values).map do |(field, value)|
|
|
569
|
-
arel_table[field].matches("%#{value}%")
|
|
570
|
-
end
|
|
571
|
-
where conditions.inject(:or)
|
|
713
|
+
variants.any?(&:backordered?)
|
|
572
714
|
end
|
|
573
715
|
|
|
574
716
|
# Suitable for displaying only variants that has at least one option value.
|
|
@@ -584,78 +726,91 @@ module Spree
|
|
|
584
726
|
@total_on_hand ||= if any_variants_not_track_inventory?
|
|
585
727
|
BigDecimal::INFINITY
|
|
586
728
|
else
|
|
587
|
-
if
|
|
588
|
-
|
|
729
|
+
if variants.loaded?
|
|
730
|
+
variants.sum(&:total_on_hand)
|
|
589
731
|
else
|
|
590
|
-
|
|
732
|
+
stock_levels.loaded? ? stock_levels.sum(&:available_count) : stock_levels.sum('count_on_hand - allocated_count')
|
|
591
733
|
end
|
|
592
734
|
end
|
|
593
735
|
end
|
|
594
736
|
|
|
595
|
-
#
|
|
596
|
-
|
|
597
|
-
|
|
737
|
+
# @deprecated Use {#stock_levels}; removed in 6.1.
|
|
738
|
+
def stock_items
|
|
739
|
+
Spree::Deprecation.warn('Spree::Product#stock_items is deprecated and will be removed in Spree 6.1. Use #stock_levels instead.')
|
|
740
|
+
stock_levels
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# @deprecated Use #default_variant — the master variant is being removed in Spree 6.1.
|
|
598
744
|
def master
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
# Returns the brand taxon for the product
|
|
603
|
-
# @return [Spree::Taxon]
|
|
604
|
-
def brand_taxon
|
|
605
|
-
@brand_taxon ||= if classification_count.zero?
|
|
606
|
-
nil
|
|
607
|
-
elsif Spree.use_translations?
|
|
608
|
-
taxons.joins(:taxonomy).
|
|
609
|
-
join_translation_table(Taxonomy).
|
|
610
|
-
find_by(Taxonomy.translation_table_alias => { name: Spree.t(:taxonomy_brands_name) })
|
|
611
|
-
elsif taxons.loaded?
|
|
612
|
-
taxons.find { |taxon| taxon.taxonomy.name == Spree.t(:taxonomy_brands_name) }
|
|
613
|
-
else
|
|
614
|
-
taxons.joins(:taxonomy).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_brands_name) })
|
|
615
|
-
end
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
# Returns the brand name for the product
|
|
619
|
-
# @return [String]
|
|
620
|
-
def brand_name
|
|
621
|
-
brand_taxon&.name
|
|
745
|
+
Spree::Deprecation.warn('Spree::Product#master is deprecated and will be removed in Spree 6.1. Use #default_variant instead.')
|
|
746
|
+
default_variant
|
|
622
747
|
end
|
|
623
748
|
|
|
624
|
-
|
|
625
|
-
|
|
749
|
+
# @deprecated Use #variants — will be removed in Spree 6.1.
|
|
750
|
+
def variants_including_master
|
|
751
|
+
Spree::Deprecation.warn('Spree::Product#variants_including_master is deprecated and will be removed in Spree 6.1. Use #variants instead.')
|
|
752
|
+
variants
|
|
753
|
+
end
|
|
626
754
|
|
|
627
|
-
|
|
755
|
+
# @deprecated Use #default_variant_id — will be removed in Spree 6.1.
|
|
756
|
+
def master_id
|
|
757
|
+
default_variant_id
|
|
628
758
|
end
|
|
629
759
|
|
|
630
|
-
|
|
631
|
-
|
|
760
|
+
# The product's primary category — the first manually-assigned category.
|
|
761
|
+
# Renamed from #main_taxon in 6.0.
|
|
762
|
+
def primary_category
|
|
763
|
+
return if categories_count.zero?
|
|
632
764
|
|
|
633
|
-
|
|
765
|
+
@primary_category ||= categories.first
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
# @deprecated Use {#primary_category}.
|
|
769
|
+
def main_taxon
|
|
770
|
+
Spree::Deprecation.warn('Spree::Product#main_taxon is deprecated and will be removed in Spree 6.1. Use #primary_category instead.')
|
|
771
|
+
primary_category
|
|
634
772
|
end
|
|
635
773
|
|
|
636
774
|
def any_variant_in_stock_or_backorderable?
|
|
637
775
|
if has_variants?
|
|
638
|
-
|
|
776
|
+
variants.in_stock_or_backorderable.not_discontinued.exists?
|
|
639
777
|
else
|
|
640
|
-
|
|
778
|
+
default_variant.in_stock_or_backorderable?
|
|
641
779
|
end
|
|
642
780
|
end
|
|
643
781
|
|
|
644
782
|
# Check if the product is digital by checking if any of its shipping methods are digital delivery
|
|
645
783
|
# This is used to determine if the product is digital and should have a digital delivery price
|
|
646
|
-
# instead of a physical shipping price
|
|
784
|
+
# instead of a physical shipping price. The profile kind declares it —
|
|
785
|
+
# capabilities are never stated twice.
|
|
647
786
|
#
|
|
648
787
|
# @return [Boolean]
|
|
649
788
|
def digital?
|
|
650
|
-
|
|
789
|
+
# Memoized via nil-check: MemoizedData resets caches by assigning nil.
|
|
790
|
+
return @digital unless @digital.nil?
|
|
791
|
+
|
|
792
|
+
@digital = !!resolved_delivery_profile&.digital?
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
# The profile that governs this product. The association is required for
|
|
796
|
+
# new records; the store-default fallback covers rows predating the
|
|
797
|
+
# backfill (never expected in practice, cheap to be safe about).
|
|
798
|
+
#
|
|
799
|
+
# @return [Spree::DeliveryProfile, nil]
|
|
800
|
+
def resolved_delivery_profile
|
|
801
|
+
# Memoized via nil-check, like +digital?+ above: the store-default branch
|
|
802
|
+
# is an uncached lookup, and every variant on the product asks.
|
|
803
|
+
return @resolved_delivery_profile unless @resolved_delivery_profile.nil?
|
|
804
|
+
|
|
805
|
+
@resolved_delivery_profile = delivery_profile || (store && Spree::DeliveryProfile.default_for(store))
|
|
651
806
|
end
|
|
652
807
|
|
|
653
|
-
def
|
|
808
|
+
def auto_match_collections
|
|
654
809
|
return if deleted?
|
|
655
810
|
return if archived?
|
|
656
|
-
return if store.nil? || store.
|
|
811
|
+
return if store.nil? || store.collections.automatic.none?
|
|
657
812
|
|
|
658
|
-
Spree::Products::
|
|
813
|
+
Spree::Products::AutoMatchCollectionsJob.set(wait: 30.seconds).perform_later(id)
|
|
659
814
|
end
|
|
660
815
|
|
|
661
816
|
def to_csv(store = nil)
|
|
@@ -670,21 +825,22 @@ module Spree
|
|
|
670
825
|
else
|
|
671
826
|
[]
|
|
672
827
|
end
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
828
|
+
# Through the shared helper so the value row is built from exactly the
|
|
829
|
+
# definitions, in exactly the order, that Export#custom_fields_headers
|
|
830
|
+
# used for the header row — a mismatch shifts every value column.
|
|
831
|
+
custom_field_values = custom_fields_for_csv(self, store)
|
|
832
|
+
categories_for_csv ||= categories.reorder(depth: :desc).first(3).pluck(:pretty_name)
|
|
833
|
+
categories_for_csv.fill(nil, categories_for_csv.size...3)
|
|
678
834
|
|
|
679
835
|
csv_lines = []
|
|
680
|
-
all_variants =
|
|
836
|
+
all_variants = variants.to_a
|
|
681
837
|
default_currency = store.default_currency
|
|
682
838
|
additional_currencies = store.supported_currencies_list.map(&:iso_code) - [default_currency]
|
|
683
839
|
|
|
684
840
|
# Primary rows in the store's default currency
|
|
685
841
|
all_variants.each_with_index do |variant, index|
|
|
686
|
-
csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, index, properties_for_csv,
|
|
687
|
-
|
|
842
|
+
csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, index, properties_for_csv, categories_for_csv, store,
|
|
843
|
+
custom_field_values).call
|
|
688
844
|
end
|
|
689
845
|
|
|
690
846
|
# Price-only rows for each additional currency
|
|
@@ -714,257 +870,118 @@ module Spree
|
|
|
714
870
|
private
|
|
715
871
|
|
|
716
872
|
# Determines which variant should be used for displaying media.
|
|
717
|
-
# Priority:
|
|
873
|
+
# Priority: default_variant > first variant with media
|
|
718
874
|
def find_variant_for_images
|
|
719
|
-
return
|
|
720
|
-
return default_variant if has_variants? && default_variant.has_media?
|
|
875
|
+
return default_variant if default_variant&.has_media?
|
|
721
876
|
return find_variant_with_images if has_media?
|
|
722
877
|
|
|
723
878
|
nil
|
|
724
879
|
end
|
|
725
880
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
@pending_variants_params = nil
|
|
735
|
-
end
|
|
736
|
-
|
|
737
|
-
def pending_media?
|
|
738
|
-
@pending_media_params.present?
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
def apply_pending_media
|
|
742
|
-
return unless @pending_media_params
|
|
743
|
-
|
|
744
|
-
apply_media(@pending_media_params)
|
|
745
|
-
@pending_media_params = nil
|
|
746
|
-
end
|
|
747
|
-
|
|
748
|
-
def apply_media(media_params)
|
|
749
|
-
# Eager-load Asset descendants once so the type allowlist is stable across
|
|
750
|
-
# the loop (and across requests once subclasses are referenced). Computed
|
|
751
|
-
# per-call rather than at class-load to avoid forcing autoload of every
|
|
752
|
-
# Asset subclass during boot.
|
|
753
|
-
allowed_types = [Spree::Asset, *Spree::Asset.descendants].map(&:name).to_set
|
|
754
|
-
media_params.each do |raw|
|
|
755
|
-
attrs = raw.respond_to?(:to_h) ? raw.to_h : raw
|
|
756
|
-
attrs = attrs.with_indifferent_access
|
|
757
|
-
|
|
758
|
-
# Upsert path: entries with an `id` patch an existing asset (alt,
|
|
759
|
-
# position, variant_ids). Entries with a `signed_id` create+attach.
|
|
760
|
-
# Omitting an entry leaves it alone — explicit DELETE on the dedicated
|
|
761
|
-
# media endpoint is still the only way to remove an asset.
|
|
762
|
-
asset_id = attrs.delete(:id)
|
|
763
|
-
if asset_id.present?
|
|
764
|
-
asset = media.find_by_param(asset_id) || next
|
|
765
|
-
asset.update!(attrs.except(:signed_id, :type))
|
|
766
|
-
next
|
|
767
|
-
end
|
|
768
|
-
|
|
769
|
-
signed_id = attrs.delete(:signed_id)
|
|
770
|
-
next if signed_id.blank?
|
|
881
|
+
# Additive, unlike the legacy prototype callback which replaced both sets —
|
|
882
|
+
# the type seeds the product when attached (creation, later assignment,
|
|
883
|
+
# reassignment) and never removes anything; detaching seeds nothing.
|
|
884
|
+
# The already-linked sets come from the join tables in one query each: the
|
|
885
|
+
# cached associations can be stale here (pending variants insert option-type
|
|
886
|
+
# joins through product_option_types within the same save).
|
|
887
|
+
def sync_associations_from_product_type
|
|
888
|
+
return unless product_type
|
|
771
889
|
|
|
772
|
-
|
|
773
|
-
|
|
890
|
+
missing_option_type_ids = product_type.option_type_ids - product_option_types.pluck(:option_type_id)
|
|
891
|
+
Spree::OptionType.where(id: missing_option_type_ids).each { |option_type| option_types << option_type }
|
|
774
892
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
end
|
|
781
|
-
|
|
782
|
-
def apply_variants(variants_params)
|
|
783
|
-
variant_ids_in_payload = []
|
|
784
|
-
master_touched = false
|
|
785
|
-
mutated = false
|
|
786
|
-
|
|
787
|
-
variants_params.each do |variant_data|
|
|
788
|
-
variant_data = variant_data.to_h.with_indifferent_access
|
|
789
|
-
variant_id = variant_data.delete(:id)
|
|
790
|
-
options = variant_data[:options]
|
|
791
|
-
|
|
792
|
-
if variant_id.present?
|
|
793
|
-
variant = variants_including_master.find_by_param!(variant_id)
|
|
794
|
-
variant.update!(variant_data)
|
|
795
|
-
variant_ids_in_payload << variant.id
|
|
796
|
-
mutated = true
|
|
797
|
-
elsif options.blank? || (options.is_a?(Array) && options.empty?)
|
|
798
|
-
# An entry with no options addresses the master variant. Building a
|
|
799
|
-
# non-master here would create a phantom duplicate (the auto-built
|
|
800
|
-
# master already exists, and `variants` excludes it). Upsert onto
|
|
801
|
-
# the master instead — the merchant-visible "default variant" on a
|
|
802
|
-
# simple product IS the master.
|
|
803
|
-
variant_data = variant_data.except(:options)
|
|
804
|
-
target = find_or_build_master
|
|
805
|
-
target.assign_attributes(variant_data)
|
|
806
|
-
target.save!
|
|
807
|
-
master_touched = true
|
|
808
|
-
mutated = true
|
|
809
|
-
else
|
|
810
|
-
variant = variants.build
|
|
811
|
-
variant.assign_attributes(variant_data)
|
|
812
|
-
variant.save!
|
|
813
|
-
variant_ids_in_payload << variant.id
|
|
814
|
-
mutated = true
|
|
815
|
-
end
|
|
816
|
-
end
|
|
817
|
-
|
|
818
|
-
# Remove variants not in the payload (only non-master). If only the
|
|
819
|
-
# master was touched (simple product), leave existing non-master
|
|
820
|
-
# variants alone — the payload is partial, not a full replacement.
|
|
821
|
-
if variant_ids_in_payload.any? && !master_touched
|
|
822
|
-
removed = variants.where.not(id: variant_ids_in_payload).destroy_all
|
|
823
|
-
mutated ||= removed.any?
|
|
824
|
-
end
|
|
825
|
-
|
|
826
|
-
sync_variant_state! if mutated
|
|
827
|
-
end
|
|
828
|
-
|
|
829
|
-
# Re-syncs the in-memory derived variant state after `apply_variants`
|
|
830
|
-
# mutates variants out-of-band (the variant counter is bumped via
|
|
831
|
-
# `Spree::Product.increment_counter` in a Variant callback, and
|
|
832
|
-
# `default_variant` is memoized). Without this, a freshly built/updated
|
|
833
|
-
# product serialized in the same request returns a stale `variant_count`
|
|
834
|
-
# and a stale `price` (delegated to the memoized `default_variant`).
|
|
835
|
-
#
|
|
836
|
-
# The `default_variant` memo reset here changes shape once master is
|
|
837
|
-
# removed (it becomes a `belongs_to` FK). See
|
|
838
|
-
# docs/plans/6.0-remove-master-variant.md (Phase 3).
|
|
839
|
-
# @return [void]
|
|
840
|
-
def sync_variant_state!
|
|
841
|
-
# `variant_count` is maintained by a direct counter update in a Variant
|
|
842
|
-
# callback, so the in-memory attribute is stale here — re-read it from the
|
|
843
|
-
# row without reloading the whole record.
|
|
844
|
-
self[:variant_count] = self.class.where(id: id).pick(:variant_count)
|
|
845
|
-
variants.reset
|
|
846
|
-
@default_variant = nil
|
|
847
|
-
@default_variant_id = nil
|
|
848
|
-
end
|
|
849
|
-
|
|
850
|
-
def add_associations_from_prototype
|
|
851
|
-
if prototype_id && prototype = Spree::Prototype.find_by(id: prototype_id)
|
|
852
|
-
self.option_types = prototype.option_types
|
|
853
|
-
self.taxons = prototype.taxons
|
|
854
|
-
end
|
|
893
|
+
# Scoped to the product's own store, matching `category_ids=`: a type
|
|
894
|
+
# pointing at another store's category must not drag it onto the product.
|
|
895
|
+
missing_category_ids = product_type.category_ids - product_categories.pluck(:category_id)
|
|
896
|
+
Spree::Category.for_store(assignable_store).where(id: missing_category_ids).
|
|
897
|
+
each { |category| categories << category }
|
|
855
898
|
end
|
|
856
899
|
|
|
857
900
|
def any_variants_not_track_inventory?
|
|
858
|
-
return true unless
|
|
901
|
+
return true unless store_preference(:track_inventory_levels)
|
|
859
902
|
|
|
860
|
-
if
|
|
861
|
-
|
|
903
|
+
if variants.loaded?
|
|
904
|
+
variants.any? { |v| !v.track_inventory? }
|
|
862
905
|
else
|
|
863
|
-
|
|
906
|
+
variants.where(track_inventory: false).exists?
|
|
864
907
|
end
|
|
865
908
|
end
|
|
866
909
|
|
|
867
|
-
#
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
values = option_values_hash.values
|
|
872
|
-
values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
|
|
873
|
-
|
|
874
|
-
default_currency = store&.default_currency || Spree::Store.default.default_currency
|
|
875
|
-
master_price = master.price_in(default_currency).amount
|
|
876
|
-
|
|
877
|
-
values.each do |ids|
|
|
878
|
-
variant = variants.create!(option_value_ids: ids)
|
|
879
|
-
variant.set_price(default_currency, master_price) if master_price.present?
|
|
880
|
-
end
|
|
881
|
-
save
|
|
910
|
+
# Every product must have at least one variant. If none was built via the
|
|
911
|
+
# delegated setters or supplied as nested attributes, create an empty one.
|
|
912
|
+
def ensure_default_variant
|
|
913
|
+
variants.create! if variants.empty?
|
|
882
914
|
end
|
|
883
915
|
|
|
884
|
-
def
|
|
885
|
-
return
|
|
916
|
+
def set_default_variant
|
|
917
|
+
return if default_variant_id.present?
|
|
886
918
|
|
|
887
|
-
|
|
919
|
+
first_variant = preferred_default_variant
|
|
920
|
+
update_column(:default_variant_id, first_variant.id) if first_variant
|
|
888
921
|
end
|
|
889
922
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
923
|
+
# Re-point the default variant when the current one has been removed. Sticky
|
|
924
|
+
# otherwise — an in-stock change never re-points (see the plan's open question).
|
|
925
|
+
def auto_promote_default_variant
|
|
926
|
+
current = association(:default_variant).reader
|
|
927
|
+
return if current && current.deleted_at.nil?
|
|
893
928
|
|
|
894
|
-
|
|
895
|
-
|
|
929
|
+
next_variant = preferred_default_variant(reload: true)
|
|
930
|
+
update_column(:default_variant_id, next_variant&.id) if next_variant && next_variant.id != default_variant_id
|
|
896
931
|
end
|
|
897
932
|
|
|
898
|
-
|
|
899
|
-
|
|
933
|
+
# The product's public identity — its headline price, its SKU, and the
|
|
934
|
+
# Omnibus `prior_price`, which is a compliance claim about this shop's own
|
|
935
|
+
# pricing history. Where the operator sells the item themselves that is the
|
|
936
|
+
# listing that should speak for the product, not whichever seller happens
|
|
937
|
+
# to sort first.
|
|
938
|
+
def preferred_default_variant(reload: false)
|
|
939
|
+
candidates = reload ? variants.reload : variants
|
|
940
|
+
candidates.detect { |variant| variant.resolved_seller_id.nil? } || candidates.first
|
|
900
941
|
end
|
|
901
942
|
|
|
902
|
-
def
|
|
903
|
-
|
|
904
|
-
master.new_record? ||
|
|
905
|
-
master.changed? ||
|
|
906
|
-
(
|
|
907
|
-
Spree::Config.enable_legacy_default_price &&
|
|
908
|
-
master.default_price &&
|
|
909
|
-
(
|
|
910
|
-
master.default_price.new_record? ||
|
|
911
|
-
master.default_price.changed?
|
|
912
|
-
)
|
|
913
|
-
)
|
|
914
|
-
)
|
|
943
|
+
def assign_default_tax_category
|
|
944
|
+
self.tax_category = Spree::TaxCategory.default(assignable_store) if new_record? && self[:tax_category_id].blank?
|
|
915
945
|
end
|
|
916
946
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
# Fix for issue #5306
|
|
920
|
-
def save_master
|
|
921
|
-
if master_updated?
|
|
922
|
-
master.save!
|
|
923
|
-
@nested_changes = true
|
|
924
|
-
end
|
|
925
|
-
end
|
|
947
|
+
def stamp_template_delivery_profile
|
|
948
|
+
return if delivery_profile_id.present?
|
|
926
949
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
if Spree::Config.enable_legacy_default_price
|
|
931
|
-
# We call master.default_price here to ensure price is initialized.
|
|
932
|
-
# Required to avoid Variant#check_price validation failing on create.
|
|
933
|
-
master.default_price
|
|
934
|
-
end
|
|
950
|
+
self.delivery_profile_id = product_type&.delivery_profile_id ||
|
|
951
|
+
(store && Spree::DeliveryProfile.default_for(store)&.id)
|
|
952
|
+
end
|
|
935
953
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
954
|
+
def delivery_profile_must_belong_to_store
|
|
955
|
+
return if delivery_profile.nil? || store.nil?
|
|
956
|
+
return if delivery_profile.store_id == store_id
|
|
939
957
|
|
|
940
|
-
|
|
941
|
-
end
|
|
942
|
-
end
|
|
958
|
+
errors.add(:delivery_profile, :invalid)
|
|
943
959
|
end
|
|
944
960
|
|
|
945
|
-
|
|
946
|
-
|
|
961
|
+
# A seller from another store would put one store's catalog inside another
|
|
962
|
+
# store's seller — and every seller-scoped read is rooted in exactly that
|
|
963
|
+
# association.
|
|
964
|
+
def seller_must_belong_to_store
|
|
965
|
+
return if seller.nil? || store.nil?
|
|
966
|
+
return if seller.store_id == store_id
|
|
947
967
|
|
|
948
|
-
|
|
949
|
-
name = I18n.t('spree.seed.shipping.categories.default')
|
|
950
|
-
self.shipping_category = Spree::ShippingCategory.find_or_create_by!(name: name)
|
|
951
|
-
end
|
|
968
|
+
errors.add(:seller, :invalid)
|
|
952
969
|
end
|
|
953
970
|
|
|
954
971
|
def run_touch_callbacks
|
|
955
972
|
run_callbacks(:touch)
|
|
956
973
|
end
|
|
957
974
|
|
|
958
|
-
def
|
|
959
|
-
@
|
|
975
|
+
def category_and_ancestors
|
|
976
|
+
@category_and_ancestors ||= categories.map(&:self_and_ancestors).flatten.uniq
|
|
960
977
|
end
|
|
961
978
|
|
|
962
|
-
# Iterate through this
|
|
963
|
-
def
|
|
964
|
-
if
|
|
965
|
-
Spree::Products::
|
|
979
|
+
# Iterate through this product's categories and touch their timestamps in a batch
|
|
980
|
+
def touch_categories
|
|
981
|
+
if categories.any?
|
|
982
|
+
Spree::Products::TouchCategoriesJob.
|
|
966
983
|
set(wait: 5.seconds).
|
|
967
|
-
perform_later(
|
|
984
|
+
perform_later(category_and_ancestors.map(&:id))
|
|
968
985
|
end
|
|
969
986
|
end
|
|
970
987
|
|
|
@@ -975,48 +992,33 @@ module Spree
|
|
|
975
992
|
end
|
|
976
993
|
end
|
|
977
994
|
|
|
978
|
-
def
|
|
979
|
-
|
|
980
|
-
|
|
995
|
+
def remove_category(category)
|
|
996
|
+
removed_product_categories = product_categories.where(category: category)
|
|
997
|
+
removed_product_categories.each(&:remove_from_list)
|
|
981
998
|
end
|
|
982
999
|
|
|
983
1000
|
def discontinue_on_must_be_later_than_make_active_at
|
|
984
|
-
Spree::Deprecation.warn('Spree::Product#discontinue_on_must_be_later_than_make_active_at is deprecated and will be removed in Spree 6.0.')
|
|
985
1001
|
if discontinue_on < make_active_at
|
|
986
1002
|
errors.add(:discontinue_on, :invalid_date_range)
|
|
987
1003
|
end
|
|
988
1004
|
end
|
|
989
1005
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1006
|
+
# Mirrors the machine's bang events: an illegal move raised.
|
|
1007
|
+
def run_status_workflow(workflow)
|
|
1008
|
+
result = workflow.call(product: self)
|
|
1009
|
+
|
|
1010
|
+
if result.failure?
|
|
1011
|
+
# ResultError#to_s unwraps an ActiveModel::Errors into its full
|
|
1012
|
+
# messages; its `value` would inspect the object into the message.
|
|
1013
|
+
errors.add(:base, result.error.to_s)
|
|
1014
|
+
raise ActiveRecord::RecordInvalid, self
|
|
1015
|
+
end
|
|
993
1016
|
|
|
994
|
-
def requires_shipping_category?
|
|
995
1017
|
true
|
|
996
1018
|
end
|
|
997
1019
|
|
|
998
|
-
def
|
|
1020
|
+
def eligible_for_collection_matching?
|
|
999
1021
|
previously_new_record? || tag_list_previously_changed? || available_on_previously_changed?
|
|
1000
1022
|
end
|
|
1001
|
-
|
|
1002
|
-
def after_activate
|
|
1003
|
-
# Implement your logic here
|
|
1004
|
-
end
|
|
1005
|
-
|
|
1006
|
-
def after_archive
|
|
1007
|
-
# Implement your logic here
|
|
1008
|
-
end
|
|
1009
|
-
|
|
1010
|
-
def after_draft
|
|
1011
|
-
# Implement your logic here
|
|
1012
|
-
end
|
|
1013
|
-
|
|
1014
|
-
def publish_product_activated_event
|
|
1015
|
-
publish_event('product.activated')
|
|
1016
|
-
end
|
|
1017
|
-
|
|
1018
|
-
def publish_product_archived_event
|
|
1019
|
-
publish_event('product.archived')
|
|
1020
|
-
end
|
|
1021
1023
|
end
|
|
1022
1024
|
end
|