spree_cm_commissioner 2.3.1 → 2.3.2

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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/admin/product_completion_steps_controller.rb +17 -1
  4. data/app/controllers/spree/admin/tenants_controller.rb +1 -1
  5. data/app/interactors/spree_cm_commissioner/create_ticket.rb +2 -0
  6. data/app/interactors/spree_cm_commissioner/host_matcher.rb +1 -1
  7. data/app/interactors/spree_cm_commissioner/vendor_creation_telegram_alert_sender.rb +1 -1
  8. data/app/mailers/spree/order_mailer_decorator.rb +8 -9
  9. data/app/models/concerns/spree_cm_commissioner/service_recommendations.rb +2 -2
  10. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
  11. data/app/models/spree_cm_commissioner/product_completion_step.rb +4 -1
  12. data/app/models/spree_cm_commissioner/product_completion_step_banner.rb +12 -0
  13. data/app/models/spree_cm_commissioner/product_completion_steps/social_entry_url.rb +86 -19
  14. data/app/models/spree_cm_commissioner/tenant.rb +10 -1
  15. data/app/models/spree_cm_commissioner/vendor_place.rb +1 -1
  16. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +9 -0
  17. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +1 -1
  18. data/app/views/shared/_asset_field.html.erb +13 -0
  19. data/app/views/spree/admin/product_completion_steps/_form.html.erb +28 -2
  20. data/app/views/spree/admin/product_completion_steps/_supported_fields.html.erb +33 -0
  21. data/app/views/spree/admin/tenants/_form.html.erb +1 -1
  22. data/config/initializers/spree_permitted_attributes.rb +1 -0
  23. data/config/locales/en.yml +4 -0
  24. data/config/locales/km.yml +65 -61
  25. data/config/routes.rb +4 -0
  26. data/lib/spree_cm_commissioner/test_helper/factories/product_completion_step_banner_factory.rb +7 -0
  27. data/lib/spree_cm_commissioner/version.rb +1 -1
  28. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87608fab14f473c498987cb8ecf0793926b031a1368d9a1d03f05fc3bf03e819
4
- data.tar.gz: 9472eaf172e1346757684e9c2b69cec671053927d1a99739f13d0eea1fcb17ce
3
+ metadata.gz: f69009264c3f351003ddd4885f403db68bc0a589a9e28123188a26bdd382b3d6
4
+ data.tar.gz: fea37b83621c4eb39ee2936ae4d72c377ee6d0767fb240be01c2de82e63d4c83
5
5
  SHA512:
6
- metadata.gz: b3da7fbad39575ce43d6b39e088c24ec878095d88a6652e4dd07d87ae0b474403ef368dba1233e0a2d3a2084a03c45d1932c20eee21ee6a65f99e0cb8e4d1e1d
7
- data.tar.gz: 92f4f8a8a446c765568b23b8a1183f7f2f83a499a608a59a34ab315314bf4ea256fbb8264da0da2e80ad0dab527a9fed4c8498a9c77342e186f61386dfad735b
6
+ metadata.gz: 8530a8855d325ffae9223d3ed705be0f445bd5ce01c2479ef809f783804af5d4b90cd716a5c10076cbad561f6d57c03becf4d8ccb2e7e33bd57144c29bd1ff5e
7
+ data.tar.gz: 9d2bada64c5ac4701bb9f18d233e8948e0e8b6b9cf6fd0e923157007bd06cde7ef79b13ffa0476aad2a3ec955a268e66249b3f49097fce93136a61dfa0856f4f
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.3.1)
37
+ spree_cm_commissioner (2.3.2)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -4,6 +4,7 @@ module Spree
4
4
  belongs_to 'spree/product', find_by: :slug
5
5
 
6
6
  before_action :load_step_types, if: :member_action?
7
+ before_action :build_assets, only: %i[create update]
7
8
 
8
9
  def load_step_types
