spree_cm_commissioner 2.3.0.pre.pre16 → 2.3.0.pre.pre17
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/README.md +33 -0
- data/app/controllers/blazer/base_controller_decorator.rb +20 -1
- data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +40 -0
- data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +47 -9
- data/app/controllers/spree_cm_commissioner/admin/variants_controller_decorator.rb +1 -1
- data/app/factory/spree_cm_commissioner/vendor_telegram_message_factory.rb +65 -0
- data/app/helpers/spree/base_helper_decorator.rb +2 -19
- data/app/interactors/spree_cm_commissioner/vendor_creation_telegram_alert_sender.rb +28 -0
- data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +1 -1
- data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +1 -1
- data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +1 -1
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +1 -1
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +1 -1
- data/app/jobs/spree_cm_commissioner/vendor_creation_telegram_alert_sender_job.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +51 -0
- data/app/models/concerns/spree_cm_commissioner/option_type_attr_type.rb +7 -0
- data/app/models/concerns/spree_cm_commissioner/option_value_attr_type.rb +25 -0
- data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +3 -14
- data/app/models/concerns/spree_cm_commissioner/variant_options_concern.rb +18 -4
- data/app/models/spree_cm_commissioner/block.rb +19 -0
- data/app/models/spree_cm_commissioner/product_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +0 -5
- data/app/models/spree_cm_commissioner/variant_options.rb +8 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +6 -0
- data/app/overrides/spree/admin/variants/_form/add_permanent_stock.html.erb.deface +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_cart_serializer.rb +12 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb +31 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +11 -0
- data/app/services/spree_cm_commissioner/intercity_taxi_order/create.rb +67 -0
- data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +79 -0
- data/app/services/spree_cm_commissioner/{transit/base_route_order_metrics_updater.rb → routes/base_update_order_metrics.rb} +2 -2
- data/app/services/spree_cm_commissioner/{transit/route_previous_trip_count_decrementer_service.rb → routes/decrement_previous_trip_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{transit/route_trip_count_decrementer_service.rb → routes/decrement_trip_count.rb} +2 -2
- data/app/services/spree_cm_commissioner/{transit/route_fulfilled_order_count_incrementer_service.rb → routes/increment_fulfilled_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{transit/route_order_count_incrementer_service.rb → routes/increment_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{transit/route_trip_count_incrementer_service.rb → routes/increment_trip_count.rb} +2 -2
- data/app/services/spree_cm_commissioner/trips/search.rb +65 -0
- data/app/views/blazer/queries/embed/_content.html.erb +51 -2
- data/app/views/spree/admin/option_types/_color_field.html.erb +21 -0
- data/app/views/spree/admin/option_types/_option_value_fields.html.erb +2 -0
- data/app/views/spree/admin/variants/_color_field.html.erb +28 -0
- data/app/views/spree/admin/variants/_date_field.html.erb +11 -1
- data/app/views/spree/admin/variants/_default_field.html.erb +7 -3
- data/app/views/spree/admin/variants/_option_values.html.erb +5 -1
- data/app/views/spree/admin/variants/_time_field.html.erb +7 -1
- data/app/views/spree/order_mailer/_adjustment.html.erb +7 -0
- data/app/views/spree_cm_commissioner/event_transactional_mailer/_event_banner.html.erb +9 -3
- data/config/locales/km.yml +64 -0
- data/config/routes.rb +4 -1
- data/lib/spree_cm_commissioner/test_helper/factories/option_type_factory.rb +18 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +30 -0
- data/lib/spree_cm_commissioner/trip_result.rb +15 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +20 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 76d6f1a36e751b1723a6a6f7d6ba016dcf6df4c24d4e66322fadd7cb6d99f133
|
|
4
|
+
data.tar.gz: 5ee3fb8b1c36b92b04b349218a72db179b9e5afffe18342b73c6bafea2375600
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d402702c7778ddf7a44be7b6370fb943c7e091ee1d9cc5214ac4ffa97fde73a2347f072692661a815daf85fd1a18441d44b79c2e4246fce686b5f9822554a5b5
|
|
7
|
+
data.tar.gz: 3adbb3c9b40e612c65be67ae0853047bd5f1b1c82ac5daf7e91dc494a1f16d51f005eca9dbc48adf83467d3edb25e466114d9716abec8d43b0e7edff1b0753f2
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -32,6 +32,39 @@ If your server was running, restart it so that it can find the assets properly.
|
|
|
32
32
|
|
|
33
33
|
Update the semantic version number in `lib/spree_cm_commissioner/version.rb`, then create and push a new tag to GitHub. When the tag is pushed, the GitHub Actions workflow will automatically build the gem and publish it to RubyGems.
|
|
34
34
|
|
|
35
|
+
Here's a cleaner and more polished version of your section:
|
|
36
|
+
|
|
37
|
+
## PostgreSQL Configuration
|
|
38
|
+
|
|
39
|
+
By default, this Rails engine uses your current system login to connect to the PostgreSQL database. To allow passwordless connections, you need to configure PostgreSQL to trust local connections by editing `pg_hba.conf`:
|
|
40
|
+
|
|
41
|
+
```conf
|
|
42
|
+
# pg_hba.conf
|
|
43
|
+
local all all trust
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
With this `trust` configuration, PostgreSQL expects a role that matches your current system username.
|
|
47
|
+
|
|
48
|
+
To check your system username:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
whoami # e.g., superdev
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then create a matching PostgreSQL role:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
createuser -U postgres -s superdev
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or simply run the following to create a role matching your current system user:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
createuser -U postgres -s $(whoami)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> 💡 We recommend using [Postgres.app](https://postgresapp.com) for local development. It supports multiple PostgreSQL versions and provides a developer-friendly setup out of the box.
|
|
67
|
+
|
|
35
68
|
## Config
|
|
36
69
|
|
|
37
70
|
### Rake tasks
|
|
@@ -12,11 +12,30 @@ module Blazer
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def restrict_organizer_access
|
|
15
|
-
return
|
|
15
|
+
return if spree_current_user.admin?
|
|
16
|
+
|
|
17
|
+
return unless spree_current_user.organizer? || vendor_permissions?
|
|
16
18
|
return if controller_name == 'queries' && %w[show run].include?(action_name)
|
|
17
19
|
|
|
18
20
|
raise ActionController::RoutingError, 'Unauthorized'
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def current_vendor
|
|
26
|
+
@current_vendor ||= vendors&.find_by(id: session[:vendor_id]) || vendors&.first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def vendors
|
|
30
|
+
@vendors ||= spree_current_user&.vendors
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def vendor_permissions?
|
|
34
|
+
return false unless current_vendor
|
|
35
|
+
|
|
36
|
+
permissions = spree_current_user.permissions_for_vendor(current_vendor.id)
|
|
37
|
+
permissions.exists?(entry: 'shared_console/reports', action: 'view_reports')
|
|
38
|
+
end
|
|
20
39
|
end
|
|
21
40
|
end
|
|
22
41
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
module IntercityTaxi
|
|
6
|
+
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
7
|
+
def create
|
|
8
|
+
order = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
|
|
9
|
+
trip_id: params[:trip_id],
|
|
10
|
+
from_date: params[:from_date],
|
|
11
|
+
to_date: params[:to_date],
|
|
12
|
+
user_id: params[:user_id],
|
|
13
|
+
quantity: params[:quantity]
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
render_serialized_payload { serialize_resource(order) }
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
render_error_payload(e.message)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update
|
|
22
|
+
order = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
|
|
23
|
+
order_number: params[:id],
|
|
24
|
+
params: params
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
render_serialized_payload { serialize_resource(order) }
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
render_error_payload(e.message)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def resource_serializer
|
|
33
|
+
SpreeCmCommissioner::V2::Storefront::IntercityTaxiCartSerializer
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -3,21 +3,59 @@ module Spree
|
|
|
3
3
|
module V2
|
|
4
4
|
module Storefront
|
|
5
5
|
class TripSearchController < ::Spree::Api::V2::ResourceController
|
|
6
|
+
CACHE_EXPIRES_IN = 5.minutes
|
|
7
|
+
|
|
6
8
|
def index
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
render_serialized_payload do
|
|
10
|
+
Rails.cache.fetch(collection_cache_key, collection_cache_opts) do
|
|
11
|
+
trips = SpreeCmCommissioner::TripQuery.new(
|
|
12
|
+
origin_id: params[:origin_id],
|
|
13
|
+
destination_id: params[:destination_id],
|
|
14
|
+
date: params[:date],
|
|
15
|
+
vendor_id: params[:vendor_id],
|
|
16
|
+
number_of_guests: params[:number_of_guests],
|
|
17
|
+
params: params
|
|
18
|
+
).call
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
serialize_collection(trips)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
private
|
|
20
26
|
|
|
27
|
+
# override from ContentCacheable
|
|
28
|
+
def max_age
|
|
29
|
+
CACHE_EXPIRES_IN.to_i
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# override
|
|
33
|
+
def collection_cache_key
|
|
34
|
+
cache_key_parts = [
|
|
35
|
+
'trip_search',
|
|
36
|
+
params[:origin_id],
|
|
37
|
+
params[:destination_id],
|
|
38
|
+
params[:date],
|
|
39
|
+
params[:vendor_id],
|
|
40
|
+
params[:number_of_guests],
|
|
41
|
+
resource_includes&.sort&.join(','),
|
|
42
|
+
sparse_fields&.sort&.join(','),
|
|
43
|
+
serializer_params.to_json,
|
|
44
|
+
params[:page]&.to_s&.strip,
|
|
45
|
+
params[:per_page]&.to_s&.strip
|
|
46
|
+
].compact.join('-')
|
|
47
|
+
|
|
48
|
+
Digest::MD5.hexdigest(cache_key_parts)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# override
|
|
52
|
+
def collection_cache_opts
|
|
53
|
+
{
|
|
54
|
+
namespace: Spree::Api::Config[:api_v2_collection_cache_namespace],
|
|
55
|
+
expires_in: CACHE_EXPIRES_IN
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
21
59
|
# override
|
|
22
60
|
def default_resource_includes
|
|
23
61
|
[
|
|
@@ -17,7 +17,7 @@ module SpreeCmCommissioner
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# construct option values base on name & create new option value when not exist.
|
|
20
|
-
# then set to variant.
|
|
20
|
+
# then set to variant. Empty values will remove the option value from variant.
|
|
21
21
|
def build_option_values
|
|
22
22
|
option_values = permitted_resource_params.delete(:option_values_attributes).to_h.values
|
|
23
23
|
return if option_values.blank?
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# 📋 ---[New Vendor]---
|
|
2
|
+
#
|
|
3
|
+
# 📝 Name: {{vendor.name}}
|
|
4
|
+
#
|
|
5
|
+
# ✉️ Email: <code>{{vendor.notification_email || vendor.support_email}}</code>
|
|
6
|
+
# 📱 Phone Number: {{vendor.phone_number}}
|
|
7
|
+
#
|
|
8
|
+
# 👤 Created by: {{first_user.name || 'Admin'}}
|
|
9
|
+
# 🗓️ Created at: {{vendor.created_at}}
|
|
10
|
+
#
|
|
11
|
+
# 🔗 URL: {{vendor.organizer_url}}
|
|
12
|
+
module SpreeCmCommissioner
|
|
13
|
+
class VendorTelegramMessageFactory < TelegramMessageFactory
|
|
14
|
+
attr_reader :vendor
|
|
15
|
+
|
|
16
|
+
def initialize(vendor:)
|
|
17
|
+
@vendor = vendor
|
|
18
|
+
|
|
19
|
+
title = '✨ New Vendor ✨'
|
|
20
|
+
|
|
21
|
+
super(title: title)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def body
|
|
25
|
+
[
|
|
26
|
+
"📝 Name: #{vendor.name}",
|
|
27
|
+
'',
|
|
28
|
+
email_line,
|
|
29
|
+
phone_line,
|
|
30
|
+
'',
|
|
31
|
+
creator_line,
|
|
32
|
+
created_at_line,
|
|
33
|
+
'',
|
|
34
|
+
url_line
|
|
35
|
+
].compact.join("\n")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def email_line
|
|
41
|
+
return unless vendor.notification_email.present? || vendor.support_email.present?
|
|
42
|
+
|
|
43
|
+
"✉️ Email: #{inline_code(vendor.notification_email || vendor.support_email)}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def phone_line
|
|
47
|
+
return if vendor.contact_us.blank?
|
|
48
|
+
|
|
49
|
+
"📱 Phone Number: #{vendor.contact_us}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def creator_line
|
|
53
|
+
creator_email = vendor.users.first&.email
|
|
54
|
+
"👤 Created by: #{creator_email}" if creator_email.present?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def created_at_line
|
|
58
|
+
"🗓️ Created at: #{pretty_date(vendor.created_at)}" if vendor.created_at.present?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def url_line
|
|
62
|
+
"🔗 URL: #{vendor.organizer_url}" if vendor.organizer_url.present?
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -20,25 +20,8 @@ module Spree
|
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def custom_product_line_item_url(line_item
|
|
24
|
-
|
|
25
|
-
options.merge!(locale: locale_param)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
localize = if options[:locale].present?
|
|
29
|
-
"/#{options[:locale]}"
|
|
30
|
-
else
|
|
31
|
-
''
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
line_item = Spree::LineItem.find(line_item.id)
|
|
35
|
-
payload = { order_number: line_item.order.number, line_item_id: line_item.id }
|
|
36
|
-
|
|
37
|
-
jwt_token = SpreeCmCommissioner::LineItemJwtToken.encode(payload, line_item.order.token)
|
|
38
|
-
|
|
39
|
-
return if line_item.number.blank? && jwt_token.blank?
|
|
40
|
-
|
|
41
|
-
"#{current_store.formatted_url + localize}/a/#{line_item.qr_data}"
|
|
23
|
+
def custom_product_line_item_url(line_item)
|
|
24
|
+
main_app.url_for("/a/#{line_item.qr_data}")
|
|
42
25
|
end
|
|
43
26
|
end
|
|
44
27
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VendorCreationTelegramAlertSender < BaseInteractor
|
|
3
|
+
delegate :vendor, to: :context
|
|
4
|
+
|
|
5
|
+
def call
|
|
6
|
+
return if admin_chat_id.blank?
|
|
7
|
+
|
|
8
|
+
send_alert
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def send_alert
|
|
12
|
+
TelegramNotificationSender.call(
|
|
13
|
+
chat_id: admin_chat_id,
|
|
14
|
+
message: alert_message,
|
|
15
|
+
parse_mode: 'HTML'
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def alert_message
|
|
20
|
+
factory = SpreeCmCommissioner::VendorTelegramMessageFactory.new(vendor: vendor)
|
|
21
|
+
factory.message
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def admin_chat_id
|
|
25
|
+
ENV.fetch('EXCEPTION_NOTIFIER_TELEGRAM_CHANNEL_ID', nil)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class RouteFulfilledOrderCountIncrementerJob < ApplicationUniqueJob
|
|
4
4
|
def perform(order_id:)
|
|
5
5
|
order = Spree::Order.find(order_id)
|
|
6
|
-
SpreeCmCommissioner::
|
|
6
|
+
SpreeCmCommissioner::Routes::IncrementFulfilledOrderCount.call(order: order)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class RouteOrderCountIncrementerJob < ApplicationUniqueJob
|
|
4
4
|
def perform(order_id:)
|
|
5
5
|
order = Spree::Order.find(order_id)
|
|
6
|
-
SpreeCmCommissioner::
|
|
6
|
+
SpreeCmCommissioner::Routes::IncrementOrderCount.call(order: order)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -6,7 +6,7 @@ module SpreeCmCommissioner
|
|
|
6
6
|
queue_as :default
|
|
7
7
|
|
|
8
8
|
def perform(previous_route_id:)
|
|
9
|
-
SpreeCmCommissioner::
|
|
9
|
+
SpreeCmCommissioner::Routes::DecrementPreviousTripCount.call(previous_route_id: previous_route_id)
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class RouteTripCountDecrementerJob < ApplicationUniqueJob
|
|
4
4
|
def perform(trip_id:)
|
|
5
5
|
trip = SpreeCmCommissioner::Trip.find(trip_id)
|
|
6
|
-
SpreeCmCommissioner::
|
|
6
|
+
SpreeCmCommissioner::Routes::DecrementTripCount.call(trip: trip)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -3,7 +3,7 @@ module SpreeCmCommissioner
|
|
|
3
3
|
class RouteTripCountIncrementerJob < ApplicationUniqueJob
|
|
4
4
|
def perform(trip_id:)
|
|
5
5
|
trip = SpreeCmCommissioner::Trip.find(trip_id)
|
|
6
|
-
SpreeCmCommissioner::
|
|
6
|
+
SpreeCmCommissioner::Routes::IncrementTripCount.call(trip: trip)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VendorCreationTelegramAlertSenderJob < ApplicationUniqueJob
|
|
3
|
+
queue_as :telegram_bot
|
|
4
|
+
|
|
5
|
+
def perform(vendor_id)
|
|
6
|
+
vendor = Spree::Vendor.find(vendor_id)
|
|
7
|
+
SpreeCmCommissioner::VendorCreationTelegramAlertSender.call(vendor: vendor)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -63,11 +63,22 @@ module SpreeCmCommissioner
|
|
|
63
63
|
|
|
64
64
|
def pickup_place_name = public_metadata['pickup_place_name']
|
|
65
65
|
def drop_off_place_name = public_metadata['drop_off_place_name']
|
|
66
|
+
|
|
66
67
|
def pickup_lat = public_metadata['pickup_lat']&.to_f
|
|
67
68
|
def pickup_lng = public_metadata['pickup_lng']&.to_f
|
|
68
69
|
def drop_off_lat = public_metadata['drop_off_lat']&.to_f
|
|
69
70
|
def drop_off_lng = public_metadata['drop_off_lng']&.to_f
|
|
70
71
|
def passenger_count = public_metadata['passenger_count']&.to_i
|
|
72
|
+
def distance_km = public_metadata['distance_km']&.to_f
|
|
73
|
+
def ordered_points = public_metadata['ordered_points']
|
|
74
|
+
def base_km = public_metadata['base_km']&.to_f
|
|
75
|
+
def detour_pickup_km = public_metadata['detour_pickup_km']&.to_f
|
|
76
|
+
def detour_dropoff_km = public_metadata['detour_dropoff_km']&.to_f
|
|
77
|
+
def extra_pickup_km = public_metadata['extra_pickup_km']&.to_f
|
|
78
|
+
def extra_dropoff_km = public_metadata['extra_dropoff_km']&.to_f
|
|
79
|
+
def extra_pickup_charge_usd = public_metadata['extra_pickup_charge_usd']&.to_f
|
|
80
|
+
def extra_dropoff_charge_usd = public_metadata['extra_dropoff_charge_usd']&.to_f
|
|
81
|
+
def estimated_time_minutes = public_metadata['estimated_time_minutes']&.to_i
|
|
71
82
|
def pickup_oob_confirmed = private_metadata['pickup_oob_confirmed']
|
|
72
83
|
def drop_off_oob_confirmed = private_metadata['drop_off_oob_confirmed']
|
|
73
84
|
|
|
@@ -123,6 +134,46 @@ module SpreeCmCommissioner
|
|
|
123
134
|
set_public_metadata_value('passenger_count', value)
|
|
124
135
|
end
|
|
125
136
|
|
|
137
|
+
def distance_km=(value)
|
|
138
|
+
set_public_metadata_value('distance_km', value)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def ordered_points=(value)
|
|
142
|
+
set_public_metadata_value('ordered_points', value)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def base_km=(value)
|
|
146
|
+
set_public_metadata_value('base_km', value)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def detour_pickup_km=(value)
|
|
150
|
+
set_public_metadata_value('detour_pickup_km', value)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def detour_dropoff_km=(value)
|
|
154
|
+
set_public_metadata_value('detour_dropoff_km', value)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def extra_pickup_km=(value)
|
|
158
|
+
set_public_metadata_value('extra_pickup_km', value)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def extra_dropoff_km=(value)
|
|
162
|
+
set_public_metadata_value('extra_dropoff_km', value)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def extra_pickup_charge_usd=(value)
|
|
166
|
+
set_public_metadata_value('extra_pickup_charge_usd', value)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def extra_dropoff_charge_usd=(value)
|
|
170
|
+
set_public_metadata_value('extra_dropoff_charge_usd', value)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def estimated_time_minutes=(value)
|
|
174
|
+
set_public_metadata_value('estimated_time_minutes', value)
|
|
175
|
+
end
|
|
176
|
+
|
|
126
177
|
private
|
|
127
178
|
|
|
128
179
|
def set_public_metadata_value(key, value)
|
|
@@ -10,6 +10,7 @@ module SpreeCmCommissioner
|
|
|
10
10
|
array
|
|
11
11
|
date
|
|
12
12
|
time
|
|
13
|
+
color
|
|
13
14
|
coordinate
|
|
14
15
|
state_selection
|
|
15
16
|
payment_option
|
|
@@ -41,6 +42,8 @@ module SpreeCmCommissioner
|
|
|
41
42
|
'bib-display-prefix' => 'boolean',
|
|
42
43
|
'bib-pre-generation-on-create' => 'boolean',
|
|
43
44
|
'seat-number-positions' => 'array',
|
|
45
|
+
'color' => 'color',
|
|
46
|
+
'ticket-type' => 'string',
|
|
44
47
|
'seat-type' => 'string',
|
|
45
48
|
'intercity-taxi' => 'string'
|
|
46
49
|
}.freeze
|
|
@@ -71,6 +74,10 @@ module SpreeCmCommissioner
|
|
|
71
74
|
name.in?(RESERVED_OPTIONS.keys)
|
|
72
75
|
end
|
|
73
76
|
|
|
77
|
+
def ticket_type?
|
|
78
|
+
name == 'ticket-type'
|
|
79
|
+
end
|
|
80
|
+
|
|
74
81
|
def set_reverved_options_attributes
|
|
75
82
|
self.attr_type = RESERVED_OPTIONS[name]
|
|
76
83
|
self.kind = :variant
|
|
@@ -13,6 +13,8 @@ module SpreeCmCommissioner
|
|
|
13
13
|
|
|
14
14
|
before_validation :construct_time, if: :attr_type_time?
|
|
15
15
|
before_validation :construct_date, if: :attr_type_date?
|
|
16
|
+
before_validation :construct_color, if: :attr_type_color?
|
|
17
|
+
before_validation :construct_ticket_type, if: :ticket_type?
|
|
16
18
|
before_validation :normalize_items, if: :attr_type_array?
|
|
17
19
|
|
|
18
20
|
after_save :update_variants_metadata, if: :saved_change_to_name?
|
|
@@ -27,6 +29,10 @@ module SpreeCmCommissioner
|
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
def ticket_type?
|
|
33
|
+
option_type.present? && option_type&.ticket_type?
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def items
|
|
31
37
|
return nil unless attr_type_array?
|
|
32
38
|
return nil if name.nil?
|
|
@@ -92,6 +98,13 @@ module SpreeCmCommissioner
|
|
|
92
98
|
self.name = name.split(',').map(&:strip).join(',')
|
|
93
99
|
end
|
|
94
100
|
|
|
101
|
+
# Ticket types are case-sensitive: "STANDARD" and "Standard" are distinct ticket types.
|
|
102
|
+
# Only strip whitespace; preserve the exact case entered by the user.
|
|
103
|
+
def construct_ticket_type
|
|
104
|
+
self.name = name&.strip&.presence
|
|
105
|
+
self.presentation = name&.strip&.presence
|
|
106
|
+
end
|
|
107
|
+
|
|
95
108
|
def construct_time
|
|
96
109
|
hour, minute = extract_time_from_time_select
|
|
97
110
|
hour, minute = extract_time_from_default_format if hour.nil? || minute.nil?
|
|
@@ -141,5 +154,17 @@ module SpreeCmCommissioner
|
|
|
141
154
|
self.name = nil if parse_date(name).blank?
|
|
142
155
|
self.presentation = name if parse_date(presentation) != parse_date(name)
|
|
143
156
|
end
|
|
157
|
+
|
|
158
|
+
def construct_color
|
|
159
|
+
self.name = parse_hex_color(name)
|
|
160
|
+
self.presentation = name if parse_hex_color(presentation) != parse_hex_color(name)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def parse_hex_color(value)
|
|
164
|
+
return nil if value.nil?
|
|
165
|
+
return nil unless value.match?(/^#[0-9A-Fa-f]{6}$/)
|
|
166
|
+
|
|
167
|
+
value.upcase
|
|
168
|
+
end
|
|
144
169
|
end
|
|
145
170
|
end
|
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module RouteOrderCountable
|
|
3
|
-
def
|
|
3
|
+
def has_trip_ids? # rubocop:disable Naming/PredicateName
|
|
4
4
|
preload_trip_ids.any?
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def increment_route_fulfilled_order_count
|
|
8
|
-
return unless
|
|
8
|
+
return unless has_trip_ids?
|
|
9
9
|
|
|
10
10
|
SpreeCmCommissioner::Transit::RouteFulfilledOrderCountIncrementerJob.perform_later(order_id: id)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def increment_route_order_count
|
|
14
|
-
return unless
|
|
14
|
+
return unless has_trip_ids?
|
|
15
15
|
|
|
16
16
|
SpreeCmCommissioner::Transit::RouteOrderCountIncrementerJob.perform_later(order_id: id)
|
|
17
17
|
end
|
|
18
|
-
|
|
19
|
-
# Calling `.trip_ids` directly can cause many slow database queries (N+1 problem)
|
|
20
|
-
# every time `.should_update_trip_ids?` or `.preload_trip_ids` runs.
|
|
21
|
-
# To avoid this, we store a precomputed list of trip IDs in `private_metadata`.
|
|
22
|
-
def preload_trip_ids=(preload_trip_ids = [])
|
|
23
|
-
self.private_metadata = (private_metadata || {}).merge('preload_trip_ids' => preload_trip_ids)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def preload_trip_ids
|
|
27
|
-
private_metadata&.fetch('preload_trip_ids', []) || []
|
|
28
|
-
end
|
|
29
18
|
end
|
|
30
19
|
end
|
|
@@ -29,10 +29,18 @@ module SpreeCmCommissioner
|
|
|
29
29
|
:bib_pre_generation_on_create?,
|
|
30
30
|
:seat_number_positions,
|
|
31
31
|
:seat_number_layouts,
|
|
32
|
+
:color,
|
|
33
|
+
:ticket_type,
|
|
32
34
|
:seat_type,
|
|
33
35
|
to: :options
|
|
34
36
|
end
|
|
35
37
|
|
|
38
|
+
# Override variant.rb to return cached formatted options text, avoiding repeated database queries.
|
|
39
|
+
# Falls back to database queries & computing the format if preload data is unavailable.
|
|
40
|
+
def options_text
|
|
41
|
+
@options_text ||= public_metadata[:preload_options_text].presence || Spree::Variants::VisableOptionsPresenter.new(self).to_sentence
|
|
42
|
+
end
|
|
43
|
+
|
|
36
44
|
def options
|
|
37
45
|
@options ||= VariantOptions.new(self)
|
|
38
46
|
end
|
|
@@ -87,17 +95,23 @@ module SpreeCmCommissioner
|
|
|
87
95
|
options.payment_option == 'post-paid'
|
|
88
96
|
end
|
|
89
97
|
|
|
90
|
-
# save optins to public_metadata so we don't have to query option types & option values when needed them.
|
|
91
|
-
# once variant changed, we update metadata.
|
|
92
98
|
def set_options_to_public_metadata
|
|
93
99
|
self.public_metadata ||= {}
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
# Cache option values as a hash to avoid N+1 queries when accessing options.
|
|
102
|
+
# Stores {option_type_name => option_value_name} pairs that can be retrieved
|
|
103
|
+
# without loading option_types and option_values associations.
|
|
104
|
+
# Example: "Red, 256GB" - formatted options for quick display
|
|
105
|
+
self.public_metadata[:cm_options] = option_values.each_with_object({}) do |option_value, hash|
|
|
96
106
|
option_type_name = option_value.option_type.name
|
|
97
107
|
hash[option_type_name] = find_option_value_name_for(option_type_name: option_type_name)
|
|
98
108
|
end
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
# Cache formatted options text for quick retrieval via #options_text.
|
|
111
|
+
# Precomputes the human-readable format (e.g., "Red, 256GB") to avoid
|
|
112
|
+
# repeated formatting and association queries.
|
|
113
|
+
# Example: {"color" => "red", "storage" => "256GB"} - option_type_name => option_value_name pairs
|
|
114
|
+
self.public_metadata[:preload_options_text] = Spree::Variants::VisableOptionsPresenter.new(self).to_sentence
|
|
101
115
|
end
|
|
102
116
|
|
|
103
117
|
def set_options_to_public_metadata!
|
|
@@ -25,6 +25,8 @@ module SpreeCmCommissioner
|
|
|
25
25
|
validates :y, presence: true, numericality: true
|
|
26
26
|
validates :rotation, presence: true, numericality: { greater_than_or_equal_to: -360, less_than_or_equal_to: 360 }
|
|
27
27
|
|
|
28
|
+
validate :cannot_unassign_variant_with_active_blocks
|
|
29
|
+
|
|
28
30
|
before_validation :assign_layout_from_section, if: -> { seat_layout.nil? && seat_section.present? }
|
|
29
31
|
|
|
30
32
|
def label_required?
|
|
@@ -50,5 +52,22 @@ module SpreeCmCommissioner
|
|
|
50
52
|
def assign_layout_from_section
|
|
51
53
|
self.seat_layout = seat_section.seat_layout
|
|
52
54
|
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def cannot_unassign_variant_with_active_blocks
|
|
59
|
+
return if variant_id_was.blank? || variant_id.present?
|
|
60
|
+
|
|
61
|
+
active_blocks = all_reserved_blocks.reserved_or_on_hold
|
|
62
|
+
return unless active_blocks.exists?
|
|
63
|
+
|
|
64
|
+
block_count = active_blocks.count
|
|
65
|
+
block_statuses = active_blocks.pluck(:status).uniq.map { |s| s.humanize.downcase }
|
|
66
|
+
|
|
67
|
+
errors.add(
|
|
68
|
+
:variant,
|
|
69
|
+
"cannot be unassigned because #{block_count} #{block_count == 1 ? 'block is' : 'blocks are'} #{block_statuses.join(' and ')} by guests"
|
|
70
|
+
)
|
|
71
|
+
end
|
|
53
72
|
end
|
|
54
73
|
end
|
|
@@ -7,6 +7,7 @@ module SpreeCmCommissioner
|
|
|
7
7
|
base.include SpreeCmCommissioner::Metafield
|
|
8
8
|
base.include SpreeCmCommissioner::TenantUpdatable
|
|
9
9
|
base.include SpreeCmCommissioner::ServiceType
|
|
10
|
+
base.include SpreeCmCommissioner::ServiceRecommendations
|
|
10
11
|
|
|
11
12
|
base.has_many :variant_kind_option_types, -> { where(kind: :variant).order(:position) },
|
|
12
13
|
through: :product_option_types, source: :option_type
|