spree_core 6.0.0.beta4 → 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
|
@@ -91,9 +91,12 @@ module Spree
|
|
|
91
91
|
# @param record [Spree::Return, Spree::Claim, Spree::Exchange] what asked
|
|
92
92
|
# for the refund; it originates the rows and carries any failure
|
|
93
93
|
# @param refunder [Object, nil] whoever is issuing it
|
|
94
|
+
# @param tax_amount [BigDecimal] the tax inside +amount+, divided across
|
|
95
|
+
# the rows in proportion to what each refunds
|
|
94
96
|
# @return [Array<Spree::Refund>] the refunds written, in drain order
|
|
95
|
-
def refund_order_payments(order:, amount:, record:, refunder: nil)
|
|
97
|
+
def refund_order_payments(order:, amount:, record:, refunder: nil, tax_amount: 0)
|
|
96
98
|
remaining = amount.to_d
|
|
99
|
+
tax_remaining = tax_amount.to_d
|
|
97
100
|
refunds = []
|
|
98
101
|
shares = refundable_shares(order)
|
|
99
102
|
# With no payment to draw on the caller reports that instead.
|
|
@@ -109,13 +112,21 @@ module Spree
|
|
|
109
112
|
creditable = [refundable, remaining].min
|
|
110
113
|
next unless creditable.positive?
|
|
111
114
|
|
|
115
|
+
# The last row takes what is left, so the rows' tax adds up exactly.
|
|
116
|
+
row_tax = if creditable == remaining
|
|
117
|
+
tax_remaining
|
|
118
|
+
else
|
|
119
|
+
Spree::Money::Rounding.to_currency(tax_amount.to_d * creditable / amount.to_d, order.currency)
|
|
120
|
+
end
|
|
121
|
+
|
|
112
122
|
result = Spree.refund_create_workflow.call(
|
|
113
123
|
payment: payment,
|
|
114
124
|
amount: creditable,
|
|
115
125
|
order: order,
|
|
116
126
|
reason: reason,
|
|
117
127
|
refunder: refunder,
|
|
118
|
-
originator: record
|
|
128
|
+
originator: record,
|
|
129
|
+
tax_amount: row_tax
|
|
119
130
|
)
|
|
120
131
|
# `failure` raises, so this ends the drain — the caller's own error
|
|
121
132
|
# vocabulary decorates the 422.
|
|
@@ -123,6 +134,7 @@ module Spree
|
|
|
123
134
|
|
|
124
135
|
refunds << result.value
|
|
125
136
|
remaining -= creditable
|
|
137
|
+
tax_remaining -= row_tax
|
|
126
138
|
end
|
|
127
139
|
|
|
128
140
|
refunds
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Refunds
|
|
5
|
+
# The tax half of putting money back: asking the order's tax provider what
|
|
6
|
+
# tax goes back with it, and filing the credit once the money has moved.
|
|
7
|
+
#
|
|
8
|
+
# Shared by returns, claims and exchanges, which give back tax for the same
|
|
9
|
+
# reason they give back money (see Spree::PostSale::Taxation).
|
|
10
|
+
module TaxCredit
|
|
11
|
+
extend ActiveSupport::Concern
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
# Runs a provider calculation, refusing the step when the provider could
|
|
16
|
+
# not answer. Going on would refund tax nobody worked out — too little,
|
|
17
|
+
# and the customer is short; none, and the merchant finds out in a filing.
|
|
18
|
+
def with_tax_provider(record)
|
|
19
|
+
yield
|
|
20
|
+
rescue Spree::Tax::ProviderError => error
|
|
21
|
+
Rails.error.report(error, handled: true, context: tax_report_context(record), source: 'spree.post_sale')
|
|
22
|
+
|
|
23
|
+
# A refusal names its own problem; an outage is worth retrying, and its
|
|
24
|
+
# raw message names the endpoint rather than anything the caller can fix.
|
|
25
|
+
failure(record, error.is_a?(Spree::Tax::CalculationRefused) ? error.message.to_s : :tax_provider_unavailable)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# A record withdrawn before anything went back loses its rows — left in
|
|
29
|
+
# place they would count against what a later return of the same units
|
|
30
|
+
# may credit.
|
|
31
|
+
def clear_tax_credit(record)
|
|
32
|
+
record.clear_tax!
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Files the credit with the provider after the money has moved.
|
|
36
|
+
# Reported rather than raised: the refund has gone through and the record
|
|
37
|
+
# is already settled, so failing here would fail a refund that happened.
|
|
38
|
+
def file_tax_credit(record, lines, amount)
|
|
39
|
+
order = record.order
|
|
40
|
+
order.tax_provider.refund(order, lines, amount: amount, tax_date: order.completed_at)
|
|
41
|
+
rescue Spree::Tax::ProviderError => error
|
|
42
|
+
Rails.error.report(error, handled: true, context: tax_report_context(record), source: 'spree.post_sale')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Divides a refund across lines in proportion to what each is worth, to
|
|
46
|
+
# the cent, so the tax given back on each line follows the money on it.
|
|
47
|
+
#
|
|
48
|
+
# @param amount [BigDecimal]
|
|
49
|
+
# @param worth [Hash{Object => BigDecimal}] line => what it is worth
|
|
50
|
+
# @param currency [String]
|
|
51
|
+
# @return [Hash{Object => BigDecimal}]
|
|
52
|
+
def allocate_refund(amount, worth, currency)
|
|
53
|
+
weights = worth.values.map { |value| Spree::Money::Rounding.to_minor_units(value, currency) }
|
|
54
|
+
return worth.transform_values { 0.to_d } if weights.sum.zero?
|
|
55
|
+
|
|
56
|
+
shares = Spree::Adjusters::LargestRemainder.largest_remainder_shares(
|
|
57
|
+
Spree::Money::Rounding.to_minor_units(amount, currency), weights
|
|
58
|
+
)
|
|
59
|
+
worth.keys.zip(shares).to_h { |line, cents| [line, Spree::Money::Rounding.from_minor_units(cents, currency)] }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def tax_report_context(record)
|
|
63
|
+
{ record_type: record.class.name, record_id: record.id, order_id: record.order_id }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -10,38 +10,21 @@ module Spree
|
|
|
10
10
|
# 6.0-delivery-rate-provider.md; a store needing one before then can put
|
|
11
11
|
# the URL in the return's `metadata`.
|
|
12
12
|
class Approve < Spree::Workflow
|
|
13
|
+
include Spree::PostSale::Approval
|
|
14
|
+
|
|
13
15
|
hooks :validate, :after_approve
|
|
14
16
|
|
|
15
17
|
# @param return_record [Spree::Return]
|
|
16
18
|
# @param approver [Object, nil] who is approving it (see Spree.actor_classes)
|
|
17
19
|
def perform(return_record:, approver: nil)
|
|
18
20
|
super
|
|
19
|
-
|
|
20
|
-
step :ensure_approvable
|
|
21
|
-
run_hooks :validate
|
|
22
|
-
|
|
23
|
-
ApplicationRecord.transaction do
|
|
24
|
-
step :mark_approved
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
run_hooks :after_approve
|
|
28
|
-
return_record.publish_event('return.approved')
|
|
29
|
-
success(return_record.reload)
|
|
21
|
+
approve
|
|
30
22
|
end
|
|
31
23
|
|
|
32
24
|
private
|
|
33
25
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def mark_approved
|
|
39
|
-
return_record.update!(
|
|
40
|
-
status: 'approved',
|
|
41
|
-
approved_at: Time.current,
|
|
42
|
-
created_by: return_record.created_by || approver
|
|
43
|
-
)
|
|
44
|
-
end
|
|
26
|
+
def post_sale_record = return_record
|
|
27
|
+
def approvable_status = 'requested'
|
|
45
28
|
end
|
|
46
29
|
end
|
|
47
30
|
end
|
|
@@ -7,6 +7,8 @@ module Spree
|
|
|
7
7
|
# holding the customer's items, and the way out is a refund, not a
|
|
8
8
|
# cancellation.
|
|
9
9
|
class Cancel < Spree::Workflow
|
|
10
|
+
include Spree::PostSale::Cancellation
|
|
11
|
+
|
|
10
12
|
hooks :validate, :after_cancel
|
|
11
13
|
|
|
12
14
|
# @param return_record [Spree::Return]
|
|
@@ -14,39 +16,20 @@ module Spree
|
|
|
14
16
|
def perform(return_record:, reason: nil)
|
|
15
17
|
super
|
|
16
18
|
|
|
17
|
-
step :ensure_cancellable
|
|
18
|
-
run_hooks :validate
|
|
19
|
-
|
|
20
|
-
ApplicationRecord.transaction do
|
|
21
|
-
step :mark_canceled
|
|
22
|
-
end
|
|
23
|
-
|
|
24
19
|
# Carrier I/O, after the status is committed: a customer who abandons
|
|
25
20
|
# a return must not be left holding live prepaid postage the merchant
|
|
26
21
|
# is still paying for.
|
|
27
|
-
external_step :refund_prepaid_label
|
|
28
|
-
|
|
29
|
-
run_hooks :after_cancel
|
|
30
|
-
return_record.publish_event('return.canceled')
|
|
31
|
-
success(return_record.reload)
|
|
22
|
+
cancel { external_step :refund_prepaid_label }
|
|
32
23
|
end
|
|
33
24
|
|
|
34
25
|
private
|
|
35
26
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
failure(return_record, :not_cancellable)
|
|
40
|
-
end
|
|
27
|
+
def post_sale_record = return_record
|
|
28
|
+
def cancellable_statuses = %w[requested approved]
|
|
41
29
|
|
|
42
30
|
def refund_prepaid_label
|
|
43
31
|
Spree::Fulfillments::StandDownProvider.refund_labels(return_record)
|
|
44
32
|
end
|
|
45
|
-
|
|
46
|
-
def mark_canceled
|
|
47
|
-
memo = [return_record.memo, reason].compact_blank.join("\n")
|
|
48
|
-
return_record.update!(status: 'canceled', canceled_at: Time.current, memo: memo.presence)
|
|
49
|
-
end
|
|
50
33
|
end
|
|
51
34
|
end
|
|
52
35
|
end
|
|
@@ -8,6 +8,7 @@ module Spree
|
|
|
8
8
|
# commerce and deliberately does not live in core.
|
|
9
9
|
class Create < Spree::Workflow
|
|
10
10
|
include Spree::Returns::ReturnableQuantity
|
|
11
|
+
include Spree::Refunds::TaxCredit
|
|
11
12
|
|
|
12
13
|
hooks :validate, :after_create
|
|
13
14
|
|
|
@@ -32,8 +33,11 @@ module Spree
|
|
|
32
33
|
# is written.
|
|
33
34
|
run_hooks :validate
|
|
34
35
|
|
|
36
|
+
# The tax is worked out with the return rather than after it, so no
|
|
37
|
+
# return ever offers a refund whose tax nobody calculated.
|
|
35
38
|
ApplicationRecord.transaction do
|
|
36
39
|
step :build_return
|
|
40
|
+
step :calculate_tax
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
run_hooks :after_create
|
|
@@ -93,6 +97,10 @@ module Spree
|
|
|
93
97
|
failure(@return_record) unless @return_record.save
|
|
94
98
|
end
|
|
95
99
|
|
|
100
|
+
def calculate_tax
|
|
101
|
+
with_tax_provider(return_record) { return_record.calculate_tax! }
|
|
102
|
+
end
|
|
103
|
+
|
|
96
104
|
# Where goods come back to when nobody said. Goods return where they
|
|
97
105
|
# shipped from, which is right until a merchant runs a dedicated
|
|
98
106
|
# returns centre — so a location that has opted out of receiving them
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Returns
|
|
3
|
-
# Records what the warehouse actually received.
|
|
4
|
-
#
|
|
5
|
-
# Partial and damaged receipt is the normal case, not an edge case: the
|
|
6
|
-
# customer said three items were coming, two arrived, one of those is not
|
|
7
|
-
# resellable. Quantities and resellable flags therefore come from the
|
|
8
|
-
# caller rather than from the request, which is precisely what a state
|
|
9
|
-
# machine transition could not express.
|
|
3
|
+
# Records what the warehouse actually received (see Spree::PostSale::Receipt).
|
|
10
4
|
class Receive < Spree::Workflow
|
|
5
|
+
include Spree::PostSale::Receipt
|
|
6
|
+
|
|
11
7
|
hooks :validate, :before_restock, :after_receive
|
|
12
8
|
|
|
13
9
|
# @param return_record [Spree::Return]
|
|
@@ -16,79 +12,12 @@ module Spree
|
|
|
16
12
|
# @param received_by [Object, nil]
|
|
17
13
|
def perform(return_record:, items: nil, received_by: nil)
|
|
18
14
|
super
|
|
19
|
-
|
|
20
|
-
step :ensure_receivable
|
|
21
|
-
step :normalize_items
|
|
22
|
-
run_hooks :validate
|
|
23
|
-
|
|
24
|
-
ApplicationRecord.transaction do
|
|
25
|
-
step :record_receipt
|
|
26
|
-
run_hooks :before_restock
|
|
27
|
-
step :restock_resellable_items
|
|
28
|
-
step :mark_received
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
run_hooks :after_receive
|
|
32
|
-
return_record.publish_event('return.received')
|
|
33
|
-
success(return_record.reload)
|
|
15
|
+
receive
|
|
34
16
|
end
|
|
35
17
|
|
|
36
18
|
private
|
|
37
19
|
|
|
38
|
-
def
|
|
39
|
-
failure(return_record, :not_approved) unless return_record.approved?
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def normalize_items
|
|
43
|
-
@normalized_items =
|
|
44
|
-
if items.nil?
|
|
45
|
-
return_record.return_line_items.map do |line|
|
|
46
|
-
{ return_line_item: line, quantity: line.quantity, resellable: true }
|
|
47
|
-
end
|
|
48
|
-
else
|
|
49
|
-
items.map { |item| normalize_item(item) }
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
failure(return_record, :no_items_received) if @normalized_items.sum { |item| item[:quantity] }.zero?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def normalize_item(item)
|
|
56
|
-
line = item[:return_line_item]
|
|
57
|
-
quantity = item[:quantity].to_i
|
|
58
|
-
|
|
59
|
-
failure(return_record, :item_not_on_return) unless line&.return_id == return_record.id
|
|
60
|
-
failure(return_record, :invalid_quantity) if quantity.negative? || quantity > line.quantity
|
|
61
|
-
|
|
62
|
-
{ return_line_item: line, quantity: quantity, resellable: item.fetch(:resellable, true) }
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def record_receipt
|
|
66
|
-
@normalized_items.each do |item|
|
|
67
|
-
item[:return_line_item].update!(
|
|
68
|
-
received_quantity: item[:quantity],
|
|
69
|
-
resellable: item[:resellable]
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Only resellable goods go back into sellable stock — a damaged return
|
|
75
|
-
# is received but never restocked.
|
|
76
|
-
def restock_resellable_items
|
|
77
|
-
@normalized_items.each do |item|
|
|
78
|
-
next unless item[:resellable]
|
|
79
|
-
next unless item[:quantity].positive?
|
|
80
|
-
|
|
81
|
-
return_record.stock_location.restock(
|
|
82
|
-
item[:return_line_item].variant,
|
|
83
|
-
item[:quantity],
|
|
84
|
-
return_record
|
|
85
|
-
)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def mark_received
|
|
90
|
-
return_record.update!(status: 'received', received_at: Time.current)
|
|
91
|
-
end
|
|
20
|
+
def post_sale_record = return_record
|
|
92
21
|
end
|
|
93
22
|
end
|
|
94
23
|
end
|
|
@@ -9,6 +9,7 @@ module Spree
|
|
|
9
9
|
# a crash leaves a refunded return with no credit issued.
|
|
10
10
|
class Refund < Spree::Workflow
|
|
11
11
|
include Spree::Refunds::OrderPayments
|
|
12
|
+
include Spree::Refunds::TaxCredit
|
|
12
13
|
|
|
13
14
|
hooks :validate, :before_refund, :after_refund
|
|
14
15
|
|
|
@@ -30,7 +31,13 @@ module Spree
|
|
|
30
31
|
run_hooks :validate
|
|
31
32
|
run_hooks :before_refund
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
step :settle_tax unless @amount_to_refund.zero?
|
|
35
|
+
|
|
36
|
+
# Nothing is owed (a free gift sent back), so the return closes
|
|
37
|
+
# without a refund row, store credit or tax credit.
|
|
38
|
+
if @amount_to_refund.zero?
|
|
39
|
+
step :mark_refunded
|
|
40
|
+
elsif internal_refund?
|
|
34
41
|
ApplicationRecord.transaction do
|
|
35
42
|
step :issue_store_credit
|
|
36
43
|
step :mark_refunded
|
|
@@ -40,7 +47,7 @@ module Spree
|
|
|
40
47
|
step :mark_refunded
|
|
41
48
|
end
|
|
42
49
|
|
|
43
|
-
external_step :refund_tax
|
|
50
|
+
external_step :refund_tax unless @amount_to_refund.zero?
|
|
44
51
|
run_hooks :after_refund
|
|
45
52
|
return_record.publish_event('return.refunded')
|
|
46
53
|
success(return_record.reload)
|
|
@@ -62,8 +69,20 @@ module Spree
|
|
|
62
69
|
received = return_record.return_line_items.select { |line| line.refund_amount.positive? }
|
|
63
70
|
return if received.empty?
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
file_tax_credit(return_record, received, @amount_to_refund)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Rewrites the credit rows for what actually goes back — the units that
|
|
76
|
+
# arrived, and of those only the part of their worth being refunded —
|
|
77
|
+
# before any money moves, so the refund carries exactly the tax the rows
|
|
78
|
+
# record.
|
|
79
|
+
def settle_tax
|
|
80
|
+
lines = return_record.return_line_items.to_a
|
|
81
|
+
short = @amount_to_refund < return_record.refund_total
|
|
82
|
+
amounts = allocate_refund(@amount_to_refund, lines.index_with(&:refund_amount), return_record.currency) if short
|
|
83
|
+
|
|
84
|
+
with_tax_provider(return_record) { return_record.settle_tax!(amounts: amounts) }
|
|
85
|
+
@tax_amount = return_record.credited_tax_total
|
|
67
86
|
end
|
|
68
87
|
|
|
69
88
|
def internal_refund?
|
|
@@ -76,7 +95,7 @@ module Spree
|
|
|
76
95
|
# attribute, and ActiveModel raises when an error names one that
|
|
77
96
|
# does not exist on the record.
|
|
78
97
|
return_record.errors.add(:base, :invalid_refund_method,
|
|
79
|
-
message:
|
|
98
|
+
message: I18n.t('spree.errors.messages.invalid_refund_method'))
|
|
80
99
|
failure(return_record)
|
|
81
100
|
end
|
|
82
101
|
failure(return_record, :not_received) unless return_record.received?
|
|
@@ -85,12 +104,15 @@ module Spree
|
|
|
85
104
|
# Only what actually came back is refundable — a customer who sent two of
|
|
86
105
|
# three items gets two items' worth. One figure serves as both the
|
|
87
106
|
# default and the ceiling, so a caller naming an amount cannot ask for
|
|
88
|
-
# more than a caller who names none would get.
|
|
107
|
+
# more than a caller who names none would get. Zero is accepted only when
|
|
108
|
+
# zero is owed, so a return the customer is owed money on cannot be
|
|
109
|
+
# closed without paying them.
|
|
89
110
|
def resolve_amount
|
|
90
111
|
refundable = return_record.refundable_total.to_d
|
|
91
112
|
@amount_to_refund = amount ? amount.to_d : refundable
|
|
92
113
|
|
|
93
|
-
failure(return_record, :
|
|
114
|
+
failure(return_record, :refund_amount_negative) if @amount_to_refund.negative?
|
|
115
|
+
failure(return_record, :refund_amount_required) if @amount_to_refund.zero? && refundable.positive?
|
|
94
116
|
failure(return_record, :refund_exceeds_balance) if @amount_to_refund > refundable
|
|
95
117
|
end
|
|
96
118
|
|
|
@@ -111,7 +133,8 @@ module Spree
|
|
|
111
133
|
order: return_record.order,
|
|
112
134
|
amount: @amount_to_refund,
|
|
113
135
|
record: return_record,
|
|
114
|
-
refunder: refunder
|
|
136
|
+
refunder: refunder,
|
|
137
|
+
tax_amount: @tax_amount
|
|
115
138
|
)
|
|
116
139
|
|
|
117
140
|
failure(return_record, :no_refundable_payments) if @refunds.empty?
|
|
@@ -82,8 +82,7 @@ module Spree
|
|
|
82
82
|
return unless requirement.class.requires_file?
|
|
83
83
|
return if submission.file.attached?
|
|
84
84
|
|
|
85
|
-
errors.add(:file, :blank, message:
|
|
86
|
-
default: 'A file is required for this requirement'))
|
|
85
|
+
errors.add(:file, :blank, message: I18n.t('spree.seller_requirements.file_required'))
|
|
87
86
|
failure(submission, errors)
|
|
88
87
|
end
|
|
89
88
|
|
|
@@ -102,9 +102,17 @@ module Spree
|
|
|
102
102
|
gross = amounts.values.sum.to_d
|
|
103
103
|
return if gross <= 0
|
|
104
104
|
|
|
105
|
+
taxes = refund.refunded_line_taxes
|
|
106
|
+
|
|
105
107
|
# Nil from any line means the attribution cannot be trusted whole, so
|
|
106
108
|
# the blend answers for the refund rather than part of it.
|
|
107
|
-
earnings = amounts.map
|
|
109
|
+
earnings = amounts.map do |line_item_id, amount|
|
|
110
|
+
if taxes.key?(line_item_id)
|
|
111
|
+
line_goods_earning(line_item_id, amount.to_d, taxes[line_item_id].to_d)
|
|
112
|
+
else
|
|
113
|
+
line_earning(line_item_id, amount.to_d)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
108
116
|
return if earnings.any?(&:nil?)
|
|
109
117
|
|
|
110
118
|
quantize(earnings.sum * (refunded.to_d.abs / gross))
|
|
@@ -124,6 +132,9 @@ module Spree
|
|
|
124
132
|
# The commission comes from the row written against that line at
|
|
125
133
|
# placement, not from today's rate: rates change, a clamped fee is not the
|
|
126
134
|
# rate times the base, and a fixed rate is charged per unit.
|
|
135
|
+
#
|
|
136
|
+
# This is the reading for a refund that cannot say how much tax it gave
|
|
137
|
+
# back — a return or claim opened before they carried tax.
|
|
127
138
|
def line_earning(line_item_id, amount)
|
|
128
139
|
line_item = order_line_items[line_item_id]
|
|
129
140
|
paid = line_item&.amount.to_d
|
|
@@ -141,6 +152,22 @@ module Spree
|
|
|
141
152
|
earned
|
|
142
153
|
end
|
|
143
154
|
|
|
155
|
+
# The same for a refund that knows the tax it gave back on the line. The
|
|
156
|
+
# goods in it are measured against the line's worth before tax, after
|
|
157
|
+
# discounts, and carry their share of the commission; the tax goes back
|
|
158
|
+
# only when the seller collected it — when the marketplace remits it, the
|
|
159
|
+
# seller never had it to return.
|
|
160
|
+
def line_goods_earning(line_item_id, amount, tax)
|
|
161
|
+
line_item = order_line_items[line_item_id]
|
|
162
|
+
worth = line_item ? [line_item.discounted_amount.to_d, 0].max - line_item.included_tax_total.to_d : 0.to_d
|
|
163
|
+
return unless worth.positive?
|
|
164
|
+
|
|
165
|
+
goods = amount - tax
|
|
166
|
+
earned = goods - (commission_totals[line_item_id].to_d * goods / worth)
|
|
167
|
+
earned += tax unless order.seller&.tax_remittance == 'platform'
|
|
168
|
+
earned
|
|
169
|
+
end
|
|
170
|
+
|
|
144
171
|
def order_line_items
|
|
145
172
|
@order_line_items ||= order.line_items.index_by(&:id)
|
|
146
173
|
end
|
|
@@ -51,15 +51,15 @@ module Spree
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def ensure_purchasable
|
|
54
|
-
failure(owner,
|
|
55
|
-
failure(owner,
|
|
54
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.provider_has_no_labels')) unless provider.class.generates_labels?
|
|
55
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.already_purchased')) if owner.shipping_labels.active.exists?
|
|
56
56
|
|
|
57
57
|
case owner
|
|
58
58
|
when Spree::Fulfillment
|
|
59
|
-
failure(owner,
|
|
60
|
-
failure(owner,
|
|
59
|
+
failure(owner, I18n.t('spree.fulfillments.errors.cannot_purchase_label')) unless owner.unfulfilled?
|
|
60
|
+
failure(owner, I18n.t('spree.fulfillments.errors.order_draft')) if owner.order&.draft?
|
|
61
61
|
when Spree::Return
|
|
62
|
-
failure(owner,
|
|
62
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.return_closed')) if owner.received? || owner.refunded? || owner.canceled?
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
@@ -71,7 +71,7 @@ module Spree
|
|
|
71
71
|
# re-read inside it.
|
|
72
72
|
def claim_purchase
|
|
73
73
|
owner.with_lock do
|
|
74
|
-
failure(owner,
|
|
74
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.already_purchased')) if owner.shipping_labels.active.exists?
|
|
75
75
|
|
|
76
76
|
@shipping_label = owner.shipping_labels.create!(
|
|
77
77
|
store: owner.store,
|
|
@@ -81,7 +81,7 @@ module Spree
|
|
|
81
81
|
)
|
|
82
82
|
end
|
|
83
83
|
rescue ActiveRecord::RecordNotUnique
|
|
84
|
-
failure(owner,
|
|
84
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.already_purchased'))
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
# Undoes the claim when the carrier refuses, so a failed purchase does
|
|
@@ -105,7 +105,7 @@ module Spree
|
|
|
105
105
|
|
|
106
106
|
return if @purchase.is_a?(Spree::LabelPurchase) && @purchase.valid?
|
|
107
107
|
|
|
108
|
-
failure(owner,
|
|
108
|
+
failure(owner, I18n.t('spree.fulfillments.errors.label_purchase_failed'))
|
|
109
109
|
rescue Spree::Core::LabelPurchaseRefused => e
|
|
110
110
|
failure(owner, e.message)
|
|
111
111
|
end
|
|
@@ -41,15 +41,15 @@ module Spree
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def ensure_recordable
|
|
44
|
-
failure(owner,
|
|
45
|
-
failure(owner,
|
|
46
|
-
failure(owner,
|
|
44
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.already_purchased')) if owner.shipping_labels.active.exists?
|
|
45
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.tracking_number_required')) if tracking_number.blank?
|
|
46
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.file_required')) if file.blank?
|
|
47
47
|
|
|
48
48
|
case owner
|
|
49
49
|
when Spree::Fulfillment
|
|
50
|
-
failure(owner,
|
|
50
|
+
failure(owner, I18n.t('spree.fulfillments.errors.cannot_purchase_label')) if owner.canceled? || owner.delivered?
|
|
51
51
|
when Spree::Return
|
|
52
|
-
failure(owner,
|
|
52
|
+
failure(owner, I18n.t('spree.shipping_labels.errors.return_closed')) if owner.received? || owner.refunded? || owner.canceled?
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -42,7 +42,7 @@ module Spree
|
|
|
42
42
|
return if shipping_label.refundable?
|
|
43
43
|
|
|
44
44
|
reason = shipping_label.uploaded? ? 'uploaded_not_refundable' : 'not_refundable'
|
|
45
|
-
failure(shipping_label,
|
|
45
|
+
failure(shipping_label, I18n.t("spree.shipping_labels.errors.#{reason}"))
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# A provider that knows why the carrier said no raises with its words;
|
|
@@ -52,7 +52,7 @@ module Spree
|
|
|
52
52
|
|
|
53
53
|
return if %w[refunded refund_requested].include?(@outcome)
|
|
54
54
|
|
|
55
|
-
failure(shipping_label,
|
|
55
|
+
failure(shipping_label, I18n.t('spree.shipping_labels.errors.refund_failed'))
|
|
56
56
|
rescue Spree::Core::LabelRefundRefused => e
|
|
57
57
|
failure(shipping_label, e.message)
|
|
58
58
|
end
|
|
@@ -61,19 +61,19 @@ module Spree
|
|
|
61
61
|
|
|
62
62
|
return if @normalized_items.any?
|
|
63
63
|
|
|
64
|
-
failure(receivable,
|
|
64
|
+
failure(receivable, I18n.t("spree.#{error_scope}.errors.no_items_received"))
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
# A line the payload names but counts nothing on is left out rather than
|
|
68
68
|
# refused: a receive screen submits every row, most of them untouched.
|
|
69
69
|
def normalize_item(item)
|
|
70
70
|
line = item[:item]
|
|
71
|
-
failure(receivable,
|
|
71
|
+
failure(receivable, I18n.t("spree.#{error_scope}.errors.item_not_on_document")) unless line_belongs?(line)
|
|
72
72
|
|
|
73
73
|
accepted = item[:quantity_accepted].to_i
|
|
74
74
|
rejected = item[:quantity_rejected].to_i
|
|
75
75
|
if accepted.negative? || rejected.negative?
|
|
76
|
-
failure(receivable,
|
|
76
|
+
failure(receivable, I18n.t("spree.#{error_scope}.errors.invalid_receipt_quantity", variant: line.variant_name))
|
|
77
77
|
end
|
|
78
78
|
return nil if accepted.zero? && rejected.zero?
|
|
79
79
|
|
|
@@ -93,7 +93,7 @@ module Spree
|
|
|
93
93
|
repeated = lines.group_by(&:id).values.find { |group| group.length > 1 }
|
|
94
94
|
return if repeated.nil?
|
|
95
95
|
|
|
96
|
-
failure(receivable,
|
|
96
|
+
failure(receivable, I18n.t("spree.#{error_scope}.errors.repeated_item", variant: repeated.first.variant_name))
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def build_receipt
|
|
@@ -49,11 +49,11 @@ module Spree
|
|
|
49
49
|
private
|
|
50
50
|
|
|
51
51
|
def ensure_cancelable
|
|
52
|
-
failure(stock_transfer,
|
|
52
|
+
failure(stock_transfer, I18n.t('spree.stock_transfer.errors.already_closed')) if stock_transfer.closed?
|
|
53
53
|
return unless stock_transfer.in_flight?
|
|
54
54
|
return if IN_TRANSIT_RESOLUTIONS.include?(on_in_transit)
|
|
55
55
|
|
|
56
|
-
failure(stock_transfer,
|
|
56
|
+
failure(stock_transfer, I18n.t('spree.stock_transfer.errors.in_transit_resolution_required'))
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Whether the units come back or are written off, the destination is no
|