spree_core 6.0.0.beta4 → 6.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/app/helpers/spree/images_helper.rb +0 -57
- data/app/jobs/spree/base_job.rb +21 -0
- data/app/jobs/spree/carts/reap_expired_job.rb +2 -2
- data/app/jobs/spree/collections/regenerate_time_based_job.rb +6 -3
- data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +8 -5
- data/app/jobs/spree/payments/cancel_unused_sessions_job.rb +61 -0
- data/app/jobs/spree/search_provider/index_job.rb +4 -0
- data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
- data/app/jobs/spree/uploads/purge_unattached_job.rb +49 -0
- data/app/mailers/spree/admin_user_mailer.rb +4 -19
- data/app/mailers/spree/base_mailer.rb +97 -13
- data/app/mailers/spree/email_template_mailer.rb +27 -0
- data/app/mailers/spree/export_mailer.rb +3 -4
- data/app/mailers/spree/import_mailer.rb +3 -4
- data/app/mailers/spree/invitation_mailer.rb +24 -7
- data/app/mailers/spree/seller_user_mailer.rb +4 -16
- data/app/mailers/spree/webhook_mailer.rb +2 -2
- data/app/models/concerns/spree/acted_by.rb +1 -1
- data/app/models/concerns/spree/admin_user_methods.rb +15 -1
- data/app/models/concerns/spree/api_typed.rb +91 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +41 -13
- data/app/models/concerns/spree/calculator/currency_amounts.rb +1 -1
- data/app/models/concerns/spree/calculator/tiers.rb +63 -0
- data/app/models/concerns/spree/canonical_decimals.rb +61 -0
- data/app/models/concerns/spree/customer_methods.rb +23 -18
- data/app/models/concerns/spree/derived_number.rb +11 -13
- data/app/models/concerns/spree/email_template_identity.rb +52 -0
- data/app/models/concerns/spree/parameterizable_name.rb +2 -0
- data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
- data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
- data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
- data/app/models/concerns/spree/preference_schema/json_schema.rb +139 -0
- data/app/models/concerns/spree/preference_schema.rb +62 -32
- data/app/models/concerns/spree/product_scopes.rb +16 -2
- data/app/models/concerns/spree/provider_listing.rb +41 -0
- data/app/models/concerns/spree/publishable.rb +82 -14
- data/app/models/concerns/spree/purchase/addresses.rb +23 -10
- data/app/models/concerns/spree/purchase/currency.rb +1 -1
- data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
- data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
- data/app/models/concerns/spree/purchase/locale.rb +1 -1
- data/app/models/concerns/spree/purchase/payment_processing.rb +8 -4
- data/app/models/concerns/spree/purchase/purchase_order.rb +9 -4
- data/app/models/concerns/spree/purchase/store_credits.rb +36 -1
- data/app/models/concerns/spree/purchase/totals.rb +49 -8
- data/app/models/concerns/spree/ransackable_attributes.rb +46 -0
- data/app/models/concerns/spree/receivable.rb +1 -1
- data/app/models/concerns/spree/searchable.rb +0 -4
- data/app/models/concerns/spree/secret_preferences.rb +59 -0
- data/app/models/concerns/spree/signed_id_attachments.rb +54 -0
- data/app/models/concerns/spree/single_store_resource.rb +1 -1
- data/app/models/concerns/spree/stores/email_branding.rb +32 -0
- data/app/models/concerns/spree/stores/markets.rb +5 -2
- data/app/models/concerns/spree/type_labels.rb +38 -0
- data/app/models/concerns/spree/typed_adjustment_line.rb +5 -6
- data/app/models/concerns/spree/typed_associations.rb +12 -18
- data/app/models/concerns/spree/vat_price_calculation.rb +5 -6
- data/app/models/spree/address.rb +12 -2
- data/app/models/spree/adjusters/largest_remainder.rb +14 -1
- data/app/models/spree/adjusters/promotion.rb +4 -19
- data/app/models/spree/allowed_origin.rb +3 -1
- data/app/models/spree/api_key.rb +5 -6
- data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
- data/app/models/spree/base.rb +99 -22
- data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
- data/app/models/spree/calculator/flat_rate.rb +3 -5
- data/app/models/spree/calculator/flexi_rate.rb +4 -6
- data/app/models/spree/calculator/percent_on_line_item.rb +2 -2
- data/app/models/spree/calculator/price_sack.rb +5 -7
- data/app/models/spree/calculator/shipping/digital_delivery.rb +3 -5
- data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +7 -7
- data/app/models/spree/calculator/shipping/flat_rate.rb +5 -7
- data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -6
- data/app/models/spree/calculator/shipping/per_item.rb +3 -5
- data/app/models/spree/calculator/shipping/price_sack.rb +5 -7
- data/app/models/spree/calculator/tiered_flat_rate.rb +7 -24
- data/app/models/spree/calculator/tiered_percent.rb +7 -25
- data/app/models/spree/cart.rb +78 -18
- data/app/models/spree/catalog.rb +1 -0
- data/app/models/spree/catalog_price.rb +0 -7
- data/app/models/spree/catalog_price_tier.rb +0 -8
- data/app/models/spree/category.rb +3 -1
- data/app/models/spree/channel/gating.rb +3 -2
- data/app/models/spree/channel.rb +6 -4
- data/app/models/spree/claim.rb +22 -1
- data/app/models/spree/claim_line_item.rb +41 -6
- data/app/models/spree/collection.rb +2 -1
- data/app/models/spree/collection_rule.rb +5 -21
- data/app/models/spree/commission_line.rb +1 -1
- data/app/models/spree/commission_rate.rb +7 -4
- data/app/models/spree/commission_rate_value.rb +1 -1
- data/app/models/spree/commission_rule.rb +4 -11
- data/app/models/spree/commission_rules/category_rule.rb +2 -5
- data/app/models/spree/commission_rules/item_total_rule.rb +2 -2
- data/app/models/spree/commission_rules/product_rule.rb +1 -1
- data/app/models/spree/commission_rules/seller_rule.rb +2 -5
- data/app/models/spree/company.rb +1 -0
- data/app/models/spree/company_invitation.rb +1 -0
- data/app/models/spree/consent_record.rb +0 -1
- data/app/models/spree/credit_card.rb +1 -1
- data/app/models/spree/current.rb +23 -0
- data/app/models/spree/custom_field.rb +2 -2
- data/app/models/spree/custom_field_definition.rb +20 -35
- data/app/models/spree/custom_fields/boolean.rb +1 -1
- data/app/models/spree/custom_fields/json.rb +1 -1
- data/app/models/spree/customer_group.rb +1 -0
- data/app/models/spree/data_feed/google.rb +0 -2
- data/app/models/spree/data_request.rb +2 -1
- data/app/models/spree/delivery_method.rb +36 -14
- data/app/models/spree/delivery_method_rule.rb +4 -11
- data/app/models/spree/delivery_method_rules/channel_rule.rb +2 -5
- data/app/models/spree/delivery_method_rules/item_total_rule.rb +2 -2
- data/app/models/spree/delivery_profile.rb +1 -0
- data/app/models/spree/delivery_rate.rb +7 -8
- data/app/models/spree/delivery_rate_provider/base.rb +1 -0
- data/app/models/spree/delivery_zone.rb +1 -2
- data/app/models/spree/delivery_zone_member.rb +4 -4
- data/app/models/spree/digital_asset.rb +7 -0
- data/app/models/spree/digital_asset_provider/base.rb +2 -0
- data/app/models/spree/digital_link.rb +2 -5
- data/app/models/spree/discount.rb +1 -1
- data/app/models/spree/email_template.rb +25 -0
- data/app/models/spree/email_template_draft.rb +13 -0
- data/app/models/spree/email_template_revision.rb +17 -0
- data/app/models/spree/email_templates/entry.rb +118 -0
- data/app/models/spree/email_templates/sample_record.rb +16 -0
- data/app/models/spree/emails/branding.rb +80 -0
- data/app/models/spree/exchange.rb +16 -1
- data/app/models/spree/exchange_line_item.rb +121 -5
- data/app/models/spree/export.rb +0 -1
- data/app/models/spree/exports/products.rb +10 -1
- data/app/models/spree/exports/report.rb +3 -3
- data/app/models/spree/fee.rb +4 -2
- data/app/models/spree/file_upload.rb +155 -0
- data/app/models/spree/fulfillment.rb +66 -30
- data/app/models/spree/fulfillment_changer.rb +2 -2
- data/app/models/spree/fulfillment_group.rb +6 -1
- data/app/models/spree/fulfillment_item.rb +0 -4
- data/app/models/spree/fulfillment_provider/base.rb +1 -0
- data/app/models/spree/gateway/bogus.rb +6 -0
- data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
- data/app/models/spree/gateway.rb +6 -2
- data/app/models/spree/gift_card.rb +18 -8
- data/app/models/spree/gift_card_batch.rb +3 -5
- data/app/models/spree/import.rb +5 -7
- data/app/models/spree/import_mapping.rb +1 -2
- data/app/models/spree/import_row.rb +2 -2
- data/app/models/spree/import_schemas/products.rb +1 -0
- data/app/models/spree/integration.rb +9 -19
- data/app/models/spree/invitation.rb +5 -5
- data/app/models/spree/line_item.rb +15 -4
- data/app/models/spree/locale.rb +11 -8
- data/app/models/spree/market.rb +8 -2
- data/app/models/spree/media.rb +1 -9
- data/app/models/spree/newsletter_subscriber.rb +4 -0
- data/app/models/spree/option_type.rb +2 -1
- data/app/models/spree/option_value.rb +2 -1
- data/app/models/spree/order.rb +60 -142
- data/app/models/spree/order_group.rb +18 -4
- data/app/models/spree/order_inventory.rb +10 -5
- data/app/models/spree/order_promotion.rb +1 -1
- data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
- data/app/models/spree/order_routing/strategy/base.rb +4 -2
- data/app/models/spree/order_routing/strategy/rules.rb +1 -1
- data/app/models/spree/order_routing_rule.rb +6 -22
- data/app/models/spree/package_type.rb +2 -2
- data/app/models/spree/payment/gateway_options.rb +15 -53
- data/app/models/spree/payment/processing.rb +2 -4
- data/app/models/spree/payment.rb +21 -13
- data/app/models/spree/payment_method/check.rb +2 -0
- data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
- data/app/models/spree/payment_method/store_credit.rb +15 -13
- data/app/models/spree/payment_method.rb +78 -6
- data/app/models/spree/payment_session.rb +5 -1
- data/app/models/spree/payment_setup_session.rb +1 -0
- data/app/models/spree/payout_provider/base.rb +3 -1
- data/app/models/spree/pickup_point_provider/base.rb +2 -0
- data/app/models/spree/policy.rb +1 -0
- data/app/models/spree/price.rb +17 -14
- data/app/models/spree/price_adjustment_tier.rb +4 -0
- data/app/models/spree/price_list.rb +14 -9
- data/app/models/spree/price_rules/channel_rule.rb +3 -6
- data/app/models/spree/price_rules/customer_group_rule.rb +3 -6
- data/app/models/spree/price_rules/market_rule.rb +2 -5
- data/app/models/spree/price_rules/user_rule.rb +1 -3
- data/app/models/spree/product/channels.rb +9 -2
- data/app/models/spree/product.rb +54 -30
- data/app/models/spree/product_submission.rb +0 -12
- data/app/models/spree/product_type.rb +1 -1
- data/app/models/spree/promotion/actions/create_line_items.rb +28 -18
- data/app/models/spree/promotion/rules/category.rb +1 -5
- data/app/models/spree/promotion/rules/channel.rb +2 -9
- data/app/models/spree/promotion/rules/country.rb +3 -8
- data/app/models/spree/promotion/rules/currency.rb +1 -5
- data/app/models/spree/promotion/rules/customer_group.rb +2 -9
- data/app/models/spree/promotion/rules/first_order.rb +0 -4
- data/app/models/spree/promotion/rules/item_total.rb +6 -10
- data/app/models/spree/promotion/rules/market.rb +2 -9
- data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
- data/app/models/spree/promotion/rules/option_value.rb +2 -6
- data/app/models/spree/promotion/rules/product.rb +1 -5
- data/app/models/spree/promotion/rules/user.rb +0 -4
- data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
- data/app/models/spree/promotion.rb +36 -6
- data/app/models/spree/promotion_action.rb +7 -14
- data/app/models/spree/promotion_action_line_item.rb +1 -1
- data/app/models/spree/promotion_handler/coupon.rb +59 -27
- data/app/models/spree/promotion_rule.rb +7 -14
- data/app/models/spree/purchase_order.rb +3 -0
- data/app/models/spree/quantity_rule.rb +1 -1
- data/app/models/spree/refund.rb +41 -18
- data/app/models/spree/return.rb +31 -2
- data/app/models/spree/return_line_item.rb +65 -14
- data/app/models/spree/role.rb +7 -7
- data/app/models/spree/saved_report.rb +2 -1
- data/app/models/spree/search_provider/base.rb +4 -0
- data/app/models/spree/seller.rb +10 -2
- data/app/models/spree/seller_payout.rb +8 -0
- data/app/models/spree/seller_requirement.rb +5 -12
- data/app/models/spree/seller_requirement_custom_field.rb +1 -2
- data/app/models/spree/seller_requirement_submission.rb +3 -2
- data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
- data/app/models/spree/seller_transfer.rb +6 -0
- data/app/models/spree/shipping_label.rb +2 -0
- data/app/models/spree/stock/availability_validator.rb +2 -2
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
- data/app/models/spree/stock/packer.rb +2 -2
- data/app/models/spree/stock/splitter/weight.rb +1 -1
- data/app/models/spree/stock_level/custom_events.rb +6 -0
- data/app/models/spree/stock_level.rb +3 -1
- data/app/models/spree/stock_location.rb +1 -0
- data/app/models/spree/stock_movement.rb +1 -1
- data/app/models/spree/stock_receipt_item.rb +1 -1
- data/app/models/spree/stock_reservation.rb +2 -2
- data/app/models/spree/stock_transfer.rb +6 -2
- data/app/models/spree/store.rb +70 -19
- data/app/models/spree/store_credit.rb +13 -13
- data/app/models/spree/store_credit_event.rb +5 -5
- data/app/models/spree/supplier.rb +1 -0
- data/app/models/spree/tax_category.rb +1 -0
- data/app/models/spree/tax_exemption_certificate.rb +3 -0
- data/app/models/spree/tax_identifier.rb +3 -1
- data/app/models/spree/tax_line.rb +53 -3
- data/app/models/spree/tax_provider/base.rb +71 -14
- data/app/models/spree/tax_provider/internal.rb +70 -41
- data/app/models/spree/tax_provider/recorded_share.rb +114 -0
- data/app/models/spree/tax_rate.rb +30 -8
- data/app/models/spree/taxon.rb +1 -1
- data/app/models/spree/taxonomy.rb +0 -1
- data/app/models/spree/update_status.rb +25 -0
- data/app/models/spree/variant.rb +10 -12
- data/app/models/spree/variant_media.rb +1 -1
- data/app/models/spree/webhook_delivery.rb +1 -0
- data/app/models/spree/webhook_endpoint.rb +46 -13
- data/app/models/spree/wishlist.rb +1 -6
- data/app/models/spree/wishlist_item/custom_events.rb +6 -0
- data/app/presenters/spree/csv/customer_presenter.rb +1 -1
- data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
- data/app/presenters/spree/csv/order_line_item_presenter.rb +10 -6
- data/app/presenters/spree/csv/price_list_price_presenter.rb +3 -8
- data/app/presenters/spree/csv/product_variant_presenter.rb +59 -20
- data/app/presenters/spree/csv/purchase_order_item_presenter.rb +1 -3
- data/app/serializers/spree/emails/base_serializer.rb +14 -0
- data/app/serializers/spree/emails/export_serializer.rb +13 -0
- data/app/serializers/spree/emails/import_serializer.rb +19 -0
- data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
- data/app/serializers/spree/emails/store_serializer.rb +31 -0
- data/app/serializers/spree/emails/user_serializer.rb +12 -0
- data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
- data/app/services/spree/carts/create.rb +8 -2
- data/app/services/spree/carts/destroy.rb +2 -2
- data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
- data/app/services/spree/carts/split_by_seller.rb +1 -1
- data/app/services/spree/carts/update.rb +22 -8
- data/app/services/spree/catalogs/set_product_terms.rb +1 -1
- data/app/services/spree/categories/add_products.rb +13 -31
- data/app/services/spree/categories/remove_products.rb +12 -51
- data/app/services/spree/checkout/advance.rb +1 -4
- data/app/services/spree/collections/add_products.rb +10 -23
- data/app/services/spree/collections/remove_products.rb +7 -43
- data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
- data/app/services/spree/data_requests/create.rb +1 -1
- data/app/services/spree/deliveries/destroy.rb +2 -2
- data/app/services/spree/email_templates/check.rb +58 -0
- data/app/services/spree/email_templates/no_sample_record.rb +7 -0
- data/app/services/spree/email_templates/preview.rb +99 -0
- data/app/services/spree/emails/file_system.rb +23 -0
- data/app/services/spree/emails/filters.rb +84 -0
- data/app/services/spree/emails/liquid_context.rb +48 -0
- data/app/services/spree/emails/rendered_email.rb +14 -0
- data/app/services/spree/emails/renderer.rb +125 -0
- data/app/services/spree/emails/template.rb +51 -0
- data/app/services/spree/emails/template_data.rb +23 -0
- data/app/services/spree/emails/template_resolver.rb +109 -0
- data/app/services/spree/emails/text_converter.rb +72 -0
- data/app/services/spree/emails/translation_inliner.rb +134 -0
- data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
- data/app/services/spree/imports/row_processors/product_variant.rb +77 -34
- data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
- data/app/services/spree/locales.rb +43 -10
- data/app/services/spree/newsletter/subscribe.rb +8 -12
- data/app/services/spree/orders/build_fulfillments.rb +3 -2
- data/app/services/spree/orders/create.rb +5 -4
- data/app/services/spree/orders/discounts/create.rb +14 -11
- data/app/services/spree/orders/discounts/destroy.rb +1 -0
- data/app/services/spree/orders/discounts/update.rb +1 -0
- data/app/services/spree/orders/fees/create.rb +1 -0
- data/app/services/spree/orders/fees/destroy.rb +1 -0
- data/app/services/spree/orders/fees/update.rb +1 -0
- data/app/services/spree/orders/remove_line_item.rb +13 -3
- data/app/services/spree/orders/update.rb +10 -6
- data/app/services/spree/orders/update_item.rb +6 -3
- data/app/services/spree/orders/update_statuses.rb +3 -1
- data/app/services/spree/prices/bulk_upsert.rb +32 -25
- data/app/services/spree/product_memberships.rb +61 -0
- data/app/services/spree/returns/eligibility_validator.rb +1 -1
- data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
- data/app/services/spree/seeds/commission_rates.rb +1 -1
- data/app/services/spree/seeds/digital_delivery.rb +1 -1
- data/app/services/spree/seeds/payment_methods.rb +2 -2
- data/app/services/spree/seeds/saved_reports.rb +3 -3
- data/app/services/spree/seeds/stores.rb +1 -1
- data/app/services/spree/sellers/requirements.rb +1 -2
- data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
- data/app/services/spree/shipping_labels/store_file.rb +3 -3
- data/app/services/spree/stock_levels/correct.rb +3 -3
- data/app/services/spree/stock_reservations/reserve.rb +2 -3
- data/app/services/spree/store_credits/apply.rb +15 -13
- data/app/services/spree/stores/provision_defaults.rb +23 -25
- data/app/services/spree/update_check.rb +160 -0
- data/app/services/spree/uploads/assign_stores.rb +92 -0
- data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
- data/app/validators/spree/attachment_content_type_validator.rb +19 -2
- data/app/validators/spree/locale_codes_validator.rb +22 -0
- data/app/views/layouts/spree/base_mailer.html.erb +2 -43
- data/app/views/layouts/spree/base_mailer.liquid +79 -0
- data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/export_mailer/export_done.liquid +12 -0
- data/app/views/spree/import_mailer/import_done.liquid +15 -0
- data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
- data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
- data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
- data/app/views/spree/shared/_mailer_button.html.erb +4 -5
- data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
- data/app/views/spree/shared/_summary_row.liquid +8 -0
- data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
- data/app/workflows/spree/carts/add_item.rb +4 -4
- data/app/workflows/spree/carts/complete.rb +13 -3
- data/app/workflows/spree/carts/merge.rb +1 -1
- data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
- data/app/workflows/spree/carts/upsert_items.rb +18 -9
- data/app/workflows/spree/claims/approve.rb +5 -22
- data/app/workflows/spree/claims/cancel.rb +5 -21
- data/app/workflows/spree/claims/create.rb +15 -0
- data/app/workflows/spree/claims/deny.rb +7 -0
- data/app/workflows/spree/claims/resolve.rb +34 -31
- data/app/workflows/spree/customers/anonymize.rb +3 -4
- data/app/workflows/spree/customers/create.rb +1 -1
- data/app/workflows/spree/data_requests/fulfill.rb +2 -2
- data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
- data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
- data/app/workflows/spree/email_templates/publish.rb +70 -0
- data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
- data/app/workflows/spree/email_templates/revert.rb +29 -0
- data/app/workflows/spree/email_templates/save_draft.rb +85 -0
- data/app/workflows/spree/exchanges/approve.rb +5 -22
- data/app/workflows/spree/exchanges/cancel.rb +5 -21
- data/app/workflows/spree/exchanges/create.rb +6 -0
- data/app/workflows/spree/exchanges/fulfill.rb +75 -51
- data/app/workflows/spree/exchanges/receive.rb +6 -65
- data/app/workflows/spree/fulfillments/cancel.rb +1 -1
- data/app/workflows/spree/fulfillments/create.rb +53 -49
- data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
- data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
- data/app/workflows/spree/fulfillments/replacements.rb +59 -0
- data/app/workflows/spree/gift_cards/apply.rb +2 -2
- data/app/workflows/spree/orders/add_item.rb +5 -1
- data/app/workflows/spree/orders/cancel.rb +3 -3
- data/app/workflows/spree/orders/complete.rb +16 -8
- data/app/workflows/spree/payments/capture.rb +20 -5
- data/app/workflows/spree/payments/process.rb +5 -3
- data/app/workflows/spree/post_sale/approval.rb +35 -0
- data/app/workflows/spree/post_sale/cancellation.rb +44 -0
- data/app/workflows/spree/post_sale/receipt.rb +92 -0
- data/app/workflows/spree/price_lists/update.rb +4 -4
- data/app/workflows/spree/products/nested_attributes.rb +1 -1
- data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
- data/app/workflows/spree/purchase_orders/close.rb +3 -31
- data/app/workflows/spree/purchase_orders/create.rb +3 -1
- data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
- data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
- data/app/workflows/spree/purchase_orders/receive.rb +1 -1
- data/app/workflows/spree/purchase_orders/update.rb +5 -34
- data/app/workflows/spree/receivables/closing_short.rb +46 -0
- data/app/workflows/spree/receivables/draft_editing.rb +48 -0
- data/app/workflows/spree/refunds/create.rb +5 -2
- data/app/workflows/spree/refunds/order_payments.rb +14 -2
- data/app/workflows/spree/refunds/tax_credit.rb +67 -0
- data/app/workflows/spree/returns/approve.rb +5 -22
- data/app/workflows/spree/returns/cancel.rb +5 -22
- data/app/workflows/spree/returns/create.rb +8 -0
- data/app/workflows/spree/returns/receive.rb +5 -76
- data/app/workflows/spree/returns/refund.rb +31 -8
- data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
- data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
- data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
- data/app/workflows/spree/shipping_labels/record.rb +7 -7
- data/app/workflows/spree/shipping_labels/refund.rb +2 -2
- data/app/workflows/spree/stock_receipts/recording.rb +4 -4
- data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
- data/app/workflows/spree/stock_transfers/close.rb +3 -30
- data/app/workflows/spree/stock_transfers/create.rb +3 -1
- data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
- data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
- data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
- data/app/workflows/spree/stock_transfers/receive.rb +1 -1
- data/app/workflows/spree/stock_transfers/update.rb +5 -33
- data/config/currency_exponents.json +180 -0
- data/config/i18n-tasks.yml +32 -18
- data/config/initializers/alba.rb +7 -0
- data/config/language_names.csv +264 -0
- data/config/locales/ar.yml +811 -0
- data/config/locales/be.yml +855 -0
- data/config/locales/bg.yml +723 -0
- data/config/locales/ca.yml +689 -0
- data/config/locales/content_locales.rb +9 -0
- data/config/locales/cs.yml +855 -0
- data/config/locales/da.yml +771 -0
- data/config/locales/de-CH.yml +248 -0
- data/config/locales/de.yml +835 -0
- data/config/locales/el.yml +769 -0
- data/config/locales/en.yml +47 -505
- data/config/locales/es-CL.yml +301 -0
- data/config/locales/es-EC.yml +186 -0
- data/config/locales/es-MX.yml +334 -0
- data/config/locales/es.yml +831 -0
- data/config/locales/et.yml +644 -0
- data/config/locales/fa.yml +709 -0
- data/config/locales/fi.yml +771 -0
- data/config/locales/fr.yml +830 -0
- data/config/locales/hu.yml +751 -0
- data/config/locales/id.yml +756 -0
- data/config/locales/it.yml +833 -0
- data/config/locales/ja.yml +726 -0
- data/config/locales/km.yml +759 -0
- data/config/locales/ko.yml +575 -0
- data/config/locales/ku.yml +753 -0
- data/config/locales/lt.yml +821 -0
- data/config/locales/lv.yml +685 -0
- data/config/locales/nb.yml +824 -0
- data/config/locales/ne.yml +823 -0
- data/config/locales/nl.yml +826 -0
- data/config/locales/pl.yml +835 -0
- data/config/locales/pt-BR.yml +547 -0
- data/config/locales/pt.yml +636 -0
- data/config/locales/ro.yml +750 -0
- data/config/locales/ru.yml +834 -0
- data/config/locales/sk.yml +680 -0
- data/config/locales/sl-SI.yml +600 -0
- data/config/locales/sv.yml +827 -0
- data/config/locales/th.yml +745 -0
- data/config/locales/tr.yml +777 -0
- data/config/locales/uk.yml +836 -0
- data/config/locales/vi.yml +768 -0
- data/config/locales/zh-CN.yml +786 -0
- data/config/locales/zh-TW.yml +834 -0
- data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
- data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
- data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
- data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
- data/db/migrate/20261009000001_change_money_columns_to_decimal_19_4.rb +116 -0
- data/db/migrate/20261010000001_add_store_id_to_active_storage_blobs.rb +12 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
- data/lib/generators/spree/dummy/templates/rails/test.rb +26 -0
- data/lib/spree/checkout/default_requirements.rb +37 -13
- data/lib/spree/core/configuration.rb +19 -1
- data/lib/spree/core/controller_helpers/store.rb +1 -1
- data/lib/spree/core/dependencies.rb +5 -4
- data/lib/spree/core/emails/escaped_output.rb +60 -0
- data/lib/spree/core/engine.rb +72 -165
- data/lib/spree/core/permissions/default_catalog.rb +5 -0
- data/lib/spree/core/preferences/invalid_preferences.rb +18 -0
- data/lib/spree/core/preferences/json_conversion.rb +186 -0
- data/lib/spree/core/preferences/masking.rb +55 -2
- data/lib/spree/core/preferences/preferable.rb +343 -103
- data/lib/spree/core/preferences/preferable_class_methods.rb +188 -136
- data/lib/spree/core/previews/import_preview.rb +10 -0
- data/lib/spree/core/ransack_configuration.rb +13 -2
- data/lib/spree/core/tax/provider_error.rb +37 -0
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +91 -373
- data/lib/spree/emails/editable_templates.rb +82 -0
- data/lib/spree/events/catalog.rb +189 -0
- data/lib/spree/events.rb +21 -1
- data/lib/spree/hooks.rb +0 -17
- data/lib/spree/i18n.rb +32 -15
- data/lib/spree/iso_data.rb +7 -0
- data/lib/spree/localized_number.rb +6 -0
- data/lib/spree/migrations.rb +4 -1
- data/lib/spree/money.rb +196 -15
- data/lib/spree/reporting/adapters/base.rb +7 -6
- data/lib/spree/reporting/adapters/live.rb +6 -6
- data/lib/spree/reporting/default_vocabulary.rb +22 -10
- data/lib/spree/reporting/hydration.rb +1 -1
- data/lib/spree/reporting/query.rb +8 -0
- data/lib/spree/reporting/registry.rb +13 -0
- data/lib/spree/reporting/schema.rb +10 -10
- data/lib/spree/testing_support/editable_email_templates.rb +61 -0
- data/lib/spree/testing_support/emails.rb +22 -0
- data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
- data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
- data/lib/spree/testing_support/factories/order_factory.rb +2 -2
- data/lib/spree/testing_support/factories/product_factory.rb +9 -0
- data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
- data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
- data/lib/spree/testing_support/i18n.rb +3 -94
- data/lib/spree/testing_support/lifecycle_events.rb +1 -1
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- data/lib/spree/testing_support/preferences.rb +0 -5
- data/lib/spree/testing_support/store.rb +9 -1
- data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +67 -1
- data/lib/spree/uploads.rb +101 -0
- data/lib/tasks/categories_and_collections.rake +3 -3
- data/lib/tasks/core.rake +6 -4
- data/lib/tasks/markets.rake +2 -1
- data/lib/tasks/media.rake +3 -3
- data/lib/tasks/migrate_users_to_customers.rake +23 -18
- data/lib/tasks/money.rake +46 -0
- data/lib/tasks/package_types.rake +1 -1
- data/lib/tasks/preferences_json.rake +101 -0
- data/lib/tasks/seller_sample_data.rake +2 -2
- data/lib/tasks/store_settings.rake +13 -6
- data/lib/tasks/typed_adjustments_migration.rake +1 -1
- data/lib/tasks/uploads.rake +19 -0
- data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
- metadata +219 -46
- data/app/jobs/spree/products/touch_categories_job.rb +0 -15
- data/app/jobs/spree/products/touch_taxons_job.rb +0 -13
- data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
- data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
- data/app/models/spree/base_analytics_event_handler.rb +0 -54
- data/app/models/spree/invitations/store.rb +0 -6
- data/app/models/spree/preference.rb +0 -14
- data/app/models/spree/promotion_handler/page.rb +0 -25
- data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
- data/app/models/spree/stock/differentiator.rb +0 -45
- data/app/presenters/spree/filters/options_presenter.rb +0 -47
- data/app/services/spree/carts/empty.rb +0 -42
- data/app/services/spree/country_to_timezone.rb +0 -273
- data/app/services/spree/payments/create.rb +0 -92
- data/app/validators/spree/url_validator.rb +0 -23
- data/app/views/active_storage/blobs/_blob.html.erb +0 -14
- data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/export_mailer/export_done.html.erb +0 -7
- data/app/views/spree/import_mailer/import_done.html.erb +0 -12
- data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
- data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
- data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
- data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
- data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
- data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
- data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
- data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
- data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
- data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
- data/app/workflows/spree/returns/purchase_label.rb +0 -32
- data/lib/spree/analytics.rb +0 -11
- data/lib/spree/core/preferences/scoped_store.rb +0 -34
- data/lib/spree/core/preferences/store.rb +0 -105
- data/lib/spree/translation_migrations.rb +0 -52
data/lib/spree/money.rb
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
require 'money'
|
|
2
2
|
|
|
3
3
|
Money.locale_backend = :i18n
|
|
4
|
+
Money.default_currency = 'USD'
|
|
4
5
|
Money.rounding_mode = BigDecimal::ROUND_HALF_UP
|
|
5
6
|
|
|
6
7
|
module Spree
|
|
7
8
|
class Money
|
|
9
|
+
# An amount that is not a canonical decimal string: a JSON number, a
|
|
10
|
+
# localized or padded string, or more decimals than the field allows.
|
|
11
|
+
class InvalidFormat < ArgumentError
|
|
12
|
+
# @return [Symbol, nil] the request field the amount came from
|
|
13
|
+
attr_reader :field
|
|
14
|
+
|
|
15
|
+
def initialize(message = nil, field: nil)
|
|
16
|
+
super(message)
|
|
17
|
+
@field = field
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
8
21
|
# How Spree rounds an amount to a currency.
|
|
9
22
|
#
|
|
10
23
|
# Lives here beside the rounding mode above because "how much is this worth
|
|
@@ -14,42 +27,156 @@ module Spree
|
|
|
14
27
|
module Rounding
|
|
15
28
|
module_function
|
|
16
29
|
|
|
30
|
+
# ISO 4217 minor units, the one table Ruby and the TypeScript clients
|
|
31
|
+
# share. The Money gem disagrees for a few currencies (it writes HUF
|
|
32
|
+
# without decimals), so Spree reads its own copy.
|
|
33
|
+
EXPONENTS = JSON.parse(File.read(File.expand_path('../../config/currency_exponents.json', __dir__))).freeze
|
|
34
|
+
|
|
35
|
+
# Every currency Spree knows, the closed list the API types publish as
|
|
36
|
+
# `CurrencyCode`.
|
|
37
|
+
CURRENCY_CODES = EXPONENTS.keys.sort.freeze
|
|
38
|
+
|
|
39
|
+
# Unit prices may be quoted below the minor unit ("0.0125" USD); amounts
|
|
40
|
+
# that change hands never are.
|
|
41
|
+
UNIT_PRICE_DECIMALS = 4
|
|
42
|
+
|
|
43
|
+
CANONICAL_DECIMAL = /\A-?\d+(\.\d+)?\z/
|
|
44
|
+
|
|
45
|
+
ROUNDING_MODES = {
|
|
46
|
+
half_up: BigDecimal::ROUND_HALF_UP,
|
|
47
|
+
half_even: BigDecimal::ROUND_HALF_EVEN,
|
|
48
|
+
half_down: BigDecimal::ROUND_HALF_DOWN,
|
|
49
|
+
up: BigDecimal::ROUND_UP,
|
|
50
|
+
down: BigDecimal::ROUND_DOWN
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
17
53
|
# The number of decimal places a currency is written in: 2 for most, 0
|
|
18
54
|
# for yen, 3 for dinar. An unknown currency falls back to two places
|
|
19
55
|
# rather than raising — a sale is not the moment to discover a typo in a
|
|
20
56
|
# currency code.
|
|
21
57
|
#
|
|
22
|
-
# Note that Spree's money columns are `decimal(_, 2)` throughout, so a
|
|
23
|
-
# three-decimal currency still loses its last place on the way into the
|
|
24
|
-
# database. Rounding here to the currency's own precision keeps the
|
|
25
|
-
# arithmetic honest and means widening those columns would be the only
|
|
26
|
-
# change needed; it does not by itself make Spree dinar-exact.
|
|
27
|
-
#
|
|
28
58
|
# @param currency [String, ::Money::Currency, nil]
|
|
29
59
|
# @return [Integer]
|
|
30
60
|
def precision(currency)
|
|
31
|
-
|
|
61
|
+
code = currency.respond_to?(:iso_code) ? currency.iso_code : currency.to_s.upcase
|
|
62
|
+
EXPONENTS[code] || ::Money::Currency.find(code)&.exponent || EXPONENTS['USD']
|
|
32
63
|
end
|
|
33
64
|
|
|
34
|
-
# Rounds to the currency's own minor unit,
|
|
35
|
-
#
|
|
65
|
+
# Rounds to the currency's own minor unit, so amounts reconcile to the
|
|
66
|
+
# cent when they are summed.
|
|
36
67
|
#
|
|
37
68
|
# @param amount [Numeric, String, nil]
|
|
38
69
|
# @param currency [String, ::Money::Currency, nil]
|
|
70
|
+
# @param mode [Symbol] one of {ROUNDING_MODES}; tax providers pick their own
|
|
39
71
|
# @return [BigDecimal]
|
|
40
|
-
def to_currency(amount, currency)
|
|
41
|
-
quantize(amount, precision(currency))
|
|
72
|
+
def to_currency(amount, currency, mode: :half_up)
|
|
73
|
+
quantize(amount, precision(currency), mode: mode)
|
|
42
74
|
end
|
|
43
75
|
|
|
44
76
|
# The same rounding against a precision already in hand.
|
|
45
77
|
#
|
|
46
78
|
# @param amount [Numeric, String, nil]
|
|
47
79
|
# @param precision [Integer]
|
|
80
|
+
# @param mode [Symbol] one of {ROUNDING_MODES}
|
|
48
81
|
# @return [BigDecimal]
|
|
49
|
-
def quantize(amount, precision)
|
|
50
|
-
BigDecimal(amount.to_s).round(precision,
|
|
82
|
+
def quantize(amount, precision, mode: :half_up)
|
|
83
|
+
BigDecimal(amount.to_s).round(precision, ROUNDING_MODES.fetch(mode))
|
|
51
84
|
end
|
|
52
85
|
|
|
86
|
+
# Reads a money amount from an API request. Only a canonical decimal
|
|
87
|
+
# string is accepted — never a JSON number, which may already have lost
|
|
88
|
+
# precision as a float, and never more decimals than the field holds.
|
|
89
|
+
#
|
|
90
|
+
# @param value [String, nil]
|
|
91
|
+
# @param currency [String, ::Money::Currency, nil] nil when unknown,
|
|
92
|
+
# which allows up to {UNIT_PRICE_DECIMALS} decimals
|
|
93
|
+
# @param unit_price [Boolean] allows up to {UNIT_PRICE_DECIMALS} decimals
|
|
94
|
+
# @return [BigDecimal, nil]
|
|
95
|
+
# @raise [Spree::Money::InvalidFormat]
|
|
96
|
+
def parse_canonical(value, currency, unit_price: false)
|
|
97
|
+
amount = parse_canonical_decimal(value)
|
|
98
|
+
return if amount.nil?
|
|
99
|
+
|
|
100
|
+
allowed = if currency.nil? || unit_price
|
|
101
|
+
[precision(currency), UNIT_PRICE_DECIMALS].max
|
|
102
|
+
else
|
|
103
|
+
precision(currency)
|
|
104
|
+
end
|
|
105
|
+
raise InvalidFormat, "has more than #{allowed} decimal places" if decimal_places(value) > allowed
|
|
106
|
+
|
|
107
|
+
amount
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Reads a rate or percentage from an API request: a canonical decimal
|
|
111
|
+
# string with no limit on its decimals.
|
|
112
|
+
#
|
|
113
|
+
# @param value [String, nil]
|
|
114
|
+
# @return [BigDecimal, nil]
|
|
115
|
+
# @raise [Spree::Money::InvalidFormat]
|
|
116
|
+
def parse_canonical_decimal(value)
|
|
117
|
+
return if value.nil?
|
|
118
|
+
raise InvalidFormat, 'must be a decimal string like "19.99"' unless value.is_a?(String) && value.match?(CANONICAL_DECIMAL)
|
|
119
|
+
|
|
120
|
+
BigDecimal(value)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Reads a decimal from a caller that may send a number or text: a blank
|
|
124
|
+
# is nil, a number passes, and text must be a plain decimal ("16.50"),
|
|
125
|
+
# never read as a wrong number the way `"1,599.99".to_d` reads 1.
|
|
126
|
+
#
|
|
127
|
+
# @param value [Numeric, String, nil]
|
|
128
|
+
# @return [BigDecimal, nil]
|
|
129
|
+
# @raise [Spree::Money::InvalidFormat]
|
|
130
|
+
def parse_decimal(value)
|
|
131
|
+
return if value.nil? || (value.is_a?(String) && value.strip.empty?)
|
|
132
|
+
return BigDecimal(value.to_s) if value.is_a?(Numeric)
|
|
133
|
+
|
|
134
|
+
parse_canonical_decimal(value.is_a?(String) ? value.strip : value)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Writes an amount the way the API and exports carry it: exactly the
|
|
138
|
+
# currency's decimal places ("10.00", "1.500", "100"). A unit price keeps
|
|
139
|
+
# up to {UNIT_PRICE_DECIMALS}, with zeros beyond the currency's own
|
|
140
|
+
# places dropped ("0.0125", "19.99").
|
|
141
|
+
#
|
|
142
|
+
# @param amount [Numeric, String, nil]
|
|
143
|
+
# @param currency [String, ::Money::Currency, nil]
|
|
144
|
+
# @param unit_price [Boolean]
|
|
145
|
+
# @return [String, nil]
|
|
146
|
+
def format(amount, currency, unit_price: false)
|
|
147
|
+
return if amount.nil?
|
|
148
|
+
|
|
149
|
+
places = precision(currency)
|
|
150
|
+
kept = unit_price ? [places, UNIT_PRICE_DECIMALS].max : places
|
|
151
|
+
rounded = quantize(amount, kept)
|
|
152
|
+
whole, fraction = (rounded.zero? ? BigDecimal(0) : rounded).to_s('F').split('.')
|
|
153
|
+
fraction = without_trailing_zeros(fraction.to_s).ljust(places, '0')
|
|
154
|
+
fraction.empty? ? whole : "#{whole}.#{fraction}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Writes a rate or percentage with no trailing zeros ("0.23", "23").
|
|
158
|
+
#
|
|
159
|
+
# @param value [Numeric, String, nil]
|
|
160
|
+
# @return [String, nil]
|
|
161
|
+
def format_decimal(value)
|
|
162
|
+
return if value.nil?
|
|
163
|
+
|
|
164
|
+
without_trailing_zeros(BigDecimal(value.to_s).to_s('F')).delete_suffix('.')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Trailing zeros carry no value, so "1000.0" is a whole yen.
|
|
168
|
+
def decimal_places(value)
|
|
169
|
+
value.include?('.') ? without_trailing_zeros(value.split('.').last).length : 0
|
|
170
|
+
end
|
|
171
|
+
private_class_method :decimal_places
|
|
172
|
+
|
|
173
|
+
# Scans back to the last other digit, in linear time on request input.
|
|
174
|
+
def without_trailing_zeros(text)
|
|
175
|
+
last = text.rindex(/[^0]/)
|
|
176
|
+
last ? text[0..last] : ''
|
|
177
|
+
end
|
|
178
|
+
private_class_method :without_trailing_zeros
|
|
179
|
+
|
|
53
180
|
# An amount as a whole number of the currency's smallest unit — cents for
|
|
54
181
|
# most currencies, whole yen for one written without decimals.
|
|
55
182
|
#
|
|
@@ -67,14 +194,65 @@ module Spree
|
|
|
67
194
|
(BigDecimal(amount.to_s) * (10**precision(currency))).round.to_i
|
|
68
195
|
end
|
|
69
196
|
|
|
197
|
+
# An amount in hundredths of the currency, whatever its own minor unit:
|
|
198
|
+
# the unit payment gateways are called with until they take amounts with
|
|
199
|
+
# their currency.
|
|
200
|
+
#
|
|
201
|
+
# @param amount [Numeric, String]
|
|
202
|
+
# @return [Integer]
|
|
203
|
+
def to_hundredths(amount)
|
|
204
|
+
quantize(BigDecimal(amount.to_s) * 100, 0).to_i
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# @param hundredths [Integer] as {#to_hundredths} returns
|
|
208
|
+
# @return [BigDecimal]
|
|
209
|
+
def from_hundredths(hundredths)
|
|
210
|
+
BigDecimal(hundredths) / 100
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Whether an amount is missing or zero. Text that is not a number is
|
|
214
|
+
# neither, so a malformed value is never mistaken for "nothing to pay".
|
|
215
|
+
#
|
|
216
|
+
# @param value [Numeric, String, nil]
|
|
217
|
+
# @return [Boolean]
|
|
218
|
+
def blank_or_zero?(value)
|
|
219
|
+
return true if value.blank?
|
|
220
|
+
|
|
221
|
+
BigDecimal(value.to_s, exception: false)&.zero? || false
|
|
222
|
+
end
|
|
223
|
+
|
|
70
224
|
# @param units [Integer] whole minor units, as {#to_minor_units} returns
|
|
71
225
|
# @param currency [String, ::Money::Currency, nil]
|
|
72
226
|
# @return [BigDecimal]
|
|
73
227
|
def from_minor_units(units, currency)
|
|
74
228
|
BigDecimal(units) / (10**precision(currency))
|
|
75
229
|
end
|
|
230
|
+
|
|
231
|
+
# Makes the Money gem agree with {EXPONENTS}, so formatting, `cents`
|
|
232
|
+
# and anything else built on it use ISO 4217 decimals too.
|
|
233
|
+
def register_with_money_gem!
|
|
234
|
+
EXPONENTS.each do |code, exponent|
|
|
235
|
+
subunit_to_unit = 10**exponent
|
|
236
|
+
currency = ::Money::Currency.find(code)
|
|
237
|
+
|
|
238
|
+
if currency.nil?
|
|
239
|
+
::Money::Currency.register(
|
|
240
|
+
iso_code: code, name: code, symbol: code, subunit: nil, subunit_to_unit: subunit_to_unit,
|
|
241
|
+
priority: 100, symbol_first: false, thousands_separator: ',', decimal_mark: '.'
|
|
242
|
+
)
|
|
243
|
+
elsif currency.subunit_to_unit != subunit_to_unit
|
|
244
|
+
# The smallest cash amount is counted in subunits, so it scales
|
|
245
|
+
# with them: 5 forints stays 5 forints, not 0.05.
|
|
246
|
+
scaled = currency.smallest_denomination &&
|
|
247
|
+
currency.smallest_denomination * subunit_to_unit / currency.subunit_to_unit
|
|
248
|
+
::Money::Currency.inherit(code.downcase.to_sym, subunit_to_unit: subunit_to_unit, smallest_denomination: scaled)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
76
252
|
end
|
|
77
253
|
|
|
254
|
+
Rounding.register_with_money_gem!
|
|
255
|
+
|
|
78
256
|
include Comparable
|
|
79
257
|
|
|
80
258
|
class << self
|
|
@@ -97,13 +275,16 @@ module Spree
|
|
|
97
275
|
delegate :cents, :currency, :to_d, :positive?, :zero?, to: :money
|
|
98
276
|
|
|
99
277
|
def initialize(amount, options = {})
|
|
100
|
-
::Money.default_currency ||= Spree::Store.default.default_currency || 'USD'
|
|
101
278
|
@money = Monetize.parse(amount, (options[:currency] || Spree::Store.default.default_currency))
|
|
102
279
|
@options = Spree::Money.default_formatting_rules.merge(options)
|
|
103
280
|
end
|
|
104
281
|
|
|
282
|
+
# The amount in hundredths of the currency, whatever its own minor unit —
|
|
283
|
+
# the unit payment gateways have always been called with.
|
|
284
|
+
#
|
|
285
|
+
# @return [Integer]
|
|
105
286
|
def amount_in_cents
|
|
106
|
-
(
|
|
287
|
+
Rounding.to_hundredths(to_d)
|
|
107
288
|
end
|
|
108
289
|
|
|
109
290
|
def abs
|
|
@@ -22,24 +22,25 @@ module Spree
|
|
|
22
22
|
|
|
23
23
|
def cast_value(metric, raw)
|
|
24
24
|
case metric.format
|
|
25
|
-
when :money
|
|
25
|
+
when :money then BigDecimal((raw || 0).to_s)
|
|
26
|
+
when :decimal, :percent then BigDecimal((raw || 0).to_s).round(2)
|
|
26
27
|
else raw.to_i
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def zero_for(metric)
|
|
31
|
-
metric.format == :integer ? 0 : 0
|
|
32
|
+
metric.format == :integer ? 0 : BigDecimal(0)
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Computes requested ratio metrics from their aggregated components.
|
|
35
36
|
def apply_derived(metrics)
|
|
36
37
|
query.metrics.select(&:derived?).each do |metric|
|
|
37
38
|
numerator, denominator = metric.ratio.map { |name| metrics[name] || 0 }
|
|
38
|
-
ratio = denominator.
|
|
39
|
+
ratio = denominator.zero? ? BigDecimal(0) : BigDecimal(numerator.to_s) / BigDecimal(denominator.to_s)
|
|
39
40
|
# A percent metric carries the number a merchant reads (42.5), not
|
|
40
41
|
# the fraction, so clients never have to know to multiply.
|
|
41
42
|
ratio *= 100 if metric.format == :percent
|
|
42
|
-
metrics[metric.name] = ratio.round(2)
|
|
43
|
+
metrics[metric.name] = metric.money? ? ratio : ratio.round(2)
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
|
|
@@ -58,12 +59,12 @@ module Spree
|
|
|
58
59
|
def growth_rate(current, previous)
|
|
59
60
|
return nil if previous.nil?
|
|
60
61
|
if previous.zero?
|
|
61
|
-
return 0
|
|
62
|
+
return BigDecimal(0) if current.zero?
|
|
62
63
|
|
|
63
64
|
return nil
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
(((current - previous) / previous.
|
|
67
|
+
((BigDecimal(current.to_s) - BigDecimal(previous.to_s)) / BigDecimal(previous.to_s) * 100).round(1)
|
|
67
68
|
end
|
|
68
69
|
end
|
|
69
70
|
end
|
|
@@ -642,12 +642,12 @@ module Spree
|
|
|
642
642
|
end.to_h
|
|
643
643
|
end
|
|
644
644
|
|
|
645
|
-
# Both periods span the same number of days
|
|
646
|
-
# differ by at most one partial edge. Pairing runs
|
|
647
|
-
# the nth bucket of this period with the nth of the
|
|
648
|
-
# the ranges share a start offset: a range
|
|
649
|
-
# its partial first bucket with the previous
|
|
650
|
-
# bucket. A bucket past the end of the previous list has no
|
|
645
|
+
# Both periods span the same number of days (or of whole months), so
|
|
646
|
+
# their bucket counts differ by at most one partial edge. Pairing runs
|
|
647
|
+
# from the start — the nth bucket of this period with the nth of the
|
|
648
|
+
# previous — because the ranges share a start offset: a range
|
|
649
|
+
# beginning mid-month pairs its partial first bucket with the previous
|
|
650
|
+
# period's partial first bucket. A bucket past the end of the previous list has no
|
|
651
651
|
# counterpart and pairs with nothing rather than wrapping around.
|
|
652
652
|
def bucket_pairs(grain)
|
|
653
653
|
current = expected_buckets(query.time_range, grain)
|
|
@@ -113,16 +113,27 @@ module Spree
|
|
|
113
113
|
SUM(COALESCE((SELECT SUM(r.amount) FROM %{refunds} r WHERE r.order_id = %{orders}.id), 0))
|
|
114
114
|
SQL
|
|
115
115
|
|
|
116
|
-
# A refund
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
# A refund that gives tax back carries it in its own column, so the goods
|
|
117
|
+
# and the tax it returned are netted from goods and tax respectively.
|
|
118
|
+
REFUNDED_GOODS_SUBQUERY = <<~SQL.squish.freeze
|
|
119
|
+
SUM(COALESCE((SELECT SUM(r.amount - r.tax_amount) FROM %{refunds} r WHERE r.order_id = %{orders}.id), 0))
|
|
120
|
+
SQL
|
|
121
|
+
|
|
122
|
+
REFUNDED_TAX_SUBQUERY = <<~SQL.squish.freeze
|
|
123
|
+
SUM(COALESCE((SELECT SUM(r.tax_amount) FROM %{refunds} r WHERE r.order_id = %{orders}.id), 0))
|
|
124
|
+
SQL
|
|
125
|
+
|
|
126
|
+
# A refund is issued against the order, not a line, so its goods are
|
|
127
|
+
# apportioned by the line's share of the order's pre-tax value. The
|
|
128
|
+
# denominator is summed over the order's own lines rather than read from
|
|
129
|
+
# item_total: item_total is priced before discounts, so on a discounted
|
|
130
|
+
# order the shares would not add up to one and part of the refund would
|
|
131
|
+
# vanish. The `* 1.0` forces real division: SQLite divides two
|
|
132
|
+
# integer-valued decimals as integers, which silently rounds every share
|
|
133
|
+
# to zero.
|
|
123
134
|
LINE_ITEM_REFUNDS_SUBQUERY = <<~SQL.squish.freeze
|
|
124
135
|
SUM(
|
|
125
|
-
COALESCE((SELECT SUM(r.amount) FROM %{refunds} r WHERE r.order_id = %{orders}.id), 0)
|
|
136
|
+
COALESCE((SELECT SUM(r.amount - r.tax_amount) FROM %{refunds} r WHERE r.order_id = %{orders}.id), 0)
|
|
126
137
|
* CASE
|
|
127
138
|
WHEN COALESCE((SELECT SUM(sibling.pre_tax_amount) FROM %{line_items} sibling
|
|
128
139
|
WHERE sibling.order_id = %{orders}.id), 0) > 0
|
|
@@ -293,7 +304,7 @@ module Spree
|
|
|
293
304
|
metric :gross_sales, sql: 'SUM(%{line_items}.price * %{line_items}.quantity)',
|
|
294
305
|
base: :line_items, format: :money
|
|
295
306
|
metric :discounts, sql: 'SUM(%{line_items}.discount_total)', base: :line_items, format: :money
|
|
296
|
-
metric :returns, sql:
|
|
307
|
+
metric :returns, sql: REFUNDED_GOODS_SUBQUERY, base: :orders, format: :money
|
|
297
308
|
# Net of returns as well as discounts, so this is the figure a
|
|
298
309
|
# merchant recognises as "what we actually sold".
|
|
299
310
|
metric :net_sales, sql: "SUM(%{line_items}.pre_tax_amount) - #{LINE_ITEM_REFUNDS_SUBQUERY}",
|
|
@@ -303,7 +314,8 @@ module Spree
|
|
|
303
314
|
# Every shopper-visible fee except duties, which the chain counts separately.
|
|
304
315
|
metric :fees, sql: "SUM(COALESCE(%{orders}.fee_total, 0)) - #{DUTIES_SUBQUERY}",
|
|
305
316
|
base: :orders, format: :money, suggests: 'net_sales'
|
|
306
|
-
|
|
317
|
+
# Net of the tax given back, as net_sales is net of the goods.
|
|
318
|
+
metric :taxes, sql: "SUM(%{orders}.additional_tax_total + %{orders}.included_tax_total) - #{REFUNDED_TAX_SUBQUERY}",
|
|
307
319
|
base: :orders, format: :money, suggests: 'net_sales'
|
|
308
320
|
# The order grand total, net of what came back.
|
|
309
321
|
metric :total_sales, sql: "SUM(%{orders}.total) - #{REFUNDS_SUBQUERY}",
|
|
@@ -21,7 +21,7 @@ module Spree
|
|
|
21
21
|
dimension = definition(name)
|
|
22
22
|
# A row whose key is NULL is a real group — an order with no market or
|
|
23
23
|
# channel — and needs a name a person can read, not a blank cell.
|
|
24
|
-
return { id: nil, label:
|
|
24
|
+
return { id: nil, label: I18n.t('spree.reporting.unassigned'), meta: {} } if raw.nil? && !plain?(dimension)
|
|
25
25
|
return { id: raw.to_s, label: Schema.value_label(dimension, raw), meta: {} } if dimension.enumerated_values
|
|
26
26
|
return raw unless dimension.hydrate
|
|
27
27
|
|
|
@@ -126,12 +126,20 @@ module Spree
|
|
|
126
126
|
# year" means February to February, and an equal-day-count shift off a
|
|
127
127
|
# 28-day February would land in January. ActiveSupport maps February 29
|
|
128
128
|
# to February 28 in a non-leap year.
|
|
129
|
+
#
|
|
130
|
+
# A range of whole calendar months (a full quarter, `last_month`) shifts
|
|
131
|
+
# by the same number of months for the same reason: July – September is
|
|
132
|
+
# 92 days, and shifting it by days would start on March 31, putting a
|
|
133
|
+
# one-day March bucket at the front of the previous period.
|
|
129
134
|
def previous_time_range
|
|
130
135
|
first = time_range.first.in_time_zone(time_zone)
|
|
131
136
|
last = time_range.last.in_time_zone(time_zone)
|
|
132
137
|
|
|
133
138
|
if compare == 'previous_year'
|
|
134
139
|
(first - 1.year)..(last - 1.year)
|
|
140
|
+
elsif first == first.beginning_of_month && last == last.end_of_month
|
|
141
|
+
months = (last.year * 12 + last.month) - (first.year * 12 + first.month) + 1
|
|
142
|
+
(first - months.months)..(last - months.months).end_of_month
|
|
135
143
|
else
|
|
136
144
|
days = (last.to_date - first.to_date).to_i + 1
|
|
137
145
|
(first - days.days)..(last - days.days)
|
|
@@ -45,6 +45,19 @@ module Spree
|
|
|
45
45
|
# total is suppressed rather than rendered as a headline.
|
|
46
46
|
def per_group? = requires_grouping.present?
|
|
47
47
|
def money? = format == :money
|
|
48
|
+
|
|
49
|
+
# A value as the API and exports write it: money to the currency's
|
|
50
|
+
# decimal places, other decimals without trailing zeros, counts as
|
|
51
|
+
# integers.
|
|
52
|
+
#
|
|
53
|
+
# @param value [Numeric, nil]
|
|
54
|
+
# @param currency [String, nil]
|
|
55
|
+
# @return [String, Integer, nil]
|
|
56
|
+
def format_value(value, currency)
|
|
57
|
+
return value if value.nil? || format == :integer
|
|
58
|
+
|
|
59
|
+
money? ? Spree::Money::Rounding.format(value, currency) : Spree::Money::Rounding.format_decimal(value)
|
|
60
|
+
end
|
|
48
61
|
end
|
|
49
62
|
|
|
50
63
|
# A dimension groups rows. The definition owns every behavior keyed off
|
|
@@ -32,7 +32,7 @@ module Spree
|
|
|
32
32
|
metrics: metrics.map { |m| metric_entry(m) },
|
|
33
33
|
dimensions: dimensions.map { |d| dimension_entry(d, metrics) },
|
|
34
34
|
time_range: {
|
|
35
|
-
presets: (Query::PRESETS + Query::RELATIVE_PRESETS).map { |p| { name: p, label:
|
|
35
|
+
presets: (Query::PRESETS + Query::RELATIVE_PRESETS).map { |p| { name: p, label: catalog_text('presets', p, :label) } },
|
|
36
36
|
relative: %w[last_<n>_days last_<n>_weeks last_<n>_months],
|
|
37
37
|
absolute: 'ISO 8601 dates or datetimes in `since` / `until`, resolved in the store timezone'
|
|
38
38
|
},
|
|
@@ -50,7 +50,7 @@ module Spree
|
|
|
50
50
|
# @param group [Symbol] :metrics | :dimensions
|
|
51
51
|
# @return [String] localized label (humanized name when none registered)
|
|
52
52
|
def label_for(group, name)
|
|
53
|
-
|
|
53
|
+
catalog_text(group, name, :label)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
private
|
|
@@ -61,7 +61,7 @@ module Spree
|
|
|
61
61
|
metrics.group_by { |metric| @registry.family_of(metric) }.map do |family, family_metrics|
|
|
62
62
|
{
|
|
63
63
|
name: family,
|
|
64
|
-
label:
|
|
64
|
+
label: catalog_text('families', family, :label),
|
|
65
65
|
metrics: family_metrics.map(&:name),
|
|
66
66
|
dimensions: @registry.dimensions.values.
|
|
67
67
|
select { |d| @allowed.call(d) && @registry.base!(d.base).family == family }.map(&:name)
|
|
@@ -73,8 +73,8 @@ module Spree
|
|
|
73
73
|
{
|
|
74
74
|
name: metric.name,
|
|
75
75
|
family: @registry.family_of(metric),
|
|
76
|
-
label:
|
|
77
|
-
description:
|
|
76
|
+
label: catalog_text('metrics', metric.name, :label),
|
|
77
|
+
description: catalog_text('metrics', metric.name, :description),
|
|
78
78
|
format: metric.format,
|
|
79
79
|
currency: (metric.money? ? @store.default_currency : nil),
|
|
80
80
|
derived: metric.derived?,
|
|
@@ -88,8 +88,8 @@ module Spree
|
|
|
88
88
|
def dimension_entry(dimension, metrics)
|
|
89
89
|
{
|
|
90
90
|
name: dimension.name,
|
|
91
|
-
label:
|
|
92
|
-
description:
|
|
91
|
+
label: catalog_text('dimensions', dimension.name, :label),
|
|
92
|
+
description: catalog_text('dimensions', dimension.name, :description),
|
|
93
93
|
type: dimension.type,
|
|
94
94
|
grains: dimension.grains,
|
|
95
95
|
lookup: dimension.lookup,
|
|
@@ -104,8 +104,8 @@ module Spree
|
|
|
104
104
|
}.compact
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
def
|
|
108
|
-
|
|
107
|
+
def catalog_text(group, name, facet)
|
|
108
|
+
I18n.t("spree.reporting.#{group}.#{name}.#{facet}", default: (facet == :label ? name.to_s.humanize : nil))
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
|
|
@@ -117,7 +117,7 @@ module Spree
|
|
|
117
117
|
# @param value [String]
|
|
118
118
|
# @return [String]
|
|
119
119
|
def self.value_label(dimension, value)
|
|
120
|
-
|
|
120
|
+
I18n.t("spree.reporting.values.#{dimension.name}.#{value}", default: value.to_s.humanize)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Registers core's staff password reset as an editable email for the length
|
|
2
|
+
# of an example, with a fixed sample, so specs in any engine can exercise the
|
|
3
|
+
# template editor without spree_emails' customer emails. Restores whatever was
|
|
4
|
+
# registered under the key before.
|
|
5
|
+
#
|
|
6
|
+
# RSpec.describe '...' do
|
|
7
|
+
# include_context 'with an editable email template'
|
|
8
|
+
# end
|
|
9
|
+
module Spree
|
|
10
|
+
module TestingSupport
|
|
11
|
+
class EmailTemplateSample
|
|
12
|
+
def self.record_type
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(store:, record_id: nil)
|
|
17
|
+
@store = store
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def currency
|
|
21
|
+
@store.default_currency
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def recent_records(limit: 5)
|
|
25
|
+
@store.orders.order(created_at: :desc).limit(limit).map do |order|
|
|
26
|
+
Spree::EmailTemplates::SampleRecord.new(id: order.prefixed_id, label: order.number, created_at: order.created_at)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def variables
|
|
31
|
+
{
|
|
32
|
+
user: { 'first_name' => 'Ann', 'email' => 'ann@example.com' },
|
|
33
|
+
order: { 'number' => 'R123456789', 'customer_name' => 'Ann Lee' },
|
|
34
|
+
reset_url: "#{@store.storefront_url}/reset-password?token=preview"
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
RSpec.shared_context 'with an editable email template' do
|
|
42
|
+
let(:editable_key) { 'spree/admin_user_mailer/password_reset_email' }
|
|
43
|
+
|
|
44
|
+
# Templates have per-language versions only in the store's languages.
|
|
45
|
+
before { allow_any_instance_of(Spree::Store).to receive(:supported_locales_list).and_return(%w[de en fr]) }
|
|
46
|
+
|
|
47
|
+
around do |example|
|
|
48
|
+
registry = Spree.editable_email_templates
|
|
49
|
+
previous = registry[editable_key]
|
|
50
|
+
previous_layout = registry['layouts/spree/base_mailer']
|
|
51
|
+
registry.register(editable_key, kind: :email, sample: 'Spree::TestingSupport::EmailTemplateSample')
|
|
52
|
+
registry.register('layouts/spree/base_mailer', kind: :layout)
|
|
53
|
+
|
|
54
|
+
begin
|
|
55
|
+
example.run
|
|
56
|
+
ensure
|
|
57
|
+
previous ? registry.register(previous.key, kind: previous.kind, sample: previous.sample) : registry.delete(editable_key)
|
|
58
|
+
previous_layout ? registry.register(previous_layout.key, kind: :layout) : registry.delete('layouts/spree/base_mailer')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module TestingSupport
|
|
3
|
+
module Emails
|
|
4
|
+
# The decoded text of every part of an email, HTML and plain text
|
|
5
|
+
# together. Rendered emails are quoted-printable on the wire, so the raw
|
|
6
|
+
# encoded body splits long lines and turns "=" into "=3D".
|
|
7
|
+
#
|
|
8
|
+
# @param message [Mail::Message, ActionMailer::MessageDelivery]
|
|
9
|
+
# @return [String]
|
|
10
|
+
def email_body(message)
|
|
11
|
+
message = message.message if message.respond_to?(:message) && !message.is_a?(Mail::Message)
|
|
12
|
+
parts = message.multipart? ? message.parts : [message]
|
|
13
|
+
|
|
14
|
+
parts.map(&:decoded).join("\n")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
RSpec.configure do |config|
|
|
21
|
+
config.include Spree::TestingSupport::Emails
|
|
22
|
+
end
|
|
@@ -31,7 +31,7 @@ FactoryBot.define do
|
|
|
31
31
|
country = Spree::Country.by_iso('US')
|
|
32
32
|
|
|
33
33
|
if Spree::DeliveryMethod.none?
|
|
34
|
-
create(:
|
|
34
|
+
create(:delivery_method).tap do |delivery_method|
|
|
35
35
|
delivery_method.calculator.preferred_amount = evaluator.shipping_cost
|
|
36
36
|
delivery_method.calculator.preferred_currency = cart.store.default_currency
|
|
37
37
|
delivery_method.calculator.save!
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :email_template, class: Spree::EmailTemplate do
|
|
3
|
+
store { Spree::Store.default || create(:store) }
|
|
4
|
+
key { 'spree/admin_user_mailer/password_reset_email' }
|
|
5
|
+
locale { 'any' }
|
|
6
|
+
subject { 'Reset {{ store.name }}' }
|
|
7
|
+
body { '<mj-section><mj-column><mj-text>Hello</mj-text></mj-column></mj-section>' }
|
|
8
|
+
base_subject { 'Reset {{ store.name }}' }
|
|
9
|
+
base_body { '<mj-section><mj-column><mj-text>Default</mj-text></mj-column></mj-section>' }
|
|
10
|
+
published_at { Time.current }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
factory :email_template_draft, class: Spree::EmailTemplateDraft do
|
|
14
|
+
store { Spree::Store.default || create(:store) }
|
|
15
|
+
key { 'spree/admin_user_mailer/password_reset_email' }
|
|
16
|
+
locale { 'any' }
|
|
17
|
+
subject { 'Reset {{ store.name }}' }
|
|
18
|
+
body { '<mj-section><mj-column><mj-text>Draft</mj-text></mj-column></mj-section>' }
|
|
19
|
+
base_subject { 'Reset {{ store.name }}' }
|
|
20
|
+
base_body { '<mj-section><mj-column><mj-text>Default</mj-text></mj-column></mj-section>' }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
factory :email_template_revision, class: Spree::EmailTemplateRevision do
|
|
24
|
+
email_template
|
|
25
|
+
subject { 'Reset {{ store.name }}' }
|
|
26
|
+
body { '<mj-section><mj-column><mj-text>Hello</mj-text></mj-column></mj-section>' }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -39,7 +39,7 @@ FactoryBot.define do
|
|
|
39
39
|
create(:line_item, order: order, price: evaluator.line_items_price)
|
|
40
40
|
order.line_items.reload # to ensure order.line_items is accessible after
|
|
41
41
|
|
|
42
|
-
order.update_column(:
|
|
42
|
+
order.update_column(:total_quantity, order.line_items.count)
|
|
43
43
|
order.reload
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -78,7 +78,7 @@ FactoryBot.define do
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
stock_location = order.line_items&.first&.variant&.stock_levels&.first&.stock_location || create(:stock_location)
|
|
81
|
-
create(:
|
|
81
|
+
create(:fulfillment, order: order, cost: evaluator.shipment_cost, stock_location: stock_location)
|
|
82
82
|
order.fulfillments.reload
|
|
83
83
|
|
|
84
84
|
order.recalculate_totals!
|