9
10
  @step_types = [
@@ -14,10 +15,13 @@ module Spree
14
15
 
15
16
  # override
16
17
  def permitted_resource_params
18
+ return @permitted_resource_params if defined?(@permitted_resource_params)
19
+
17
20
  key = ActiveModel::Naming.param_key(@object)
18
21
  permit_keys = params.require(key).keys
19
22
 
20
- params.require(key).permit(permit_keys)
23
+ @permitted_resource_params = params.require(key).permit(permit_keys)
24
+ @permitted_resource_params
21
25
  end
22
26
 
23
27
  # @overrided
@@ -54,6 +58,18 @@ module Spree
54
58
  def location_after_save
55
59
  edit_object_url(@object)
56
60
  end
61
+
62
+ def remove_banner
63
+ @object.banner&.destroy
64
+ flash[:success] = Spree.t(:successfully_removed)
65
+ redirect_to edit_object_url(@object)
66
+ end
67
+
68
+ private
69
+
70
+ def build_assets
71
+ @object.build_banner(attachment: permitted_resource_params.delete(:banner)) if permitted_resource_params[:banner].present?
72
+ end
57
73
  end
58
74
  end
59
75
  end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  module Admin
3
3
  class TenantsController < Spree::Admin::ResourceController
4
- before_action :load_vector_icons, only: %i[new edit]
4
+ before_action :load_vector_icons, only: %i[new edit create update]
5
5
 
6
6
  # override
7
7
  def collection
@@ -48,6 +48,8 @@ module SpreeCmCommissioner
48
48
  name: 'ticket-type',
49
49
  presentation: 'Ticket Type'
50
50
  )
51
+ # auto assign the option type to the ticket
52
+ @ticket.option_types << @option_type unless @ticket.option_types.include?(@option_type)
51
53
 
52
54
  @option_value = Spree::OptionValue.find_or_create_by!(
53
55
  name: @ticket.name,
@@ -22,7 +22,7 @@ module SpreeCmCommissioner
22
22
  end
23
23
 
24
24
  def find_tenant(normalized_host)
25
- SpreeCmCommissioner::Tenant.find_by(host: "https://#{normalized_host}")
25
+ SpreeCmCommissioner::Tenant.find_by(host: normalized_host)
26
26
  end
27
27
  end
28
28
  end
@@ -22,7 +22,7 @@ module SpreeCmCommissioner
22
22
  end
23
23
 
24
24
  def admin_chat_id
25
- ENV.fetch('EXCEPTION_NOTIFIER_TELEGRAM_CHANNEL_ID', nil)
25
+ Spree::Store.default&.preferred_telegram_new_vendor_alert_chat_id
26
26
  end
27
27
  end
28
28
  end
@@ -37,22 +37,21 @@ module Spree
37
37
 
38
38
  def setup_tenant_and_store
39
39
  @tenant = @order.tenant
40
- if @tenant.present?
41
- @brand_color = @tenant.preferences[:brand_primary_color]
42
- @vendor_logo_url = @tenant.active_vendor&.logo&.original_url
43
- @current_store = @tenant
44
- else
45
- @current_store = @order.store
46
- end
40
+ @current_store = @order.store
41
+ return if @tenant.blank?
42
+
43
+ @brand_color = @tenant.preferences[:brand_primary_color]
44
+ @vendor_logo_url = @tenant.active_vendor&.logo&.original_url
47
45
  end
48
46
 
49
47
  def build_subject(resend)
50
48
  prefix = resend ? "[#{Spree.t(:resend).upcase}] " : ''
51
- "#{prefix}#{@current_store&.name} Booking Confirmation ##{@order.number}"
49
+ store_name = @tenant.present? ? @tenant.name : @current_store&.name
50
+ "#{prefix}#{store_name} Booking Confirmation ##{@order.number}"
52
51
  end
53
52
 
54
53
  def store_url
55
- @tenant.present? ? @current_store.host : @current_store.url
54
+ @tenant.present? ? @tenant.url : @current_store.url
56
55
  end
57
56
 
58
57
  def ticket_email(guest, email)
@@ -50,9 +50,9 @@ module SpreeCmCommissioner
50
50
  private
51
51
 
52
52
  def tenant_link_or_default(tenant, default_link)
53
- return default_link unless tenant.respond_to?(:host)
53
+ return default_link unless tenant.respond_to?(:url)
54
54
 
55
- site = tenant.host.to_s.strip
55
+ site = tenant.url.to_s.strip
56
56
  site.presence || default_link
57
57
  rescue StandardError
58
58
  default_link
@@ -6,6 +6,7 @@ module SpreeCmCommissioner
6
6
  preference :sms_sender_id, :string
7
7
  preference :telegram_order_alert_chat_id, :string
8
8
  preference :telegram_order_request_alert_chat_id, :string
9
+ preference :telegram_new_vendor_alert_chat_id, :string
9
10
  preference :assetlinks, :string, default: ''
10
11
  preference :apple_app_site_association, :string, default: ''
11
12
  end
@@ -4,6 +4,8 @@ module SpreeCmCommissioner
4
4
 
5
5
  belongs_to :product, class_name: '::Spree::Product', optional: false
6
6
 
7
+ has_one :banner, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::ProductCompletionStepBanner'
8
+
7
9
  # When a completion step is changed, regenerate completion steps for all line items
8
10
  # of the product so they reflect the latest step configuration.
9
11
  after_destroy :regenerate_line_items_completion_steps
@@ -28,7 +30,8 @@ module SpreeCmCommissioner
28
30
  action_label: action_label,
29
31
  action_url: action_url_for(line_item),
30
32
  completed_at: existing_data&.dig('completed_at'),
31
- completed: completed?(line_item)
33
+ completed: completed?(line_item),
34
+ banner_url: banner.present? ? banner.original_url : nil
32
35
  }
