spree_cm_commissioner 2.3.0.pre.pre5 → 2.3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acb7fbb875f336918c3f656d38400951956f6e4ae5c1cefa44ce946912334c17
4
- data.tar.gz: 4ec51fea1eb1807a071805fe98d175ad631c009198ee1ea28ffdcfd396176cd2
3
+ metadata.gz: d30ecd3195208c3763b429a17013f18a5582fd162581838135ae0736e8e7be60
4
+ data.tar.gz: d11dea0d955b0f7d6d2b62ed907d1b3996d34a89789b705192068b5941e8b9ac
5
5
  SHA512:
6
- metadata.gz: 9fe75fa019c2603487547366cf6c9d579c33a1d51559eb7b61a243806c3a11d244522fad83f4afb17a724af9ffb03077a65a9ca313b566ced63ae4fe0b0f97e4
7
- data.tar.gz: 6c1674cefe433ac9cbe1f934397eb63818d738a7c3ecbc614fd66451ade6dab719a1643ff1314d62ca79794c60829129bddb7bd191a24a69fb09bfa39f10d098
6
+ metadata.gz: c1a81f26c31ca303b364d34f8976ecb53d5b924d1515ba633085975c4297772f63ef780ccd93e300cd01f310bb59708cf7386993bd081897a69c8baee84d7828
7
+ data.tar.gz: 3a5fcd628e1698a57a7a3e43da83ff2dfe43831feccd5b41a385ec1405b22c3da4867a89eaed3fdc4ceefd57f7c893ae42e28dc6075746dad78512159d09eb5f
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.3.0.pre.pre5)
37
+ spree_cm_commissioner (2.3.0.pre.pre6)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -8,26 +8,21 @@ module Spree
8
8
  module V2
9
9
  module Storefront
10
10
  class SeatLayoutsController < Spree::Api::V2::ResourceController
11
- # GET /api/v2/storefront/seat_layouts?
12
- # - variant_ids[]=[1,2]
13
- # - include_details=true|false (optional, default: true)
14
- def index
15
- render_serialized_payload do
16
- collection_serializer.new(
17
- collection,
18
- { include: resource_includes, params: serializer_params }
19
- ).serializable_hash
20
- end
21
- end
22
-
23
11
  # GET /api/v2/storefront/seat_layouts/:id
24
12
  # - include_details=true|false (optional, default: true)
25
13
  def show
26
14
  render_serialized_payload { serialize_resource(resource) }
27
15
  end
28
16
 
17
+ # This method is used by index:
18
+ # GET /api/v2/storefront/seat_layouts?
19
+ # - variant_ids[]=[1,2]
20
+ # - include_details=true|false (optional, default: true)
21
+ #
29
22
  # override
30
23
  def collection
24
+ return @collection if defined?(@collection)
25
+
31
26
  scope = if include_details?
32
27
  SpreeCmCommissioner::SeatLayout.active.includes(:top_level_blocks, seat_sections: :blocks)
33
28
  else
@@ -56,6 +51,13 @@ module Spree
56
51
  ]
57
52
  end
58
53
 
54
+ # override
55
+ def required_schema
56
+ return nil unless action_name == 'index'
57
+
58
+ SpreeCmCommissioner::SeatLayoutSchema
59
+ end
60
+
59
61
  # override
60
62
  def resource_serializer
61
63
  SpreeCmCommissioner::V2::Storefront::SeatLayoutSerializer
@@ -25,6 +25,10 @@ module SpreeCmCommissioner
25
25
  # Why? Clamping masks bugs in Redis deduction. If Redis deducted 5 but this
26
26
  # job tries to deduct 10, clamping silently loses the 5-unit discrepancy.
27
27
  # Validation errors are better than silent data loss.
28
+ #
29
+ # ❌ DO NOT use increment! because it skips model validations.
30
+ # We need validations to run so negative quantities are caught and surfaced
31
+ # as errors, not silently allowed by the database.
28
32
  inventory_item.update!(quantity_available: inventory_item.quantity_available + quantity)
29
33
  end
30
34
 
@@ -0,0 +1,26 @@
1
+ module SpreeCmCommissioner
2
+ module Seats
3
+ class BulkReleaseOnHoldBlocksJob < ApplicationJob
4
+ queue_as :default
5
+
6
+ # Thin wrapper that calls BulkReleaseOnHoldBlocks service.
7
+ # Handles error logging and re-raising for Sidekiq retries.
8
+ #
9
+ # Args:
10
+ # cutoff_days: Number of days before considering a block "stale" (default: 7)
11
+ def perform(cutoff_days = 7)
12
+ SpreeCmCommissioner::Seats::BulkReleaseOnHoldBlocks.new(cutoff_days: cutoff_days).call
13
+ rescue StandardError => e
14
+ CmAppLogger.error(
15
+ label: 'SpreeCmCommissioner::Seats::BulkReleaseOnHoldBlocksJob#perform',
16
+ data: {
17
+ error_class: e.class.name,
18
+ error_message: e.message,
19
+ backtrace: e.backtrace&.first(10)&.join("\n")
20
+ }
21
+ )
22
+ raise
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module SpreeCmCommissioner
2
+ module Seats
3
+ class ReleaseExpiredBlocksJob < ApplicationJob
4
+ queue_as :default
5
+
6
+ # Thin wrapper that calls ReleaseExpiredBlocks service.
7
+ # Handles error logging and re-raising for Sidekiq retries.
8
+ def perform
9
+ SpreeCmCommissioner::Seats::ReleaseExpiredBlocks.new.call
10
+ rescue StandardError => e
11
+ CmAppLogger.error(
12
+ label: 'SpreeCmCommissioner::Seats::ReleaseExpiredBlocksJob#perform',
13
+ data: {
14
+ error_class: e.class.name,
15
+ error_message: e.message,
16
+ backtrace: e.backtrace&.first(10)&.join("\n")
17
+ }
18
+ )
19
+ raise
20
+ end
21
+ end
22
+ end
23
+ end
@@ -13,9 +13,17 @@ module SpreeCmCommissioner
13
13
  # For certain large events, admin can disable this manually.
