spree_cm_commissioner 2.4.1 → 2.4.3

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: 4e32e4116a234b679abcfe3a9d7005bd61890d80936839c32330ecf2b421dadf
4
- data.tar.gz: a7759f377e09d49281150257a496e44af61e4cabb67a8bec0bacb20a73ea439d
3
+ metadata.gz: 5f580394367847906251ac52666c73ae79bb1a117948805217616c0a83f9300a
4
+ data.tar.gz: e8b7ae961151a074aab2084e7600b55da2969405143cf1b9e4cd1b95fcd7040b
5
5
  SHA512:
6
- metadata.gz: 5119cedb570e77e3417bf723ea84926231e4d72000686312a194c1cfb4764061b5df9801923a5db629d8b8adccfd8a70378a238a0c099af46abe8f5c6a2da1d0
7
- data.tar.gz: adeee4909603c0294be64345ea3de5b310e2dcaa174669137794b56dd23b800ddd9a1f429da636df23953d80154a3b96835cb19a81274fc0908edaae1ebd13d9
6
+ metadata.gz: 35e303df725eb93bb52a3eceb55672668cea800d77dd7c4e4d938b9caf0585477044e20fe646f2a2a7da5209a4ceff6c78ccf8e6adb862378373a66bd1525d19
7
+ data.tar.gz: f91afe8506292cf778cfb3b8bfdc87f5d2545c2e079abdf5f5d881441c3ae758f218bcbb6798756f860734b5c3af82be30250c2c0a2f5a9e2aea96782d7623ed
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.4.1)
37
+ spree_cm_commissioner (2.4.3)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
data/README.md CHANGED
@@ -525,3 +525,35 @@ def wrap_with_multitenant_without(&block)
525
525
  MultiTenant.without(&block)
526
526
  end