33
36
  end
34
37
 
@@ -0,0 +1,12 @@
1
+ module SpreeCmCommissioner
2
+ class ProductCompletionStepBanner < Asset
3
+ # 16x9 aspect ratio
4
+ def asset_styles
5
+ {
6
+ mini: '160x90>',
7
+ small: '480x270>',
8
+ medium: '960x540>'
9
+ }
10
+ end
11
+ end
12
+ end
@@ -5,6 +5,7 @@ module SpreeCmCommissioner
5
5
  # - https://t.me/ThePlatformKHBot?start={order.number}
6
6
  # - https://example.com?guest_name={guests[0].first_name}&phone={guests[0].phone_number}
7
7
  # - https://example.com?line_item_id={line_item.id}&quantity={line_item.quantity}
8
+ # - https://example.com?bill_address.first_name={bill_address.first_name}&bill_address.lastname={bill_address.lastname}
8
9
  preference :entry_point_link, :string
9
10
 
10
11
  # Allowed fields per object (excludes private/reference fields like *_id, *_by_id, metadata, etc.)
@@ -14,7 +15,11 @@ module SpreeCmCommissioner
14
15
  state
15
16
  email
16
17
  total
18
+ display_total
17
19
  item_total
20
+ display_item_total
21
+ adjustment_total
22
+ display_adjustment_total
18
23
  created_at
19
24
  completed_at
20
25
  token
@@ -23,12 +28,31 @@ module SpreeCmCommissioner
23
28
  currency
24
29
  item_count
25
30
  channel
31
+ special_instructions
32
+ ].freeze
33
+
34
+ BILL_ADDRESS_ALLOWED_FIELDS = %w[
35
+ first_name
36
+ last_name
37
+ full_name
38
+ address1
39
+ address2
40
+ city
41
+ zipcode
42
+ phone
43
+ state_name
44
+ alternative_phone
45
+ company
46
+ label
47
+ age
48
+ gender
26
49
  ].freeze
27
50
 
28
51
  LINE_ITEM_ALLOWED_FIELDS = %w[
29
52
  qr_data
30
53
  quantity
31
54
  price
55
+ display_price
32
56
  from_date
33
57
  to_date
34
58
  number
@@ -45,7 +69,6 @@ module SpreeCmCommissioner
45
69
  dob
46
70
  gender
47
71
  age
48
- email
49
72
  phone_number
50
73
  address
51
74
  seat_number
@@ -60,9 +83,6 @@ module SpreeCmCommissioner
60
83
  expectation
61
84
  other_occupation
62
85
  other_organization
63
- entry_type
64
- upload_later
65
- data_fill_stage_phase
66
86
  created_at
67
87
  updated_at
68
88
  ].freeze
@@ -72,32 +92,79 @@ module SpreeCmCommissioner
72
92
  return nil if preferred_entry_point_link.blank?
73
93
 
74
94
  url = preferred_entry_point_link.dup
95
+ url = replace_order_placeholders(url, line_item)
96
+ url = replace_bill_address_placeholders(url, line_item)
97
+ url = replace_line_item_placeholders(url, line_item)
98
+
99
+ replace_guest_placeholders(url, line_item)
100
+ end
101
+
102
+ def completed?(line_item)
103
+ return false if line_item.completion_steps.blank?
104
+
105
+ step_data = line_item.completion_steps.find { |step| step['position'].to_s == position.to_s }
106
+ step_data&.dig('completed_at').present?
107
+ end
108
+
109
+ # Returns a hash of all supported placeholder fields grouped by object type.
110
+ # Used by the admin UI to display available placeholders when configuring the entry_point_link template.
111
+ # Example usage in template: {order.number}, {bill_address.first_name}, {line_item.quantity}, {guests[0].email}
112
+ #
113
+ # @return [Hash] Hash with keys :order, :bill_address, :line_item, :guests
114
+ # Each key maps to an array of allowed field names for that object type
115
+ def supported_fields
116
+ {
117
+ order: ORDER_ALLOWED_FIELDS,
118
+ bill_address: BILL_ADDRESS_ALLOWED_FIELDS,
119
+ line_item: LINE_ITEM_ALLOWED_FIELDS,
120
+ guests: GUEST_ALLOWED_FIELDS
121
+ }
122
+ end
123
+
124
+ private
75
125
 
