spree_core 6.0.0.beta3 → 6.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/app/helpers/spree/images_helper.rb +0 -57
- data/app/jobs/spree/base_job.rb +21 -0
- data/app/jobs/spree/carts/reap_expired_job.rb +2 -2
- data/app/jobs/spree/collections/regenerate_time_based_job.rb +6 -3
- data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +8 -5
- data/app/jobs/spree/payments/cancel_unused_sessions_job.rb +61 -0
- data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
- data/app/mailers/spree/admin_user_mailer.rb +4 -19
- data/app/mailers/spree/base_mailer.rb +96 -13
- data/app/mailers/spree/email_template_mailer.rb +27 -0
- data/app/mailers/spree/export_mailer.rb +3 -4
- data/app/mailers/spree/import_mailer.rb +3 -4
- data/app/mailers/spree/invitation_mailer.rb +24 -7
- data/app/mailers/spree/seller_user_mailer.rb +4 -16
- data/app/mailers/spree/webhook_mailer.rb +2 -2
- data/app/models/concerns/spree/acted_by.rb +1 -1
- data/app/models/concerns/spree/admin_user_methods.rb +13 -1
- data/app/models/concerns/spree/api_typed.rb +91 -0
- data/app/models/concerns/spree/calculator/tiers.rb +76 -0
- data/app/models/concerns/spree/customer_methods.rb +15 -14
- data/app/models/concerns/spree/email_template_identity.rb +52 -0
- data/app/models/concerns/spree/parameterizable_name.rb +2 -0
- data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
- data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
- data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
- data/app/models/concerns/spree/preference_schema.rb +4 -12
- data/app/models/concerns/spree/product_scopes.rb +11 -1
- data/app/models/concerns/spree/provider_listing.rb +41 -0
- data/app/models/concerns/spree/publishable.rb +82 -14
- data/app/models/concerns/spree/purchase/addresses.rb +23 -10
- data/app/models/concerns/spree/purchase/currency.rb +1 -1
- data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
- data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
- data/app/models/concerns/spree/purchase/locale.rb +1 -1
- data/app/models/concerns/spree/purchase/payment_processing.rb +7 -3
- data/app/models/concerns/spree/purchase/purchase_order.rb +2 -2
- data/app/models/concerns/spree/purchase/totals.rb +49 -1
- data/app/models/concerns/spree/receivable.rb +1 -1
- data/app/models/concerns/spree/searchable.rb +0 -4
- data/app/models/concerns/spree/secret_preferences.rb +59 -0
- data/app/models/concerns/spree/single_store_resource.rb +1 -1
- data/app/models/concerns/spree/stores/email_branding.rb +31 -0
- data/app/models/concerns/spree/type_labels.rb +38 -0
- data/app/models/concerns/spree/typed_adjustment_line.rb +1 -1
- data/app/models/concerns/spree/typed_associations.rb +6 -0
- data/app/models/spree/address.rb +11 -1
- data/app/models/spree/adjusters/promotion.rb +1 -15
- data/app/models/spree/allowed_origin.rb +0 -1
- data/app/models/spree/api_key.rb +5 -6
- data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
- data/app/models/spree/base.rb +70 -22
- data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/flat_rate.rb +1 -3
- data/app/models/spree/calculator/flexi_rate.rb +1 -3
- data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
- data/app/models/spree/calculator/price_sack.rb +1 -3
- data/app/models/spree/calculator/shipping/digital_delivery.rb +1 -3
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -3
- data/app/models/spree/calculator/shipping/flat_rate.rb +1 -3
- data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -3
- data/app/models/spree/calculator/shipping/per_item.rb +1 -3
- data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
- data/app/models/spree/calculator/tiered_flat_rate.rb +6 -23
- data/app/models/spree/calculator/tiered_percent.rb +7 -25
- data/app/models/spree/cart.rb +78 -18
- data/app/models/spree/channel/gating.rb +1 -1
- data/app/models/spree/channel.rb +4 -4
- data/app/models/spree/claim.rb +21 -1
- data/app/models/spree/claim_line_item.rb +41 -6
- data/app/models/spree/collection.rb +0 -1
- data/app/models/spree/collection_rule.rb +5 -21
- data/app/models/spree/commission_line.rb +1 -1
- data/app/models/spree/commission_rate.rb +2 -3
- data/app/models/spree/commission_rate_value.rb +1 -1
- data/app/models/spree/commission_rule.rb +4 -11
- data/app/models/spree/commission_rules/product_rule.rb +1 -1
- data/app/models/spree/company_invitation.rb +1 -0
- data/app/models/spree/consent_record.rb +0 -1
- data/app/models/spree/credit_card.rb +1 -1
- data/app/models/spree/current.rb +23 -0
- data/app/models/spree/custom_field.rb +2 -2
- data/app/models/spree/custom_field_definition.rb +9 -23
- data/app/models/spree/custom_fields/boolean.rb +1 -1
- data/app/models/spree/custom_fields/json.rb +1 -1
- data/app/models/spree/data_feed/google.rb +0 -2
- data/app/models/spree/data_request.rb +1 -1
- data/app/models/spree/delivery_method.rb +30 -13
- data/app/models/spree/delivery_method_rule.rb +4 -11
- data/app/models/spree/delivery_rate.rb +4 -5
- data/app/models/spree/delivery_rate_provider/base.rb +1 -0
- data/app/models/spree/delivery_zone.rb +0 -1
- data/app/models/spree/delivery_zone_member.rb +4 -4
- data/app/models/spree/digital_asset.rb +7 -0
- data/app/models/spree/digital_asset_provider/base.rb +2 -0
- data/app/models/spree/digital_link.rb +2 -5
- data/app/models/spree/discount.rb +1 -1
- data/app/models/spree/email_template.rb +25 -0
- data/app/models/spree/email_template_draft.rb +13 -0
- data/app/models/spree/email_template_revision.rb +17 -0
- data/app/models/spree/email_templates/entry.rb +118 -0
- data/app/models/spree/email_templates/sample_record.rb +16 -0
- data/app/models/spree/emails/branding.rb +80 -0
- data/app/models/spree/exchange.rb +15 -1
- data/app/models/spree/exchange_line_item.rb +121 -5
- data/app/models/spree/export.rb +0 -1
- data/app/models/spree/exports/products.rb +10 -1
- data/app/models/spree/exports/report.rb +1 -1
- data/app/models/spree/fee.rb +4 -2
- data/app/models/spree/fulfillment.rb +53 -27
- data/app/models/spree/fulfillment_changer.rb +2 -2
- data/app/models/spree/fulfillment_item.rb +0 -4
- data/app/models/spree/fulfillment_provider/base.rb +1 -0
- data/app/models/spree/gateway/bogus.rb +4 -0
- data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
- data/app/models/spree/gateway.rb +5 -1
- data/app/models/spree/gift_card.rb +12 -1
- data/app/models/spree/gift_card_batch.rb +0 -1
- data/app/models/spree/import.rb +2 -7
- data/app/models/spree/import_mapping.rb +1 -2
- data/app/models/spree/import_row.rb +2 -2
- data/app/models/spree/import_schemas/products.rb +1 -0
- data/app/models/spree/integration.rb +8 -18
- data/app/models/spree/invitation.rb +5 -5
- data/app/models/spree/line_item.rb +13 -2
- data/app/models/spree/locale.rb +11 -8
- data/app/models/spree/market.rb +1 -0
- data/app/models/spree/newsletter_subscriber.rb +3 -0
- data/app/models/spree/order.rb +56 -138
- data/app/models/spree/order_group.rb +11 -2
- data/app/models/spree/order_inventory.rb +10 -5
- data/app/models/spree/order_promotion.rb +1 -1
- data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
- data/app/models/spree/order_routing/strategy/base.rb +4 -2
- data/app/models/spree/order_routing/strategy/rules.rb +1 -1
- data/app/models/spree/order_routing_rule.rb +6 -22
- data/app/models/spree/package_type.rb +1 -2
- data/app/models/spree/payment/gateway_options.rb +14 -52
- data/app/models/spree/payment/processing.rb +2 -4
- data/app/models/spree/payment.rb +20 -5
- data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
- data/app/models/spree/payment_method/store_credit.rb +4 -4
- data/app/models/spree/payment_method.rb +36 -4
- data/app/models/spree/payment_session.rb +5 -1
- data/app/models/spree/payment_setup_session.rb +1 -0
- data/app/models/spree/payout_provider/base.rb +3 -1
- data/app/models/spree/pickup_point_provider/base.rb +2 -0
- data/app/models/spree/price_list.rb +1 -2
- data/app/models/spree/price_rules/channel_rule.rb +1 -1
- data/app/models/spree/price_rules/customer_group_rule.rb +1 -1
- data/app/models/spree/product.rb +31 -7
- data/app/models/spree/product_submission.rb +0 -12
- data/app/models/spree/product_type.rb +0 -1
- data/app/models/spree/promotion/actions/create_line_items.rb +27 -17
- data/app/models/spree/promotion/rules/category.rb +0 -4
- data/app/models/spree/promotion/rules/channel.rb +0 -4
- data/app/models/spree/promotion/rules/country.rb +0 -4
- data/app/models/spree/promotion/rules/currency.rb +0 -4
- data/app/models/spree/promotion/rules/customer_group.rb +0 -4
- data/app/models/spree/promotion/rules/first_order.rb +0 -4
- data/app/models/spree/promotion/rules/item_total.rb +4 -8
- data/app/models/spree/promotion/rules/market.rb +0 -4
- data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
- data/app/models/spree/promotion/rules/option_value.rb +0 -3
- data/app/models/spree/promotion/rules/product.rb +0 -4
- data/app/models/spree/promotion/rules/user.rb +0 -4
- data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
- data/app/models/spree/promotion.rb +35 -6
- data/app/models/spree/promotion_action.rb +7 -14
- data/app/models/spree/promotion_action_line_item.rb +1 -1
- data/app/models/spree/promotion_handler/coupon.rb +59 -27
- data/app/models/spree/promotion_rule.rb +7 -14
- data/app/models/spree/purchase_order.rb +1 -0
- data/app/models/spree/quantity_rule.rb +1 -1
- data/app/models/spree/refund.rb +28 -3
- data/app/models/spree/return.rb +30 -2
- data/app/models/spree/return_line_item.rb +65 -14
- data/app/models/spree/role.rb +7 -7
- data/app/models/spree/saved_report.rb +1 -1
- data/app/models/spree/seller.rb +2 -1
- data/app/models/spree/seller_payout.rb +7 -0
- data/app/models/spree/seller_requirement.rb +4 -12
- data/app/models/spree/seller_requirement_custom_field.rb +1 -2
- data/app/models/spree/seller_requirement_submission.rb +2 -2
- data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
- data/app/models/spree/seller_transfer.rb +5 -0
- data/app/models/spree/shipping_label.rb +1 -0
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
- data/app/models/spree/stock/packer.rb +2 -2
- data/app/models/spree/stock/splitter/weight.rb +1 -1
- data/app/models/spree/stock_level/custom_events.rb +6 -0
- data/app/models/spree/stock_level.rb +1 -1
- data/app/models/spree/stock_movement.rb +1 -1
- data/app/models/spree/stock_receipt_item.rb +1 -1
- data/app/models/spree/stock_reservation.rb +2 -2
- data/app/models/spree/stock_transfer.rb +4 -2
- data/app/models/spree/store.rb +33 -7
- data/app/models/spree/store_credit.rb +8 -9
- data/app/models/spree/store_credit_event.rb +5 -5
- data/app/models/spree/tax_exemption_certificate.rb +2 -0
- data/app/models/spree/tax_identifier.rb +3 -1
- data/app/models/spree/tax_line.rb +53 -3
- data/app/models/spree/tax_provider/base.rb +71 -14
- data/app/models/spree/tax_provider/internal.rb +59 -33
- data/app/models/spree/tax_provider/recorded_share.rb +114 -0
- data/app/models/spree/tax_rate.rb +1 -2
- data/app/models/spree/taxonomy.rb +0 -1
- data/app/models/spree/variant.rb +4 -3
- data/app/models/spree/variant_media.rb +1 -1
- data/app/models/spree/webhook_endpoint.rb +10 -13
- data/app/models/spree/wishlist.rb +1 -6
- data/app/models/spree/wishlist_item/custom_events.rb +6 -0
- data/app/presenters/spree/csv/customer_presenter.rb +1 -1
- data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
- data/app/presenters/spree/csv/order_line_item_presenter.rb +1 -1
- data/app/presenters/spree/csv/product_variant_presenter.rb +42 -12
- data/app/serializers/spree/emails/base_serializer.rb +14 -0
- data/app/serializers/spree/emails/export_serializer.rb +13 -0
- data/app/serializers/spree/emails/import_serializer.rb +19 -0
- data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
- data/app/serializers/spree/emails/store_serializer.rb +31 -0
- data/app/serializers/spree/emails/user_serializer.rb +12 -0
- data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
- data/app/services/spree/carts/create.rb +8 -2
- data/app/services/spree/carts/destroy.rb +2 -2
- data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
- data/app/services/spree/carts/split_by_seller.rb +1 -1
- data/app/services/spree/carts/update.rb +19 -5
- data/app/services/spree/catalogs/set_product_terms.rb +1 -1
- data/app/services/spree/categories/add_products.rb +13 -31
- data/app/services/spree/categories/remove_products.rb +12 -51
- data/app/services/spree/checkout/advance.rb +1 -4
- data/app/services/spree/collections/add_products.rb +10 -23
- data/app/services/spree/collections/remove_products.rb +7 -43
- data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
- data/app/services/spree/data_requests/create.rb +1 -1
- data/app/services/spree/deliveries/destroy.rb +2 -2
- data/app/services/spree/email_templates/check.rb +58 -0
- data/app/services/spree/email_templates/no_sample_record.rb +7 -0
- data/app/services/spree/email_templates/preview.rb +99 -0
- data/app/services/spree/emails/file_system.rb +23 -0
- data/app/services/spree/emails/filters.rb +84 -0
- data/app/services/spree/emails/liquid_context.rb +48 -0
- data/app/services/spree/emails/rendered_email.rb +14 -0
- data/app/services/spree/emails/renderer.rb +125 -0
- data/app/services/spree/emails/template.rb +51 -0
- data/app/services/spree/emails/template_data.rb +23 -0
- data/app/services/spree/emails/template_resolver.rb +109 -0
- data/app/services/spree/emails/text_converter.rb +72 -0
- data/app/services/spree/emails/translation_inliner.rb +134 -0
- data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
- data/app/services/spree/imports/row_processors/product_variant.rb +65 -34
- data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
- data/app/services/spree/locales.rb +4 -5
- data/app/services/spree/newsletter/subscribe.rb +8 -12
- data/app/services/spree/orders/build_fulfillments.rb +3 -2
- data/app/services/spree/orders/create.rb +2 -1
- data/app/services/spree/orders/discounts/create.rb +7 -4
- data/app/services/spree/orders/discounts/destroy.rb +1 -0
- data/app/services/spree/orders/discounts/update.rb +1 -0
- data/app/services/spree/orders/fees/create.rb +1 -0
- data/app/services/spree/orders/fees/destroy.rb +1 -0
- data/app/services/spree/orders/fees/update.rb +1 -0
- data/app/services/spree/orders/remove_line_item.rb +13 -3
- data/app/services/spree/orders/update.rb +10 -6
- data/app/services/spree/orders/update_item.rb +6 -3
- data/app/services/spree/orders/update_statuses.rb +3 -1
- data/app/services/spree/product_memberships.rb +61 -0
- data/app/services/spree/returns/eligibility_validator.rb +1 -1
- data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
- data/app/services/spree/seeds/commission_rates.rb +1 -1
- data/app/services/spree/seeds/digital_delivery.rb +1 -1
- data/app/services/spree/seeds/payment_methods.rb +2 -2
- data/app/services/spree/seeds/saved_reports.rb +3 -3
- data/app/services/spree/sellers/requirements.rb +1 -2
- data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
- data/app/services/spree/shipping_labels/store_file.rb +3 -3
- data/app/services/spree/stock_levels/correct.rb +3 -3
- data/app/services/spree/stock_reservations/reserve.rb +2 -3
- data/app/services/spree/store_credits/apply.rb +15 -13
- data/app/services/spree/stores/provision_defaults.rb +15 -14
- data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
- data/app/views/layouts/spree/base_mailer.html.erb +2 -43
- data/app/views/layouts/spree/base_mailer.liquid +79 -0
- data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/export_mailer/export_done.liquid +12 -0
- data/app/views/spree/import_mailer/import_done.liquid +15 -0
- data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
- data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
- data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/shared/_mailer_button.html.erb +4 -5
- data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
- data/app/views/spree/shared/_summary_row.liquid +8 -0
- data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
- data/app/workflows/spree/carts/add_item.rb +4 -4
- data/app/workflows/spree/carts/complete.rb +13 -3
- data/app/workflows/spree/carts/merge.rb +1 -1
- data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
- data/app/workflows/spree/carts/upsert_items.rb +18 -9
- data/app/workflows/spree/claims/approve.rb +5 -22
- data/app/workflows/spree/claims/cancel.rb +5 -21
- data/app/workflows/spree/claims/create.rb +15 -0
- data/app/workflows/spree/claims/deny.rb +7 -0
- data/app/workflows/spree/claims/resolve.rb +34 -31
- data/app/workflows/spree/customers/anonymize.rb +3 -4
- data/app/workflows/spree/customers/create.rb +1 -1
- data/app/workflows/spree/data_requests/fulfill.rb +2 -2
- data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
- data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
- data/app/workflows/spree/email_templates/publish.rb +70 -0
- data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
- data/app/workflows/spree/email_templates/revert.rb +29 -0
- data/app/workflows/spree/email_templates/save_draft.rb +85 -0
- data/app/workflows/spree/exchanges/approve.rb +5 -22
- data/app/workflows/spree/exchanges/cancel.rb +5 -21
- data/app/workflows/spree/exchanges/create.rb +6 -0
- data/app/workflows/spree/exchanges/fulfill.rb +75 -51
- data/app/workflows/spree/exchanges/receive.rb +6 -65
- data/app/workflows/spree/fulfillments/cancel.rb +1 -1
- data/app/workflows/spree/fulfillments/create.rb +51 -47
- data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
- data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
- data/app/workflows/spree/fulfillments/replacements.rb +59 -0
- data/app/workflows/spree/gift_cards/apply.rb +2 -2
- data/app/workflows/spree/orders/add_item.rb +5 -1
- data/app/workflows/spree/orders/cancel.rb +3 -3
- data/app/workflows/spree/orders/complete.rb +16 -8
- data/app/workflows/spree/payments/process.rb +2 -2
- data/app/workflows/spree/post_sale/approval.rb +35 -0
- data/app/workflows/spree/post_sale/cancellation.rb +44 -0
- data/app/workflows/spree/post_sale/receipt.rb +92 -0
- data/app/workflows/spree/products/nested_attributes.rb +1 -1
- data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
- data/app/workflows/spree/purchase_orders/close.rb +3 -31
- data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
- data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
- data/app/workflows/spree/purchase_orders/receive.rb +1 -1
- data/app/workflows/spree/purchase_orders/update.rb +5 -34
- data/app/workflows/spree/receivables/closing_short.rb +46 -0
- data/app/workflows/spree/receivables/draft_editing.rb +48 -0
- data/app/workflows/spree/refunds/create.rb +5 -2
- data/app/workflows/spree/refunds/order_payments.rb +14 -2
- data/app/workflows/spree/refunds/tax_credit.rb +67 -0
- data/app/workflows/spree/returns/approve.rb +5 -22
- data/app/workflows/spree/returns/cancel.rb +5 -22
- data/app/workflows/spree/returns/create.rb +8 -0
- data/app/workflows/spree/returns/receive.rb +5 -76
- data/app/workflows/spree/returns/refund.rb +31 -8
- data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
- data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
- data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
- data/app/workflows/spree/shipping_labels/record.rb +5 -5
- data/app/workflows/spree/shipping_labels/refund.rb +2 -2
- data/app/workflows/spree/stock_receipts/recording.rb +4 -4
- data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
- data/app/workflows/spree/stock_transfers/close.rb +3 -30
- data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
- data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
- data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
- data/app/workflows/spree/stock_transfers/receive.rb +1 -1
- data/app/workflows/spree/stock_transfers/update.rb +5 -33
- data/config/i18n-tasks.yml +32 -18
- data/config/initializers/alba.rb +7 -0
- data/config/language_names.csv +264 -0
- data/config/locales/ar.yml +811 -0
- data/config/locales/be.yml +855 -0
- data/config/locales/bg.yml +723 -0
- data/config/locales/ca.yml +689 -0
- data/config/locales/cs.yml +855 -0
- data/config/locales/da.yml +771 -0
- data/config/locales/de-CH.yml +248 -0
- data/config/locales/de.yml +835 -0
- data/config/locales/el.yml +769 -0
- data/config/locales/en.yml +37 -503
- data/config/locales/es-CL.yml +301 -0
- data/config/locales/es-EC.yml +186 -0
- data/config/locales/es-MX.yml +334 -0
- data/config/locales/es.yml +831 -0
- data/config/locales/et.yml +644 -0
- data/config/locales/fa.yml +709 -0
- data/config/locales/fi.yml +771 -0
- data/config/locales/fr.yml +830 -0
- data/config/locales/hu.yml +751 -0
- data/config/locales/id.yml +756 -0
- data/config/locales/it.yml +833 -0
- data/config/locales/ja.yml +726 -0
- data/config/locales/km.yml +759 -0
- data/config/locales/ko.yml +575 -0
- data/config/locales/ku.yml +753 -0
- data/config/locales/lt.yml +821 -0
- data/config/locales/lv.yml +685 -0
- data/config/locales/nb.yml +824 -0
- data/config/locales/ne.yml +823 -0
- data/config/locales/nl.yml +826 -0
- data/config/locales/pl.yml +835 -0
- data/config/locales/pt-BR.yml +547 -0
- data/config/locales/pt.yml +636 -0
- data/config/locales/ro.yml +750 -0
- data/config/locales/ru.yml +834 -0
- data/config/locales/sk.yml +680 -0
- data/config/locales/sl-SI.yml +600 -0
- data/config/locales/sv.yml +827 -0
- data/config/locales/th.yml +745 -0
- data/config/locales/tr.yml +777 -0
- data/config/locales/uk.yml +836 -0
- data/config/locales/vi.yml +768 -0
- data/config/locales/zh-CN.yml +786 -0
- data/config/locales/zh-TW.yml +834 -0
- data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
- data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
- data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
- data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
- data/lib/generators/spree/dummy/templates/rails/test.rb +7 -0
- data/lib/spree/checkout/default_requirements.rb +37 -13
- data/lib/spree/core/configuration.rb +1 -1
- data/lib/spree/core/controller_helpers/store.rb +1 -1
- data/lib/spree/core/dependencies.rb +5 -4
- data/lib/spree/core/emails/escaped_output.rb +60 -0
- data/lib/spree/core/engine.rb +48 -167
- data/lib/spree/core/permissions/default_catalog.rb +5 -0
- data/lib/spree/core/preferences/json_conversion.rb +182 -0
- data/lib/spree/core/preferences/masking.rb +4 -1
- data/lib/spree/core/preferences/preferable.rb +137 -83
- data/lib/spree/core/preferences/preferable_class_methods.rb +81 -146
- data/lib/spree/core/previews/import_preview.rb +10 -0
- data/lib/spree/core/tax/provider_error.rb +37 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +85 -370
- data/lib/spree/emails/editable_templates.rb +82 -0
- data/lib/spree/events/catalog.rb +189 -0
- data/lib/spree/events.rb +21 -1
- data/lib/spree/hooks.rb +0 -17
- data/lib/spree/i18n.rb +32 -15
- data/lib/spree/reporting/adapters/live.rb +6 -6
- data/lib/spree/reporting/default_vocabulary.rb +22 -10
- data/lib/spree/reporting/hydration.rb +1 -1
- data/lib/spree/reporting/query.rb +8 -0
- data/lib/spree/reporting/schema.rb +10 -10
- data/lib/spree/testing_support/editable_email_templates.rb +61 -0
- data/lib/spree/testing_support/emails.rb +22 -0
- data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
- data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
- data/lib/spree/testing_support/factories/order_factory.rb +2 -2
- data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
- data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
- data/lib/spree/testing_support/i18n.rb +3 -94
- data/lib/spree/testing_support/lifecycle_events.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/spree/testing_support/preferences.rb +0 -5
- data/lib/spree/testing_support/store.rb +9 -1
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +45 -0
- data/lib/tasks/categories_and_collections.rake +3 -3
- data/lib/tasks/core.rake +6 -4
- data/lib/tasks/media.rake +3 -3
- data/lib/tasks/package_types.rake +1 -1
- data/lib/tasks/preferences_json.rake +101 -0
- data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
- metadata +188 -44
- data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
- data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
- data/app/models/spree/base_analytics_event_handler.rb +0 -54
- data/app/models/spree/invitations/store.rb +0 -6
- data/app/models/spree/preference.rb +0 -14
- data/app/models/spree/promotion_handler/page.rb +0 -25
- data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
- data/app/models/spree/stock/differentiator.rb +0 -45
- data/app/presenters/spree/filters/options_presenter.rb +0 -47
- data/app/services/spree/carts/empty.rb +0 -42
- data/app/services/spree/country_to_timezone.rb +0 -273
- data/app/services/spree/payments/create.rb +0 -92
- data/app/validators/spree/url_validator.rb +0 -23
- data/app/views/active_storage/blobs/_blob.html.erb +0 -14
- data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/export_mailer/export_done.html.erb +0 -7
- data/app/views/spree/import_mailer/import_done.html.erb +0 -12
- data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
- data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
- data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
- data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
- data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
- data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
- data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
- data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
- data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
- data/app/workflows/spree/returns/purchase_label.rb +0 -32
- data/lib/spree/analytics.rb +0 -11
- data/lib/spree/core/preferences/scoped_store.rb +0 -34
- data/lib/spree/core/preferences/store.rb +0 -105
- data/lib/spree/translation_migrations.rb +0 -52
|
@@ -5,6 +5,9 @@ module Spree
|
|
|
5
5
|
has_prefix_id :dig
|
|
6
6
|
|
|
7
7
|
publishes_lifecycle_events
|
|
8
|
+
publishes_event 'digital.created', deprecated_alias_of: 'digital_asset.created'
|
|
9
|
+
publishes_event 'digital.updated', deprecated_alias_of: 'digital_asset.updated'
|
|
10
|
+
publishes_event 'digital.deleted', deprecated_alias_of: 'digital_asset.deleted'
|
|
8
11
|
|
|
9
12
|
# Provider config lives under one key in `metadata` so a provider owns its
|
|
10
13
|
# own namespace without colliding with other developer metadata. The values
|
|
@@ -22,6 +25,10 @@ module Spree
|
|
|
22
25
|
# and provider-backed assets (which carry no file) are exempt.
|
|
23
26
|
DEFAULT_PROVIDER = 'Spree::DigitalAssetProvider::File'.freeze
|
|
24
27
|
|
|
28
|
+
# The API names a provider by its shorthand (`file`); the column keeps the
|
|
29
|
+
# class name.
|
|
30
|
+
normalizes :provider_type, with: ->(value) { Spree::ApiTyped.class_name_for(Spree.digital_asset_providers, value) }
|
|
31
|
+
|
|
25
32
|
validate :provider_type_is_registered, if: -> { provider_type.present? }
|
|
26
33
|
# Only ask a valid provider whether it needs a file — an unregistered
|
|
27
34
|
# provider_type is already rejected above, and calling requires_attachment?
|
|
@@ -3,12 +3,9 @@ module Spree
|
|
|
3
3
|
has_prefix_id :dl # Spree-specific: digital link
|
|
4
4
|
|
|
5
5
|
publishes_lifecycle_events
|
|
6
|
+
publishes_event :downloaded
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
has_secure_token on: :save
|
|
9
|
-
else
|
|
10
|
-
has_secure_token
|
|
11
|
-
end
|
|
8
|
+
has_secure_token on: :save
|
|
12
9
|
|
|
13
10
|
if defined?(Spree::Security::DigitalLinks)
|
|
14
11
|
include Spree::Security::DigitalLinks
|
|
@@ -45,7 +45,7 @@ module Spree
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def exactly_one_adjustable
|
|
48
|
-
errors.add(:base, :exactly_one_adjustable, message:
|
|
48
|
+
errors.add(:base, :exactly_one_adjustable, message: I18n.t('spree.errors.messages.exactly_one_adjustable')) unless [line_item, fulfillment].compact.one?
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# A store's published version of an editable email template (an email, the
|
|
3
|
+
# layout or a shared partial). Found before the template file whenever a
|
|
4
|
+
# customer email renders, so it is what customers receive. Reverting marks it
|
|
5
|
+
# reverted rather than deleting it, so its revisions stay reachable.
|
|
6
|
+
class EmailTemplate < Spree.base_class
|
|
7
|
+
has_prefix_id :etpl
|
|
8
|
+
|
|
9
|
+
include Spree::EmailTemplateIdentity
|
|
10
|
+
include Spree::HasStatus
|
|
11
|
+
include Spree::ActedBy
|
|
12
|
+
|
|
13
|
+
has_status :published, :reverted, default: :published
|
|
14
|
+
|
|
15
|
+
publishes_events :published, :reverted
|
|
16
|
+
|
|
17
|
+
acted_by :published_by
|
|
18
|
+
acted_by :reverted_by
|
|
19
|
+
|
|
20
|
+
has_many :revisions, -> { order(created_at: :desc) }, class_name: 'Spree::EmailTemplateRevision',
|
|
21
|
+
dependent: :destroy, inverse_of: :email_template
|
|
22
|
+
|
|
23
|
+
validates :published_at, presence: true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Work in progress on an editable email template, one per store, template
|
|
3
|
+
# and language. Shared by every admin of the store: `lock_version` refuses a
|
|
4
|
+
# save made from a stale copy, and `updated_by` says who saved it last.
|
|
5
|
+
class EmailTemplateDraft < Spree.base_class
|
|
6
|
+
has_prefix_id :etdr
|
|
7
|
+
|
|
8
|
+
include Spree::EmailTemplateIdentity
|
|
9
|
+
include Spree::ActedBy
|
|
10
|
+
|
|
11
|
+
acted_by :updated_by
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# One published version of a store's email template, kept so it can be
|
|
3
|
+
# restored. Append-only.
|
|
4
|
+
class EmailTemplateRevision < Spree.base_class
|
|
5
|
+
has_prefix_id :etrv
|
|
6
|
+
|
|
7
|
+
include Spree::ActedBy
|
|
8
|
+
|
|
9
|
+
belongs_to :email_template, class_name: 'Spree::EmailTemplate', inverse_of: :revisions
|
|
10
|
+
|
|
11
|
+
acted_by :published_by
|
|
12
|
+
|
|
13
|
+
validates :body, presence: true
|
|
14
|
+
|
|
15
|
+
delegate :store, :key, :locale, to: :email_template
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module EmailTemplates
|
|
3
|
+
# One editable email template for one store and language, as the editor
|
|
4
|
+
# shows it: what customers receive now, the draft in progress and
|
|
5
|
+
# Spree's default. For one language, Spree's translation keys are written
|
|
6
|
+
# out as text in it, so the merchant reads and edits plain words.
|
|
7
|
+
class Entry
|
|
8
|
+
include ActiveModel::Model
|
|
9
|
+
include ActiveModel::Attributes
|
|
10
|
+
|
|
11
|
+
attribute :definition
|
|
12
|
+
attribute :locale, :string, default: Spree::EmailTemplate::ANY_LOCALE
|
|
13
|
+
attribute :published
|
|
14
|
+
attribute :draft
|
|
15
|
+
attribute :customized_locales, default: -> { [] }
|
|
16
|
+
|
|
17
|
+
delegate :key, :kind, to: :definition
|
|
18
|
+
|
|
19
|
+
# @param store [Spree::Store]
|
|
20
|
+
# @param locale [String] a language code, or "any"
|
|
21
|
+
# @return [Array<Spree::EmailTemplates::Entry>] every editable template
|
|
22
|
+
def self.all(store, locale: Spree::EmailTemplate::ANY_LOCALE)
|
|
23
|
+
published = store.email_templates.published.where(locale: [locale, Spree::EmailTemplate::ANY_LOCALE]).
|
|
24
|
+
sort_by { |template| template.locale == locale ? 0 : 1 }.uniq(&:key).index_by(&:key)
|
|
25
|
+
drafts = store.email_template_drafts.where(locale: locale).includes(:updated_by).index_by(&:key)
|
|
26
|
+
customized = store.email_templates.published.pluck(:key, :locale).group_by(&:first)
|
|
27
|
+
|
|
28
|
+
Spree.editable_email_templates.map do |definition|
|
|
29
|
+
new(definition: definition, locale: locale, published: published[definition.key], draft: drafts[definition.key],
|
|
30
|
+
customized_locales: customized.fetch(definition.key, []).map(&:last).sort)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param store [Spree::Store]
|
|
35
|
+
# @param id [String] the template's key with dots, e.g. "spree.order_mailer.confirm_email"
|
|
36
|
+
# @param locale [String]
|
|
37
|
+
# @return [Spree::EmailTemplates::Entry]
|
|
38
|
+
# @raise [ActiveRecord::RecordNotFound] when no editable template has that id
|
|
39
|
+
def self.for_id(store, id, locale: Spree::EmailTemplate::ANY_LOCALE)
|
|
40
|
+
definition = Spree.editable_email_templates[id.to_s.tr('.', '/')]
|
|
41
|
+
raise ActiveRecord::RecordNotFound, "Couldn't find email template #{id}" unless definition
|
|
42
|
+
|
|
43
|
+
locale = locale.presence || Spree::EmailTemplate::ANY_LOCALE
|
|
44
|
+
new(
|
|
45
|
+
definition: definition, locale: locale,
|
|
46
|
+
published: store.email_templates.published.where(key: definition.key, locale: [locale, Spree::EmailTemplate::ANY_LOCALE]).
|
|
47
|
+
min_by { |template| template.locale == locale ? 0 : 1 },
|
|
48
|
+
draft: store.email_template_drafts.for_key(definition.key, locale).first,
|
|
49
|
+
customized_locales: store.email_templates.published.where(key: definition.key).order(:locale).pluck(:locale)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The key with dots, so it fits in a URL segment.
|
|
54
|
+
def id
|
|
55
|
+
key.tr('/', '.')
|
|
56
|
+
end
|
|
57
|
+
alias prefixed_id id
|
|
58
|
+
|
|
59
|
+
def customized?
|
|
60
|
+
published.present?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Spree::Emails::Template, nil] the template file Spree or the app ships
|
|
64
|
+
def default
|
|
65
|
+
@default ||= Spree::Emails::TemplateResolver.for_mailers.find_default(key)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [String, nil] the language of the published version customers
|
|
69
|
+
# receive: this one, or "any" when the version for every language applies
|
|
70
|
+
def published_locale
|
|
71
|
+
published&.locale
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# What customers receive now.
|
|
75
|
+
def subject
|
|
76
|
+
readable(customized? ? published.subject : default&.subject, escape: false)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def body
|
|
80
|
+
readable(customized? ? published.body : default&.body)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def default_subject
|
|
84
|
+
readable(default&.subject, escape: false)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def default_body
|
|
88
|
+
readable(default&.body)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def base_subject
|
|
92
|
+
readable(outdated_version&.base_subject, escape: false)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def base_body
|
|
96
|
+
readable(outdated_version&.base_body)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The default the store's version started from, when Spree's default has
|
|
100
|
+
# changed since, so the editor can show what changed.
|
|
101
|
+
#
|
|
102
|
+
# @return [Spree::EmailTemplate, Spree::EmailTemplateDraft, nil]
|
|
103
|
+
def outdated_version
|
|
104
|
+
# An open draft is what gets published next, so it alone decides.
|
|
105
|
+
version = draft || published
|
|
106
|
+
version if version&.default_changed?(default)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
def readable(text, escape: true)
|
|
112
|
+
return text if locale == Spree::EmailTemplate::ANY_LOCALE
|
|
113
|
+
|
|
114
|
+
Spree::Emails::TranslationInliner.call(text, locale: locale, escape: escape)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module EmailTemplates
|
|
3
|
+
# A record of the store an email can be previewed with, as the editor's
|
|
4
|
+
# record picker lists it.
|
|
5
|
+
class SampleRecord
|
|
6
|
+
include ActiveModel::Model
|
|
7
|
+
include ActiveModel::Attributes
|
|
8
|
+
|
|
9
|
+
attribute :id, :string
|
|
10
|
+
attribute :label, :string
|
|
11
|
+
attribute :created_at, :datetime
|
|
12
|
+
|
|
13
|
+
alias prefixed_id id
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Emails
|
|
3
|
+
# How a store's customer emails look: its colors and font over the
|
|
4
|
+
# defaults the shipped layout was designed with. Templates read it as
|
|
5
|
+
# `store.branding`, so it outlives any edit or revert of a template.
|
|
6
|
+
class Branding
|
|
7
|
+
include ActiveModel::Model
|
|
8
|
+
include ActiveModel::Attributes
|
|
9
|
+
|
|
10
|
+
COLOR_FORMAT = /\A#\h{6}\z/
|
|
11
|
+
|
|
12
|
+
# Email-safe fonts, with fallback stacks for clients that do not load
|
|
13
|
+
# web fonts. Family names stay unquoted: templates escape what they print.
|
|
14
|
+
FONTS = {
|
|
15
|
+
'inter' => { family: 'Inter, Helvetica, Arial, sans-serif', heading_family: 'Geist, Inter, Helvetica, Arial, sans-serif' },
|
|
16
|
+
'system' => { family: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif' },
|
|
17
|
+
'helvetica' => { family: 'Helvetica, Arial, sans-serif' },
|
|
18
|
+
'georgia' => { family: 'Georgia, Times New Roman, Times, serif' },
|
|
19
|
+
'roboto' => { family: 'Roboto, Helvetica, Arial, sans-serif', url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700' },
|
|
20
|
+
'lato' => { family: 'Lato, Helvetica, Arial, sans-serif', url: 'https://fonts.googleapis.com/css2?family=Lato:wght@400;700' },
|
|
21
|
+
'merriweather' => { family: 'Merriweather, Georgia, serif', url: 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700' }
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
DEFAULT_FONT = 'inter'.freeze
|
|
25
|
+
DEFAULT_COLORS = {
|
|
26
|
+
background_color: '#FFFFFF', card_color: '#F6F6F6', text_color: '#726A6A', heading_color: '#332C2C'
|
|
27
|
+
}.freeze
|
|
28
|
+
COLORS = %i[accent_color background_color card_color text_color heading_color].freeze
|
|
29
|
+
|
|
30
|
+
COLORS.each { |color| attribute color, :string }
|
|
31
|
+
attribute :font, :string
|
|
32
|
+
|
|
33
|
+
validates(*COLORS, format: { with: COLOR_FORMAT }, allow_blank: true)
|
|
34
|
+
validates :font, inclusion: { in: FONTS.keys }, allow_blank: true
|
|
35
|
+
|
|
36
|
+
# What templates read as `store.branding`. A value that is not a color
|
|
37
|
+
# or a known font falls back to the default, so nothing else ever reaches
|
|
38
|
+
# an email's CSS, unsaved preview values included.
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash{String => String, nil}]
|
|
41
|
+
def to_h
|
|
42
|
+
accent = color(:accent_color)
|
|
43
|
+
typeface_key = FONTS.key?(font) ? font : DEFAULT_FONT
|
|
44
|
+
typeface = FONTS[typeface_key]
|
|
45
|
+
colors = DEFAULT_COLORS.to_h { |name, default| [name, color(name) || default] }
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
**colors,
|
|
49
|
+
accent_color: accent,
|
|
50
|
+
link_color: accent || colors[:heading_color],
|
|
51
|
+
# Without an accent, buttons keep the outlined look the layout ships with.
|
|
52
|
+
button_color: accent || '#FFFFFF',
|
|
53
|
+
button_text_color: accent ? readable_text_on(accent) : '#1F2222',
|
|
54
|
+
button_border: accent ? 'none' : '1px solid #E8E9E9',
|
|
55
|
+
font: typeface_key,
|
|
56
|
+
font_family: typeface[:family],
|
|
57
|
+
heading_font_family: typeface[:heading_family] || typeface[:family],
|
|
58
|
+
font_url: typeface[:url]
|
|
59
|
+
}.stringify_keys
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def color(name)
|
|
65
|
+
value = public_send(name).to_s
|
|
66
|
+
value.upcase if value.match?(COLOR_FORMAT)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Black or white, whichever reads better on the color (WCAG relative luminance).
|
|
70
|
+
def readable_text_on(hex)
|
|
71
|
+
red, green, blue = hex.delete('#').scan(/../).map do |channel|
|
|
72
|
+
value = channel.to_i(16) / 255.0
|
|
73
|
+
value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055)**2.4
|
|
74
|
+
end
|
|
75
|
+
luminance = (0.2126 * red) + (0.7152 * green) + (0.0722 * blue)
|
|
76
|
+
luminance > 0.179 ? '#000000' : '#FFFFFF'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -17,13 +17,14 @@ module Spree
|
|
|
17
17
|
include Spree::HasStatus
|
|
18
18
|
include Spree::HasCustomFields
|
|
19
19
|
include Spree::Metadata
|
|
20
|
+
include Spree::PostSale::Taxation
|
|
20
21
|
|
|
21
22
|
publishes_lifecycle_events
|
|
23
|
+
publishes_events :approved, :canceled, :fulfilled, :received, :requested
|
|
22
24
|
|
|
23
25
|
has_status :requested, :approved, :received, :fulfilled, :canceled,
|
|
24
26
|
default: :requested
|
|
25
27
|
|
|
26
|
-
belongs_to :store, class_name: 'Spree::Store'
|
|
27
28
|
belongs_to :order, class_name: 'Spree::Order', inverse_of: :exchanges
|
|
28
29
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
|
29
30
|
belongs_to :reason, class_name: 'Spree::ReturnReason', optional: true, inverse_of: :exchanges
|
|
@@ -53,5 +54,18 @@ module Spree
|
|
|
53
54
|
def display_price_difference
|
|
54
55
|
Spree::Money.new(price_difference, currency: currency)
|
|
55
56
|
end
|
|
57
|
+
|
|
58
|
+
def taxed_lines
|
|
59
|
+
exchange_line_items
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# The replacement is a sale of its own, taxed as the order would be today
|
|
65
|
+
# rather than read off the original line, whose category it may not share.
|
|
66
|
+
def estimate_tax(lines, amounts: nil)
|
|
67
|
+
super
|
|
68
|
+
order.tax_provider.estimate_replacement(order, lines, **order.tax_estimate_inputs)
|
|
69
|
+
end
|
|
56
70
|
end
|
|
57
71
|
end
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
# One swap on a {Spree::Exchange}: a quantity of one variant going back,
|
|
3
3
|
# the same quantity of another coming out.
|
|
4
|
+
#
|
|
5
|
+
# Both halves are taxed: the units coming back are credited the tax the
|
|
6
|
+
# customer paid on them (`original_*`), and the replacement is taxed as a new
|
|
7
|
+
# sale (`new_*`), each for the announced quantity.
|
|
4
8
|
class ExchangeLineItem < Spree.base_class
|
|
9
|
+
include Spree::PostSale::TaxedLine
|
|
10
|
+
|
|
5
11
|
has_prefix_id :eli
|
|
6
12
|
|
|
7
13
|
belongs_to :exchange, class_name: 'Spree::Exchange', inverse_of: :exchange_line_items
|
|
@@ -19,24 +25,134 @@ module Spree
|
|
|
19
25
|
|
|
20
26
|
delegate :order, :currency, to: :exchange
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
extend Spree::DisplayMoney
|
|
29
|
+
money_methods :original_price, :new_variant_price, :price_difference, :original_tax_total, :new_tax_total
|
|
30
|
+
|
|
31
|
+
# What the customer paid for the units coming back, after discounts and
|
|
32
|
+
# with the tax charged on top — not the list price, which would
|
|
33
|
+
# over-credit a discounted line.
|
|
24
34
|
def original_price
|
|
25
|
-
|
|
35
|
+
original_amount_for(quantity) + original_additional_tax_total
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def original_pre_tax_amount
|
|
39
|
+
original_amount_for(quantity) - original_included_tax_total
|
|
40
|
+
end
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
def original_tax_total
|
|
43
|
+
original_included_tax_total + original_additional_tax_total
|
|
28
44
|
end
|
|
29
45
|
|
|
46
|
+
# What the replacement costs, tax on top included.
|
|
30
47
|
def new_variant_price
|
|
31
|
-
|
|
48
|
+
replacement_amount_for(quantity) + new_additional_tax_total
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def new_pre_tax_amount
|
|
52
|
+
replacement_amount_for(quantity) - new_included_tax_total
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def new_tax_total
|
|
56
|
+
new_included_tax_total + new_additional_tax_total
|
|
32
57
|
end
|
|
33
58
|
|
|
34
59
|
def price_difference
|
|
35
60
|
new_variant_price - original_price
|
|
36
61
|
end
|
|
37
62
|
|
|
63
|
+
# The replacement keeps the deal the customer had on what it replaces:
|
|
64
|
+
# its price carries the original line's discount in the same proportion,
|
|
65
|
+
# so swapping a size of a discounted item costs nothing.
|
|
66
|
+
#
|
|
67
|
+
# @param units [Integer]
|
|
68
|
+
# @return [BigDecimal]
|
|
69
|
+
def replacement_amount_for(units)
|
|
70
|
+
price = new_variant&.price_in(currency)&.amount.to_d
|
|
71
|
+
|
|
72
|
+
Spree::Money::Rounding.to_currency(price * units.to_i * discount_ratio, currency)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The units this swap covers: none once canceled, what arrived once the
|
|
76
|
+
# warehouse has counted, what was announced before.
|
|
77
|
+
#
|
|
78
|
+
# @return [Integer]
|
|
79
|
+
def credited_quantity
|
|
80
|
+
return 0 if exchange.canceled?
|
|
81
|
+
|
|
82
|
+
exchange.received? || exchange.fulfilled? ? received_quantity.to_i : quantity.to_i
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @return [BigDecimal] what the units coming back are worth, tax included
|
|
86
|
+
def credited_worth
|
|
87
|
+
units = credited_quantity
|
|
88
|
+
return 0.to_d if quantity.to_i.zero?
|
|
89
|
+
|
|
90
|
+
original_amount_for(units) +
|
|
91
|
+
Spree::Money::Rounding.to_currency((original_additional_tax_total / quantity) * units, currency)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# What the provider taxes the replacement on (see
|
|
95
|
+
# Spree::TaxProvider::Base#estimate_replacement).
|
|
96
|
+
def taxable_basis
|
|
97
|
+
replacement_amount_for(credited_quantity)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def tax_category_id
|
|
101
|
+
new_variant&.tax_category_id
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# What the units that arrived are credited, from the rows as they stand.
|
|
105
|
+
#
|
|
106
|
+
# @return [BigDecimal]
|
|
107
|
+
def settled_credit
|
|
108
|
+
original_amount_for(received_quantity) + tax_lines.credits.additional.sum(:amount)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# What their replacements cost, from the rows as they stand.
|
|
112
|
+
#
|
|
113
|
+
# @return [BigDecimal]
|
|
114
|
+
def settled_charge
|
|
115
|
+
replacement_amount_for(received_quantity) + tax_lines.charges.additional.sum(:amount)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# The tax inside {#settled_credit} less the tax inside {#settled_charge}.
|
|
119
|
+
#
|
|
120
|
+
# @return [BigDecimal]
|
|
121
|
+
def settled_tax
|
|
122
|
+
credited_tax_total - tax_lines.charges.sum(:amount)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# @return [void]
|
|
126
|
+
def fold_tax_lines!
|
|
127
|
+
rows = tax_lines.reload.to_a
|
|
128
|
+
original_included, original_additional = tax_totals_from(rows.select(&:credit?))
|
|
129
|
+
new_included, new_additional = tax_totals_from(rows.reject(&:credit?))
|
|
130
|
+
|
|
131
|
+
update_columns(
|
|
132
|
+
original_included_tax_total: original_included,
|
|
133
|
+
original_additional_tax_total: original_additional,
|
|
134
|
+
new_included_tax_total: new_included,
|
|
135
|
+
new_additional_tax_total: new_additional
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
38
139
|
private
|
|
39
140
|
|
|
141
|
+
def original_amount_for(units)
|
|
142
|
+
line_item&.discounted_amount_for(units) || 0.to_d
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def discount_ratio
|
|
146
|
+
amount = line_item&.amount.to_d
|
|
147
|
+
return 1 unless amount.positive?
|
|
148
|
+
|
|
149
|
+
[line_item.discounted_amount, 0].max / amount
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def recorded_tax_total
|
|
153
|
+
original_tax_total + new_tax_total
|
|
154
|
+
end
|
|
155
|
+
|
|
40
156
|
def set_original_variant_from_line_item
|
|
41
157
|
self.original_variant ||= line_item&.variant
|
|
42
158
|
end
|
data/app/models/spree/export.rb
CHANGED
|
@@ -30,7 +30,6 @@ module Spree
|
|
|
30
30
|
#
|
|
31
31
|
# Associations
|
|
32
32
|
#
|
|
33
|
-
belongs_to :store, class_name: 'Spree::Store'
|
|
34
33
|
# Optional so secret-API-key callers (apps / server-to-server) can create
|
|
35
34
|
# exports without a human user attached. The email notification is
|
|
36
35
|
# skipped for these — apps poll instead.
|
|
@@ -9,13 +9,22 @@ module Spree
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def variant_includes
|
|
12
|
-
[:images, :prices, :
|
|
12
|
+
[:images, :prices, { stock_levels: :stock_location, option_values: [:option_type] }]
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def multi_line_csv?
|
|
16
16
|
true
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# The seller scopes which locations the inventory columns cover; their
|
|
20
|
+
# default, worked out once per export, carries the full row's stock.
|
|
21
|
+
def to_csv_options
|
|
22
|
+
@to_csv_options ||= {
|
|
23
|
+
seller: seller,
|
|
24
|
+
default_stock_location: Spree::StockLocation.owned_by(store_id: store.id, seller_id: seller&.id).active.order_default.first || store.default_stock_location
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
19
28
|
# when doing full product export, we want to exclude archived products
|
|
20
29
|
def scope
|
|
21
30
|
if search_params.nil?
|
|
@@ -52,7 +52,7 @@ module Spree
|
|
|
52
52
|
totals = query.metrics.map { |m| result.totals[m.name][:value] }
|
|
53
53
|
# With a breakdown the totals close the file under a "Total" label in
|
|
54
54
|
# the first dimension column; without one the totals are the only row.
|
|
55
|
-
label = query.dimensions.empty? ? [] : [
|
|
55
|
+
label = query.dimensions.empty? ? [] : [I18n.t('spree.reporting.export.total')] + Array.new(query.dimensions.size - 1, '')
|
|
56
56
|
csv << Spree::CSV::FormulaSanitizer.row(label + totals)
|
|
57
57
|
end
|
|
58
58
|
end
|
data/app/models/spree/fee.rb
CHANGED
|
@@ -10,8 +10,10 @@ module Spree
|
|
|
10
10
|
# Extensions may append their own kinds — the list is validated, not frozen.
|
|
11
11
|
# `duty` rows are written by a duties provider's adjuster and snapshot the
|
|
12
12
|
# classification they were calculated from in `metadata` — never re-derive
|
|
13
|
-
# a duty from the live catalog.
|
|
14
|
-
|
|
13
|
+
# a duty from the live catalog. `exchange` is what a customer owes when an
|
|
14
|
+
# exchange's replacement costs more, put on the order for the merchant to
|
|
15
|
+
# collect.
|
|
16
|
+
KINDS = %w[surcharge handling gift_wrap cod payment duty exchange]
|
|
15
17
|
|
|
16
18
|
has_prefix_id :fee
|
|
17
19
|
|