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 +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb +14 -12
- data/app/interactors/spree_cm_commissioner/inventory_item_syncer.rb +4 -0
- data/app/jobs/spree_cm_commissioner/seats/bulk_release_on_hold_blocks_job.rb +26 -0
- data/app/jobs/spree_cm_commissioner/seats/release_expired_blocks_job.rb +23 -0
- data/app/models/concerns/spree_cm_commissioner/event_metadata.rb +11 -3
- data/app/models/spree_cm_commissioner/seat_layout.rb +7 -9
- data/app/models/spree_cm_commissioner/seats/blocks_holder.rb +0 -1
- data/app/request_schemas/spree_cm_commissioner/seat_layout_schema.rb +7 -0
- data/app/services/spree_cm_commissioner/seats/bulk_release_on_hold_blocks.rb +42 -0
- data/app/services/spree_cm_commissioner/seats/release_expired_blocks.rb +34 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +7 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d30ecd3195208c3763b429a17013f18a5582fd162581838135ae0736e8e7be60
         | 
| 4 | 
            +
              data.tar.gz: d11dea0d955b0f7d6d2b62ed907d1b3996d34a89789b705192068b5941e8b9ac
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c1a81f26c31ca303b364d34f8976ecb53d5b924d1515ba633085975c4297772f63ef780ccd93e300cd01f310bb59708cf7386993bd081897a69c8baee84d7828
         | 
| 7 | 
            +
              data.tar.gz: 3a5fcd628e1698a57a7a3e43da83ff2dfe43831feccd5b41a385ec1405b22c3da4867a89eaed3fdc4ceefd57f7c893ae42e28dc6075746dad78512159d09eb5f
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
| @@ -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 | 
            -
                  #  | 
| 17 | 
            -
                  #  | 
| 18 | 
            -
                  # | 
| 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 | 
            -
                    #  | 
| 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 | 
            -
                    #  | 
| 37 | 
            -
                     | 
| 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,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
         | 
    
        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. | 
| 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- | 
| 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
         |