76
- # Replace order placeholders: {order.field_name}
77
- line_item.order.attributes.each do |field, value|
78
- url.gsub!("{order.#{field}}", value.to_s) if ORDER_ALLOWED_FIELDS.include?(field)
126
+ def replace_order_placeholders(url, line_item)
127
+ return url if line_item.order.blank?
128
+
129
+ ORDER_ALLOWED_FIELDS.each do |field|
130
+ value = line_item.order.public_send(field)
131
+ url.gsub!("{order.#{field}}", value.to_s) if value.present?
79
132
  end
80
133
 
81
- # Replace line_item placeholders: {line_item.field_name}
82
- line_item.attributes.each do |field, value|
83
- url.gsub!("{line_item.#{field}}", value.to_s) if LINE_ITEM_ALLOWED_FIELDS.include?(field)
134
+ url
135
+ end
136
+
137
+ def replace_bill_address_placeholders(url, line_item)
138
+ return url if line_item.order&.bill_address.blank?
139
+
140
+ BILL_ADDRESS_ALLOWED_FIELDS.each do |field|
141
+ value = line_item.order.bill_address.public_send(field)
142
+ url.gsub!("{bill_address.#{field}}", value.to_s) if value.present?
84
143
  end
85
144
 
86
- # Replace guest placeholders: {guests[index].field_name}
87
- line_item.guests.each_with_index do |guest, index|
88
- guest.attributes.each do |field, value|
89
- url.gsub!("{guests[#{index}].#{field}}", value.to_s) if GUEST_ALLOWED_FIELDS.include?(field)
90
- end
145
+ url
146
+ end
147
+
148
+ def replace_line_item_placeholders(url, line_item)
149
+ LINE_ITEM_ALLOWED_FIELDS.each do |field|
150
+ value = line_item.public_send(field)
151
+ url.gsub!("{line_item.#{field}}", value.to_s) if value.present?
91
152
  end
92
153
 
93
154
  url
94
155
  end
95
156
 
96
- def completed?(line_item)
97
- return false if line_item.completion_steps.blank?
157
+ def replace_guest_placeholders(url, line_item)
158
+ return url if line_item.guests.blank?
98
159
 
99
- step_data = line_item.completion_steps.find { |step| step['position'].to_s == position.to_s }
100
- step_data&.dig('completed_at').present?
160
+ line_item.guests.each_with_index do |guest, index|
161
+ GUEST_ALLOWED_FIELDS.each do |field|
162
+ value = guest.public_send(field)
163
+ url.gsub!("{guests[#{index}].#{field}}", value.to_s) if value.present?
164
+ end
165
+ end
166
+
167
+ url
101
168
  end
102
169
  end
103
170
  end
@@ -12,7 +12,10 @@ module SpreeCmCommissioner
12
12
  through: :vendors,
13
13
  source: :payment_methods
14
14
 
15
- validates :host, uniqueness: true, presence: true
15
+ validates :host, presence: true, uniqueness: true,
16
+ format: { without: %r{\Ahttps?://},
17
+ message: :format
18
+ }
16
19
 
17
20
  enum state: { enabled: 0, disabled: 1 }
18
21
 
@@ -22,6 +25,12 @@ module SpreeCmCommissioner
22
25
  vendors.where(state: :active).first
23
26
  end
24
27
 
28
+ def url
29
+ return if host.blank?
30
+
31
+ Rails.env.development? || Rails.env.test? ? "http://#{host}" : "https://#{host}"
32
+ end
33
+
25
34
  private
26
35
 
27
36
  def generate_slug
@@ -1,6 +1,6 @@
1
1
  module SpreeCmCommissioner
2
2
  class VendorPlace < Base
3
- enum place_type: { location: 0, branch: 1, stop: 2 }
3
+ enum place_type: { venue: 0, branch: 1, stop: 2, location: 3 }
4
4
  acts_as_list scope: :vendor
5
5
 
6
6
  belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
@@ -37,6 +37,15 @@
37
37
  <% end %>
38
38
  </div>
39
39
 
40
+ <!-- Telegram New Vendor Alert Chat ID -->
41
+ <div class="col-6">
42
+ <%= f.field_container :preferred_telegram_new_vendor_alert_chat_id do %>
43
+ <%= f.label :preferred_telegram_new_vendor_alert_chat_id, Spree.t(:telegram_new_vendor_alert_chat_id) %>
44
+ <%= f.text_field :preferred_telegram_new_vendor_alert_chat_id, class: 'form-control' %>
45
+ <%= f.error_message_on :preferred_telegram_new_vendor_alert_chat_id %>
46
+ <% end %>
47
+ </div>
48
+
40
49
  <!-- Assetlinks -->
41
50
  <div class="col-12">
42
51
  <%= f.field_container :preferred_assetlinks do %>
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
4
4
  class TripVehicleSerializer < BaseSerializer
5
5
  set_type :vehicle
6
6
 
7
- attributes :id, :code, :vehicle_type
7
+ attributes :id, :code, :vehicle_type, :number_of_seats
8
8
 
9
9
  has_many :vehicle_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer
10
10
  has_many :amenities, serializer: ::SpreeCmCommissioner::V2::Storefront::AmenitySerializer
@@ -3,6 +3,8 @@
3
3
  <%# :asset %>
4
4
  <%# :remove_url, optional %>
5
5
  <%# :classes, optional %>
6
+ <%# :aspect_ratio, optional %>
7
+ <%# :size_info, optional %>
6
8
  <%# :form %>
7
9
  <%# :required, optional, default: false %>
8
10
 
@@ -16,6 +18,17 @@
16
18
  class: 'rounded border mb-4 mw-100',
17
19
  style: 'max-height: 100px') if asset.present? && asset.attachment.present? %>