14
14
  store_public_metadata :allow_manual_search_for_operator, :boolean, default: true
15
15
 
16
- # We store the preload_seat_layout_ids in public_metadata which lets
17
- # us check if a seat layout exists without triggering a database query.
18
- # The IDs are automatically updated whenever the seat_layout is saved.
16
+ # ⚠️ CRITICAL: Cached seat layout IDs kept in sync by SeatLayout callback.
17
+ # If callback is removed/modified without updating this, availability checks will fail.
18
+ #
19
+ # Sync callback (see seat_layout.rb:L26-35):
20
+ # after_commit :sync_seat_layout_id_to_layoutable!, if: -> { layoutable.present? }
21
+ # def sync_seat_layout_id_to_layoutable!
22
+ # layoutable.update!(preload_seat_layout_ids: layoutable.seat_layouts.pluck(:id))
23
+ # end
24
+ #
25
+ # TODO: Not scalable for many seat layouts. Consider storing boolean flag
26
+ # (has_active_seat_layout) instead when seat layouts become frequent.
19
27
  store_public_metadata :preload_seat_layout_ids, :array, default: []
20
28
 
21
29
  before_validation :validate_at_least_one_check_in_flow_presence
@@ -23,23 +23,21 @@ module SpreeCmCommissioner
23
23
 
24
24
  accepts_nested_attributes_for :seat_sections, :top_level_blocks, allow_destroy: true
25
25
 
26
+ # ⚠️ CRITICAL: Syncs preload_seat_layout_ids metadata to layoutable.
27
+ # DO NOT REMOVE without implementing alternative sync (see event_metadata.rb:L16-24).
26
28
  after_commit :sync_seat_layout_id_to_layoutable!, if: -> { layoutable.present? }
27
29
 
28
30
  private
29
31
 
30
32
  def sync_seat_layout_id_to_layoutable!
33
+ # Update layoutable's seat layout metadata after any change (create/update/destroy).
34
+ # This keeps the cache in sync with the actual seat_layouts association.
31
35
  if layoutable.respond_to?(:seat_layouts)
32
- # for layoutable (eg. event) that has many seat layouts, we update preload_seat_layout_ids attribute.
33
- # either as a column or as a key in a JSONB column (e.g., metadata)
36
+ # Event has many seat layouts: cache all IDs for quick availability checks.
34
37
  layoutable.update!(preload_seat_layout_ids: layoutable.seat_layouts.pluck(:id))
35
38
  elsif layoutable.respond_to?(:seat_layout)
36
- # layoutable (eg. taxon, vehicle) must have preload_seat_layout_id attribute,
37
- # either as a column or as a key in a JSONB column (e.g., metadata)
38
- if destroyed?
39
- layoutable.update!(preload_seat_layout_id: nil)
40
- else
41
- layoutable.update!(preload_seat_layout_id: id)
42
- end
39
+ # Taxon/Vehicle has single seat layout: cache the ID (or nil if destroyed).
40
+ layoutable.update!(preload_seat_layout_id: destroyed? ? nil : id)
43
41
  end
44
42
  end
45
43
  end
@@ -26,7 +26,6 @@ module SpreeCmCommissioner
26
26
  guests_with_blocks.each do |guest|
27
27
  next unless guest.can_hold_block?
28
28
 
29
- # here i want to set if guest can hold? (it mean should it hold block & follow inventory logics with count instead)
30
29
  inventory_items.each do |inventory_item|
31
30
  reserved_blocks << hold_specific_block!(inventory_item, guest)
32
31
  end
