spree_cm_commissioner 2.7.1.pre.pre5 → 2.7.1.pre.pre6
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/.env.example +12 -0
- data/.gitignore +0 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/concerns/spree_cm_commissioner/order_concern.rb +1 -0
- data/app/controllers/spree/admin/classifications_controller.rb +1 -1
- data/app/controllers/spree/admin/inventory_holds_controller.rb +64 -0
- data/app/controllers/spree/admin/inventory_monitorings_controller.rb +7 -3
- data/app/controllers/spree/admin/stock_managements_controller.rb +10 -1
- data/app/controllers/spree/api/v2/operator/recalculate_tickets_controller.rb +1 -1
- data/app/controllers/spree/api/v2/storefront/pricing_previews_controller.rb +39 -0
- data/app/controllers/spree/api/v2/tenant/base_controller.rb +0 -4
- data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +1 -0
- data/app/controllers/spree/api/v2/tenant/pricing_previews_controller.rb +39 -0
- data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +88 -0
- data/app/finders/spree_cm_commissioner/accommodations/find.rb +6 -5
- data/app/finders/spree_cm_commissioner/inventory_items/recently_changed_finder.rb +43 -33
- data/app/helpers/spree_cm_commissioner/admin/homepage_segment_helper.rb +0 -2
- data/app/jobs/spree_cm_commissioner/inventory_holds/bulk_release_stale_job.rb +11 -0
- data/app/jobs/spree_cm_commissioner/inventory_holds/bulk_release_stale_payment_locked_job.rb +11 -0
- data/app/jobs/spree_cm_commissioner/inventory_holds/release_job.rb +29 -0
- data/app/jobs/spree_cm_commissioner/inventory_items/bulk_adjust_quantities_on_hold_job.rb +20 -0
- data/app/models/concerns/spree_cm_commissioner/homepage_section_bitwise.rb +1 -2
- data/app/models/concerns/spree_cm_commissioner/homepage_section_relatable_concern.rb +25 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +1 -2
- data/app/models/concerns/spree_cm_commissioner/order_holdable.rb +71 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +13 -17
- data/app/models/concerns/spree_cm_commissioner/product_type.rb +1 -1
- data/app/models/spree_cm_commissioner/homepage_background.rb +3 -0
- data/app/models/spree_cm_commissioner/homepage_section.rb +3 -0
- data/app/models/spree_cm_commissioner/inventory_hold.rb +30 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +8 -0
- data/app/models/spree_cm_commissioner/line_item_decorator.rb +10 -0
- data/app/models/spree_cm_commissioner/maintenance_tasks/cache_invalidation.rb +14 -0
- data/app/models/spree_cm_commissioner/menu_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/menu_item_decorator.rb +12 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +6 -6
- data/app/models/spree_cm_commissioner/payment_decorator.rb +12 -0
- data/app/models/spree_cm_commissioner/pricing_action.rb +2 -2
- data/app/models/spree_cm_commissioner/pricing_actions/create_guest_adjustments.rb +34 -42
- data/app/models/spree_cm_commissioner/pricing_actions/create_line_item_adjustments.rb +17 -10
- data/app/models/spree_cm_commissioner/pricing_actions/create_route_adjustments.rb +38 -39
- data/app/models/spree_cm_commissioner/pricing_model.rb +13 -15
- data/app/models/spree_cm_commissioner/pricing_rule.rb +1 -1
- data/app/models/spree_cm_commissioner/pricing_rules/age_group.rb +9 -12
- data/app/models/spree_cm_commissioner/pricing_rules/extra_drop_off_distance.rb +2 -2
- data/app/models/spree_cm_commissioner/pricing_rules/extra_pick_up_distance.rb +2 -2
- data/app/models/spree_cm_commissioner/pricing_rules/nationality.rb +18 -19
- data/app/models/spree_cm_commissioner/pricing_rules/nationality_group.rb +7 -16
- data/app/models/spree_cm_commissioner/product_decorator.rb +5 -6
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +16 -9
- data/app/models/spree_cm_commissioner/reserved_block.rb +4 -0
- data/app/models/spree_cm_commissioner/stock_item_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/taxon_decorator.rb +4 -5
- data/app/models/spree_cm_commissioner/taxonomy_decorator.rb +1 -10
- data/app/models/spree_cm_commissioner/tenant.rb +0 -9
- data/app/models/spree_cm_commissioner/user_decorator.rb +0 -3
- data/app/models/spree_cm_commissioner/variant_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/variant_options.rb +0 -4
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +4 -10
- data/app/overrides/spree/admin/products/_form/enable_inventory_hold.html.erb.deface +18 -0
- data/app/overrides/spree/admin/shared/sub_menu/_stock/inventory_holds_tab.html.erb.deface +3 -0
- data/app/queries/spree_cm_commissioner/multi_leg_trips_query.rb +1 -1
- data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +1 -0
- data/app/serializers/spree/v2/tenant/pricing_preview_serializer.rb +15 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/pricing_preview_serializer.rb +15 -0
- data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +26 -8
- data/app/services/spree_cm_commissioner/checkout/advance_decorator.rb +1 -1
- data/app/services/spree_cm_commissioner/checkout/update_decorator.rb +1 -1
- data/app/services/spree_cm_commissioner/guests/update_seat.rb +55 -0
- data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +5 -2
- data/app/services/spree_cm_commissioner/inventory_holds/acquire.rb +175 -0
- data/app/services/spree_cm_commissioner/inventory_holds/bulk_release_stale.rb +41 -0
- data/app/services/spree_cm_commissioner/inventory_holds/bulk_release_stale_payment_locked.rb +11 -0
- data/app/services/spree_cm_commissioner/inventory_holds/convert.rb +86 -0
- data/app/services/spree_cm_commissioner/inventory_holds/release.rb +100 -0
- data/app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb +65 -0
- data/app/services/spree_cm_commissioner/inventory_items/reset.rb +23 -1
- data/app/services/spree_cm_commissioner/order_holds/hold.rb +71 -0
- data/app/services/spree_cm_commissioner/order_holds/lock_for_payment.rb +153 -0
- data/app/services/spree_cm_commissioner/order_holds/release.rb +56 -0
- data/app/services/spree_cm_commissioner/order_holds/reserve.rb +46 -0
- data/app/services/spree_cm_commissioner/pricing_models/activate.rb +41 -0
- data/app/services/spree_cm_commissioner/pricing_models/preview.rb +93 -0
- data/app/services/spree_cm_commissioner/reserved_blocks/hold.rb +4 -6
- data/app/services/spree_cm_commissioner/vendor_places/base.rb +2 -7
- data/app/services/spree_cm_commissioner/vendor_places/bulk_create.rb +3 -3
- data/app/views/spree/admin/inventory_holds/_search.html.erb +128 -0
- data/app/views/spree/admin/inventory_holds/index.html.erb +122 -0
- data/app/views/spree/admin/inventory_monitorings/index.html.erb +45 -24
- data/app/views/spree/admin/stock_managements/index.html.erb +32 -2
- data/config/initializers/spree_permitted_attributes.rb +0 -3
- data/config/locales/en.yml +21 -12
- data/config/locales/km.yml +21 -6
- data/config/routes.rb +10 -17
- data/db/migrate/20260327090000_create_cm_inventory_holds.rb +27 -0
- data/db/migrate/20260327090001_add_quantity_on_hold_to_cm_inventory_items.rb +5 -0
- data/db/migrate/20260410080000_add_payment_locked_at_to_cm_reserved_blocks.rb +5 -0
- data/db/migrate/20260506090000_remove_default_from_cm_guests_nationality_group.rb +6 -0
- data/docs/tenant/test.com.md +88 -0
- data/lib/spree_cm_commissioner/cached_inventory_item.rb +10 -1
- data/lib/spree_cm_commissioner/pricing_models/guest_context.rb +81 -0
- data/lib/spree_cm_commissioner/pricing_models/line_item_context.rb +63 -0
- data/lib/spree_cm_commissioner/pricing_models/order_context.rb +61 -0
- data/lib/spree_cm_commissioner/pricing_models/preview_adjustment.rb +30 -0
- data/lib/spree_cm_commissioner/pricing_models/pricing_preview.rb +15 -0
- data/lib/spree_cm_commissioner/test_helper/factories/inventory_hold_factory.rb +43 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_action_factory.rb +4 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_group_factory.rb +1 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +6 -7
- metadata +44 -87
- data/app/controllers/spree/api/v2/tenant/free_vote_claims_controller.rb +0 -37
- data/app/controllers/spree/api/v2/tenant/show_contestants_controller.rb +0 -51
- data/app/controllers/spree/api/v2/tenant/show_people_controller.rb +0 -49
- data/app/controllers/spree/api/v2/tenant/show_person_assignments_controller.rb +0 -36
- data/app/controllers/spree/api/v2/tenant/shows_controller.rb +0 -34
- data/app/controllers/spree/api/v2/tenant/votes_controller.rb +0 -94
- data/app/controllers/spree/api/v2/tenant/voting_contestants_controller.rb +0 -40
- data/app/controllers/spree/api/v2/tenant/voting_credit_transactions_controller.rb +0 -41
- data/app/controllers/spree/api/v2/tenant/voting_credits_controller.rb +0 -31
- data/app/interactors/spree_cm_commissioner/guest_seat_updater.rb +0 -45
- data/app/jobs/spree_cm_commissioner/vote_fraud_event_job.rb +0 -9
- data/app/jobs/spree_cm_commissioner/voting_credit_allocation_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/voting_credit_de_allocation_job.rb +0 -10
- data/app/models/concerns/spree_cm_commissioner/order_seatable.rb +0 -81
- data/app/models/spree_cm_commissioner/maintenance_tasks/voting_session.rb +0 -19
- data/app/models/spree_cm_commissioner/pricing_model_handler/order.rb +0 -73
- data/app/models/spree_cm_commissioner/show.rb +0 -154
- data/app/models/spree_cm_commissioner/show_contestant.rb +0 -37
- data/app/models/spree_cm_commissioner/show_contestant_image.rb +0 -11
- data/app/models/spree_cm_commissioner/show_contestant_video.rb +0 -4
- data/app/models/spree_cm_commissioner/show_episode.rb +0 -109
- data/app/models/spree_cm_commissioner/show_person.rb +0 -15
- data/app/models/spree_cm_commissioner/show_person_assignment.rb +0 -20
- data/app/models/spree_cm_commissioner/show_person_image.rb +0 -11
- data/app/models/spree_cm_commissioner/vote.rb +0 -16
- data/app/models/spree_cm_commissioner/vote_fraud_event.rb +0 -19
- data/app/models/spree_cm_commissioner/voting_contestant.rb +0 -34
- data/app/models/spree_cm_commissioner/voting_credit.rb +0 -72
- data/app/models/spree_cm_commissioner/voting_credit_transaction.rb +0 -55
- data/app/models/spree_cm_commissioner/voting_session.rb +0 -153
- data/app/serializers/spree/v2/tenant/show_contestant_serializer.rb +0 -21
- data/app/serializers/spree/v2/tenant/show_episode_serializer.rb +0 -17
- data/app/serializers/spree/v2/tenant/show_person_assignment_serializer.rb +0 -16
- data/app/serializers/spree/v2/tenant/show_person_serializer.rb +0 -13
- data/app/serializers/spree/v2/tenant/show_serializer.rb +0 -26
- data/app/serializers/spree/v2/tenant/video_serializer.rb +0 -9
- data/app/serializers/spree/v2/tenant/vote_serializer.rb +0 -14
- data/app/serializers/spree/v2/tenant/voting_contestant_serializer.rb +0 -18
- data/app/serializers/spree/v2/tenant/voting_credit_serializer.rb +0 -10
- data/app/serializers/spree/v2/tenant/voting_credit_transaction_serializer.rb +0 -14
- data/app/serializers/spree/v2/tenant/voting_session_serializer.rb +0 -14
- data/app/services/spree_cm_commissioner/fraud_check.rb +0 -275
- data/app/services/spree_cm_commissioner/vote_counters/audit_counters.rb +0 -35
- data/app/services/spree_cm_commissioner/vote_counters/base.rb +0 -31
- data/app/services/spree_cm_commissioner/vote_counters/increment.rb +0 -34
- data/app/services/spree_cm_commissioner/vote_counters/per_contestant_counter.rb +0 -25
- data/app/services/spree_cm_commissioner/vote_counters/rebuild_from_db.rb +0 -41
- data/app/services/spree_cm_commissioner/vote_credit_deductor.rb +0 -68
- data/app/services/spree_cm_commissioner/vote_package/create.rb +0 -162
- data/app/services/spree_cm_commissioner/vote_package/update.rb +0 -172
- data/app/services/spree_cm_commissioner/vote_processor.rb +0 -133
- data/app/services/spree_cm_commissioner/voting_contestants/advancer.rb +0 -334
- data/app/services/spree_cm_commissioner/voting_contestants/assigner.rb +0 -32
- data/app/services/spree_cm_commissioner/voting_contestants/bulk_updater.rb +0 -104
- data/app/services/spree_cm_commissioner/voting_credits/allocate.rb +0 -77
- data/app/services/spree_cm_commissioner/voting_credits/claim_free_votes.rb +0 -119
- data/app/services/spree_cm_commissioner/voting_credits/credit_calculator.rb +0 -35
- data/app/services/spree_cm_commissioner/voting_credits/de_allocate.rb +0 -87
- data/config/schemas/show_contestant_highlight_video.json +0 -12
- data/db/migrate/20260309230148_create_cm_show_people.rb +0 -14
- data/db/migrate/20260309230149_create_cm_show_people_assignments.rb +0 -16
- data/db/migrate/20260310082711_create_cm_show_contestants.rb +0 -28
- data/db/migrate/20260310082720_create_cm_voting_sessions.rb +0 -21
- data/db/migrate/20260310082721_create_cm_voting_contestants.rb +0 -23
- data/db/migrate/20260310082734_add_voting_fields_to_spree_taxons.rb +0 -9
- data/db/migrate/20260310082735_add_type_to_spree_products.rb +0 -6
- data/db/migrate/20260310082749_create_cm_voting_credits.rb +0 -27
- data/db/migrate/20260326080200_create_cm_voting_credit_transactions.rb +0 -27
- data/db/migrate/20260330160000_create_cm_votes.rb +0 -25
- data/db/migrate/20260401072500_add_advanced_from_to_cm_voting_contestants.rb +0 -7
- data/db/migrate/20260402000001_add_voting_credit_scope_to_spree_taxons.rb +0 -6
- data/db/migrate/20260402000002_rename_scopeable_to_votable_in_cm_voting_credits.rb +0 -12
- data/db/migrate/20260403070000_add_name_to_cm_voting_sessions.rb +0 -5
- data/db/migrate/20260406000001_add_vendor_id_to_voting_tables.rb +0 -6
- data/db/migrate/20260406000001_rename_votes_remaining_to_amount_in_cm_voting_credits.rb +0 -11
- data/db/migrate/20260408085255_add_show_id_and_vendor_id_to_cm_voting_sessions.rb +0 -9
- data/db/migrate/20260420000001_rename_type_to_credit_type_in_cm_voting_credits.rb +0 -25
- data/db/migrate/20260422000001_create_cm_vote_fraud_events.rb +0 -23
- data/docs/sql/jsonb_query_guide.md +0 -57
- data/lib/spree_cm_commissioner/test_helper/factories/show_episode_factory.rb +0 -12
- data/lib/spree_cm_commissioner/test_helper/factories/show_factory.rb +0 -95
- data/lib/spree_cm_commissioner/test_helper/factories/vote_credit_factory.rb +0 -37
- data/lib/spree_cm_commissioner/test_helper/factories/vote_factory.rb +0 -28
- data/lib/spree_cm_commissioner/test_helper/factories/voting_credit_transaction_factory.rb +0 -11
- data/lib/spree_cm_commissioner/test_helper/factories/voting_session_factory.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f88085ac9f274a24587f64dca172c3219d84b76e02385419388bc84196bdbc2e
|
|
4
|
+
data.tar.gz: 3865b929388b20b69d3a641bbdf2695ae79055e5636c5fed7033bc297b511ff5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec9862ee95fb74dc2031f22534084dbbfe01c7fd5351a248014f5a8011a97fd990d0145b1536d697877face277480bdc837fa126e64b73a41039a55268072182
|
|
7
|
+
data.tar.gz: 4b84c53d6a14a05702b5eaa78a76b1eb6f80471f15b4cc95bee9fef3772f00abe49f40c9c9d0cd78ec7761d5aa012c1d4546931a121e7e8ae2e264e7788e5bb2
|
data/.env.example
CHANGED
|
@@ -50,3 +50,15 @@ EXPORT_PRESIGNED_URL_EXPIRATION_MINUTES=15
|
|
|
50
50
|
|
|
51
51
|
# import order batch size
|
|
52
52
|
IMPORT_ORDERS_BATCH_SIZE=50
|
|
53
|
+
|
|
54
|
+
# Inventory Hold System
|
|
55
|
+
# See: app/services/spree_cm_commissioner/order_holds/hold.rb
|
|
56
|
+
HOLD_DURATION_IN_MINUTES=8 # How long a hold is active after checkout begins (address step)
|
|
57
|
+
|
|
58
|
+
# See: app/services/spree_cm_commissioner/order_holds/lock_for_payment.rb
|
|
59
|
+
PAYMENT_LOCK_MIN_DURATION_IN_MINUTES=5 # Minimum remaining hold time required when entering the payment step; hold is extended if below this threshold
|
|
60
|
+
|
|
61
|
+
# See: app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb
|
|
62
|
+
MAX_ACTIVE_HOLDS_PER_USER=3 # Max concurrent active holds allowed per user across all orders
|
|
63
|
+
MAX_HOLDS_PER_IP_PER_HOUR=5 # Max hold attempts per IP address within a rolling 1-hour window (abuse prevention)
|
|
64
|
+
HOLD_COOLDOWN_AFTER_EXPIRY_IN_MINUTES=2 # Cooldown period before a user can re-acquire a hold after one expired on them
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -5,7 +5,7 @@ module Spree
|
|
|
5
5
|
|
|
6
6
|
def recalculate_conversions
|
|
7
7
|
if @taxon.parent.event?
|
|
8
|
-
SpreeCmCommissioner::MaintenanceTasks::Event.pending.
|
|
8
|
+
SpreeCmCommissioner::MaintenanceTasks::Event.pending.create_or_find_by(
|
|
9
9
|
maintainable_type: 'Spree::Taxon',
|
|
10
10
|
maintainable_id: @taxon.parent.id
|
|
11
11
|
) do |task|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
class InventoryHoldsController < BaseController
|
|
4
|
+
def index
|
|
5
|
+
authorize! :manage, SpreeCmCommissioner::InventoryHold
|
|
6
|
+
|
|
7
|
+
q = params.fetch(:q, {})
|
|
8
|
+
q = q.respond_to?(:to_unsafe_h) ? q.to_unsafe_h.deep_dup : q.deep_dup
|
|
9
|
+
q['s'] ||= 'created_at desc'
|
|
10
|
+
q['created_at_gt'] = parse_time_or_nil(q['created_at_gt'], :beginning_of_day) if q['created_at_gt'].present?
|
|
11
|
+
q['created_at_lt'] = parse_time_or_nil(q['created_at_lt'], :end_of_day) if q['created_at_lt'].present?
|
|
12
|
+
|
|
13
|
+
@search = scope.ransack(q)
|
|
14
|
+
result_scope = @search.result(distinct: true)
|
|
15
|
+
|
|
16
|
+
@holds = result_scope
|
|
17
|
+
.includes(order: [:user, { line_items: { variant: { product: :vendor } } }])
|
|
18
|
+
.page(params[:page])
|
|
19
|
+
.per(params[:per_page] || Spree::Backend::Config[:admin_orders_per_page])
|
|
20
|
+
|
|
21
|
+
@status_counts = result_scope.reorder(nil).group(:status).count
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def release
|
|
25
|
+
authorize! :manage, SpreeCmCommissioner::InventoryHold
|
|
26
|
+
|
|
27
|
+
hold = SpreeCmCommissioner::InventoryHold.find(params[:id])
|
|
28
|
+
|
|
29
|
+
if hold.finalized?
|
|
30
|
+
flash[:error] = "Hold ##{hold.id} is already #{hold.status} and cannot be released."
|
|
31
|
+
return redirect_to admin_inventory_holds_path(back_params)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
result = SpreeCmCommissioner::InventoryHolds::Release.call(hold: hold, reason: :user_canceled)
|
|
35
|
+
|
|
36
|
+
if result.success?
|
|
37
|
+
flash[:success] = "Hold ##{hold.id} for order #{hold.order.number} has been released."
|
|
38
|
+
else
|
|
39
|
+
flash[:error] = "Failed to release hold ##{hold.id}: #{result.error}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
redirect_to admin_inventory_holds_path(back_params)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def back_params
|
|
48
|
+
raw_params = params[:back_params].presence || params
|
|
49
|
+
ActionController::Parameters.new(raw_params).permit(:page, :per_page, q: {})
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def scope
|
|
53
|
+
SpreeCmCommissioner::InventoryHold
|
|
54
|
+
.accessible_by(current_ability, :manage)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parse_time_or_nil(value, boundary)
|
|
58
|
+
Time.zone.parse(value).public_send(boundary)
|
|
59
|
+
rescue StandardError
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -5,19 +5,20 @@ module Spree
|
|
|
5
5
|
authorize! :manage, SpreeCmCommissioner::InventoryItem
|
|
6
6
|
|
|
7
7
|
@time_range = params[:time_range] || 7
|
|
8
|
-
@
|
|
8
|
+
@product_type = params[:product_type].presence
|
|
9
9
|
@vendor_id = params[:vendor_id]
|
|
10
10
|
|
|
11
11
|
finder = SpreeCmCommissioner::InventoryItems::RecentlyChangedFinder.new(
|
|
12
12
|
time_range: @time_range.to_i.days.ago,
|
|
13
13
|
limit: 1000,
|
|
14
14
|
vendor_id: @vendor_id,
|
|
15
|
-
|
|
15
|
+
product_type: @product_type
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
@inventory_items = finder.execute
|
|
19
19
|
@total_count = @inventory_items.size
|
|
20
20
|
@out_of_sync_count = @inventory_items.count { |item| item[:out_of_sync] }
|
|
21
|
+
@on_hold_out_of_sync_count = @inventory_items.count { |item| item[:on_hold_out_of_sync] }
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def reset
|
|
@@ -32,7 +33,10 @@ module Spree
|
|
|
32
33
|
flash[:error] = "Failed to reset inventory: #{result.message}"
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
redirect_to action: :index,
|
|
36
|
+
redirect_to action: :index,
|
|
37
|
+
time_range: params[:time_range],
|
|
38
|
+
product_type: params[:product_type],
|
|
39
|
+
vendor_id: params[:vendor_id]
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
42
|
end
|
|
@@ -5,7 +5,7 @@ module Spree
|
|
|
5
5
|
|
|
6
6
|
before_action :load_parent
|
|
7
7
|
|
|
8
|
-
helper_method :inventory_item_message
|
|
8
|
+
helper_method :inventory_item_message, :inventory_item_hold_message
|
|
9
9
|
|
|
10
10
|
def load_parent
|
|
11
11
|
@product = Spree::Product.find_by(slug: params[:product_id])
|
|
@@ -75,6 +75,15 @@ module Spree
|
|
|
75
75
|
end
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
def inventory_item_hold_message(inventory_item, cached_inventory_item)
|
|
79
|
+
synced = inventory_item.quantity_on_hold == cached_inventory_item.quantity_on_hold
|
|
80
|
+
if synced
|
|
81
|
+
"Synced: Quantity on hold matches in both DB and Redis (#{cached_inventory_item.quantity_on_hold})."
|
|
82
|
+
else
|
|
83
|
+
"Out of sync: Redis shows #{cached_inventory_item.quantity_on_hold} on hold, which doesn't match the database."
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
78
87
|
private
|
|
79
88
|
|
|
80
89
|
def load_inventories
|
|
@@ -7,7 +7,7 @@ module Spree
|
|
|
7
7
|
before_action :load_taxon, only: :create
|
|
8
8
|
|
|
9
9
|
def create
|
|
10
|
-
SpreeCmCommissioner::MaintenanceTasks::Event.pending.
|
|
10
|
+
SpreeCmCommissioner::MaintenanceTasks::Event.pending.create_or_find_by(
|
|
11
11
|
maintainable: @taxon
|
|
12
12
|
).async_execute
|
|
13
13
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
class PricingPreviewsController < ::Spree::Api::V2::BaseController
|
|
6
|
+
# GET /api/v2/tenant/pricing_previews
|
|
7
|
+
def index
|
|
8
|
+
permitted_preview_params = params.permit(SpreeCmCommissioner::PricingModels::OrderContext::PREVIEW_PERMITTED_PARAMS)
|
|
9
|
+
order_context = SpreeCmCommissioner::PricingModels::OrderContext.load!(permitted_preview_params[:order_context])
|
|
10
|
+
pricing_models_updated_at = SpreeCmCommissioner::PricingModel.joins(:vendor)
|
|
11
|
+
.where(spree_vendors: { tenant_id: nil })
|
|
12
|
+
.maximum(:updated_at)
|
|
13
|
+
cache_key = 'pricing:preview:v2:' \
|
|
14
|
+
"#{Digest::SHA256.hexdigest(permitted_preview_params.to_json)}:" \
|
|
15
|
+
"#{pricing_models_updated_at.to_i}"
|
|
16
|
+
|
|
17
|
+
result = Rails.cache.fetch(cache_key, expires_in: 60.seconds) do
|
|
18
|
+
SpreeCmCommissioner::PricingModels::Preview.call(order_context: order_context)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if result.success?
|
|
22
|
+
render_serialized_payload { serialize_resource(result.value[:pricing_preview]) }
|
|
23
|
+
else
|
|
24
|
+
render_error_payload(result.error, 422)
|
|
25
|
+
end
|
|
26
|
+
rescue ArgumentError => e
|
|
27
|
+
render_error_payload(e.message, 422)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def resource_serializer
|
|
33
|
+
SpreeCmCommissioner::V2::Storefront::PricingPreviewSerializer
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -29,6 +29,7 @@ module Spree
|
|
|
29
29
|
result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
|
|
30
30
|
order: spree_current_order,
|
|
31
31
|
remark: params[:remark],
|
|
32
|
+
passenger_count: params[:passenger_count],
|
|
32
33
|
pickup_map_place_attributes: params[:pickup_map_place_attributes]&.permit(
|
|
33
34
|
:place_name,
|
|
34
35
|
:lat,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class PricingPreviewsController < BaseController
|
|
6
|
+
# GET /api/v2/tenant/pricing_previews
|
|
7
|
+
def index
|
|
8
|
+
permitted_preview_params = params.permit(SpreeCmCommissioner::PricingModels::OrderContext::PREVIEW_PERMITTED_PARAMS)
|
|
9
|
+
order_context = SpreeCmCommissioner::PricingModels::OrderContext.load!(permitted_preview_params[:order_context])
|
|
10
|
+
pricing_models_updated_at = SpreeCmCommissioner::PricingModel.joins(:vendor)
|
|
11
|
+
.where(spree_vendors: { tenant_id: current_tenant.id })
|
|
12
|
+
.maximum(:updated_at)
|
|
13
|
+
cache_key = 'pricing:preview:v2:' \
|
|
14
|
+
"#{Digest::SHA256.hexdigest(permitted_preview_params.to_json)}:" \
|
|
15
|
+
"#{pricing_models_updated_at.to_i}"
|
|
16
|
+
|
|
17
|
+
result = Rails.cache.fetch(cache_key, expires_in: 60.seconds) do
|
|
18
|
+
SpreeCmCommissioner::PricingModels::Preview.call(order_context: order_context)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if result.success?
|
|
22
|
+
render_serialized_payload { serialize_resource(result.value[:pricing_preview]) }
|
|
23
|
+
else
|
|
24
|
+
render_error_payload(result.error, 422)
|
|
25
|
+
end
|
|
26
|
+
rescue ArgumentError => e
|
|
27
|
+
render_error_payload(e.message, 422)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def resource_serializer
|
|
33
|
+
Spree::V2::Tenant::PricingPreviewSerializer
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -48,6 +48,8 @@ module SpreeCmCommissioner
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def line_item_content(line_item)
|
|
51
|
+
return intercity_taxi_line_item_content(line_item) if intercity_taxi?(line_item)
|
|
52
|
+
|
|
51
53
|
text = []
|
|
52
54
|
|
|
53
55
|
text << bold(line_item.product.name.to_s)
|
|
@@ -82,6 +84,8 @@ module SpreeCmCommissioner
|
|
|
82
84
|
text << "Email: #{inline_code(order.email)}" if order.email.present?
|
|
83
85
|
text << "Delivery Address: #{formatted_shipping_address.presence || 'N/A'}" if order.delivery_required?
|
|
84
86
|
|
|
87
|
+
append_intercity_taxi_footer(text) if intercity_taxi_order?
|
|
88
|
+
|
|
85
89
|
if show_details_link && order.guests.any?
|
|
86
90
|
text << ''
|
|
87
91
|
text << 'View Tickets:'
|
|
@@ -125,5 +129,89 @@ module SpreeCmCommissioner
|
|
|
125
129
|
|
|
126
130
|
formatted_rows
|
|
127
131
|
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def intercity_taxi?(line_item)
|
|
136
|
+
trip_for(line_item)&.intercity_taxi?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def intercity_taxi_order?
|
|
140
|
+
selected_line_items.any? { |li| intercity_taxi?(li) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def trip_for(line_item)
|
|
144
|
+
return nil unless line_item.respond_to?(:trip_id) && line_item.trip_id.present?
|
|
145
|
+
|
|
146
|
+
@trip_cache ||= {}
|
|
147
|
+
@trip_cache[line_item.trip_id] ||= SpreeCmCommissioner::Trip.find_by(id: line_item.trip_id)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def intercity_taxi_line_item_content(line_item)
|
|
151
|
+
text = []
|
|
152
|
+
route_text = taxi_route_text(line_item)
|
|
153
|
+
departure_text = taxi_departure_text(line_item)
|
|
154
|
+
text << bold('🚗 Trip Details')
|
|
155
|
+
text << "Route: #{route_text}" if route_text.present?
|
|
156
|
+
text << "Passengers: #{line_item.passenger_count || line_item.quantity}"
|
|
157
|
+
text << "Departure: #{departure_text}" if departure_text.present?
|
|
158
|
+
|
|
159
|
+
text.compact.join("\n")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def taxi_route_text(line_item)
|
|
163
|
+
trip = trip_for(line_item)
|
|
164
|
+
return line_item.product.name if trip.blank?
|
|
165
|
+
|
|
166
|
+
origin = trip.origin_place&.name
|
|
167
|
+
destination = trip.destination_place&.name
|
|
168
|
+
vehicle = trip.vehicle_type&.name
|
|
169
|
+
|
|
170
|
+
parts = [origin, destination].compact_blank.join(' → ')
|
|
171
|
+
vehicle.present? ? "#{parts} (#{vehicle})" : parts
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def taxi_departure_text(line_item)
|
|
175
|
+
return nil unless line_item.date_present?
|
|
176
|
+
|
|
177
|
+
line_item.from_date.strftime('%b %d, %Y · %H:%M')
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def nationality_text
|
|
181
|
+
values = order.saved_guests.map { |sg| sg.nationality&.name.presence || sg.nationality_group&.humanize }.compact.uniq
|
|
182
|
+
return nil if values.empty?
|
|
183
|
+
|
|
184
|
+
values.join(', ')
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def append_intercity_taxi_footer(text)
|
|
188
|
+
text << "Nationality: #{nationality_text}" if nationality_text.present?
|
|
189
|
+
|
|
190
|
+
pickup = selected_line_items.filter_map(&:pickup_map_place).first
|
|
191
|
+
dropoff = selected_line_items.filter_map(&:dropoff_map_place).first
|
|
192
|
+
|
|
193
|
+
append_map_place(text, '📍 Pick-up Location', pickup) if pickup
|
|
194
|
+
append_map_place(text, '📍 Drop-off Location', dropoff) if dropoff
|
|
195
|
+
|
|
196
|
+
remark = selected_line_items.filter_map { |li| li.remark.presence }.first
|
|
197
|
+
return if remark.blank?
|
|
198
|
+
|
|
199
|
+
text << ''
|
|
200
|
+
text << bold('🗒️ Notes to Driver')
|
|
201
|
+
text << ERB::Util.html_escape(remark)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def append_map_place(text, title, map_place)
|
|
205
|
+
text << ''
|
|
206
|
+
text << bold(title)
|
|
207
|
+
text << "Address: #{ERB::Util.html_escape(map_place.place_name)}" if map_place.place_name.present?
|
|
208
|
+
text << "Google Maps: #{ERB::Util.html_escape(google_maps_link(map_place))}" if google_maps_link(map_place).present?
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def google_maps_link(map_place)
|
|
212
|
+
return nil if map_place.lat.blank? || map_place.lng.blank?
|
|
213
|
+
|
|
214
|
+
"https://www.google.com/maps/search/?api=1&query=#{map_place.lat},#{map_place.lng}"
|
|
215
|
+
end
|
|
128
216
|
end
|
|
129
217
|
end
|
|
@@ -13,11 +13,11 @@ module SpreeCmCommissioner
|
|
|
13
13
|
def execute
|
|
14
14
|
scope
|
|
15
15
|
.where(default_state_id: state_id)
|
|
16
|
-
.where(
|
|
17
|
-
.where('
|
|
18
|
-
|
|
16
|
+
.where('spree_variants.track_inventory = FALSE OR cm_inventory_items.inventory_date BETWEEN ? AND ?', from_date, to_date)
|
|
17
|
+
.where('spree_variants.track_inventory = FALSE OR cm_inventory_items.quantity_available > 0')
|
|
18
|
+
.where('COALESCE(CAST(spree_variants.public_metadata->\'cm_options\'->>\'number-of-adults\' AS INTEGER), 0) +
|
|
19
|
+
COALESCE(CAST(spree_variants.public_metadata->\'cm_options\'->>\'number-of-kids\' AS INTEGER), 0) >= ?', number_of_guests
|
|
19
20
|
)
|
|
20
|
-
.where('inventory_items.quantity_available > 0')
|
|
21
21
|
.distinct
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -25,7 +25,8 @@ module SpreeCmCommissioner
|
|
|
25
25
|
|
|
26
26
|
def scope
|
|
27
27
|
Spree::Vendor
|
|
28
|
-
.joins(
|
|
28
|
+
.joins(products: :variants)
|
|
29
|
+
.joins('LEFT OUTER JOIN cm_inventory_items ON cm_inventory_items.variant_id = spree_variants.id')
|
|
29
30
|
.where(primary_product_type: :accommodation, state: :active)
|
|
30
31
|
end
|
|
31
32
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module InventoryItems
|
|
3
3
|
class RecentlyChangedFinder
|
|
4
|
-
def initialize(time_range: 7.days.ago, limit: 1000, vendor_id: nil,
|
|
4
|
+
def initialize(time_range: 7.days.ago, limit: 1000, vendor_id: nil, product_type: nil)
|
|
5
5
|
@time_range = time_range
|
|
6
6
|
@limit = limit
|
|
7
7
|
@vendor_id = vendor_id
|
|
8
|
-
@
|
|
8
|
+
@product_type = product_type
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
# Finds recently changed items with Redis comparison
|
|
@@ -22,13 +22,15 @@ module SpreeCmCommissioner
|
|
|
22
22
|
|
|
23
23
|
def fetch_recent_items
|
|
24
24
|
query = SpreeCmCommissioner::InventoryItem.active
|
|
25
|
-
.where('cm_inventory_items.updated_at > ?', @time_range)
|
|
26
25
|
.joins(variant: :product)
|
|
26
|
+
.where(spree_variants: { track_inventory: true })
|
|
27
|
+
.where('cm_inventory_items.updated_at > ?', @time_range)
|
|
27
28
|
.select(
|
|
28
29
|
'cm_inventory_items.id',
|
|
29
30
|
'cm_inventory_items.variant_id',
|
|
30
31
|
'cm_inventory_items.inventory_date',
|
|
31
32
|
'cm_inventory_items.quantity_available',
|
|
33
|
+
'cm_inventory_items.quantity_on_hold',
|
|
32
34
|
'cm_inventory_items.updated_at',
|
|
33
35
|
'cm_inventory_items.product_type',
|
|
34
36
|
'spree_variants.sku',
|
|
@@ -36,16 +38,7 @@ module SpreeCmCommissioner
|
|
|
36
38
|
'spree_products.slug as product_slug'
|
|
37
39
|
)
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
query = case @filter_type
|
|
41
|
-
when 'permanent'
|
|
42
|
-
query.where(cm_inventory_items: { inventory_date: nil })
|
|
43
|
-
when 'non_permanent'
|
|
44
|
-
query.where.not(cm_inventory_items: { inventory_date: nil })
|
|
45
|
-
else
|
|
46
|
-
query
|
|
47
|
-
end
|
|
48
|
-
|
|
41
|
+
query = query.where(cm_inventory_items: { product_type: @product_type }) if @product_type.present?
|
|
49
42
|
query = query.where(spree_variants: { vendor_id: @vendor_id }) if @vendor_id.present?
|
|
50
43
|
|
|
51
44
|
query.limit(@limit).order('cm_inventory_items.updated_at DESC')
|
|
@@ -55,34 +48,51 @@ module SpreeCmCommissioner
|
|
|
55
48
|
def fetch_quantity_in_redis_batch(items)
|
|
56
49
|
return [] if items.empty?
|
|
57
50
|
|
|
58
|
-
|
|
51
|
+
stock_keys = items.map(&:redis_key)
|
|
52
|
+
hold_keys = items.map(&:redis_hold_key)
|
|
53
|
+
all_keys = stock_keys + hold_keys
|
|
59
54
|
|
|
60
55
|
# Use mget for efficient batch Redis fetch
|
|
61
|
-
|
|
62
|
-
redis.mget(*
|
|
56
|
+
all_values = SpreeCmCommissioner.inventory_redis_pool.with do |redis|
|
|
57
|
+
redis.mget(*all_keys)
|
|
63
58
|
end
|
|
64
59
|
|
|
60
|
+
quantity_in_redis_array = all_values.first(items.length)
|
|
61
|
+
on_hold_in_redis_array = all_values.last(items.length)
|
|
62
|
+
|
|
65
63
|
# Combine with DB data
|
|
66
64
|
items.each_with_index.map do |item, index|
|
|
67
|
-
|
|
68
|
-
quantity_available = item.quantity_available
|
|
69
|
-
|
|
70
|
-
{
|
|
71
|
-
inventory_item_id: item.id,
|
|
72
|
-
variant_id: item.variant_id,
|
|
73
|
-
product_name: item.product_name,
|
|
74
|
-
product_slug: item.product_slug,
|
|
75
|
-
sku: item.sku,
|
|
76
|
-
inventory_date: item.inventory_date,
|
|
77
|
-
quantity_available: quantity_available,
|
|
78
|
-
quantity_in_redis: quantity_in_redis,
|
|
79
|
-
difference: quantity_in_redis ? (quantity_in_redis - quantity_available) : nil,
|
|
80
|
-
out_of_sync: quantity_in_redis.nil? || quantity_in_redis != quantity_available,
|
|
81
|
-
last_updated: item.updated_at,
|
|
82
|
-
product_type: item.product_type
|
|
83
|
-
}
|
|
65
|
+
build_item_hash(item, index, quantity_in_redis_array, on_hold_in_redis_array)
|
|
84
66
|
end
|
|
85
67
|
end
|
|
68
|
+
|
|
69
|
+
def build_item_hash(item, index, quantity_in_redis_array, on_hold_in_redis_array)
|
|
70
|
+
quantity_in_redis = quantity_in_redis_array[index]&.to_i
|
|
71
|
+
quantity_available = item.quantity_available
|
|
72
|
+
on_hold_in_redis = on_hold_in_redis_array[index]&.to_i
|
|
73
|
+
quantity_on_hold = item.quantity_on_hold
|
|
74
|
+
|
|
75
|
+
# for :out_of_sync, :on_hold_out_of_sync => nil quantity does mean no data on redis,
|
|
76
|
+
# but does not necessarily mean it's out of sync
|
|
77
|
+
{
|
|
78
|
+
inventory_item_id: item.id,
|
|
79
|
+
variant_id: item.variant_id,
|
|
80
|
+
product_name: item.product_name,
|
|
81
|
+
product_slug: item.product_slug,
|
|
82
|
+
sku: item.sku,
|
|
83
|
+
inventory_date: item.inventory_date,
|
|
84
|
+
quantity_available: quantity_available,
|
|
85
|
+
quantity_in_redis: quantity_in_redis,
|
|
86
|
+
difference: quantity_in_redis ? (quantity_in_redis - quantity_available) : nil,
|
|
87
|
+
out_of_sync: !quantity_in_redis.nil? && quantity_in_redis != quantity_available,
|
|
88
|
+
quantity_on_hold: quantity_on_hold,
|
|
89
|
+
on_hold_in_redis: on_hold_in_redis,
|
|
90
|
+
on_hold_difference: on_hold_in_redis ? (on_hold_in_redis - quantity_on_hold) : nil,
|
|
91
|
+
on_hold_out_of_sync: !on_hold_in_redis.nil? && on_hold_in_redis != quantity_on_hold,
|
|
92
|
+
last_updated: item.updated_at,
|
|
93
|
+
product_type: item.product_type
|
|
94
|
+
}
|
|
95
|
+
end
|
|
86
96
|
end
|
|
87
97
|
end
|
|
88
98
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module InventoryHolds
|
|
3
|
+
class BulkReleaseStalePaymentLockedJob < SpreeCmCommissioner::ApplicationJob
|
|
4
|
+
queue_as :inventory_hold
|
|
5
|
+
|
|
6
|
+
def perform
|
|
7
|
+
SpreeCmCommissioner::InventoryHolds::BulkReleaseStalePaymentLocked.call!
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module InventoryHolds
|
|
3
|
+
# Primary release for :active holds. Scheduled at hold creation (wait_until: expires_at).
|
|
4
|
+
# Safety net: BulkReleaseStaleJob catches any active holds this job misses.
|
|
5
|
+
class ReleaseJob < SpreeCmCommissioner::ApplicationJob
|
|
6
|
+
queue_as :inventory_hold
|
|
7
|
+
|
|
8
|
+
def perform(options = {})
|
|
9
|
+
hold = SpreeCmCommissioner::InventoryHold.find(options[:hold_id])
|
|
10
|
+
|
|
11
|
+
# Skip if already finalized (e.g. delayed or retried jobs).
|
|
12
|
+
# The release service also enforces idempotency.
|
|
13
|
+
return if hold.finalized?
|
|
14
|
+
|
|
15
|
+
# When LockForPayment runs (triggered on payment creation), the hold transitions
|
|
16
|
+
# to :payment_locked status. ReleaseJob must not touch those holds —
|
|
17
|
+
# BulkReleaseStalePaymentLockedJob (cron) is the sole cleanup mechanism for them,
|
|
18
|
+
# allowing future custom logic (e.g. extend instead of release when payments exist).
|
|
19
|
+
return if hold.payment_locked?
|
|
20
|
+
|
|
21
|
+
SpreeCmCommissioner::InventoryHolds::Release.call!(
|
|
22
|
+
hold: hold,
|
|
23
|
+
reason: :hold_expired,
|
|
24
|
+
scheduled_release: true
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module InventoryItems
|
|
3
|
+
class BulkAdjustQuantitiesOnHoldJob < ApplicationUniqueJob
|
|
4
|
+
queue_as :default
|
|
5
|
+
|
|
6
|
+
# :order_id, :inventory_id_and_quantities
|
|
7
|
+
#
|
|
8
|
+
# :order_id is included for unique job key generation to prevent duplicate jobs,
|
|
9
|
+
# though it's not used in the perform method implementation.
|
|
10
|
+
def perform(options = {})
|
|
11
|
+
raise ArgumentError, 'order_id is required' if options[:order_id].blank?
|
|
12
|
+
raise ArgumentError, 'inventory_id_and_quantities is required' if options[:inventory_id_and_quantities].blank?
|
|
13
|
+
|
|
14
|
+
SpreeCmCommissioner::InventoryItems::BulkAdjustQuantitiesOnHold.call(
|
|
15
|
+
inventory_id_and_quantities: options[:inventory_id_and_quantities]
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|