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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# A rule to apply to an order greater than (or greater than or equal to)
|
|
2
|
-
# a specific amount
|
|
2
|
+
# a specific amount. Free gifts do not count toward it.
|
|
3
3
|
module Spree
|
|
4
4
|
class Promotion
|
|
5
5
|
module Rules
|
|
@@ -12,12 +12,8 @@ module Spree
|
|
|
12
12
|
OPERATORS_MIN = ['gt', 'gte']
|
|
13
13
|
OPERATORS_MAX = ['lt', 'lte']
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def eligible?(order, _options = {})
|
|
20
|
-
item_total = order.item_total
|
|
15
|
+
def eligible?(order, options = {})
|
|
16
|
+
item_total = order.item_total - order.gift_amount(promotion: promotion, own_gift_only: options.fetch(:own_gift_only, false))
|
|
21
17
|
|
|
22
18
|
lower_limit_condition = item_total.send(preferred_operator_min == 'gte' ? :>= : :>, BigDecimal(preferred_amount_min.to_s))
|
|
23
19
|
|
|
@@ -43,7 +39,7 @@ module Spree
|
|
|
43
39
|
if preferred_amount_max.present?
|
|
44
40
|
Spree::Money.new(preferred_amount_max, currency: order.currency).to_s
|
|
45
41
|
else
|
|
46
|
-
|
|
42
|
+
I18n.t('spree.no_maximum')
|
|
47
43
|
end
|
|
48
44
|
end
|
|
49
45
|
|
|
@@ -14,10 +14,6 @@ module Spree
|
|
|
14
14
|
scope: ->(rule) { rule.promotion.store.markets }
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
def applicable?(promotable)
|
|
18
|
-
promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
17
|
def markets
|
|
22
18
|
return Spree::Market.none if preferred_market_ids.blank?
|
|
23
19
|
|
|
@@ -2,10 +2,6 @@ module Spree
|
|
|
2
2
|
class Promotion
|
|
3
3
|
module Rules
|
|
4
4
|
class OneUsePerUser < Spree::PromotionRule
|
|
5
|
-
def applicable?(promotable)
|
|
6
|
-
promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
5
|
def eligible?(order, _options = {})
|
|
10
6
|
if order.customer.present?
|
|
11
7
|
if promotion.used_by?(order.customer, [order])
|
|
@@ -49,10 +49,6 @@ module Spree
|
|
|
49
49
|
@eligible_product_ids ||= product_promotion_rules.pluck(:product_id)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def applicable?(promotable)
|
|
53
|
-
promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
52
|
def eligible?(order, _options = {})
|
|
57
53
|
return true if eligible_product_ids.empty?
|
|
58
54
|
|
|
@@ -8,10 +8,6 @@ module Spree
|
|
|
8
8
|
'customer_logged_in'
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def applicable?(promotable)
|
|
12
|
-
promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
def eligible?(order, _options = {})
|
|
16
12
|
unless order.customer.present?
|
|
17
13
|
eligibility_errors.add(:base, eligibility_error_message(:no_user_specified))
|
|
@@ -106,6 +106,28 @@ module Spree
|
|
|
106
106
|
where('spree_promotions.expires_at IS NULL OR spree_promotions.expires_at > ?', Time.current)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# The promotion a purchase's PERSISTED coupon code names, which keeps it in
|
|
110
|
+
# candidacy even before it ever applied — the discount activates on the
|
|
111
|
+
# exact recalculation where the cart first qualifies, and deactivates the
|
|
112
|
+
# same way (Shopify-parity for cart-level discount codes). In-memory
|
|
113
|
+
# assignments deliberately don't participate: unsaved codes belong to
|
|
114
|
+
# the explicit PromotionHandler::Coupon path. A batch code counts only
|
|
115
|
+
# while the purchase holds it (see #eligible?).
|
|
116
|
+
#
|
|
117
|
+
# @param purchase [Spree::Cart, Spree::Order]
|
|
118
|
+
# @return [Array<Spree::Promotion>] empty, or the one promotion
|
|
119
|
+
def self.held_by_saved_coupon_code(purchase)
|
|
120
|
+
return [] unless purchase.class.respond_to?(:column_names) && purchase.class.column_names.include?('coupon_code')
|
|
121
|
+
|
|
122
|
+
code = purchase.read_attribute(:coupon_code)
|
|
123
|
+
return [] if code.blank?
|
|
124
|
+
|
|
125
|
+
promotion = purchase.store.promotions.active.with_coupon_code(code)
|
|
126
|
+
return [] if promotion.nil? || promotion.usage_limit_exceeded?(purchase)
|
|
127
|
+
|
|
128
|
+
[promotion]
|
|
129
|
+
end
|
|
130
|
+
|
|
109
131
|
def self.order_activatable?(promotable)
|
|
110
132
|
# Carts have no cancellation concept — only placed orders can be canceled.
|
|
111
133
|
promotable && !promotable.completed? && !(promotable.is_a?(Spree::Order) && promotable.canceled?)
|
|
@@ -202,7 +224,7 @@ module Spree
|
|
|
202
224
|
|
|
203
225
|
# called anytime order.recalculate_totals! happens
|
|
204
226
|
def eligible?(promotable, options = {})
|
|
205
|
-
return false if expired? || usage_limit_exceeded?(promotable) || blacklisted?(promotable)
|
|
227
|
+
return false if expired? || usage_limit_exceeded?(promotable) || blacklisted?(promotable) || !code_held_by?(promotable)
|
|
206
228
|
|
|
207
229
|
!!eligible_rules(promotable, options)
|
|
208
230
|
end
|
|
@@ -240,7 +262,7 @@ module Spree
|
|
|
240
262
|
end
|
|
241
263
|
|
|
242
264
|
def products
|
|
243
|
-
rules.where(type:
|
|
265
|
+
rules.where(type: 'Spree::Promotion::Rules::Product').map(&:products).flatten.uniq
|
|
244
266
|
end
|
|
245
267
|
|
|
246
268
|
def usage_limit_exceeded?(promotable)
|
|
@@ -278,9 +300,9 @@ module Spree
|
|
|
278
300
|
checkouts_credited(credits)
|
|
279
301
|
end
|
|
280
302
|
|
|
281
|
-
def line_item_actionable?(order, line_item)
|
|
282
|
-
if eligible?
|
|
283
|
-
rules = eligible_rules(order)
|
|
303
|
+
def line_item_actionable?(order, line_item, options = {})
|
|
304
|
+
if eligible?(order, options)
|
|
305
|
+
rules = eligible_rules(order, options)
|
|
284
306
|
if rules.blank?
|
|
285
307
|
true
|
|
286
308
|
else
|
|
@@ -325,6 +347,13 @@ module Spree
|
|
|
325
347
|
|
|
326
348
|
private
|
|
327
349
|
|
|
350
|
+
# A batch code discounts only the cart or order holding it, which is what
|
|
351
|
+
# keeps each generated code to a single order. A cart's line item has no
|
|
352
|
+
# order, and the adjuster has asked with the cart before pricing its lines.
|
|
353
|
+
def code_held_by?(order)
|
|
354
|
+
!multi_codes? || order.nil? || coupon_codes.held_by(order).exists?
|
|
355
|
+
end
|
|
356
|
+
|
|
328
357
|
# Counts distinct checkouts behind a set of discount rows.
|
|
329
358
|
#
|
|
330
359
|
# An order placed on its own is one checkout; the several orders a split
|
|
@@ -349,7 +378,7 @@ module Spree
|
|
|
349
378
|
def not_used?
|
|
350
379
|
return true if orders.empty?
|
|
351
380
|
|
|
352
|
-
errors.add(:base, :promotion_already_used, message:
|
|
381
|
+
errors.add(:base, :promotion_already_used, message: I18n.t('spree.promotion_already_used'))
|
|
353
382
|
throw(:abort)
|
|
354
383
|
end
|
|
355
384
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
# PromotionActions perform the necessary tasks when a promotion is activated by an event and determined to be eligible.
|
|
3
3
|
module Spree
|
|
4
4
|
class PromotionAction < Spree.base_class
|
|
5
|
+
include Spree::TypeLabels
|
|
6
|
+
|
|
7
|
+
self.type_labels_scope = 'spree.promotion_action_types'
|
|
8
|
+
|
|
5
9
|
has_prefix_id :pact
|
|
6
10
|
|
|
7
11
|
registers_subclasses_via { Spree.promotions.actions }
|
|
@@ -66,25 +70,14 @@ module Spree
|
|
|
66
70
|
when :line_item
|
|
67
71
|
%w[Spree::Promotion::Actions::CreateItemAdjustments Spree::Promotion::Actions::CreateLineItems]
|
|
68
72
|
when :fulfillment
|
|
69
|
-
%w[Spree::Promotion::Actions::FreeShipping
|
|
73
|
+
%w[Spree::Promotion::Actions::FreeShipping]
|
|
70
74
|
when :order
|
|
71
|
-
%w[Spree::Promotion::Actions::CreateAdjustment
|
|
75
|
+
%w[Spree::Promotion::Actions::CreateAdjustment]
|
|
72
76
|
else
|
|
73
77
|
[]
|
|
74
78
|
end
|
|
75
79
|
end
|
|
76
80
|
|
|
77
|
-
def self.human_name
|
|
78
|
-
Spree.t("promotion_action_types.#{api_type}.name", default: api_type.titleize)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def self.human_description
|
|
82
|
-
Spree.t("promotion_action_types.#{api_type}.description", default: '')
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def human_name = self.class.human_name
|
|
86
|
-
def human_description = self.class.human_description
|
|
87
|
-
|
|
88
81
|
# Returns the key of the promotion action
|
|
89
82
|
#
|
|
90
83
|
# @return [String] eg. free_shipping
|
|
@@ -109,7 +102,7 @@ module Spree
|
|
|
109
102
|
end
|
|
110
103
|
|
|
111
104
|
def label
|
|
112
|
-
|
|
105
|
+
I18n.t('spree.promotion_label', name: promotion.name)
|
|
113
106
|
end
|
|
114
107
|
end
|
|
115
108
|
end
|
|
@@ -4,6 +4,6 @@ module Spree
|
|
|
4
4
|
belongs_to :variant, class_name: 'Spree::Variant'
|
|
5
5
|
|
|
6
6
|
validates :quantity, presence: true
|
|
7
|
-
validates :quantity, numericality: { only_integer: true, message:
|
|
7
|
+
validates :quantity, numericality: { only_integer: true, message: I18n.t('spree.validation.must_be_int') }
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -39,7 +39,7 @@ module Spree
|
|
|
39
39
|
@status_code = :gift_card_not_applied
|
|
40
40
|
# The rejection message is what the hook author wrote for the
|
|
41
41
|
# shopper; fall back to the generic string if it says nothing.
|
|
42
|
-
@error = result.error.to_s.presence ||
|
|
42
|
+
@error = result.error.to_s.presence || I18n.t('spree.gift_card_not_applied')
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -57,9 +57,25 @@ module Spree
|
|
|
57
57
|
else
|
|
58
58
|
set_error_code :coupon_code_not_found
|
|
59
59
|
end
|
|
60
|
+
release_stale_coupon_codes
|
|
60
61
|
self
|
|
61
62
|
end
|
|
62
63
|
|
|
64
|
+
# Gives back every batch code this cart or order holds but no longer
|
|
65
|
+
# shows: anything other than the code it entered while that code waits
|
|
66
|
+
# to qualify, or the codes of promotions attached to it. Without this a
|
|
67
|
+
# replaced or dropped code stays held, and is spent with the order.
|
|
68
|
+
#
|
|
69
|
+
# @return [void]
|
|
70
|
+
def release_stale_coupon_codes
|
|
71
|
+
return unless order.persisted?
|
|
72
|
+
|
|
73
|
+
stale = Spree::CouponCode.unused.held_by(order).where.not(promotion_id: order.promotions.select(:id))
|
|
74
|
+
pending = order.coupon_code.to_s.downcase.presence if status_code == :coupon_code_not_eligible
|
|
75
|
+
stale = stale.where.not(code: pending) if pending
|
|
76
|
+
stale.find_each(&:remove_from_order)
|
|
77
|
+
end
|
|
78
|
+
|
|
63
79
|
def remove(coupon_code)
|
|
64
80
|
if gift_cards_enabled? && order.gift_card
|
|
65
81
|
result = order.remove_gift_card
|
|
@@ -75,19 +91,13 @@ module Spree
|
|
|
75
91
|
|
|
76
92
|
promotion = order.promotions.with_coupon_code(coupon_code)
|
|
77
93
|
if promotion.present?
|
|
78
|
-
# Order promotion has to be destroyed before line item removing
|
|
79
|
-
order.promotions.delete(promotion)
|
|
80
94
|
clear_persisted_coupon_code(coupon_code)
|
|
95
|
+
detach_promotion(promotion)
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
remove_promotion_adjustments(promotion)
|
|
89
|
-
remove_promotion_line_items(promotion)
|
|
90
|
-
order.recalculate_totals!
|
|
97
|
+
set_success_code :adjustments_deleted
|
|
98
|
+
elsif clear_persisted_coupon_code(coupon_code)
|
|
99
|
+
# Saved before the cart qualified, so no promotion is attached yet.
|
|
100
|
+
Spree::CouponCode.held_by(order).find_by(code: coupon_code.to_s.strip.downcase)&.remove_from_order
|
|
91
101
|
|
|
92
102
|
set_success_code :adjustments_deleted
|
|
93
103
|
else
|
|
@@ -96,14 +106,26 @@ module Spree
|
|
|
96
106
|
self
|
|
97
107
|
end
|
|
98
108
|
|
|
109
|
+
# Gives the code up to another cart or order that has taken it. Unlike
|
|
110
|
+
# #remove, the entered code stays on this one, so the shopper can be
|
|
111
|
+
# told it went (Spree::Cart#remove_unavailable_coupon_code!).
|
|
112
|
+
#
|
|
113
|
+
# @param coupon_code [String]
|
|
114
|
+
# @return [self]
|
|
115
|
+
def release(coupon_code)
|
|
116
|
+
promotion = order.promotions.with_coupon_code(coupon_code)
|
|
117
|
+
detach_promotion(promotion) if promotion.present?
|
|
118
|
+
self
|
|
119
|
+
end
|
|
120
|
+
|
|
99
121
|
def set_success_code(code)
|
|
100
122
|
@status_code = code
|
|
101
|
-
@success =
|
|
123
|
+
@success = I18n.t("spree.#{code}")
|
|
102
124
|
end
|
|
103
125
|
|
|
104
126
|
def set_error_code(code, locale_options = {})
|
|
105
127
|
@status_code = code
|
|
106
|
-
@error =
|
|
128
|
+
@error = I18n.t("spree.#{code}", **locale_options)
|
|
107
129
|
end
|
|
108
130
|
|
|
109
131
|
# Returns the promotion for the order
|
|
@@ -143,6 +165,21 @@ module Spree
|
|
|
143
165
|
order.update_column(:coupon_code, nil)
|
|
144
166
|
end
|
|
145
167
|
|
|
168
|
+
def detach_promotion(promotion)
|
|
169
|
+
# Order promotion has to be destroyed before line item removing
|
|
170
|
+
order.promotions.delete(promotion)
|
|
171
|
+
|
|
172
|
+
if promotion.multi_codes?
|
|
173
|
+
promotion.coupon_codes.held_by(order).first&.remove_from_order
|
|
174
|
+
else
|
|
175
|
+
promotion.touch
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
remove_promotion_adjustments(promotion)
|
|
179
|
+
remove_promotion_line_items(promotion)
|
|
180
|
+
order.recalculate_totals!
|
|
181
|
+
end
|
|
182
|
+
|
|
146
183
|
def remove_promotion_adjustments(promotion)
|
|
147
184
|
order.discounts.where(promotion_action_id: promotion.actions.pluck(:id)).destroy_all
|
|
148
185
|
end
|
|
@@ -165,6 +202,11 @@ module Spree
|
|
|
165
202
|
return set_error_code :coupon_code_used if promotion.coupon_codes.used.where(code: order.coupon_code).exists?
|
|
166
203
|
return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded?(order)
|
|
167
204
|
|
|
205
|
+
# Claimed before anything prices the code, since the adjuster only
|
|
206
|
+
# discounts the holder, and a cart that qualifies later activates the
|
|
207
|
+
# code on recalculation without coming back here.
|
|
208
|
+
return set_error_code :coupon_code_used unless claim_coupon_code(order.coupon_code.downcase)
|
|
209
|
+
|
|
168
210
|
unless promotion.eligible?(order, options)
|
|
169
211
|
# Rules that explain themselves keep their message, but the status
|
|
170
212
|
# code is always :coupon_code_not_eligible so callers can park the
|
|
@@ -216,11 +258,6 @@ module Spree
|
|
|
216
258
|
end
|
|
217
259
|
|
|
218
260
|
if discount || created_line_items
|
|
219
|
-
if discount && !claim_coupon_code(discount, coupon_code)
|
|
220
|
-
release_coupon_code(order, coupon_code)
|
|
221
|
-
return set_error_code :coupon_code_used
|
|
222
|
-
end
|
|
223
|
-
|
|
224
261
|
order.recalculate_totals!
|
|
225
262
|
set_success_code :coupon_code_applied
|
|
226
263
|
elsif order.promotions.with_coupon_code(order.coupon_code)
|
|
@@ -249,8 +286,8 @@ module Spree
|
|
|
249
286
|
# them in, so the two serialize instead of deadlocking.
|
|
250
287
|
#
|
|
251
288
|
# @return [Boolean] false when the code can no longer be claimed
|
|
252
|
-
def claim_coupon_code(
|
|
253
|
-
record =
|
|
289
|
+
def claim_coupon_code(coupon_code)
|
|
290
|
+
record = promotion.coupon_codes.find_by(code: coupon_code)
|
|
254
291
|
return true if record.nil?
|
|
255
292
|
|
|
256
293
|
holder = record.holder
|
|
@@ -261,16 +298,11 @@ module Spree
|
|
|
261
298
|
record.lock!
|
|
262
299
|
next false if record.used? || !same_holder?(record.holder, holder) || !releasable?(holder)
|
|
263
300
|
|
|
264
|
-
|
|
301
|
+
self.class.new(holder).release(coupon_code) if holder
|
|
265
302
|
record.apply_order!(order)
|
|
266
303
|
end
|
|
267
304
|
end
|
|
268
305
|
|
|
269
|
-
# Gift cards are turned off so a holder paying with one keeps it.
|
|
270
|
-
def release_coupon_code(holder, coupon_code)
|
|
271
|
-
self.class.new(holder, enable_gift_cards: false).remove(coupon_code)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
306
|
def releasable?(holder)
|
|
275
307
|
return true if holder.nil?
|
|
276
308
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Base class for all promotion rules
|
|
2
2
|
module Spree
|
|
3
3
|
class PromotionRule < Spree.base_class
|
|
4
|
+
include Spree::TypeLabels
|
|
5
|
+
|
|
6
|
+
self.type_labels_scope = 'spree.promotion_rule_types'
|
|
7
|
+
|
|
4
8
|
has_prefix_id :prorule
|
|
5
9
|
|
|
6
10
|
registers_subclasses_via { Spree.promotions.rules }
|
|
@@ -17,8 +21,8 @@ module Spree
|
|
|
17
21
|
all.select { |rule| rule.applicable?(promotable) }
|
|
18
22
|
end
|
|
19
23
|
|
|
20
|
-
def applicable?(
|
|
21
|
-
|
|
24
|
+
def applicable?(promotable)
|
|
25
|
+
promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
def eligible?(_promotable, _options = {})
|
|
@@ -35,17 +39,6 @@ module Spree
|
|
|
35
39
|
@eligibility_errors ||= ActiveModel::Errors.new(self)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
def self.human_name
|
|
39
|
-
Spree.t("promotion_rule_types.#{api_type}.name", default: api_type.titleize)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def self.human_description
|
|
43
|
-
Spree.t("promotion_rule_types.#{api_type}.description", default: '')
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def human_name = self.class.human_name
|
|
47
|
-
def human_description = self.class.human_description
|
|
48
|
-
|
|
49
42
|
# Returns the key of the promotion rule
|
|
50
43
|
#
|
|
51
44
|
# @return [String] eg. currency
|
|
@@ -70,7 +63,7 @@ module Spree
|
|
|
70
63
|
end
|
|
71
64
|
|
|
72
65
|
def eligibility_error_message(key, options = {})
|
|
73
|
-
|
|
66
|
+
I18n.t("spree.eligibility_errors.messages.#{key}", **options)
|
|
74
67
|
end
|
|
75
68
|
end
|
|
76
69
|
end
|
|
@@ -26,6 +26,7 @@ module Spree
|
|
|
26
26
|
include Spree::Metadata
|
|
27
27
|
|
|
28
28
|
publishes_lifecycle_events
|
|
29
|
+
publishes_events :canceled, :draft, :ordered, :partially_received, :over_received, :received
|
|
29
30
|
|
|
30
31
|
has_status :draft, :ordered, :partially_received, :received, :over_received, :canceled,
|
|
31
32
|
default: :draft
|
|
@@ -83,7 +83,7 @@ module Spree
|
|
|
83
83
|
def violation_message(name, quantity)
|
|
84
84
|
return nil if satisfied_by?(quantity)
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
I18n.t('spree.cart_line_item.quantity_rule_violated',
|
|
87
87
|
li_name: name, quantities: nearest_valid(quantity).to_sentence)
|
|
88
88
|
end
|
|
89
89
|
|
data/app/models/spree/refund.rb
CHANGED
|
@@ -33,6 +33,20 @@ module Spree
|
|
|
33
33
|
validate :amount_is_less_than_or_equal_to_allowed_amount, on: :create, if: :amount
|
|
34
34
|
validate :order_is_covered_by_payment, on: :create
|
|
35
35
|
|
|
36
|
+
# Refunds for goods that came back, which settle what the customer owed
|
|
37
|
+
# for them rather than leaving it owed again.
|
|
38
|
+
scope :for_returned_items, -> { where(originator_type: returned_item_originators) }
|
|
39
|
+
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def self.returned_item_originators
|
|
42
|
+
[Spree::Return, Spree::Claim, Spree::Exchange].map(&:polymorphic_name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Boolean]
|
|
46
|
+
def for_returned_items?
|
|
47
|
+
self.class.returned_item_originators.include?(originator_type)
|
|
48
|
+
end
|
|
49
|
+
|
|
36
50
|
attr_reader :response
|
|
37
51
|
|
|
38
52
|
delegate :currency, to: :payment
|
|
@@ -76,6 +90,17 @@ module Spree
|
|
|
76
90
|
originator.refunded_line_amounts
|
|
77
91
|
end
|
|
78
92
|
|
|
93
|
+
# The tax inside {#refunded_line_amounts}, for the lines whose tax is known.
|
|
94
|
+
# A line opened before returns and claims carried tax is absent: its amount
|
|
95
|
+
# says nothing about how much of it was tax.
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash{Integer => BigDecimal}] line item id => tax
|
|
98
|
+
def refunded_line_taxes
|
|
99
|
+
return {} unless originator.respond_to?(:refunded_line_taxes)
|
|
100
|
+
|
|
101
|
+
originator.refunded_line_taxes
|
|
102
|
+
end
|
|
103
|
+
|
|
79
104
|
# Returns true if the refund is editable.
|
|
80
105
|
#
|
|
81
106
|
# Read through the refund's own order: a payment shared by a split checkout
|
|
@@ -155,15 +180,15 @@ module Spree
|
|
|
155
180
|
if response.success?
|
|
156
181
|
track_order_as_refunded(refund_total_in_cents)
|
|
157
182
|
else
|
|
158
|
-
Rails.logger.error(
|
|
183
|
+
Rails.logger.error(I18n.t('spree.gateway_error') + " #{response.to_yaml}")
|
|
159
184
|
text = response.params['message'] || response.params['response_reason_text'] || response.message
|
|
160
185
|
raise Core::GatewayError, text
|
|
161
186
|
end
|
|
162
187
|
|
|
163
188
|
response
|
|
164
189
|
rescue Spree::PaymentConnectionError => e
|
|
165
|
-
Rails.logger.error(
|
|
166
|
-
raise Core::GatewayError,
|
|
190
|
+
Rails.logger.error(I18n.t('spree.gateway_error') + " #{e.inspect}")
|
|
191
|
+
raise Core::GatewayError, I18n.t('spree.unable_to_connect_to_gateway')
|
|
167
192
|
end
|
|
168
193
|
|
|
169
194
|
def calculate_refund_amount(credit_cents)
|
data/app/models/spree/return.rb
CHANGED
|
@@ -20,13 +20,14 @@ module Spree
|
|
|
20
20
|
include Spree::HasStatus
|
|
21
21
|
include Spree::HasCustomFields
|
|
22
22
|
include Spree::Metadata
|
|
23
|
+
include Spree::PostSale::Taxation
|
|
23
24
|
|
|
24
25
|
publishes_lifecycle_events
|
|
26
|
+
publishes_events :approved, :canceled, :received, :refunded, :requested
|
|
25
27
|
|
|
26
28
|
has_status :requested, :approved, :received, :refunded, :canceled,
|
|
27
29
|
default: :requested
|
|
28
30
|
|
|
29
|
-
belongs_to :store, class_name: 'Spree::Store'
|
|
30
31
|
belongs_to :order, class_name: 'Spree::Order', inverse_of: :returns
|
|
31
32
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
|
32
33
|
belongs_to :reason, class_name: 'Spree::ReturnReason', optional: true, inverse_of: :returns
|
|
@@ -118,11 +119,16 @@ module Spree
|
|
|
118
119
|
received? || refunded?
|
|
119
120
|
end
|
|
120
121
|
|
|
121
|
-
# What the customer is owed for the items being returned.
|
|
122
|
+
# What the customer is owed for the items being returned, tax included.
|
|
122
123
|
def refund_total
|
|
123
124
|
return_line_items.sum(&:refund_amount)
|
|
124
125
|
end
|
|
125
126
|
|
|
127
|
+
# The tax inside {#refund_total}.
|
|
128
|
+
def refund_tax_total
|
|
129
|
+
return_line_items.sum(&:refund_tax_amount)
|
|
130
|
+
end
|
|
131
|
+
|
|
126
132
|
# What a refund from this return paid for, line by line. Only ever what
|
|
127
133
|
# the warehouse counted: the seller's clawback is built from this, and a
|
|
128
134
|
# line nobody counted earned nothing back.
|
|
@@ -139,6 +145,20 @@ module Spree
|
|
|
139
145
|
end
|
|
140
146
|
end
|
|
141
147
|
|
|
148
|
+
# The tax inside {#refunded_line_amounts}, for the lines whose tax is
|
|
149
|
+
# recorded.
|
|
150
|
+
#
|
|
151
|
+
# @return [Hash{Integer => BigDecimal}] line item id => tax
|
|
152
|
+
def refunded_line_taxes
|
|
153
|
+
return {} unless counted?
|
|
154
|
+
|
|
155
|
+
return_line_items.each_with_object(Hash.new(0)) do |line, taxes|
|
|
156
|
+
next if line.refund_amount.zero? || !line.settles_tax?
|
|
157
|
+
|
|
158
|
+
taxes[line.line_item_id] += line.refund_tax_amount
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
142
162
|
# What has actually been refunded so far — a return can be refunded in
|
|
143
163
|
# more than one step (part to store credit, part to the card), and store
|
|
144
164
|
# credit is its own ledger rather than a Spree::Refund row.
|
|
@@ -157,5 +177,13 @@ module Spree
|
|
|
157
177
|
def display_refunded_total
|
|
158
178
|
Spree::Money.new(refunded_total, currency: currency)
|
|
159
179
|
end
|
|
180
|
+
|
|
181
|
+
def display_refund_tax_total
|
|
182
|
+
Spree::Money.new(refund_tax_total, currency: currency)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def taxed_lines
|
|
186
|
+
return_line_items
|
|
187
|
+
end
|
|
160
188
|
end
|
|
161
189
|
end
|