527
527
  ```
528
+
529
+ # Tenant Configuration
530
+
531
+
532
+ This gem provides a flexible multi-tenant system. Each tenant can be configured through an admin interface and accessed via API endpoints.
533
+
534
+ ### Admin Tenant Form Structure
535
+
536
+ The tenant configuration form is organized into several logical sections:
537
+
538
+ - **Basic Information**: General details such as `name`, `slug`, `description`, `host`, and `status`.
539
+ - **Branding & App Links**: Custom branding options, `Android asset links`, and `Apple app site association` data.
540
+ - **Payment Icons**: Upload and manage icons for different payment statuses (`checkout`, `failed`, `success`, `loader`).
541
+ - **Redirect Settings**: Configure `redirect targets` and `excluded paths` for host-based redirection.
542
+
543
+ All sections are visually separated for clarity and improved user experience.
544
+
545
+ ### API: Retrieve Tenant by Host
546
+
547
+ With your `Client-Side` you can retrieve tenant configuration by host using a RESTful API endpoint:
548
+
549
+ ```http
550
+ GET /api/v2/storefront/tenants/:host
551
+ ```
552
+
553
+ Example request:
554
+
555
+ ```
556
+ GET /api/v2/storefront/tenants/example.host.com
557
+ ```
558
+
559
+ The response returns the tenant's configuration in JSON format, including preferences and branding information.
@@ -18,7 +18,9 @@ module Spree
18
18
 
19
19
  # override
20
20
  def collection
21
- @collection ||= collection_finder.new.execute
21
+ @collection ||= collection_finder.new.execute(
22
+ route_type: params[:route_type]
23
+ )
22
24
  end
23
25
 
24
26
  # override
@@ -3,7 +3,7 @@
3
3
  # GET /api/v2/storefront/route_places
4
4
  #
5
5
  # Finds places (origins or destinations) that are connected to a given place through existing routes.
6
- # Optionally filters results by keyword. Supports two modes:
6
+ # Optionally filters results by keyword or route_type. Supports two modes:
7
7
  #
8
8
  # Usage 1: Connected places (requires place_id)
9
9
  # - Finds places connected to a specific place via routes
@@ -27,7 +27,7 @@
27
27
  # - Returns empty collection if place_type is invalid
28
28
  # - If place_id provided: returns places connected to that place
29
29
  # - If place_id blank: returns all origins/destinations
30
- # - Filters results by keyword if provided
30
+ # - Filters results by keyword or route_type if provided
31
31
  #
32
32
  # @example Mode 1: Find destinations connected to origin place ID 123
33
33
  # GET /api/v2/storefront/route_places?place_id=123&place_type=destination
@@ -49,7 +49,8 @@ module Spree
49
49
  @collection ||= collection_finder.new(
50
50
  place_type: params[:place_type],
51
51
  place_id: params[:place_id],
52
- keyword: params[:query]
52
+ keyword: params[:query],
53
+ route_type: params[:route_type]
53
54
  ).execute
54
55
  end
55
56
 
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class TenantsController < ::Spree::Api::V2::BaseController
6
+ def show
7
+ render_serialized_payload { serialize_resource(resource) }
8
+ end
9
+
10
+ private
11
+
12
+ # override
13
+ def resource
14
+ model_class.find_by(host: params[:id])
15
+ end
16
+
17
+ # override
18
+ def model_class
19
+ SpreeCmCommissioner::Tenant
20
+ end
21
+
22
+ # override
23
+ def resource_serializer
24
+ Spree::V2::Storefront::TenantSerializer
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -3,48 +3,78 @@
3
3
  # @param place_type [String] Required. 'origin' or 'destination'
4
4
  # @param place_id [Integer] Optional. Filter by connected place ID
5
5
  # @param keyword [String] Optional. Filter by place name (case-insensitive)
6
+ # @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
6
7
  #
7
8
  # @return [ActiveRecord::Relation<SpreeCmCommissioner::Place>]
8
9
  #
9
10
  # Modes:
10
11
  # - place_id only: returns connected places (origins TO or destinations FROM given place)
11
12
  # - keyword only: filters by name
12
- # - both: connected places filtered by name
13
+ # - route_type only: filters by trip route types on those routes
14
+ # - combinations: connected places filtered by name and/or route type
13
15
  # - neither: all origins/destinations in routes
14
16
  #
15
- # @example Origins with routes to place 123
16
- # FindWithRoute.new(place_type: 'origin', place_id: 123).execute
17
+ # @example Origins with ferry trips to place 123
18
+ # FindWithRoute.new(place_type: 'origin', place_id: 123, route_type: :ferry).execute
17
19
  #
18
- # @example Destinations filtered by keyword
19
- # FindWithRoute.new(place_type: 'destination', keyword: 'Phnom').execute
20
+ # @example Destinations filtered by keyword and route type
21
+ # FindWithRoute.new(place_type: 'destination', keyword: 'Phnom', route_type: :bus).execute
20
22
  module SpreeCmCommissioner
21
23
  module Places
22
24
  class FindWithRoute
23
- attr_reader :place_type, :place_id, :keyword
25
+ attr_reader :place_type, :place_id, :keyword, :route_type
24
26
 
25
- def initialize(place_type:, place_id: nil, keyword: nil)
27
+ def initialize(place_type:, place_id: nil, keyword: nil, route_type: nil)
26
28
  @place_type = place_type
27
29
  @place_id = place_id
28
30
  @keyword = keyword
31
+ @route_type = route_type
29
32
  end
30
33
 
31
34
  def execute
32
35
  return SpreeCmCommissioner::Place.none if place_type.blank?
33
-
34
- result = if place_type == 'origin'
35
- scope = SpreeCmCommissioner::Place.with_routes_as_origin
36
- place_id.present? ? scope.where(cm_routes: { destination_place_id: place_id }) : scope
37
- else
38
- scope = SpreeCmCommissioner::Place.with_routes_as_destination
39
- place_id.present? ? scope.where(cm_routes: { origin_place_id: place_id }) : scope
40
- end
41
-
42
36
  return SpreeCmCommissioner::Place.none if place_id.present? && !SpreeCmCommissioner::Place.exists?(place_id)
43
37
 
44
- # Apply keyword filter if provided
45
- result = result.where('cm_places.name ILIKE ?', "%#{keyword}%") if keyword.present?
38
+ result = scope
39
+ result = apply_keyword_filter(result) if keyword.present?
40
+ result = apply_route_type_filter(result) if route_type.present?
46
41
  result
47
42
  end
43
+
44
+ private
45
+
46
+ def scope
47
+ return SpreeCmCommissioner::Place.none unless valid_place_type?
48
+
49
+ base_scope = origin? ? SpreeCmCommissioner::Place.with_routes_as_origin : SpreeCmCommissioner::Place.with_routes_as_destination
50
+
51
+ return base_scope if place_id.blank?
52
+
53
+ if origin?
54
+ base_scope.where(routes_as_origin: { destination_place_id: place_id })
55
+ else
56
+ base_scope.where(routes_as_destination: { origin_place_id: place_id })
57
+ end
58
+ end
59
+
60
+ def apply_keyword_filter(result)
61
+ result.where('cm_places.name ILIKE ?', "%#{keyword}%")
62
+ end
63
+
64
+ def apply_route_type_filter(result)
65
+ trips_association = origin? ? :trips_as_origin : :trips_as_destination
66
+ result.joins(trips_association)
67
+ .where(cm_trips: { route_type: route_type.to_sym })
68
+ .distinct
69
+ end
70
+
71
+ def origin?
72
+ place_type == 'origin'
73
+ end
74
+
75
+ def valid_place_type?
76
+ %w[origin destination].include?(place_type)
77
+ end
48
78
  end
49
79
  end
50
80
  end
@@ -4,21 +4,46 @@
4
4
  # 1. fulfilled_order_count (completed orders) - DESC
5
5
  # 2. order_count (total orders including incomplete) - DESC
6
6
  #
7
+ # @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
8
+ #
7
9
  # @return [ActiveRecord::Relation<SpreeCmCommissioner::Route>] Routes with origin and destination places loaded,
8
10
  # ordered from most to least popular
9
11
  #
10
- # @example
12
+ # @example Get all popular routes
13
+ # finder = SpreeCmCommissioner::Routes::FindPopular.new
14
+ # finder.execute # => Returns all routes sorted by fulfillment and order counts
15
+ #
16
+ # @example Get popular ferry routes
17
+ # finder = SpreeCmCommissioner::Routes::FindPopular.new
18
+ # finder.execute(route_type: :ferry) # => Returns ferry routes sorted by popularity
19
+ #
20
+ # @example Get popular bus routes
11
21
  # finder = SpreeCmCommissioner::Routes::FindPopular.new
12
- # finder.execute # => Returns routes sorted by fulfillment and order counts
22
+ # finder.execute(route_type: :bus) # => Returns bus routes sorted by popularity
13
23
 
14
24
  module SpreeCmCommissioner
15
25
  module Routes
16
26
  class FindPopular
17
- def execute
18
- SpreeCmCommissioner::Route
19
- .includes(:origin_place, :destination_place)
20
- .order(Arel.sql('COALESCE(fulfilled_order_count, 0) DESC'))
21
- .order(Arel.sql('COALESCE(order_count, 0) DESC'))
27
+ def execute(route_type: nil)
28
+ scope(route_type: route_type)
29
+ end
30
+
31
+ private
32
+
33
+ def scope(route_type: nil)
34
+ result = SpreeCmCommissioner::Route
35
+ .includes(:origin_place, :destination_place)
36
+
37
+ if route_type.present?
38
+ # Filter routes that have trips with the specified route_type
39
+ route_scope = SpreeCmCommissioner::Route
40
+ .joins(:trips)
41
+ .where(cm_trips: { route_type: route_type.to_sym })
42
+ result = result.where(id: route_scope.select(:id))
43
+ end
44
+
45
+ result.order(Arel.sql('COALESCE(fulfilled_order_count, 0) DESC'))
46
+ .order(Arel.sql('COALESCE(order_count, 0) DESC'))
22
47
  end
23
48
  end
24
49
  end
@@ -10,6 +10,8 @@ module SpreeCmCommissioner
10
10
  preference :payment_success_image, :string, default: ''
11
11
  preference :payment_loader, :string, default: ''
12
12
  preference :brand_primary_color, :string, default: ''
13
+ preference :redirect_target_host, :string, default: ''
14
+ preference :redirect_excluded_paths, :string, default: ''
13
15
  end
14
16
  end
15
17
  end
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
2
2
  module VehicleType
3
3
  extend ActiveSupport::Concern
4
4
 
5
- VEHICLE_TYPES = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus].freeze
5
+ VEHICLE_TYPES = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus ferry].freeze
6
6
 
7
7
  included do
8
8
  enum vehicle_type: VEHICLE_TYPES if table_exists? && column_names.include?('vehicle_type')
@@ -24,6 +24,9 @@ module SpreeCmCommissioner
24
24
  has_many :routes_as_origin, class_name: 'SpreeCmCommissioner::Route', foreign_key: :origin_place_id
25
25
  has_many :routes_as_destination, class_name: 'SpreeCmCommissioner::Route', foreign_key: :destination_place_id
26
26
 
27
+ has_many :trips_as_origin, through: :routes_as_origin, source: :trips
28
+ has_many :trips_as_destination, through: :routes_as_destination, source: :trips
29
+
27
30
  # Scopes for route-based filtering
28
31
  scope :with_routes_as_origin, -> { joins(:routes_as_origin).distinct }
29
32
  scope :with_routes_as_destination, -> { joins(:routes_as_destination).distinct }
@@ -4,6 +4,7 @@ module SpreeCmCommissioner
4
4
  required(:place_type).filled(:string)
5
5
  optional(:query).maybe(:string)
6
6
  optional(:place_id).maybe(:integer)
7
+ optional(:route_type).maybe(:string)
7
8
  end
8
9
 
9
10
  rule(:place_type) do
@@ -0,0 +1,14 @@
1
+ module Spree
2
+ module V2
3
+ module Storefront
4
+ class TenantSerializer < BaseSerializer
5
+ set_type :tenant
6
+
7
+ attributes :name, :slug, :host, :state
8
+
9
+ attribute :redirect_target_host, &:preferred_redirect_target_host
10
+ attribute :redirect_excluded_paths, &:preferred_redirect_excluded_paths
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,12 @@
1
- <div data-hook="admin_tenant_form_fields" class="row">
2
- <!-- Name Field -->
1
+
2
+ <!-- ======================== Tenant Basic Info ======================== -->
3
+ <div class="card mb-4">
4
+ <div class="card-body">
5
+ <div data-hook="admin_tenant_form_fields" class="row">
6
+ <!-- Basic Info Section -->
7
+ <div class="col-12 mb-4">
8
+ <h4>Basic Information <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(General details about the tenant)</span></h4>
9
+ </div>
3
10
  <div class="col-6">
4
11
  <%= f.field_container :name do %>
5
12
  <%= f.label :name, raw(Spree.t(:name) + required_span_tag) %>
@@ -9,8 +16,6 @@
9
16
  <% end %>
10
17
  <% end %>
11
18
  </div>
12
-
13
- <!-- Slug Field -->
14
19
  <div class="col-6">
15
20
  <%= f.field_container :slug do %>
16
21
  <%= f.label :slug, raw(Spree.t(:slug)) %>
@@ -20,8 +25,6 @@
20
25
  <% end %>
21
26
  <% end %>
22
27
  </div>
23
-
24
- <!-- Description Field -->
25
28
  <div class="col-12">
26
29
  <%= f.field_container :description do %>
27
30
  <%= f.label :description, raw(Spree.t(:description)) %>
@@ -31,8 +34,6 @@
31
34
  <% end %>
32
35
  <% end %>
33
36
  </div>
34
-
35
- <!-- Host Field -->
36
37
  <div class="col-12">
37
38
  <%= f.field_container :host do %>
38
39
  <%= f.label :host, raw(Spree.t(:host) + required_span_tag) %>
@@ -42,8 +43,37 @@
42
43
  <% end %>
43
44
  <% end %>
44
45
  </div>
46
+ <div class="col-6">
47
+ <%= f.field_container :state do %>
48
+ <%= f.label :state, raw(Spree.t(:state)) %>
49
+ <%= f.select :state,
50
+ options_for_select([["Enabled", "enabled"], ["Disabled", "disabled"]], f.object&.state),
51
+ { required: true },
52
+ class: 'select2 form-control' %>
53
+ <% if f.object.errors[:state].any? %>
54
+ <div class="error text-danger"><%= f.object.errors[:state].join(', ') %></div>
55
+ <% end %>
56
+ <% end %>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
45
61
 
46
- <!-- Assetlinks Field -->
62
+ <!-- ======================== Branding & App Links ======================== -->
63
+ <div class="card mb-4">
64
+ <div class="card-body">
65
+ <div data-hook="admin_tenant_form_fields" class="row mt-4">
66
+ <!-- Branding Section -->
67
+ <div class="col-12 mb-4">
68
+ <h4>Branding & App Links <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Brand colors and mobile app association settings)</span></h4>
69
+ </div>
70
+ <div class="col-md-6">
71
+ <%= f.label :preferred_brand_primary_color, Spree.t(:brand_primary_color), class: 'form-label' %>
72
+ <%= f.text_field :preferred_brand_primary_color, class: 'form-control color-picker', placeholder: '#FF5733', value: @object.preferred_brand_primary_color %>
73
+ <div class="color-preview" style="margin-top: 10px;">
74
+ <%= content_tag :div, nil, style: "background-color: #{@object.preferred_brand_primary_color}; width: 60px; height: 60px; border: 1px solid #ccc; box-sizing: border-box; display: block;" %>
75
+ </div>
76
+ </div>
47
77
  <div class="col-12">
48
78
  <%= f.field_container :preferred_assetlinks do %>
49
79
  <%= f.label :preferred_assetlinks, raw(Spree.t(:assetlinks)) %>
@@ -69,8 +99,6 @@
69
99
  <% end %>
70
100
  <% end %>
71
101
  </div>
72
-
73
- <!-- Apple App Site Association Field -->
74
102
  <div class="col-12">
75
103
  <%= f.field_container :preferred_apple_app_site_association do %>
76
104
  <%= f.label :preferred_apple_app_site_association, raw(Spree.t(:apple_app_site_association)) %>
@@ -100,32 +128,18 @@
100
128
  <% end %>
101
129
  <% end %>
102
130
  </div>
103
-
104
- <!-- Brand Primary Color Field -->
105
- <div class="col-md-6">
106
- <%= f.label :preferred_brand_primary_color, Spree.t(:brand_primary_color), class: 'form-label' %>
107
- <%= f.text_field :preferred_brand_primary_color, class: 'form-control color-picker', placeholder: '#FF5733', value: @object.preferred_brand_primary_color %>
108
- <div class="color-preview" style="margin-top: 10px;">
109
- <%= content_tag :div, nil, style: "background-color: #{@object.preferred_brand_primary_color}; width: 60px; height: 60px; border: 1px solid #ccc; box-sizing: border-box; display: block;" %>
110
131
  </div>
111
132
  </div>
112
-
113
- <!-- State Field -->
114
- <div class="col-6">
115
- <%= f.field_container :state do %>
116
- <%= f.label :state, raw(Spree.t(:state)) %>
117
- <%= f.select :state,
118
- options_for_select([['Enabled', 'enabled'], ['Disabled', 'disabled']], f.object&.state),
119
- { required: true },
120
- class: 'select2 form-control' %>
121
- <% if f.object.errors[:state].any? %>
122
- <div class="error text-danger"><%= f.object.errors[:state].join(', ') %></div>
123
- <% end %>
124
- <% end %>
125
- </div>
126
133
  </div>
127
134
 
128
- <div data-hook="admin_tenant_form_fields" class="row">
135
+ <!-- ======================== Payment Icons ======================== -->
136
+ <div class="card mb-4">
137
+ <div class="card-body">
138
+ <div data-hook="admin_tenant_form_fields" class="row mt-4">
139
+ <!-- Payment Icons Section -->
140
+ <div class="col-12 mb-4">
141
+ <h4>Payment Icons <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Customize payment status icons for tenant checkout experience)</span></h4>
142
+ </div>
129
143
  <div class="col-12">
130
144
  <div class="row gy-4">
131
145
  <% payment_icon_fields = [
@@ -134,7 +148,6 @@
134
148
  { field: :preferred_payment_success_image, label: Spree.t(:payment_success_image), target: 'success' },
135
149
  { field: :preferred_payment_loader, label: Spree.t(:payment_loader), target: 'loader' }
136
150
  ] %>
137
-
138
151
  <% payment_icon_fields.each do |icon| %>
139
152
  <div class="col-md-3">
140
153
  <%= f.field_container icon[:field] do %>
@@ -142,7 +155,6 @@
142
155
  <label class="form-label fw-bold mb-2" for="tenant_<%= icon[:field] %>">
143
156
  <%= icon[:label] %>
144
157
  </label>
145
-
146
158
  <div class="icon-preview-container mb-2" id="icon-preview-container-<%= icon[:field] %>">
147
159
  <span class="payment-icon-preview" id="icon-preview-<%= icon[:field] %>">
148
160
  <% if f.object.send(icon[:field]).present? %>
@@ -154,7 +166,6 @@
154
166
  <span class="remove-icon-btn <%= 'hidden' unless f.object.send(icon[:field]).present? %>" data-target="<%= icon[:field] %>">&times;</span>
155
167
  </div>
156
168
  </div>
157
-
158
169
  <%= f.collection_select icon[:field],
159
170
  @vector_icons, :path, :path,
160
171
  { include_blank: 'None' },
@@ -167,4 +178,36 @@
167
178
  <% end %>
168
179
  </div>
169
180
  </div>
181
+ </div>
182
+ </div>
183
+ </div>
184
+
185
+ <!-- ======================== Redirect Settings ======================== -->
186
+ <div class="card mb-4">
187
+ <div class="card-body">
188
+ <div data-hook="admin_tenant_form_fields" class="row mt-4">
189
+ <!-- Redirect Settings Section -->
190
+ <div class="col-12 mb-4">
191
+ <h4>Redirect Settings <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Configure host redirection and excluded paths for this tenant)</span></h4>
192
+ </div>
193
+ <div class="col-6">
194
+ <%= f.field_container :preferred_redirect_target_host do %>
195
+ <%= f.label :preferred_redirect_target_host, raw(Spree.t(:redirect_target_host)) %>
196
+ <%= f.text_field :preferred_redirect_target_host, class: 'form-control', placeholder: 'target.example.com' %>
197
+ <% if f.object.errors[:preferred_redirect_target_host].any? %>
198
+ <div class="error text-danger"><%= f.object.errors[:preferred_redirect_target_host].join(', ') %></div>
199
+ <% end %>
200
+ <% end %>
201
+ </div>
202
+ <div class="col-12">
203
+ <%= f.field_container :preferred_redirect_excluded_paths do %>
204
+ <%= f.label :preferred_redirect_excluded_paths, raw(Spree.t(:redirect_excluded_paths)) %>
205
+ <%= f.text_area :preferred_redirect_excluded_paths, class: 'form-control', placeholder: 'eg. /vpago_payments,/.well-known' %>
206
+ <% if f.object.errors[:preferred_redirect_excluded_paths].any? %>
207
+ <div class="error text-danger"><%= f.object.errors[:preferred_redirect_excluded_paths].join(', ') %></div>
208
+ <% end %>
209
+ <% end %>
210
+ </div>
211
+ </div>
212
+ </div>
170
213
  </div>
data/config/routes.rb CHANGED
@@ -579,7 +579,7 @@ Spree::Core::Engine.add_routes do
579
579
  resource :cart, controller: :cart, only: %i[show create destroy] do
580
580
  patch :restart_checkout_flow
581
581
  end
582
-
582
+ resources :tenants, only: %i[show], constraints: { id: /.+/ }
583
583
  resources :trip_places
584
584
  resources :trip_search, only: [:index]
585
585
  resources :trips, only: %i[show]
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.4.1'.freeze
2
+ VERSION = '2.4.3'.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.4.1
4
+ version: 2.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-12-08 00:00:00.000000000 Z
11
+ date: 2025-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -979,6 +979,7 @@ files:
979
979
  - app/controllers/spree/api/v2/storefront/s3_signed_urls_controller.rb
980
980
  - app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb
981
981
  - app/controllers/spree/api/v2/storefront/self_check_in_controller.rb
982
+ - app/controllers/spree/api/v2/storefront/tenants_controller.rb
982
983
  - app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb
983
984
  - app/controllers/spree/api/v2/storefront/trip_places_controller.rb
984
985
  - app/controllers/spree/api/v2/storefront/trip_search_controller.rb
@@ -1816,6 +1817,7 @@ files:
1816
1817
  - app/serializers/spree/v2/storefront/stock_location_serializer_decorator.rb
1817
1818
  - app/serializers/spree/v2/storefront/store_promotion_serializer.rb
1818
1819
  - app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb
1820
+ - app/serializers/spree/v2/storefront/tenant_serializer.rb
1819
1821
  - app/serializers/spree/v2/storefront/user_deletion_reason_serializer.rb
1820
1822
  - app/serializers/spree/v2/storefront/user_device_token_serializer.rb
1821
1823
  - app/serializers/spree/v2/storefront/user_profile_serializer.rb