spree_core 5.2.6 → 5.3.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/app/helpers/spree/addresses_helper.rb +1 -23
- data/app/helpers/spree/base_helper.rb +25 -77
- data/app/helpers/spree/currency_helper.rb +2 -2
- data/app/helpers/spree/images_helper.rb +5 -0
- data/app/helpers/spree/mail_helper.rb +1 -1
- data/app/helpers/spree/products_helper.rb +1 -1
- data/app/jobs/spree/events/subscriber_job.rb +58 -0
- data/app/jobs/spree/products/refresh_metrics_job.rb +14 -0
- data/app/jobs/spree/products/touch_taxons_job.rb +0 -1
- data/app/jobs/spree/variants/touch_job.rb +9 -0
- data/app/models/concerns/spree/adjustment_source.rb +8 -0
- data/app/models/concerns/spree/parameterizable_name.rb +1 -1
- data/app/models/concerns/spree/product_scopes.rb +54 -8
- data/app/models/concerns/spree/publishable.rb +253 -0
- data/app/models/concerns/spree/unique_name.rb +1 -1
- data/app/models/concerns/spree/user_methods.rb +21 -1
- data/app/models/spree/ability.rb +3 -3
- data/app/models/spree/address.rb +0 -3
- data/app/models/spree/adjustable/promotion_accumulator.rb +1 -1
- data/app/models/spree/adjustment.rb +32 -6
- data/app/models/spree/asset.rb +6 -3
- data/app/models/spree/base.rb +3 -1
- data/app/models/spree/classification.rb +2 -2
- data/app/models/spree/credit_card.rb +0 -3
- data/app/models/spree/current.rb +24 -3
- data/app/models/spree/custom_domain.rb +1 -1
- data/app/models/spree/customer_group.rb +94 -0
- data/app/models/spree/customer_group_user.rb +16 -0
- data/app/models/spree/customer_return.rb +2 -3
- data/app/models/spree/digital.rb +2 -4
- data/app/models/spree/digital_link.rb +2 -3
- data/app/models/spree/event.rb +104 -0
- data/app/models/spree/export.rb +7 -1
- data/app/models/spree/gift_card.rb +13 -1
- data/app/models/spree/gift_card_batch.rb +3 -1
- data/app/models/spree/image.rb +20 -5
- data/app/models/spree/import.rb +17 -12
- data/app/models/spree/import_row.rb +13 -26
- data/app/models/spree/inventory_unit.rb +0 -3
- data/app/models/spree/invitation.rb +12 -6
- data/app/models/spree/line_item.rb +57 -4
- data/app/models/spree/newsletter_subscriber.rb +2 -0
- data/app/models/spree/option_type.rb +2 -5
- data/app/models/spree/option_value.rb +1 -4
- data/app/models/spree/order/checkout.rb +7 -2
- data/app/models/spree/order/emails.rb +3 -11
- data/app/models/spree/order/store_credit.rb +5 -1
- data/app/models/spree/order.rb +80 -31
- data/app/models/spree/order_updater.rb +28 -9
- data/app/models/spree/payment/custom_events.rb +37 -0
- data/app/models/spree/payment.rb +13 -4
- data/app/models/spree/payment_capture_event.rb +0 -4
- data/app/models/spree/payment_method.rb +1 -1
- data/app/models/spree/permission_sets/default_customer.rb +3 -3
- data/app/models/spree/policy.rb +1 -8
- data/app/models/spree/post.rb +2 -7
- data/app/models/spree/post_category.rb +2 -0
- data/app/models/spree/price.rb +8 -6
- data/app/models/spree/price_list.rb +263 -0
- data/app/models/spree/price_rule.rb +26 -0
- data/app/models/spree/price_rules/customer_group_rule.rb +21 -0
- data/app/models/spree/price_rules/user_rule.rb +19 -0
- data/app/models/spree/price_rules/volume_rule.rb +21 -0
- data/app/models/spree/price_rules/zone_rule.rb +19 -0
- data/app/models/spree/product.rb +117 -76
- data/app/models/spree/product_property.rb +2 -2
- data/app/models/spree/promotion/rules/customer_group.rb +22 -0
- data/app/models/spree/promotion/rules/user.rb +2 -2
- data/app/models/spree/promotion.rb +3 -4
- data/app/models/spree/promotion_handler/coupon.rb +3 -3
- data/app/models/spree/property.rb +3 -6
- data/app/models/spree/prototype.rb +0 -3
- data/app/models/spree/refund.rb +2 -3
- data/app/models/spree/reimbursement.rb +5 -3
- data/app/models/spree/report.rb +7 -1
- data/app/models/spree/reports/products_performance.rb +1 -1
- data/app/models/spree/reports/sales_total.rb +1 -1
- data/app/models/spree/return_authorization.rb +7 -3
- data/app/models/spree/return_item.rb +15 -4
- data/app/models/spree/shipment/custom_events.rb +47 -0
- data/app/models/spree/shipment.rb +12 -5
- data/app/models/spree/shipping_category.rb +0 -3
- data/app/models/spree/shipping_method.rb +0 -3
- data/app/models/spree/stock/quantifier.rb +1 -1
- data/app/models/spree/stock_item.rb +2 -0
- data/app/models/spree/stock_location.rb +0 -3
- data/app/models/spree/stock_movement/custom_events.rb +44 -0
- data/app/models/spree/stock_movement.rb +3 -0
- data/app/models/spree/stock_transfer.rb +2 -3
- data/app/models/spree/store.rb +64 -92
- data/app/models/spree/store_credit.rb +3 -4
- data/app/models/spree/store_product.rb +14 -0
- data/app/models/spree/subscriber.rb +186 -0
- data/app/models/spree/tax_category.rb +0 -4
- data/app/models/spree/tax_rate.rb +0 -3
- data/app/models/spree/taxon.rb +3 -36
- data/app/models/spree/taxonomy.rb +0 -3
- data/app/models/spree/variant.rb +114 -21
- data/app/models/spree/webhook_delivery.rb +60 -0
- data/app/models/spree/webhook_endpoint.rb +53 -0
- data/app/models/spree/wished_item.rb +2 -4
- data/app/models/spree/wishlist.rb +2 -3
- data/app/models/spree/zone.rb +0 -9
- data/app/paginators/spree/shared/paginate.rb +4 -0
- data/app/serializers/spree/events/asset_serializer.rb +22 -0
- data/app/serializers/spree/events/base_serializer.rb +61 -0
- data/app/serializers/spree/events/customer_return_serializer.rb +20 -0
- data/app/serializers/spree/events/digital_link_serializer.rb +20 -0
- data/app/serializers/spree/events/digital_serializer.rb +18 -0
- data/app/serializers/spree/events/export_serializer.rb +22 -0
- data/app/serializers/spree/events/gift_card_batch_serializer.rb +24 -0
- data/app/serializers/spree/events/gift_card_serializer.rb +29 -0
- data/app/serializers/spree/events/image_serializer.rb +9 -0
- data/app/serializers/spree/events/import_row_serializer.rb +23 -0
- data/app/serializers/spree/events/import_serializer.rb +24 -0
- data/app/serializers/spree/events/invitation_serializer.rb +28 -0
- data/app/serializers/spree/events/line_item_serializer.rb +31 -0
- data/app/serializers/spree/events/newsletter_subscriber_serializer.rb +21 -0
- data/app/serializers/spree/events/order_serializer.rb +39 -0
- data/app/serializers/spree/events/payment_serializer.rb +24 -0
- data/app/serializers/spree/events/post_category_serializer.rb +20 -0
- data/app/serializers/spree/events/post_serializer.rb +26 -0
- data/app/serializers/spree/events/price_serializer.rb +22 -0
- data/app/serializers/spree/events/product_serializer.rb +24 -0
- data/app/serializers/spree/events/promotion_serializer.rb +32 -0
- data/app/serializers/spree/events/refund_serializer.rb +23 -0
- data/app/serializers/spree/events/reimbursement_serializer.rb +22 -0
- data/app/serializers/spree/events/report_serializer.rb +23 -0
- data/app/serializers/spree/events/return_authorization_serializer.rb +22 -0
- data/app/serializers/spree/events/return_item_serializer.rb +27 -0
- data/app/serializers/spree/events/shipment_serializer.rb +24 -0
- data/app/serializers/spree/events/stock_item_serializer.rb +22 -0
- data/app/serializers/spree/events/stock_movement_serializer.rb +22 -0
- data/app/serializers/spree/events/stock_transfer_serializer.rb +22 -0
- data/app/serializers/spree/events/store_credit_serializer.rb +30 -0
- data/app/serializers/spree/events/user_serializer.rb +18 -0
- data/app/serializers/spree/events/variant_serializer.rb +34 -0
- data/app/serializers/spree/events/wished_item_serializer.rb +20 -0
- data/app/serializers/spree/events/wishlist_serializer.rb +22 -0
- data/app/services/spree/addresses/update.rb +1 -1
- data/app/services/spree/cart/add_item.rb +1 -1
- data/app/services/spree/coupon_codes/coupon_codes_handler.rb +2 -1
- data/app/services/spree/data_feeds/google/required_attributes.rb +4 -4
- data/app/services/spree/newsletter/verify.rb +5 -0
- data/app/services/spree/products/auto_match_taxons.rb +1 -1
- data/app/services/spree/seeds/all.rb +1 -1
- data/app/services/spree/taxons/add_products.rb +8 -4
- data/app/services/spree/taxons/regenerate_products.rb +8 -0
- data/app/services/spree/taxons/remove_products.rb +12 -7
- data/app/subscribers/spree/event_log_subscriber.rb +64 -0
- data/app/subscribers/spree/export_subscriber.rb +26 -0
- data/app/subscribers/spree/invitation_email_subscriber.rb +40 -0
- data/app/subscribers/spree/product_metrics_subscriber.rb +29 -0
- data/app/subscribers/spree/report_subscriber.rb +26 -0
- data/config/locales/en.yml +126 -0
- data/db/migrate/20251110120000_create_spree_price_lists.rb +22 -0
- data/db/migrate/20251110120001_create_spree_price_rules.rb +13 -0
- data/db/migrate/20251110120002_add_price_list_id_to_spree_prices.rb +6 -0
- data/db/migrate/20251110120003_add_price_list_id_to_spree_line_items.rb +5 -0
- data/db/migrate/20251201172118_fix_indexes_on_friendly_id_slugs.rb +8 -0
- data/db/migrate/20251214000001_create_spree_webhook_endpoints.rb +19 -0
- data/db/migrate/20251214000002_create_spree_webhook_deliveries.rb +23 -0
- data/db/migrate/20251222000000_add_performance_indexes_to_spree_adjustments.rb +25 -0
- data/db/migrate/20260112000000_fix_spree_prices_unique_indexes.rb +33 -0
- data/db/migrate/20260115120000_create_spree_customer_groups.rb +14 -0
- data/db/migrate/20260115120001_create_spree_customer_group_users.rb +14 -0
- data/db/migrate/20260117140831_remove_not_null_constraint_from_policy_name.rb +5 -0
- data/db/migrate/20260118120000_add_statistics_to_store_products.rb +11 -0
- data/db/migrate/20260119120000_add_counter_caches_to_spree_products.rb +9 -0
- data/db/migrate/20260119170000_add_counter_caches_to_spree_taxons.rb +9 -0
- data/db/migrate/20260120120000_add_image_count_to_spree_variants.rb +9 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +14 -2
- data/lib/spree/core/configuration.rb +1 -0
- data/lib/spree/core/controller_helpers/auth.rb +0 -15
- data/lib/spree/core/controller_helpers/currency.rb +13 -9
- data/lib/spree/core/controller_helpers/search.rb +1 -1
- data/lib/spree/core/controller_helpers/store.rb +5 -1
- data/lib/spree/core/engine.rb +61 -78
- data/lib/spree/core/importer/order.rb +1 -1
- data/lib/spree/core/importer/product.rb +1 -1
- data/lib/spree/core/preferences/preferable.rb +14 -1
- data/lib/spree/core/pricing/context.rb +63 -0
- data/lib/spree/core/pricing/resolver.rb +129 -0
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/token_generator.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +42 -47
- data/lib/spree/events/adapters/active_support_notifications.rb +112 -0
- data/lib/spree/events/adapters/base.rb +193 -0
- data/lib/spree/events/registry.rb +99 -0
- data/lib/spree/events.rb +240 -0
- data/lib/spree/permitted_attributes.rb +13 -2
- data/lib/spree/testing_support/common_rake.rb +68 -35
- data/lib/spree/testing_support/factories/customer_group_factory.rb +6 -0
- data/lib/spree/testing_support/factories/customer_group_user_factory.rb +6 -0
- data/lib/spree/testing_support/factories/price_factory.rb +4 -0
- data/lib/spree/testing_support/factories/price_list_factory.rb +34 -0
- data/lib/spree/testing_support/factories/price_rule_factory.rb +49 -0
- data/lib/spree/testing_support/factories/promotion_rule_factory.rb +12 -0
- data/lib/spree/testing_support/factories/stock_item_factory.rb +6 -4
- data/lib/spree/testing_support/factories/store_product_factory.rb +6 -0
- data/lib/spree/testing_support/factories/taxon_factory.rb +0 -1
- data/lib/spree/testing_support/factories/webhook_delivery_factory.rb +48 -0
- data/lib/spree/testing_support/factories/webhook_endpoint_factory.rb +22 -0
- data/lib/spree/testing_support/lifecycle_events.rb +38 -0
- data/lib/spree/testing_support/store.rb +4 -2
- data/lib/spree/webhooks.rb +22 -0
- data/lib/tasks/products.rake +40 -0
- data/lib/tasks/taxons.rake +19 -0
- data/lib/tasks/variants.rake +18 -0
- metadata +112 -114
- data/app/jobs/spree/themes/duplicate_components_job.rb +0 -59
- data/app/jobs/spree/themes/screenshot_job.rb +0 -81
- data/app/models/concerns/spree/has_page_links.rb +0 -53
- data/app/models/spree/page.rb +0 -188
- data/app/models/spree/page_block.rb +0 -73
- data/app/models/spree/page_blocks/buttons.rb +0 -29
- data/app/models/spree/page_blocks/heading.rb +0 -18
- data/app/models/spree/page_blocks/image.rb +0 -20
- data/app/models/spree/page_blocks/link.rb +0 -21
- data/app/models/spree/page_blocks/mega_nav.rb +0 -33
- data/app/models/spree/page_blocks/mega_nav_with_subcategories.rb +0 -32
- data/app/models/spree/page_blocks/metafields.rb +0 -18
- data/app/models/spree/page_blocks/nav.rb +0 -15
- data/app/models/spree/page_blocks/newsletter_form.rb +0 -18
- data/app/models/spree/page_blocks/products/brand.rb +0 -15
- data/app/models/spree/page_blocks/products/buy_buttons.rb +0 -24
- data/app/models/spree/page_blocks/products/description.rb +0 -18
- data/app/models/spree/page_blocks/products/price.rb +0 -18
- data/app/models/spree/page_blocks/products/quantity_selector.rb +0 -20
- data/app/models/spree/page_blocks/products/share.rb +0 -8
- data/app/models/spree/page_blocks/products/title.rb +0 -19
- data/app/models/spree/page_blocks/products/variant_picker.rb +0 -13
- data/app/models/spree/page_blocks/subheading.rb +0 -17
- data/app/models/spree/page_blocks/text.rb +0 -16
- data/app/models/spree/page_link.rb +0 -60
- data/app/models/spree/page_section.rb +0 -222
- data/app/models/spree/page_sections/announcement_bar.rb +0 -28
- data/app/models/spree/page_sections/breadcrumbs.rb +0 -12
- data/app/models/spree/page_sections/collection_banner.rb +0 -18
- data/app/models/spree/page_sections/custom_code.rb +0 -11
- data/app/models/spree/page_sections/featured_posts.rb +0 -45
- data/app/models/spree/page_sections/featured_product.rb +0 -50
- data/app/models/spree/page_sections/featured_taxon.rb +0 -90
- data/app/models/spree/page_sections/featured_taxons.rb +0 -45
- data/app/models/spree/page_sections/footer.rb +0 -101
- data/app/models/spree/page_sections/header.rb +0 -62
- data/app/models/spree/page_sections/image_banner.rb +0 -55
- data/app/models/spree/page_sections/image_with_text.rb +0 -65
- data/app/models/spree/page_sections/main_password_footer.rb +0 -18
- data/app/models/spree/page_sections/main_password_header.rb +0 -20
- data/app/models/spree/page_sections/newsletter.rb +0 -54
- data/app/models/spree/page_sections/page_title.rb +0 -19
- data/app/models/spree/page_sections/post_details.rb +0 -19
- data/app/models/spree/page_sections/post_grid.rb +0 -19
- data/app/models/spree/page_sections/product_details.rb +0 -53
- data/app/models/spree/page_sections/product_grid.rb +0 -13
- data/app/models/spree/page_sections/related_products.rb +0 -58
- data/app/models/spree/page_sections/rich_text.rb +0 -31
- data/app/models/spree/page_sections/taxon_banner.rb +0 -18
- data/app/models/spree/page_sections/taxon_grid.rb +0 -17
- data/app/models/spree/page_sections/video.rb +0 -107
- data/app/models/spree/pages/account.rb +0 -19
- data/app/models/spree/pages/cart.rb +0 -19
- data/app/models/spree/pages/checkout.rb +0 -15
- data/app/models/spree/pages/custom.rb +0 -38
- data/app/models/spree/pages/homepage.rb +0 -72
- data/app/models/spree/pages/login.rb +0 -19
- data/app/models/spree/pages/password.rb +0 -59
- data/app/models/spree/pages/post.rb +0 -27
- data/app/models/spree/pages/post_list.rb +0 -36
- data/app/models/spree/pages/product_details.rb +0 -30
- data/app/models/spree/pages/search_results.rb +0 -43
- data/app/models/spree/pages/shop_all.rb +0 -40
- data/app/models/spree/pages/taxon.rb +0 -29
- data/app/models/spree/pages/taxon_list.rb +0 -41
- data/app/models/spree/pages/wishlist.rb +0 -15
- data/app/models/spree/theme.rb +0 -233
- data/app/models/spree/themes/default.rb +0 -97
- data/app/services/spree/taxons/touch_featured_sections.rb +0 -21
- data/db/migrate/20250120094216_create_page_builder_models.rb +0 -78
- data/db/migrate/20250305121352_remove_page_builder_indices.rb +0 -11
- data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +0 -7
- data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +0 -10
- data/lib/generators/spree/dummy/templates/initializers/devise.rb +0 -3
- data/lib/generators/spree/install/install_generator.rb +0 -219
- data/lib/generators/spree/install/templates/config/initializers/spree.rb +0 -126
- data/lib/spree/core/webhooks.rb +0 -21
- data/lib/spree/testing_support/factories/page_block_factory.rb +0 -22
- data/lib/spree/testing_support/factories/page_factory.rb +0 -33
- data/lib/spree/testing_support/factories/page_link_factory.rb +0 -7
- data/lib/spree/testing_support/factories/page_section_factory.rb +0 -27
- data/lib/spree/testing_support/factories/theme_factory.rb +0 -14
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
# Base class for event subscribers.
|
|
5
|
+
#
|
|
6
|
+
# Subscribers handle events published through the Spree event system.
|
|
7
|
+
# They provide a clean DSL for declaring which events to subscribe to
|
|
8
|
+
# and are automatically registered during Rails initialization.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic subscriber
|
|
11
|
+
# class OrderCompletedNotifier < Spree::Subscriber
|
|
12
|
+
# subscribes_to 'order.complete'
|
|
13
|
+
#
|
|
14
|
+
# def call(event)
|
|
15
|
+
# order_id = event.payload['id']
|
|
16
|
+
# Spree::OrderMailer.confirm_email(order_id).deliver_later
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# @example Multi-event subscriber
|
|
21
|
+
# class OrderAuditLogger < Spree::Subscriber
|
|
22
|
+
# subscribes_to 'order.complete', 'order.cancel', 'order.resume'
|
|
23
|
+
#
|
|
24
|
+
# def call(event)
|
|
25
|
+
# AuditLog.create!(
|
|
26
|
+
# event_name: event.name,
|
|
27
|
+
# payload: event.payload,
|
|
28
|
+
# occurred_at: event.timestamp
|
|
29
|
+
# )
|
|
30
|
+
# end
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# @example Pattern matching subscriber
|
|
34
|
+
# class OrderEventLogger < Spree::Subscriber
|
|
35
|
+
# subscribes_to 'order.*'
|
|
36
|
+
#
|
|
37
|
+
# def call(event)
|
|
38
|
+
# Rails.logger.info("Order event: #{event.name}")
|
|
39
|
+
# end
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# @example Subscriber with method routing
|
|
43
|
+
# class PaymentSubscriber < Spree::Subscriber
|
|
44
|
+
# subscribes_to 'payment.complete', 'payment.void', 'payment.refund'
|
|
45
|
+
#
|
|
46
|
+
# on 'payment.complete', :handle_complete
|
|
47
|
+
# on 'payment.void', :handle_void
|
|
48
|
+
# on 'payment.refund', :handle_refund
|
|
49
|
+
#
|
|
50
|
+
# private
|
|
51
|
+
#
|
|
52
|
+
# def handle_complete(event)
|
|
53
|
+
# # Handle payment completion
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# def handle_void(event)
|
|
57
|
+
# # Handle payment void
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# def handle_refund(event)
|
|
61
|
+
# # Handle payment refund
|
|
62
|
+
# end
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
# @example Synchronous subscriber (runs immediately, not via ActiveJob)
|
|
66
|
+
# class CriticalOrderHandler < Spree::Subscriber
|
|
67
|
+
# subscribes_to 'order.complete', async: false
|
|
68
|
+
#
|
|
69
|
+
# def call(event)
|
|
70
|
+
# # This runs synchronously
|
|
71
|
+
# end
|
|
72
|
+
# end
|
|
73
|
+
#
|
|
74
|
+
class Subscriber
|
|
75
|
+
class << self
|
|
76
|
+
# DSL method to declare which events this subscriber handles
|
|
77
|
+
#
|
|
78
|
+
# @param patterns [Array<String>] Event patterns to subscribe to
|
|
79
|
+
# @param options [Hash] Subscription options
|
|
80
|
+
# @option options [Boolean] :async (true) Whether to run async via ActiveJob
|
|
81
|
+
# @return [void]
|
|
82
|
+
#
|
|
83
|
+
# @example
|
|
84
|
+
# subscribes_to 'order.complete'
|
|
85
|
+
# subscribes_to 'order.complete', 'order.cancel'
|
|
86
|
+
# subscribes_to 'order.*'
|
|
87
|
+
# subscribes_to 'order.complete', async: false
|
|
88
|
+
#
|
|
89
|
+
def subscribes_to(*patterns, **options)
|
|
90
|
+
@subscription_patterns ||= []
|
|
91
|
+
@subscription_options = options
|
|
92
|
+
|
|
93
|
+
patterns.flatten.each do |pattern|
|
|
94
|
+
@subscription_patterns << pattern.to_s
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# DSL method to route specific events to specific methods
|
|
99
|
+
#
|
|
100
|
+
# @param pattern [String] Event pattern
|
|
101
|
+
# @param method_name [Symbol] Method to call for this event
|
|
102
|
+
# @return [void]
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
# on 'payment.complete', :handle_complete
|
|
106
|
+
# on 'payment.void', :handle_void
|
|
107
|
+
#
|
|
108
|
+
def on(pattern, method_name)
|
|
109
|
+
@event_handlers ||= {}
|
|
110
|
+
@event_handlers[pattern.to_s] = method_name
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Get all subscription patterns for this subscriber
|
|
114
|
+
#
|
|
115
|
+
# @return [Array<String>]
|
|
116
|
+
def subscription_patterns
|
|
117
|
+
@subscription_patterns ||= []
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Get subscription options
|
|
121
|
+
#
|
|
122
|
+
# @return [Hash]
|
|
123
|
+
def subscription_options
|
|
124
|
+
@subscription_options ||= {}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Get event handlers mapping
|
|
128
|
+
#
|
|
129
|
+
# @return [Hash<String, Symbol>]
|
|
130
|
+
def event_handlers
|
|
131
|
+
@event_handlers ||= {}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Class-level call method for when the class itself is used as subscriber
|
|
135
|
+
#
|
|
136
|
+
# @param event [Spree::Event]
|
|
137
|
+
def call(event)
|
|
138
|
+
new.call(event)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Handle an event
|
|
143
|
+
#
|
|
144
|
+
# Override this method in subclasses to handle events.
|
|
145
|
+
# If you've defined event handlers with `on`, this method
|
|
146
|
+
# will route to the appropriate handler automatically.
|
|
147
|
+
#
|
|
148
|
+
# @param event [Spree::Event] The event to handle
|
|
149
|
+
# @return [void]
|
|
150
|
+
def call(event)
|
|
151
|
+
handler = find_handler(event)
|
|
152
|
+
|
|
153
|
+
if handler
|
|
154
|
+
send(handler, event)
|
|
155
|
+
else
|
|
156
|
+
# Default behavior - subclasses should override
|
|
157
|
+
handle(event)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Default event handler
|
|
162
|
+
#
|
|
163
|
+
# Override this in subclasses if not using the `on` DSL
|
|
164
|
+
#
|
|
165
|
+
# @param event [Spree::Event]
|
|
166
|
+
def handle(event)
|
|
167
|
+
# Override in subclass
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
def find_handler(event)
|
|
173
|
+
handlers = self.class.event_handlers
|
|
174
|
+
|
|
175
|
+
# Try exact match first
|
|
176
|
+
return handlers[event.name] if handlers.key?(event.name)
|
|
177
|
+
|
|
178
|
+
# Try pattern matching
|
|
179
|
+
handlers.each do |pattern, method_name|
|
|
180
|
+
return method_name if event.matches?(pattern)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
nil
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class TaxCategory < Spree.base_class
|
|
3
|
-
if defined?(Spree::Webhooks::HasWebhooks)
|
|
4
|
-
include Spree::Webhooks::HasWebhooks
|
|
5
|
-
end
|
|
6
|
-
|
|
7
3
|
acts_as_paranoid
|
|
8
4
|
validates :name, presence: true, uniqueness: { case_sensitive: false, scope: spree_base_uniqueness_scope + [:deleted_at] }
|
|
9
5
|
|
|
@@ -6,9 +6,6 @@ module Spree
|
|
|
6
6
|
include Spree::AdjustmentSource
|
|
7
7
|
include Spree::Metafields
|
|
8
8
|
include Spree::Metadata
|
|
9
|
-
if defined?(Spree::Webhooks::HasWebhooks)
|
|
10
|
-
include Spree::Webhooks::HasWebhooks
|
|
11
|
-
end
|
|
12
9
|
|
|
13
10
|
with_options inverse_of: :tax_rates do
|
|
14
11
|
belongs_to :zone, class_name: 'Spree::Zone', optional: true
|
data/app/models/spree/taxon.rb
CHANGED
|
@@ -19,10 +19,6 @@ module Spree
|
|
|
19
19
|
include Spree::Metafields
|
|
20
20
|
include Spree::Metadata
|
|
21
21
|
include Spree::MemoizedData
|
|
22
|
-
include Spree::Linkable
|
|
23
|
-
if defined?(Spree::Webhooks::HasWebhooks)
|
|
24
|
-
include Spree::Webhooks::HasWebhooks
|
|
25
|
-
end
|
|
26
22
|
|
|
27
23
|
MEMOIZED_METHODS = %w[cached_self_and_descendants_ids].freeze
|
|
28
24
|
|
|
@@ -31,7 +27,7 @@ module Spree
|
|
|
31
27
|
#
|
|
32
28
|
extend FriendlyId
|
|
33
29
|
friendly_id :permalink, slug_column: :permalink, use: :history
|
|
34
|
-
acts_as_nested_set dependent: :destroy
|
|
30
|
+
acts_as_nested_set dependent: :destroy, counter_cache: :children_count
|
|
35
31
|
|
|
36
32
|
#
|
|
37
33
|
# Associations
|
|
@@ -85,10 +81,6 @@ module Spree
|
|
|
85
81
|
after_move :regenerate_pretty_name_and_permalink
|
|
86
82
|
after_move :regenerate_translations_pretty_name_and_permalink
|
|
87
83
|
|
|
88
|
-
after_commit :touch_featured_sections, on: [:update]
|
|
89
|
-
after_touch :touch_featured_sections
|
|
90
|
-
after_destroy :remove_featured_sections, if: -> { featured? }
|
|
91
|
-
|
|
92
84
|
#
|
|
93
85
|
# Scopes
|
|
94
86
|
#
|
|
@@ -167,10 +159,6 @@ module Spree
|
|
|
167
159
|
sort_order == 'manual'
|
|
168
160
|
end
|
|
169
161
|
|
|
170
|
-
def page_builder_image
|
|
171
|
-
square_image.presence || image
|
|
172
|
-
end
|
|
173
|
-
|
|
174
162
|
def active_products_with_descendants
|
|
175
163
|
@active_products_with_descendants ||= store.products.
|
|
176
164
|
joins(:classifications).
|
|
@@ -324,7 +312,7 @@ module Spree
|
|
|
324
312
|
# indicate which filters should be used for a taxon
|
|
325
313
|
# this method should be customized to your own site
|
|
326
314
|
def applicable_filters
|
|
327
|
-
Spree::Deprecation.warn('applicable_filters is deprecated and will be removed in Spree
|
|
315
|
+
Spree::Deprecation.warn('applicable_filters is deprecated and will be removed in Spree 5.5')
|
|
328
316
|
fs = []
|
|
329
317
|
# fs << ProductFilters.taxons_below(self)
|
|
330
318
|
## unless it's a root taxon? left open for demo purposes
|
|
@@ -366,6 +354,7 @@ module Spree
|
|
|
366
354
|
end
|
|
367
355
|
|
|
368
356
|
def active_products
|
|
357
|
+
Spree::Deprecation.warn('active_products is deprecated and will be removed in Spree 5.5. Please use taxon.products.active instead.')
|
|
369
358
|
products.active
|
|
370
359
|
end
|
|
371
360
|
|
|
@@ -401,20 +390,6 @@ module Spree
|
|
|
401
390
|
move_to_child_with_index(parent, idx.to_i) unless new_record?
|
|
402
391
|
end
|
|
403
392
|
|
|
404
|
-
def page_builder_url
|
|
405
|
-
return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:nested_taxons_path)
|
|
406
|
-
|
|
407
|
-
Spree::Core::Engine.routes.url_helpers.nested_taxons_path(self)
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
def featured?
|
|
411
|
-
featured_sections.any?
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
def featured_sections
|
|
415
|
-
@featured_sections ||= Spree::PageSections::FeaturedTaxon.published.by_taxon_id(id)
|
|
416
|
-
end
|
|
417
|
-
|
|
418
393
|
private
|
|
419
394
|
|
|
420
395
|
def should_regenerate_pretty_name_and_permalink?
|
|
@@ -455,13 +430,5 @@ module Spree
|
|
|
455
430
|
def regenerate_translations_pretty_name_and_permalink
|
|
456
431
|
translations.each(&:regenerate_pretty_name_and_permalink)
|
|
457
432
|
end
|
|
458
|
-
|
|
459
|
-
def touch_featured_sections
|
|
460
|
-
Spree::Taxons::TouchFeaturedSections.call(taxon_ids: [id])
|
|
461
|
-
end
|
|
462
|
-
|
|
463
|
-
def remove_featured_sections
|
|
464
|
-
featured_sections.destroy_all
|
|
465
|
-
end
|
|
466
433
|
end
|
|
467
434
|
end
|
|
@@ -4,9 +4,6 @@ module Spree
|
|
|
4
4
|
include Spree::Metafields
|
|
5
5
|
include Spree::Metadata
|
|
6
6
|
include Spree::SingleStoreResource
|
|
7
|
-
if defined?(Spree::Webhooks::HasWebhooks)
|
|
8
|
-
include Spree::Webhooks::HasWebhooks
|
|
9
|
-
end
|
|
10
7
|
|
|
11
8
|
TRANSLATABLE_FIELDS = %i[name].freeze
|
|
12
9
|
translates(*TRANSLATABLE_FIELDS, column_fallback: !Spree.always_use_translations?)
|
data/app/models/spree/variant.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Spree
|
|
|
8
8
|
include Spree::Metadata
|
|
9
9
|
include Spree::Variant::Webhooks
|
|
10
10
|
|
|
11
|
+
publishes_lifecycle_events
|
|
12
|
+
|
|
11
13
|
MEMOIZED_METHODS = %w(purchasable in_stock on_sale backorderable tax_category options_text compare_at_price)
|
|
12
14
|
|
|
13
15
|
DIMENSION_UNITS = %w[mm cm in ft]
|
|
@@ -19,7 +21,7 @@ module Spree
|
|
|
19
21
|
delegate :name, :name=, :description, :slug, :available_on, :make_active_at, :shipping_category_id,
|
|
20
22
|
:meta_description, :meta_keywords, :shipping_category, to: :product
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
normalizes :sku, with: ->(value) { value&.to_s&.strip }
|
|
23
25
|
|
|
24
26
|
# we need to have this callback before any dependent: :destroy associations
|
|
25
27
|
# https://github.com/rails/rails/issues/3458
|
|
@@ -80,6 +82,8 @@ module Spree
|
|
|
80
82
|
|
|
81
83
|
after_commit :remove_prices_from_master_variant, on: [:create, :update], unless: :is_master?
|
|
82
84
|
after_commit :remove_stock_items_from_master_variant, on: :create, unless: :is_master?
|
|
85
|
+
after_create :increment_product_variant_count, unless: :is_master?
|
|
86
|
+
after_destroy :decrement_product_variant_count, unless: :is_master?
|
|
83
87
|
|
|
84
88
|
after_touch :clear_in_stock_cache
|
|
85
89
|
|
|
@@ -208,6 +212,8 @@ module Spree
|
|
|
208
212
|
product_name_or_sku_cont(query)
|
|
209
213
|
end
|
|
210
214
|
|
|
215
|
+
# Returns the human name of the variant.
|
|
216
|
+
# @return [String] the human name of the variant
|
|
211
217
|
def human_name
|
|
212
218
|
@human_name ||= option_values.
|
|
213
219
|
joins(option_type: :product_option_types).
|
|
@@ -216,10 +222,14 @@ module Spree
|
|
|
216
222
|
pluck(:presentation).join('/')
|
|
217
223
|
end
|
|
218
224
|
|
|
225
|
+
# Returns true if the variant is available.
|
|
226
|
+
# @return [Boolean] true if the variant is available
|
|
219
227
|
def available?
|
|
220
228
|
!discontinued? && product.available?
|
|
221
229
|
end
|
|
222
230
|
|
|
231
|
+
# Returns true if the variant is in stock or backorderable.
|
|
232
|
+
# @return [Boolean] true if the variant is in stock or backorderable
|
|
223
233
|
def in_stock_or_backorderable?
|
|
224
234
|
self.class.in_stock_or_backorderable.exists?(id: id)
|
|
225
235
|
end
|
|
@@ -244,6 +254,8 @@ module Spree
|
|
|
244
254
|
end
|
|
245
255
|
end
|
|
246
256
|
|
|
257
|
+
# Returns the options text of the variant.
|
|
258
|
+
# @return [String] the options text of the variant
|
|
247
259
|
def options_text
|
|
248
260
|
@options_text ||= if option_values.loaded?
|
|
249
261
|
option_values.sort_by { |ov| ov.option_type.position }.map { |ov| "#{ov.option_type.presentation}: #{ov.presentation}" }.to_sentence(words_connector: ', ', two_words_connector: ', ')
|
|
@@ -252,11 +264,14 @@ module Spree
|
|
|
252
264
|
end
|
|
253
265
|
end
|
|
254
266
|
|
|
255
|
-
#
|
|
267
|
+
# Returns the exchange name of the variant.
|
|
268
|
+
# @return [String] the exchange name of the variant
|
|
256
269
|
def exchange_name
|
|
257
270
|
is_master? ? name : options_text
|
|
258
271
|
end
|
|
259
272
|
|
|
273
|
+
# Returns the descriptive name of the variant.
|
|
274
|
+
# @return [String] the descriptive name of the variant
|
|
260
275
|
def descriptive_name
|
|
261
276
|
is_master? ? name + ' - Master' : name + ' - ' + options_text
|
|
262
277
|
end
|
|
@@ -264,34 +279,42 @@ module Spree
|
|
|
264
279
|
# use deleted? rather than checking the attribute directly. this
|
|
265
280
|
# allows extensions to override deleted? if they want to provide
|
|
266
281
|
# their own definition.
|
|
282
|
+
# @return [Boolean] true if the variant is deleted.
|
|
267
283
|
def deleted?
|
|
268
284
|
!!deleted_at
|
|
269
285
|
end
|
|
270
286
|
|
|
271
|
-
# Returns
|
|
272
|
-
#
|
|
287
|
+
# Returns true if the variant has images.
|
|
288
|
+
# Uses loaded association when available, otherwise falls back to counter cache.
|
|
289
|
+
# @return [Boolean]
|
|
290
|
+
def has_images?
|
|
291
|
+
return images.any? if images.loaded?
|
|
292
|
+
|
|
293
|
+
image_count.positive?
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Returns default Image for Variant, falling back to product's default image.
|
|
297
|
+
# @return [Spree::Image, nil]
|
|
273
298
|
def default_image
|
|
274
|
-
@default_image ||=
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
299
|
+
@default_image ||= has_images? ? images.first : product.default_image
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Returns first Image for Variant.
|
|
303
|
+
# @return [Spree::Image, nil]
|
|
304
|
+
def primary_image
|
|
305
|
+
images.first
|
|
279
306
|
end
|
|
280
307
|
|
|
281
|
-
# Returns
|
|
282
|
-
# @return [Spree::Image]
|
|
308
|
+
# Returns second Image for Variant (for hover effects).
|
|
309
|
+
# @return [Spree::Image, nil]
|
|
283
310
|
def secondary_image
|
|
284
|
-
|
|
285
|
-
images.second
|
|
286
|
-
else
|
|
287
|
-
product.secondary_image
|
|
288
|
-
end
|
|
311
|
+
images.second
|
|
289
312
|
end
|
|
290
313
|
|
|
291
|
-
# Returns
|
|
314
|
+
# Returns all images except the default image, combining variant and product images.
|
|
292
315
|
# @return [Array<Spree::Image>]
|
|
293
316
|
def additional_images
|
|
294
|
-
@additional_images ||= (images + product.images).uniq.
|
|
317
|
+
@additional_images ||= (images + product.images).uniq.reject { |image| image.id == default_image&.id }
|
|
295
318
|
end
|
|
296
319
|
|
|
297
320
|
# Returns an array of hashes with the option type name, value and presentation
|
|
@@ -310,6 +333,9 @@ module Spree
|
|
|
310
333
|
end
|
|
311
334
|
end
|
|
312
335
|
|
|
336
|
+
# Sets the option values for the variant
|
|
337
|
+
# @param options [Array<Hash>] the options to set
|
|
338
|
+
# @return [void]
|
|
313
339
|
def options=(options = {})
|
|
314
340
|
options.each do |option|
|
|
315
341
|
next if option[:name].blank? || option[:value].blank?
|
|
@@ -318,6 +344,11 @@ module Spree
|
|
|
318
344
|
end
|
|
319
345
|
end
|
|
320
346
|
|
|
347
|
+
# Sets the option value for the given option name.
|
|
348
|
+
# @param opt_name [String] the option name to set the option value for
|
|
349
|
+
# @param opt_value [String] the option value to set
|
|
350
|
+
# @param opt_type_position [Integer] the position of the option type
|
|
351
|
+
# @return [void]
|
|
321
352
|
def set_option_value(opt_name, opt_value, opt_type_position = nil)
|
|
322
353
|
# no option values on master
|
|
323
354
|
return if is_master
|
|
@@ -354,10 +385,16 @@ module Spree
|
|
|
354
385
|
save
|
|
355
386
|
end
|
|
356
387
|
|
|
388
|
+
# Returns the option value for the given option name.
|
|
389
|
+
# @param opt_name [String] the option name to get the option value for
|
|
390
|
+
# @return [Spree::OptionValue] the option value for the given option name
|
|
357
391
|
def find_option_value(opt_name)
|
|
358
392
|
option_values.includes(:option_type).detect { |o| o.option_type.name.parameterize == opt_name.parameterize }
|
|
359
393
|
end
|
|
360
394
|
|
|
395
|
+
# Returns the presentation of the option value for the given option type.
|
|
396
|
+
# @param option_type [Spree::OptionType] the option type to get the option value for
|
|
397
|
+
# @return [String] the presentation of the option value for the given option type
|
|
361
398
|
def option_value(option_type)
|
|
362
399
|
if option_type.is_a?(Spree::OptionType)
|
|
363
400
|
option_values.detect { |o| o.option_type_id == option_type.id }.try(:presentation)
|
|
@@ -366,13 +403,17 @@ module Spree
|
|
|
366
403
|
end
|
|
367
404
|
end
|
|
368
405
|
|
|
406
|
+
# Returns the base price (global price, not from a price list) for the given currency.
|
|
407
|
+
# Use price_for(context) when you need to resolve prices including price lists.
|
|
408
|
+
# @param currency [String] the currency to get the price for
|
|
409
|
+
# @return [Spree::Price] the base price for the given currency
|
|
369
410
|
def price_in(currency)
|
|
370
411
|
currency = currency&.upcase
|
|
371
412
|
|
|
372
413
|
price = if prices.loaded? && prices.any?
|
|
373
|
-
prices.detect { |p| p.currency == currency }
|
|
414
|
+
prices.detect { |p| p.currency == currency && p.price_list_id.nil? }
|
|
374
415
|
else
|
|
375
|
-
prices.find_by(currency: currency)
|
|
416
|
+
prices.base_prices.find_by(currency: currency)
|
|
376
417
|
end
|
|
377
418
|
|
|
378
419
|
if price.nil?
|
|
@@ -390,21 +431,52 @@ module Spree
|
|
|
390
431
|
)
|
|
391
432
|
end
|
|
392
433
|
|
|
434
|
+
# Returns the amount for the given currency.
|
|
435
|
+
# @param currency [String] the currency to get the amount for
|
|
436
|
+
# @return [BigDecimal] the amount for the given currency
|
|
393
437
|
def amount_in(currency)
|
|
394
438
|
price_in(currency).try(:amount)
|
|
395
439
|
end
|
|
396
440
|
|
|
441
|
+
# Returns the compare at amount for the given currency.
|
|
442
|
+
# @param currency [String] the currency to get the compare at amount for
|
|
443
|
+
# @return [BigDecimal] the compare at amount for the given currency
|
|
397
444
|
def compare_at_amount_in(currency)
|
|
398
445
|
price_in(currency).try(:compare_at_amount)
|
|
399
446
|
end
|
|
400
447
|
|
|
448
|
+
# Sets the base price (global price, not for a price list) for the given currency.
|
|
449
|
+
# @param currency [String] the currency to set the price for
|
|
450
|
+
# @param amount [BigDecimal] the amount to set
|
|
451
|
+
# @param compare_at_amount [BigDecimal] the compare at amount to set
|
|
452
|
+
# @return [void]
|
|
401
453
|
def set_price(currency, amount, compare_at_amount = nil)
|
|
402
|
-
price = prices.find_or_initialize_by(currency: currency)
|
|
454
|
+
price = prices.base_prices.find_or_initialize_by(currency: currency)
|
|
403
455
|
price.amount = amount
|
|
404
456
|
price.compare_at_amount = compare_at_amount if compare_at_amount.present?
|
|
405
457
|
price.save!
|
|
406
458
|
end
|
|
407
459
|
|
|
460
|
+
# Returns the price for the given context or options.
|
|
461
|
+
# @param context_or_options [Spree::Pricing::Context|Hash] the context or options to get the price for
|
|
462
|
+
# @return [Spree::Price] the price for the given context or options
|
|
463
|
+
def price_for(context_or_options)
|
|
464
|
+
context = if context_or_options.is_a?(Spree::Pricing::Context)
|
|
465
|
+
context_or_options
|
|
466
|
+
elsif context_or_options.is_a?(Hash)
|
|
467
|
+
Spree::Pricing::Context.new(**context_or_options.merge(variant: self))
|
|
468
|
+
else
|
|
469
|
+
raise ArgumentError, 'Must provide a Pricing::Context or options hash'
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
Spree::Pricing::Resolver.new(context).resolve
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Sets the stock for the variant
|
|
476
|
+
# @param count_on_hand [Integer] the count on hand
|
|
477
|
+
# @param backorderable [Boolean] the backorderable flag
|
|
478
|
+
# @param stock_location [Spree::StockLocation] the stock location to set the stock for
|
|
479
|
+
# @return [void]
|
|
408
480
|
def set_stock(count_on_hand, backorderable = nil, stock_location = nil)
|
|
409
481
|
stock_location ||= Spree::Store.current.default_stock_location
|
|
410
482
|
stock_item = stock_items.find_or_initialize_by(stock_location: stock_location)
|
|
@@ -426,6 +498,9 @@ module Spree
|
|
|
426
498
|
end.sum
|
|
427
499
|
end
|
|
428
500
|
|
|
501
|
+
# Returns the price modifier amount of the variant.
|
|
502
|
+
# @param options [Hash] the options to get the price modifier amount for
|
|
503
|
+
# @return [BigDecimal] the price modifier amount of the variant
|
|
429
504
|
def price_modifier_amount(options = {})
|
|
430
505
|
return 0 unless options.present?
|
|
431
506
|
|
|
@@ -439,18 +514,26 @@ module Spree
|
|
|
439
514
|
end.sum
|
|
440
515
|
end
|
|
441
516
|
|
|
517
|
+
# Returns the compare at price of the variant.
|
|
518
|
+
# @return [BigDecimal] the compare at price of the variant
|
|
442
519
|
def compare_at_price
|
|
443
520
|
@compare_at_price ||= price_in(cost_currency).try(:compare_at_amount)
|
|
444
521
|
end
|
|
445
522
|
|
|
523
|
+
# Returns the name and sku of the variant.
|
|
524
|
+
# @return [String] the name and sku of the variant
|
|
446
525
|
def name_and_sku
|
|
447
526
|
"#{name} - #{sku}"
|
|
448
527
|
end
|
|
449
528
|
|
|
529
|
+
# Returns the sku and options text of the variant.
|
|
530
|
+
# @return [String] the sku and options text of the variant
|
|
450
531
|
def sku_and_options_text
|
|
451
532
|
"#{sku} #{options_text}".strip
|
|
452
533
|
end
|
|
453
534
|
|
|
535
|
+
# Returns true if the variant is in stock.
|
|
536
|
+
# @return [Boolean] true if the variant is in stock
|
|
454
537
|
def in_stock?
|
|
455
538
|
@in_stock ||= if association(:stock_items).loaded? && association(:stock_locations).loaded?
|
|
456
539
|
total_on_hand.positive?
|
|
@@ -461,6 +544,8 @@ module Spree
|
|
|
461
544
|
end
|
|
462
545
|
end
|
|
463
546
|
|
|
547
|
+
# Returns true if the variant is backorderable.
|
|
548
|
+
# @return [Boolean] true if the variant is backorderable
|
|
464
549
|
def backorderable?
|
|
465
550
|
@backorderable ||= Rails.cache.fetch(['variant-backorderable', cache_key_with_version]) do
|
|
466
551
|
quantifier.backorderable?
|
|
@@ -609,5 +694,13 @@ module Spree
|
|
|
609
694
|
def remove_stock_items_from_master_variant
|
|
610
695
|
product.master.stock_items.delete_all
|
|
611
696
|
end
|
|
697
|
+
|
|
698
|
+
def increment_product_variant_count
|
|
699
|
+
Spree::Product.increment_counter(:variant_count, product_id)
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def decrement_product_variant_count
|
|
703
|
+
Spree::Product.decrement_counter(:variant_count, product_id)
|
|
704
|
+
end
|
|
612
705
|
end
|
|
613
706
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
class WebhookDelivery < Spree.base_class
|
|
5
|
+
belongs_to :webhook_endpoint, class_name: 'Spree::WebhookEndpoint'
|
|
6
|
+
delegate :url, to: :webhook_endpoint
|
|
7
|
+
|
|
8
|
+
validates :event_name, presence: true
|
|
9
|
+
validates :payload, presence: true
|
|
10
|
+
|
|
11
|
+
ERROR_TYPES = %w[timeout connection_error].freeze
|
|
12
|
+
|
|
13
|
+
scope :successful, -> { where(success: true) }
|
|
14
|
+
scope :failed, -> { where(success: false) }
|
|
15
|
+
scope :pending, -> { where(delivered_at: nil) }
|
|
16
|
+
scope :recent, -> { order(created_at: :desc) }
|
|
17
|
+
scope :for_event, ->(event_name) { where(event_name: event_name) }
|
|
18
|
+
|
|
19
|
+
# Ransack configuration
|
|
20
|
+
self.whitelisted_ransackable_attributes = %w[event_name response_code execution_time success delivered_at]
|
|
21
|
+
|
|
22
|
+
# Check if the delivery was successful
|
|
23
|
+
#
|
|
24
|
+
# @return [Boolean]
|
|
25
|
+
def successful?
|
|
26
|
+
success == true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Check if the delivery failed
|
|
30
|
+
#
|
|
31
|
+
# @return [Boolean]
|
|
32
|
+
def failed?
|
|
33
|
+
success == false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check if the delivery is pending
|
|
37
|
+
#
|
|
38
|
+
# @return [Boolean]
|
|
39
|
+
def pending?
|
|
40
|
+
delivered_at.nil?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Mark delivery as completed with HTTP response
|
|
44
|
+
#
|
|
45
|
+
# @param response_code [Integer] HTTP response code
|
|
46
|
+
# @param execution_time [Integer] time in milliseconds
|
|
47
|
+
# @param response_body [String] response body from the webhook endpoint
|
|
48
|
+
def complete!(response_code: nil, execution_time:, error_type: nil, request_errors: nil, response_body: nil)
|
|
49
|
+
update!(
|
|
50
|
+
response_code: response_code,
|
|
51
|
+
execution_time: execution_time,
|
|
52
|
+
error_type: error_type,
|
|
53
|
+
request_errors: request_errors,
|
|
54
|
+
response_body: response_body,
|
|
55
|
+
success: response_code.present? && response_code.to_s.start_with?('2'),
|
|
56
|
+
delivered_at: Time.current
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|