18
20
 
21
+ <% if defined?(aspect_ratio) || defined?(size_info) %>
22
+ <small class="mb-2 form-text text-muted d-block">
23
+ <% if defined?(aspect_ratio) && aspect_ratio.present? %>
24
+ <strong>Aspect Ratio:</strong> <%= aspect_ratio %><br>
25
+ <% end %>
26
+ <% if defined?(size_info) && size_info.present? %>
27
+ <strong>Recommended Size:</strong> <%= size_info %>
28
+ <% end %>
29
+ </small>
30
+ <% end %>
31
+
19
32
  <div data-hook="file" class="form-group">
20
33
  <%= form.label field, label || Spree.t(field) %>
21
34
  <%= form.file_field field, required: required %>
@@ -1,7 +1,7 @@
1
1
  <div data-hook="admin_product_completion_step_form_fields">
2
2
  <div data-hook="product_completion_step" class="row">
3
3
  <div class="col-12 col-md-6">
4
- <div class="card mb-3">
4
+ <div class="mb-3 card">
5
5
  <div class="card-header">
6
6
  <h5 class="mb-0"><%= Spree.t(:settings) %></h5>
7
7
  </div>
@@ -32,7 +32,7 @@
32
32
  </div>
33
33
 
34
34
  <div class="col-12 col-md-6">
35
- <div class="card mb-3">
35
+ <div class="mb-3 card">
36
36
  <div class="card-header">
37
37
  <h5 class="mb-0"><%= Spree.t(:completion_settings) %></h5>
38
38
  </div>
@@ -49,6 +49,32 @@
49
49
  </div>
50
50
  </div>
51
51
  </div>
52
+
53
+ <div class="mb-3 card">
54
+ <div class="card-header">
55
+ <h5 class="mb-0"><%= Spree.t(:banner) %></h5>
56
+ </div>
57
+ <div class="card-body">
58
+ <%= render 'shared/asset_field',
59
+ field: :banner,
60
+ label: Spree.t(:banner),
61
+ asset: @object.banner,
62
+ remove_url: (@object.banner.present? ? remove_banner_admin_product_product_completion_step_url(@object.product, @object) : nil),
63
+ form: f,
64
+ size_info: 'Min: 480x270, Max: 960x540',
65
+ aspect_ratio: '16x9',
66
+ classes: ['col-md-12']
67
+ %>
68
+ </div>
69
+ </div>
52
70
  </div>
53
71
  </div>
72
+
73
+ <% if @object.is_a?(SpreeCmCommissioner::ProductCompletionSteps::SocialEntryUrl) %>
74
+ <div class="row mt-3">
75
+ <div class="col-12">
76
+ <%= render 'supported_fields' %>
77
+ </div>
78
+ </div>
79
+ <% end %>
54
80
  </div>