@@ -0,0 +1,7 @@
1
+ module SpreeCmCommissioner
2
+ class SeatLayoutSchema < ApplicationRequestSchema
3
+ params do
4
+ required(:variant_ids).value(:array).each(:integer)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ module SpreeCmCommissioner
2
+ module Seats
3
+ class BulkReleaseOnHoldBlocks
4
+ def initialize(cutoff_days: 7)
5
+ @cutoff_days = cutoff_days
6
+ end
7
+
8
+ # Bulk releases stale on_hold blocks as a housekeeping job.
9
+ # Runs weekly to catch any blocks that didn't expire properly.
10
+ #
11
+ # This is a FULL release - releases ALL on_hold blocks older than cutoff_days.
12
+ # Complements ReleaseExpiredBlocks which runs every 5 minutes.
13
+ def call
14
+ cutoff_time = @cutoff_days.days.ago
15
+
16
+ stale_blocks = SpreeCmCommissioner::ReservedBlock
17
+ .on_hold
18
+ .where('created_at < ?', cutoff_time)
19
+
20
+ count = stale_blocks.count
21
+
22
+ # Transition all stale blocks to canceled status
23
+ stale_blocks.update_all( # rubocop:disable Rails/SkipsModelValidations
24
+ status: :canceled,
25
+ expired_at: nil,
26
+ updated_by_id: nil,
27
+ updated_at: Time.current
28
+ )
29
+
30
+ CmAppLogger.log(
31
+ label: 'SpreeCmCommissioner::Seats::BulkReleaseOnHoldBlocks#call',
32
+ data: {
33
+ released_count: count,
34
+ cutoff_days: @cutoff_days,
35
+ cutoff_time: cutoff_time,
36
+ job_type: 'bulk_release'
37
+ }
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ module SpreeCmCommissioner
2
+ module Seats
3
+ class ReleaseExpiredBlocks
4
+ # Releases blocks that have expired (hold time exceeded).
5
+ # Runs every 5 minutes to keep expired holds from accumulating.
6
+ #
7
+ # This is a PARTIAL release - only releases blocks where expired_at has passed.
8
+ # Complements BulkReleaseOnHoldBlocks which does weekly housekeeping.
9
+ def call
10
+ expired_blocks = SpreeCmCommissioner::ReservedBlock
11
+ .on_hold
12
+ .where('expired_at <= ?', Time.current)
13
+
14
+ count = expired_blocks.count
15
+
16
+ # Transition all expired blocks to canceled status
17
+ expired_blocks.update_all( # rubocop:disable Rails/SkipsModelValidations
18
+ status: :canceled,
19
+ expired_at: nil,
20
+ updated_by: nil,
21
+ updated_at: Time.current
22
+ )
23
+
24
+ CmAppLogger.log(
25
+ label: 'SpreeCmCommissioner::Seats::ReleaseExpiredBlocks#call',
26
+ data: {
27
+ released_count: count,
28
+ job_type: 'partial_release'
29
+ }
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.3.0-pre5'.freeze
2
+ VERSION = '2.3.0-pre6'.freeze
3
3
 
4
4
  module_function
5
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_cm_commissioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0.pre.pre5
4
+ version: 2.3.0.pre.pre6
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-29 00:00:00.000000000 Z
11
+ date: 2025-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -1309,6 +1309,8 @@ files:
1309
1309
  - app/jobs/spree_cm_commissioner/product_event_id_to_children_syncer_job.rb
1310
1310
  - app/jobs/spree_cm_commissioner/queue_order_webhooks_requests_job.rb
1311
1311
  - app/jobs/spree_cm_commissioner/reports_assigner_job.rb
1312
+ - app/jobs/spree_cm_commissioner/seats/bulk_release_on_hold_blocks_job.rb
1313
+ - app/jobs/spree_cm_commissioner/seats/release_expired_blocks_job.rb
1312
1314
  - app/jobs/spree_cm_commissioner/sms_job.rb
1313
1315
  - app/jobs/spree_cm_commissioner/sms_pin_code_job.rb
1314
1316
  - app/jobs/spree_cm_commissioner/state_job.rb
@@ -1738,6 +1740,7 @@ files:
1738
1740
  - app/request_schemas/spree_cm_commissioner/application_request_schema.rb
1739
1741
  - app/request_schemas/spree_cm_commissioner/inventory_item_schema.rb
1740
1742
  - app/request_schemas/spree_cm_commissioner/profile_image_request_schema.rb
1743
+ - app/request_schemas/spree_cm_commissioner/seat_layout_schema.rb
1741
1744
  - app/request_schemas/spree_cm_commissioner/trip_search_request_schema.rb
1742
1745
  - app/request_schemas/spree_cm_commissioner/user_account_linkage_request_schema.rb
1743
1746
  - app/request_schemas/spree_cm_commissioner/user_profile_request_schema.rb
@@ -1946,6 +1949,8 @@ files:
1946
1949
  - app/services/spree_cm_commissioner/role_permissions_constructor.rb
1947
1950
  - app/services/spree_cm_commissioner/role_permissions_loader.rb
1948
1951
  - app/services/spree_cm_commissioner/rsa_service.rb
1952
+ - app/services/spree_cm_commissioner/seats/bulk_release_on_hold_blocks.rb
1953
+ - app/services/spree_cm_commissioner/seats/release_expired_blocks.rb
1949
1954
  - app/services/spree_cm_commissioner/seeds/roles_decorator.rb
1950
1955
  - app/services/spree_cm_commissioner/transit/base_route_order_metrics_updater.rb
1951
1956
  - app/services/spree_cm_commissioner/transit/legs_builder_service.rb