@@ -0,0 +1,33 @@
1
+ <div class="mb-3 card">
2
+ <div class="card-header">
3
+ <h5 class="mb-0">Supported Placeholder Fields</h5>
4
+ </div>
5
+ <div class="card-body">
6
+ <p class="text-muted mb-3">Use these placeholders in your entry point link template:</p>
7
+
8
+ <% if @object.is_a?(SpreeCmCommissioner::ProductCompletionSteps::SocialEntryUrl) && @object.supported_fields.present? %>
9
+ <% @object.supported_fields.each do |category, fields| %>
10
+ <div class="mb-3">
11
+ <h6 class="fw-bold text-capitalize">
12
+ <%= category.to_s.humanize %>
13
+ <span class="badge bg-secondary"><%= fields.count %></span>
14
+ </h6>
15
+ <div class="field-list">
16
+ <% fields.each do |field| %>
17
+ <div class="field-item d-inline-block me-2 mb-2">
18
+ <code class="bg-light p-2 rounded">
19
+ {<%= category %><%= category == :guests ? '[index]' : '' %>.<%=field %>}
20
+ </code>
21
+ </div>
22
+ <% end %>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+
27
+ <div class="alert alert-info mt-3">
28
+ <strong>Example:</strong><br>
29
+ <code>https://example.com?order={order.number}&name={bill_address.first_name}&guest={guests[0].first_name}</code>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
@@ -36,7 +36,7 @@
36
36
  <div class="col-12">
37
37
  <%= f.field_container :host do %>
38
38
  <%= f.label :host, raw(Spree.t(:host) + required_span_tag) %>
39
- <%= f.text_field :host, required: true, class: 'form-control', placeholder: 'host' %>
39
+ <%= f.text_field :host, required: true, class: 'form-control', placeholder: 'example.com' %>
40
40
  <% if f.object.errors[:host].any? %>
41
41
  <div class="error text-danger"><%= f.object.errors[:host].join(', ') %></div>
42
42
  <% end %>
@@ -48,6 +48,7 @@ module Spree
48
48
  :preferred_sms_sender_id,
49
49
  :preferred_telegram_order_alert_chat_id,
50
50
  :preferred_telegram_order_request_alert_chat_id,
51
+ :preferred_telegram_new_vendor_alert_chat_id,
51
52
  :preferred_assetlinks,
52
53
  :preferred_apple_app_site_association,
53
54
  { default_notification_image_attributes: {} },
@@ -509,6 +509,10 @@ en:
509
509
  attributes:
510
510
  place_id:
511
511
  taken: "has already existed with this place type"
512
+ spree_cm_commissioner/tenant:
513
+ attributes:
514
+ host:
515
+ format: "must not include protocol (http:// or https://)"
512
516
 
513
517
  mail:
514
518
  order_mailer:
@@ -370,6 +370,10 @@ km:
370
370
  attributes:
371
371
  exception_rules:
372
372
  invalid_json: "Format មិនត្រឹមត្រូវ"
373
+ spree_cm_commissioner/tenant:
374
+ attributes:
375
+ host:
376
+ format: "មិនអនុញ្ញាតឱ្យបញ្ចូលឈ្មោះដែលភ្ជាប់ជាមួយ (http:// ឬ https://)"
373
377
 
374
378
  mail:
375
379
  order_mailer:
@@ -469,64 +473,64 @@ km:
469
473
 
470
474
  blazer:
471
475
  columns:
472
- 'No.': "លេខរៀង"
473
- 'Reservation No.': "លេខកក់"
474
- 'Direction': "ទិសដៅ"
475
- 'Vehicle type': "ប្រភេទយានយន្ត"
476
- 'Seller': "អ្នកលក់"
477
- 'Paid to': "បង់ទៅ"
478
- 'Branch': "សាខា"
479
- 'Gateway': "ច្រកទ្វារ"
480
- 'Departure': "ចេញដំណើរ"
481
- 'Issued': "ចេញឲ្យ"
482
- 'Paid': "បានបង់"
483
- '# of seats': "ចំនួនកៅអី"
484
- 'Source': "ប្រភព"
485
- 'Status': "ស្ថានភាព"
486
- 'Fare': "តម្លៃឈ្នួល"
487
- 'Discount': "ការបញ្ចុះតម្លៃ"
488
- 'Commission': "កម្រៃជើងសារ"
489
- 'Amount': "ចំនួនទឹកប្រាក់"
490
- 'Metric': "រង្វាស់"
491
- 'Value': "តម្លៃ"
492
- 'Group': "ក្រុម"
493
- 'Orders': "ការបញ្ជាទិញ"
494
- 'Passengers': "អ្នកដំណើរ"
495
- 'Total fare': "តម្លៃឈ្នួលសរុប"
496
- 'Total discount': "ការបញ្ចុះតម្លៃសរុប"
497
- 'Total commission': "កម្រៃជើងសារសរុប"
498
- 'Total amount': "ចំនួនទឹកប្រាក់សរុប"
499
- 'Trip ID': "លេខសម្គាល់ជើងដំណើរ"
500
- 'Route': "ផ្លូវ"
501
- 'Origin': "ទីតាំងដើម"
502
- 'Destination': "ទីតាំងគោលដៅ"
503
- 'Departure Time': "ម៉ោងចេញដំណើរ"
504
- 'Vehicle': "យានយន្ត"
505
- 'Total Seats': "ចំនួនកៅអីសរុប"
506
- 'Bookings': "ការកក់"
507
- 'Seats Sold': "កៅអីលក់បាន"
508
- 'Occupancy %': "ភាគរយនៃការកាន់កាប់"
509
- 'Total Revenue': "ចំណូលសរុប"
510
- 'Avg Ticket Price': "តម្លៃសំបុត្រជាមធ្យម"
511
- 'Date': "កាលបរិច្ឆេទ"
512
- 'Time': "ម៉ោង"
513
- 'From': "ពី"
514
- 'To': "ទៅ"
515
- 'Capacity': "ចំណុះ"
516
- 'Available': "ទំនេរ"
517
- 'Filled %': "ភាគរយនៃការបំពេញ"
518
- 'Vehicle Code': "លេខកូដយានយន្ត"
519
- 'Plate': "ស្លាកលេខ"
520
- 'Type': "ប្រភេទ"
521
- 'Total Trips': "ចំនួនជើងដំណើរ​សរុប"
522
- 'Trips with Bookings': "ជើងដំណើរ​មានការកក់"
523
- 'Total Bookings': "ចំនួនការកក់​សរុប"
524
- 'Total Passengers': "ចំនួនអ្នកដំណើរ​សរុប"
525
- 'Avg Occupancy %': "ភាគរយនៃការកាន់កាប់​ជាមធ្យម"
526
- 'Lead Time': "រយៈពេលនៃការរង់ចាំ"
527
- 'Revenue': "ចំណូល"
528
- 'Avg Days in Advance': "ចំនួនថ្ងៃជាមធ្យមមុន"
529
- 'Current Period': "រយៈពេលបច្ចុប្បន្ន"
530
- 'Previous Period': "រយៈពេលមុន"
531
- 'Change': "បំលាស់ប្តូរ"
532
- 'Change %': "ភាគរយនៃបំលាស់ប្តូរ"
476
+ "No.": "លេខរៀង"
477
+ "Reservation No.": "លេខកក់"
478
+ "Direction": "ទិសដៅ"
479
+ "Vehicle type": "ប្រភេទយានយន្ត"
480
+ "Seller": "អ្នកលក់"
481
+ "Paid to": "បង់ទៅ"
482
+ "Branch": "សាខា"
483
+ "Gateway": "ច្រកទ្វារ"
484
+ "Departure": "ចេញដំណើរ"
485
+ "Issued": "ចេញឲ្យ"
486
+ "Paid": "បានបង់"
487
+ "# of seats": "ចំនួនកៅអី"
488
+ "Source": "ប្រភព"
489
+ "Status": "ស្ថានភាព"
490
+ "Fare": "តម្លៃឈ្នួល"
491
+ "Discount": "ការបញ្ចុះតម្លៃ"
492
+ "Commission": "កម្រៃជើងសារ"
493
+ "Amount": "ចំនួនទឹកប្រាក់"
494
+ "Metric": "រង្វាស់"
495
+ "Value": "តម្លៃ"
496
+ "Group": "ក្រុម"
497
+ "Orders": "ការបញ្ជាទិញ"
498
+ "Passengers": "អ្នកដំណើរ"
499
+ "Total fare": "តម្លៃឈ្នួលសរុប"
500
+ "Total discount": "ការបញ្ចុះតម្លៃសរុប"
501
+ "Total commission": "កម្រៃជើងសារសរុប"
502
+ "Total amount": "ចំនួនទឹកប្រាក់សរុប"
503
+ "Trip ID": "លេខសម្គាល់ជើងដំណើរ"
504
+ "Route": "ផ្លូវ"
505
+ "Origin": "ទីតាំងដើម"
506
+ "Destination": "ទីតាំងគោលដៅ"
507
+ "Departure Time": "ម៉ោងចេញដំណើរ"
508
+ "Vehicle": "យានយន្ត"
509
+ "Total Seats": "ចំនួនកៅអីសរុប"
510
+ "Bookings": "ការកក់"
511
+ "Seats Sold": "កៅអីលក់បាន"
512
+ "Occupancy %": "ភាគរយនៃការកាន់កាប់"
513
+ "Total Revenue": "ចំណូលសរុប"
514
+ "Avg Ticket Price": "តម្លៃសំបុត្រជាមធ្យម"
515
+ "Date": "កាលបរិច្ឆេទ"
516
+ "Time": "ម៉ោង"
517
+ "From": "ពី"
518
+ "To": "ទៅ"
519
+ "Capacity": "ចំណុះ"
520
+ "Available": "ទំនេរ"
521
+ "Filled %": "ភាគរយនៃការបំពេញ"
522
+ "Vehicle Code": "លេខកូដយានយន្ត"
523
+ "Plate": "ស្លាកលេខ"
524
+ "Type": "ប្រភេទ"
525
+ "Total Trips": "ចំនួនជើងដំណើរ​សរុប"
526
+ "Trips with Bookings": "ជើងដំណើរ​មានការកក់"
527
+ "Total Bookings": "ចំនួនការកក់​សរុប"
528
+ "Total Passengers": "ចំនួនអ្នកដំណើរ​សរុប"
529
+ "Avg Occupancy %": "ភាគរយនៃការកាន់កាប់​ជាមធ្យម"
530
+ "Lead Time": "រយៈពេលនៃការរង់ចាំ"
531
+ "Revenue": "ចំណូល"
532
+ "Avg Days in Advance": "ចំនួនថ្ងៃជាមធ្យមមុន"
533
+ "Current Period": "រយៈពេលបច្ចុប្បន្ន"
534
+ "Previous Period": "រយៈពេលមុន"
535
+ "Change": "បំលាស់ប្តូរ"
536
+ "Change %": "ភាគរយនៃបំលាស់ប្តូរ"
data/config/routes.rb CHANGED
@@ -163,6 +163,10 @@ Spree::Core::Engine.add_routes do
163
163
  collection do
164
164
  post :update_positions
165
165
  end
166
+
167
+ member do
168
+ delete :remove_banner
169
+ end
166
170
  end
167
171
 
168
172
  resources :product_places do
@@ -0,0 +1,7 @@
1
+ FactoryBot.define do
2
+ factory :cm_product_completion_step_banner, class: SpreeCmCommissioner::ProductCompletionStepBanner do
3
+ viewable_type { 'SpreeCmCommissioner::ProductCompletionStep' }
4
+ viewable_id { nil }
5
+ attachment { Rack::Test::UploadedFile.new(Spree::Core::Engine.root.join('spec', 'fixtures', 'files', 'icon_256x256.png'), 'image/*') }
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.3.1'.freeze
2
+ VERSION = '2.3.2'.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.1
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-14 00:00:00.000000000 Z
11
+ date: 2025-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -1484,6 +1484,7 @@ files:
1484
1484
  - app/models/spree_cm_commissioner/place.rb
1485
1485
  - app/models/spree_cm_commissioner/price_decorator.rb
1486
1486
  - app/models/spree_cm_commissioner/product_completion_step.rb
1487
+ - app/models/spree_cm_commissioner/product_completion_step_banner.rb
1487
1488
  - app/models/spree_cm_commissioner/product_completion_steps/chatrace_telegram.rb
1488
1489
  - app/models/spree_cm_commissioner/product_completion_steps/social_entry_url.rb
1489
1490
  - app/models/spree_cm_commissioner/product_decorator.rb
@@ -2101,6 +2102,7 @@ files:
2101
2102
  - app/views/spree/admin/orders/notifications.html.erb
2102
2103
  - app/views/spree/admin/product_commissions/index.html.erb
2103
2104
  - app/views/spree/admin/product_completion_steps/_form.html.erb
2105
+ - app/views/spree/admin/product_completion_steps/_supported_fields.html.erb
2104
2106
  - app/views/spree/admin/product_completion_steps/edit.html.erb
2105
2107
  - app/views/spree/admin/product_completion_steps/index.html.erb
2106
2108
  - app/views/spree/admin/product_completion_steps/new.html.erb
@@ -2922,6 +2924,7 @@ files:
2922
2924
  - lib/spree_cm_commissioner/test_helper/factories/permission.rb
2923
2925
  - lib/spree_cm_commissioner/test_helper/factories/pin_code_factory.rb
2924
2926
  - lib/spree_cm_commissioner/test_helper/factories/place_factory.rb
2927
+ - lib/spree_cm_commissioner/test_helper/factories/product_completion_step_banner_factory.rb
2925
2928
  - lib/spree_cm_commissioner/test_helper/factories/product_completion_step_factory.rb
2926
2929
  - lib/spree_cm_commissioner/test_helper/factories/product_factory.rb
2927
2930
  - lib/spree_cm_commissioner/test_helper/factories/products_taxons_factory.rb