spree_cm_commissioner 2.1.9.pre.pre2 → 2.2.0
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/.gitignore +3 -0
- data/.tool-versions +1 -1
- data/Gemfile.lock +2 -1
- data/app/controllers/spree/admin/users_controller_decorator.rb +2 -0
- data/app/controllers/spree/api/v2/storefront/account/guest_dynamic_fields_controller.rb +3 -5
- data/app/controllers/spree/api/v2/storefront/account/mark_guest_info_complete_controller.rb +3 -8
- data/app/controllers/spree/api/v2/storefront/guests_controller.rb +37 -18
- data/app/interactors/spree_cm_commissioner/account_recover.rb +2 -2
- data/app/interactors/spree_cm_commissioner/guest_dynamic_field_notification_sender.rb +11 -0
- data/app/interactors/spree_cm_commissioner/notification_reader.rb +3 -1
- data/app/interactors/spree_cm_commissioner/stock/inventory_item_resetter.rb +1 -1
- data/app/interactors/spree_cm_commissioner/team_member_adder.rb +37 -0
- data/app/interactors/spree_cm_commissioner/team_member_creator.rb +58 -0
- data/app/interactors/spree_cm_commissioner/transit/draft_order_creator.rb +4 -0
- data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +2 -21
- data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +2 -2
- data/app/interactors/spree_cm_commissioner/user_registration_with_fb_token.rb +1 -1
- data/app/interactors/spree_cm_commissioner/user_registration_with_id_token.rb +1 -1
- data/app/interactors/spree_cm_commissioner/user_telegram_web_app_authenticator.rb +2 -0
- data/app/interactors/spree_cm_commissioner/user_vendor_assigner.rb +39 -0
- data/app/interactors/spree_cm_commissioner/vattanac_bank_initiator.rb +2 -0
- data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +13 -0
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +10 -0
- data/app/models/concerns/spree_cm_commissioner/order_state_machine.rb +12 -0
- data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +30 -0
- data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +48 -0
- data/app/models/concerns/spree_cm_commissioner/service_type.rb +35 -0
- data/app/models/concerns/spree_cm_commissioner/store_metadata.rb +14 -3
- data/app/models/spree_cm_commissioner/guest.rb +3 -0
- data/app/models/spree_cm_commissioner/inventory_item.rb +1 -1
- data/app/models/spree_cm_commissioner/notification.rb +5 -1
- data/app/models/spree_cm_commissioner/order_decorator.rb +5 -0
- data/app/models/spree_cm_commissioner/redis_stock/cached_inventory_items_builder.rb +2 -2
- data/app/models/spree_cm_commissioner/redis_stock/inventory_updater.rb +2 -2
- data/app/models/spree_cm_commissioner/route.rb +12 -0
- data/app/models/spree_cm_commissioner/trip.rb +40 -0
- data/app/models/spree_cm_commissioner/user_decorator.rb +11 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_route.rb +9 -0
- data/app/notifications/spree_cm_commissioner/guest_dynamic_field_notification.rb +24 -0
- data/app/overrides/spree/admin/users/_form/user_add_on.html.erb.deface +27 -0
- data/app/services/spree_cm_commissioner/transit/base_route_order_metrics_updater.rb +62 -0
- data/app/services/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_service.rb +18 -0
- data/app/services/spree_cm_commissioner/transit/route_order_count_incrementer_service.rb +19 -0
- data/app/services/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_service.rb +30 -0
- data/app/services/spree_cm_commissioner/transit/route_trip_count_decrementer_service.rb +33 -0
- data/app/services/spree_cm_commissioner/transit/route_trip_count_incrementer_service.rb +33 -0
- data/app/services/spree_cm_commissioner/users/incomplete_guest_checker_service.rb +34 -0
- data/app/views/blazer/queries/embed/_content.html.erb +4 -2
- data/config/initializers/spree_permitted_attributes.rb +1 -0
- data/config/locales/en.yml +20 -0
- data/config/locales/km.yml +20 -0
- data/db/migrate/20250911042815_create_cm_routes.rb +16 -0
- data/db/migrate/20250911045649_create_cm_vendor_routes.rb +12 -0
- data/db/migrate/20251008064344_add_route_id_to_cm_trips.rb +7 -0
- data/db/migrate/20251009033331_add_registered_by_to_spree_users.rb +6 -0
- data/db/migrate/20251009073040_add_lock_version_to_cm_routes.rb +5 -0
- data/db/migrate/20251009073929_add_lock_version_to_cm_vendor_routes.rb +5 -0
- data/lib/spree_cm_commissioner/engine.rb +4 -0
- data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +10 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +1 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_route_factory.rb +7 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +8 -14
- data/lib/tasks/backfill_confirmed_at.rake +21 -0
- data/spree_cm_commissioner.gemspec +1 -0
- metadata +50 -4
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ServiceType concern provides enum functionality for service type fields with I18n support.
|
|
2
|
+
#
|
|
3
|
+
# How to use:
|
|
4
|
+
# class YourModel < ApplicationRecord
|
|
5
|
+
# include SpreeCmCommissioner::ServiceType
|
|
6
|
+
#
|
|
7
|
+
# service_type_enum :service_type
|
|
8
|
+
# service_type_enum :primary_service_type
|
|
9
|
+
# end
|
|
10
|
+
module SpreeCmCommissioner
|
|
11
|
+
module ServiceType
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
SERVICE_TYPES = %i[organizer transit intercity_taxi].freeze
|
|
15
|
+
|
|
16
|
+
class_methods do
|
|
17
|
+
def service_type_enum(column_name = :service_type)
|
|
18
|
+
enum column_name => SERVICE_TYPES.index_with(&:to_s)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns a hash of all service types with their translated names
|
|
23
|
+
# Example: { organizer: "Organizer", transit: "Transit", intercity_taxi: "Intercity Taxi" }
|
|
24
|
+
#
|
|
25
|
+
# Usage: SpreeCmCommissioner::ServiceType.translated_service_types
|
|
26
|
+
def self.translated_service_types
|
|
27
|
+
SERVICE_TYPES.index_with do |type|
|
|
28
|
+
I18n.t("service_types.#{type}",
|
|
29
|
+
scope: 'spree',
|
|
30
|
+
default: type.to_s.humanize
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
# store_public_metadata :completed, :boolean, default: true
|
|
21
21
|
# store_public_metadata :count, :integer, default: 5
|
|
22
|
+
# store_public_metadata :tags, :array, default: []
|
|
22
23
|
# store_private_metadata :app_token, :string, default: "XYZ"
|
|
23
24
|
# end
|
|
24
25
|
#
|
|
25
|
-
# taxon = Spree::Taxon.new(completed: true, app_token: "ABC", count:
|
|
26
|
+
# taxon = Spree::Taxon.new(completed: true, app_token: "ABC", count: 100, tags: ["new"])
|
|
26
27
|
# taxon.completed # => true, store in public_metadata
|
|
27
28
|
# taxon.completed? # => true, store in public_metadata
|
|
28
29
|
# taxon.count # => 10, store in public_metadata
|
|
@@ -33,7 +34,7 @@ module SpreeCmCommissioner
|
|
|
33
34
|
module StoreMetadata
|
|
34
35
|
extend ActiveSupport::Concern
|
|
35
36
|
|
|
36
|
-
class_methods do
|
|
37
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
|
37
38
|
def store_private_metadata(key, type, default: nil)
|
|
38
39
|
store :private_metadata, accessors: [key], coder: JSON
|
|
39
40
|
|
|
@@ -67,6 +68,8 @@ module SpreeCmCommissioner
|
|
|
67
68
|
raw_value.to_i
|
|
68
69
|
when :string
|
|
69
70
|
raw_value.to_s
|
|
71
|
+
when :array
|
|
72
|
+
Array(raw_value)
|
|
70
73
|
else
|
|
71
74
|
raw_value
|
|
72
75
|
end
|
|
@@ -76,7 +79,7 @@ module SpreeCmCommissioner
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
# Validates only new assignments (raw JSON values) for type safety
|
|
79
|
-
def define_metadata_validation(column_name, key, type)
|
|
82
|
+
def define_metadata_validation(column_name, key, type) # rubocop:disable Metrics/CyclomaticComplexity
|
|
80
83
|
case type
|
|
81
84
|
when :boolean
|
|
82
85
|
validates key, inclusion: { in: [true, false] }, allow_nil: true
|
|
@@ -90,6 +93,14 @@ module SpreeCmCommissioner
|
|
|
90
93
|
end
|
|
91
94
|
when :string
|
|
92
95
|
validates key, length: { maximum: 255 }, allow_nil: true
|
|
96
|
+
when :array
|
|
97
|
+
validate do
|
|
98
|
+
metadata = send(column_name) || {}
|
|
99
|
+
raw_value = metadata[key.to_s]
|
|
100
|
+
next if raw_value.nil?
|
|
101
|
+
|
|
102
|
+
errors.add(key, 'must be an array') unless raw_value.is_a?(Array)
|
|
103
|
+
end
|
|
93
104
|
end
|
|
94
105
|
end
|
|
95
106
|
end
|
|
@@ -335,6 +335,7 @@ module SpreeCmCommissioner
|
|
|
335
335
|
end
|
|
336
336
|
|
|
337
337
|
def pre_registration_completed?
|
|
338
|
+
return false unless line_item&.order&.completed?
|
|
338
339
|
return true unless line_item&.product&.dynamic_fields&.pre_registration&.any?
|
|
339
340
|
|
|
340
341
|
required_fields = line_item.product.dynamic_fields.pre_registration.pluck(:id)
|
|
@@ -343,6 +344,7 @@ module SpreeCmCommissioner
|
|
|
343
344
|
end
|
|
344
345
|
|
|
345
346
|
def post_registration_completed?
|
|
347
|
+
return false unless line_item&.order&.completed?
|
|
346
348
|
return true unless line_item&.product&.dynamic_fields&.post_registration&.any?
|
|
347
349
|
|
|
348
350
|
required_fields = line_item.product.dynamic_fields.post_registration.pluck(:id)
|
|
@@ -351,6 +353,7 @@ module SpreeCmCommissioner
|
|
|
351
353
|
end
|
|
352
354
|
|
|
353
355
|
def check_in_completed?
|
|
356
|
+
return false unless line_item&.order&.completed?
|
|
354
357
|
return true unless line_item&.product&.dynamic_fields&.during_check_in&.any?
|
|
355
358
|
|
|
356
359
|
required_fields = line_item.product.dynamic_fields.during_check_in.pluck(:id)
|
|
@@ -48,7 +48,7 @@ module SpreeCmCommissioner
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def adjust_quantity_in_redis(quantity)
|
|
51
|
-
SpreeCmCommissioner.
|
|
51
|
+
SpreeCmCommissioner.redis_pool.with do |redis|
|
|
52
52
|
cached_quantity_available = redis.get(redis_key)
|
|
53
53
|
# ignore if redis doesn't exist
|
|
54
54
|
return if cached_quantity_available.nil? # rubocop:disable Lint/NonLocalExitFromIterator
|
|
@@ -10,7 +10,7 @@ module SpreeCmCommissioner
|
|
|
10
10
|
|
|
11
11
|
scope :user_notifications, lambda {
|
|
12
12
|
where(
|
|
13
|
-
type: %w[order_complete_notification customer_notification]
|
|
13
|
+
type: %w[order_complete_notification customer_notification guest_dynamic_field_notification]
|
|
14
14
|
)
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -20,6 +20,10 @@ module SpreeCmCommissioner
|
|
|
20
20
|
)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
scope :markable_notifications, lambda {
|
|
24
|
+
where(read_at: nil).where.not(type: %w[guest_dynamic_field_notification])
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
belongs_to :recipient, polymorphic: true
|
|
24
28
|
belongs_to :notificable, polymorphic: true
|
|
25
29
|
end
|
|
@@ -4,6 +4,8 @@ module SpreeCmCommissioner
|
|
|
4
4
|
base.include SpreeCmCommissioner::PhoneNumberSanitizer
|
|
5
5
|
base.include SpreeCmCommissioner::OrderSeatable
|
|
6
6
|
base.include SpreeCmCommissioner::OrderStateMachine
|
|
7
|
+
base.include SpreeCmCommissioner::RouteOrderCountable
|
|
8
|
+
base.include SpreeCmCommissioner::StoreMetadata
|
|
7
9
|
|
|
8
10
|
base.scope :subscription, -> { where.not(subscription_id: nil) }
|
|
9
11
|
base.scope :paid, -> { where(payment_state: :paid) }
|
|
@@ -28,6 +30,9 @@ module SpreeCmCommissioner
|
|
|
28
30
|
base.before_create :link_by_phone_number
|
|
29
31
|
base.before_create :associate_customer
|
|
30
32
|
base.before_create :set_tenant_id
|
|
33
|
+
base.after_commit :increment_route_order_count, on: :create
|
|
34
|
+
|
|
35
|
+
base.store_private_metadata :preload_trip_ids, :array, default: []
|
|
31
36
|
|
|
32
37
|
base.validates :promo_total, base::MONEY_VALIDATION
|
|
33
38
|
base.validate :validate_channel_prefix, if: :channel_changed?
|
|
@@ -12,7 +12,7 @@ module SpreeCmCommissioner
|
|
|
12
12
|
keys = inventory_items.map { |item| "inventory:#{item.id}" }
|
|
13
13
|
return [] unless keys.any?
|
|
14
14
|
|
|
15
|
-
counts = SpreeCmCommissioner.
|
|
15
|
+
counts = SpreeCmCommissioner.redis_pool.with { |redis| redis.mget(*keys) }
|
|
16
16
|
inventory_items.map.with_index do |inventory_item, i|
|
|
17
17
|
::SpreeCmCommissioner::CachedInventoryItem.new(
|
|
18
18
|
inventory_key: keys[i],
|
|
@@ -30,7 +30,7 @@ module SpreeCmCommissioner
|
|
|
30
30
|
return count_in_redis.to_i if count_in_redis.present?
|
|
31
31
|
return inventory_item.quantity_available unless inventory_item.active?
|
|
32
32
|
|
|
33
|
-
SpreeCmCommissioner.
|
|
33
|
+
SpreeCmCommissioner.redis_pool.with do |redis|
|
|
34
34
|
redis.set(key, inventory_item.quantity_available, ex: inventory_item.redis_expired_in)
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -51,13 +51,13 @@ module SpreeCmCommissioner
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def unstock(keys, quantities)
|
|
54
|
-
SpreeCmCommissioner.
|
|
54
|
+
SpreeCmCommissioner.redis_pool.with do |redis|
|
|
55
55
|
redis.eval(unstock_redis_script, keys: keys, argv: quantities)
|
|
56
56
|
end.positive?
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def restock(keys, quantities)
|
|
60
|
-
SpreeCmCommissioner.
|
|
60
|
+
SpreeCmCommissioner.redis_pool.with do |redis|
|
|
61
61
|
redis.eval(restock_redis_script, keys: keys, argv: quantities)
|
|
62
62
|
end.positive?
|
|
63
63
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class Route < Base
|
|
3
|
+
has_many :vendor_routes, class_name: 'SpreeCmCommissioner::VendorRoute', dependent: :destroy
|
|
4
|
+
has_many :vendors, through: :vendor_routes
|
|
5
|
+
has_many :trips, inverse_of: :route
|
|
6
|
+
|
|
7
|
+
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place'
|
|
8
|
+
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place'
|
|
9
|
+
|
|
10
|
+
validates :origin_place_id, uniqueness: { scope: :destination_place_id }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -2,6 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class Trip < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::RouteType
|
|
5
|
+
include SpreeCmCommissioner::RouteTripCountCallbacks
|
|
5
6
|
|
|
6
7
|
attr_accessor :hours, :minutes, :seconds
|
|
7
8
|
|
|
@@ -20,6 +21,7 @@ module SpreeCmCommissioner
|
|
|
20
21
|
|
|
21
22
|
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
22
23
|
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
24
|
+
belongs_to :route, inverse_of: :trips
|
|
23
25
|
|
|
24
26
|
has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
|
|
25
27
|
has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
|
|
@@ -38,12 +40,17 @@ module SpreeCmCommissioner
|
|
|
38
40
|
|
|
39
41
|
# before create, duplicate seat layout from vehicle if present
|
|
40
42
|
before_validation :duplicate_seat_layout_from_vehicle, if: -> { seat_layout.nil? && vehicle.present? && vehicle.seat_layout.present? }
|
|
43
|
+
before_validation :assign_route_and_vendor_route, if: :changed_route_attributes?
|
|
41
44
|
|
|
42
45
|
accepts_nested_attributes_for :trip_stops, :product, :seat_layout, allow_destroy: true
|
|
43
46
|
|
|
44
47
|
self.whitelisted_ransackable_associations = %w[product vehicle]
|
|
45
48
|
self.whitelisted_ransackable_attributes = %w[origin_place_id destination_place_id]
|
|
46
49
|
|
|
50
|
+
def changed_route_attributes?
|
|
51
|
+
origin_place_id_changed? || destination_place_id_changed? || vendor_id_changed?
|
|
52
|
+
end
|
|
53
|
+
|
|
47
54
|
def convert_duration_to_seconds
|
|
48
55
|
return if hours.blank? && minutes.blank? && seconds.blank?
|
|
49
56
|
|
|
@@ -74,6 +81,9 @@ module SpreeCmCommissioner
|
|
|
74
81
|
private
|
|
75
82
|
|
|
76
83
|
def duplicate_seat_layout_from_vehicle
|
|
84
|
+
# Duplicate the vehicle's seat layout into this trip. Copies nested
|
|
85
|
+
# seat sections and blocks while excluding identifying columns and
|
|
86
|
+
# timestamps to avoid collisions when creating new records.
|
|
77
87
|
original = vehicle.seat_layout
|
|
78
88
|
|
|
79
89
|
self.seat_layout_attributes =
|
|
@@ -92,5 +102,35 @@ module SpreeCmCommissioner
|
|
|
92
102
|
end
|
|
93
103
|
)
|
|
94
104
|
end
|
|
105
|
+
|
|
106
|
+
# Check if any of the route context attributes are changing
|
|
107
|
+
def will_route_context_change?
|
|
108
|
+
# Returns true if any of the route context attributes will change on save.
|
|
109
|
+
will_save_change_to_route_id? ||
|
|
110
|
+
will_save_change_to_origin_place_id? ||
|
|
111
|
+
will_save_change_to_destination_place_id? ||
|
|
112
|
+
will_save_change_to_vendor_id?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def changed_route_context?
|
|
116
|
+
saved_change_to_route_id? ||
|
|
117
|
+
saved_change_to_origin_place_id? ||
|
|
118
|
+
saved_change_to_destination_place_id? ||
|
|
119
|
+
saved_change_to_vendor_id?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Find or create the Route for this trip (by origin/destination) and
|
|
123
|
+
# assign it to the trip. Also populate a default route_name from the
|
|
124
|
+
# product when the route is new/blank.
|
|
125
|
+
def assign_route_and_vendor_route
|
|
126
|
+
route = SpreeCmCommissioner::Route.find_or_create_by!(
|
|
127
|
+
origin_place_id: origin_place_id,
|
|
128
|
+
destination_place_id: destination_place_id
|
|
129
|
+
)
|
|
130
|
+
route.route_name = product.name if route.route_name.blank?
|
|
131
|
+
route.save! if route.changed?
|
|
132
|
+
|
|
133
|
+
self.route_id = route.id
|
|
134
|
+
end
|
|
95
135
|
end
|
|
96
136
|
end
|
|
@@ -6,8 +6,14 @@ module SpreeCmCommissioner
|
|
|
6
6
|
base.include SpreeCmCommissioner::UserNotification
|
|
7
7
|
base.include SpreeCmCommissioner::UserIdentity
|
|
8
8
|
base.include SpreeCmCommissioner::UserPreference
|
|
9
|
+
base.include SpreeCmCommissioner::StoreMetadata
|
|
10
|
+
base.include SpreeCmCommissioner::ServiceType
|
|
9
11
|
|
|
10
12
|
base.enum gender: %i[male female other]
|
|
13
|
+
base.enum registered_by: {
|
|
14
|
+
self_registered: 0,
|
|
15
|
+
system_registered: 1
|
|
16
|
+
}, _prefix: true
|
|
11
17
|
|
|
12
18
|
base.has_many :subscriptions, through: :customer, class_name: 'SpreeCmCommissioner::Subscription'
|
|
13
19
|
base.has_many :payments, as: :payable, class_name: 'Spree::Payment', dependent: :nullify
|
|
@@ -45,6 +51,11 @@ module SpreeCmCommissioner
|
|
|
45
51
|
|
|
46
52
|
# Store has_incomplete_guest_info in public_metadata for easy frontend access
|
|
47
53
|
base.store :public_metadata, accessors: [:has_incomplete_guest_info], coder: JSON
|
|
54
|
+
base.store_public_metadata :primary_service_type, :string
|
|
55
|
+
base.validates :primary_service_type,
|
|
56
|
+
inclusion: { in: SpreeCmCommissioner::ServiceType::SERVICE_TYPES.map(&:to_s) },
|
|
57
|
+
allow_nil: true
|
|
58
|
+
base.store_public_metadata :branch_ids, :array, default: []
|
|
48
59
|
|
|
49
60
|
base.devise :two_factor_authenticatable
|
|
50
61
|
|
|
@@ -75,6 +75,7 @@ module SpreeCmCommissioner
|
|
|
75
75
|
base.has_many :subscriptions, through: :customers, class_name: 'SpreeCmCommissioner::Subscription'
|
|
76
76
|
base.has_many :subscription_orders, through: :subscriptions, class_name: 'Spree::Order', source: :orders
|
|
77
77
|
base.has_many :promotion_categories, class_name: 'Spree::PromotionCategory', foreign_key: :vendor_id, dependent: :destroy
|
|
78
|
+
base.has_many :vendor_routes, class_name: 'SpreeCmCommissioner::VendorRoute'
|
|
78
79
|
|
|
79
80
|
base.has_many :homepage_section_relatables,
|
|
80
81
|
as: :relatable,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class VendorRoute < Base
|
|
3
|
+
belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
|
|
4
|
+
belongs_to :route, class_name: 'SpreeCmCommissioner::Route', optional: false
|
|
5
|
+
|
|
6
|
+
has_many :trips, -> (vr) { where(vendor_id: vr.vendor_id, route_id: vr.route_id) }, class_name: 'SpreeCmCommissioner::Trip'
|
|
7
|
+
validates :vendor_id, uniqueness: { scope: :route_id }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class GuestDynamicFieldNotification < NoticedFcmBase
|
|
3
|
+
def notificable
|
|
4
|
+
guest
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def guest
|
|
8
|
+
params[:guest]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def extra_payload
|
|
12
|
+
{
|
|
13
|
+
guest_id: guest.id,
|
|
14
|
+
line_item_id: guest.line_item_id,
|
|
15
|
+
order_token: guest.line_item.order.token,
|
|
16
|
+
notification_type: type
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def type
|
|
21
|
+
'guest_dynamic_field_notification'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- insert_bottom "[data-hook='admin_user_form_password_fields']" -->
|
|
2
|
+
|
|
3
|
+
<%
|
|
4
|
+
service_types = SpreeCmCommissioner::ServiceType.translated_service_types.map { |key, label| [label, key.to_s] }
|
|
5
|
+
%>
|
|
6
|
+
|
|
7
|
+
<div data-hook="admin_user_form_user_section" class="p-3 mt-4 rounded border form-group bg-light">
|
|
8
|
+
<strong class="mb-3 d-block"><%= Spree.t(:add_on) %></strong>
|
|
9
|
+
|
|
10
|
+
<%= f.field_container :primary_service_type do %>
|
|
11
|
+
<%= f.label :primary_service_type, Spree.t(:primary_service_type) %>
|
|
12
|
+
<%= f.select :primary_service_type,
|
|
13
|
+
options_for_select(service_types, f.object.primary_service_type),
|
|
14
|
+
{ include_blank: Spree.t(:select_service_type) },
|
|
15
|
+
class: 'form-control select2-clear' %>
|
|
16
|
+
<%= f.error_message_on :primary_service_type %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<div class="form-group">
|
|
20
|
+
<label><%= Spree.t(:registration_source) %></label>
|
|
21
|
+
<div class="form-control" style="background-color: #f8f9fa; cursor: not-allowed;">
|
|
22
|
+
<%= f.object.persisted? ? Spree.t("registered_by.#{f.object.registered_by}") : Spree.t("registered_by.system_registered") %>
|
|
23
|
+
</div>
|
|
24
|
+
<%# Hidden field to submit the default value for new records %>
|
|
25
|
+
<%= f.hidden_field :registered_by, value: 'system_registered' if f.object.new_record? %>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class BaseRouteOrderMetricsUpdater
|
|
4
|
+
attr_reader :order, :attribute
|
|
5
|
+
|
|
6
|
+
SUPPORTED_ATTRIBUTES = %i[order_count fulfilled_order_count].freeze
|
|
7
|
+
|
|
8
|
+
def initialize(order:, attribute:)
|
|
9
|
+
@order = order
|
|
10
|
+
@attribute = attribute&.to_sym
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.call(order:, attribute:)
|
|
14
|
+
new(order: order, attribute: attribute).call
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
return unless order && attribute && SUPPORTED_ATTRIBUTES.include?(attribute)
|
|
19
|
+
|
|
20
|
+
order.line_items.find_each { |li| process_line_item(li) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def process_line_item(line_item)
|
|
26
|
+
return unless line_item.transit?
|
|
27
|
+
|
|
28
|
+
product = line_item.variant&.product
|
|
29
|
+
return unless product&.transit?
|
|
30
|
+
|
|
31
|
+
trip = product&.trip
|
|
32
|
+
return unless trip
|
|
33
|
+
|
|
34
|
+
route = find_or_create_route_for_trip(trip)
|
|
35
|
+
ensure_trip_route(trip, route)
|
|
36
|
+
increment_route_by(route, line_item.quantity)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def find_or_create_route_for_trip(trip)
|
|
40
|
+
SpreeCmCommissioner::Route.find_or_create_by(
|
|
41
|
+
origin_place_id: trip.origin_place_id,
|
|
42
|
+
destination_place_id: trip.destination_place_id
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ensure_trip_route(trip, route)
|
|
47
|
+
trip_route_id = trip.respond_to?(:route_id) ? trip.route_id : nil
|
|
48
|
+
|
|
49
|
+
return unless trip.respond_to?(:update_column) && trip_route_id != route.id
|
|
50
|
+
|
|
51
|
+
trip.update_column(:route_id, route.id) # rubocop:disable Rails/SkipsModelValidations
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def increment_route_by(route, qty)
|
|
55
|
+
route.with_lock do
|
|
56
|
+
current_value = route.public_send(attribute) || 0
|
|
57
|
+
route.update!(attribute => current_value + qty)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/app/services/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_service.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class RouteFulfilledOrderCountIncrementerService
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(order:)
|
|
7
|
+
return failure(nil, 'Order not found') unless order
|
|
8
|
+
|
|
9
|
+
SpreeCmCommissioner::Transit::BaseRouteOrderMetricsUpdater
|
|
10
|
+
.call(order: order, attribute: :fulfilled_order_count)
|
|
11
|
+
|
|
12
|
+
success(order: order)
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
failure(nil, e.message)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class RouteOrderCountIncrementerService
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(order:)
|
|
7
|
+
return failure(nil, 'Order not found') unless order
|
|
8
|
+
|
|
9
|
+
SpreeCmCommissioner::Transit::BaseRouteOrderMetricsUpdater
|
|
10
|
+
.new(order: order, attribute: :order_count)
|
|
11
|
+
.call
|
|
12
|
+
|
|
13
|
+
success(order: order)
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
failure(nil, e.message)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/app/services/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_service.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class RoutePreviousTripCountDecrementerService
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(previous_route_id:)
|
|
7
|
+
previous_route = SpreeCmCommissioner::Route.find_by(id: previous_route_id)
|
|
8
|
+
return failure(nil, 'Route not found') unless previous_route
|
|
9
|
+
|
|
10
|
+
if previous_route.vendors.exists?
|
|
11
|
+
|
|
12
|
+
# Build a trip-like object that responds to :route and :vendor so the delegated
|
|
13
|
+
# service can locate vendor routes correctly.
|
|
14
|
+
trip_like = Struct.new(:route, :vendor).new(previous_route, previous_route.vendors.first)
|
|
15
|
+
result = SpreeCmCommissioner::Transit::RouteTripCountDecrementerService.call(trip: trip_like)
|
|
16
|
+
return result if result.failure?
|
|
17
|
+
else
|
|
18
|
+
previous_route.with_lock do
|
|
19
|
+
new_count = [previous_route.trip_count.to_i - 1, 0].max
|
|
20
|
+
previous_route.update!(trip_count: new_count)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
success(previous_route: previous_route)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
failure(nil, e.message)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class RouteTripCountDecrementerService
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(trip:)
|
|
7
|
+
return failure(nil, 'Trip not found') unless trip
|
|
8
|
+
|
|
9
|
+
vendor = trip.vendor
|
|
10
|
+
return failure(nil, 'Vendor not found') unless vendor
|
|
11
|
+
|
|
12
|
+
ActiveRecord::Base.transaction do
|
|
13
|
+
route = trip.route
|
|
14
|
+
|
|
15
|
+
route.update!(trip_count: [route.trip_count - 1, 0].max)
|
|
16
|
+
|
|
17
|
+
vendor_route = locate_vendor_route(vendor: vendor, route: route)
|
|
18
|
+
vendor_route&.update!(trip_count: [vendor_route.trip_count - 1, 0].max)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
success(trip: trip)
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
failure(nil, e.message)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def locate_vendor_route(vendor:, route:)
|
|
29
|
+
vendor.vendor_routes.find_by(route_id: route.id)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Transit
|
|
3
|
+
class RouteTripCountIncrementerService
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(trip:)
|
|
7
|
+
return failure(nil, 'Trip not found') unless trip
|
|
8
|
+
|
|
9
|
+
vendor = trip.vendor
|
|
10
|
+
return failure(nil, 'Vendor not found') unless vendor
|
|
11
|
+
|
|
12
|
+
ActiveRecord::Base.transaction do
|
|
13
|
+
route = trip.route
|
|
14
|
+
|
|
15
|
+
route.update!(trip_count: route.trip_count + 1)
|
|
16
|
+
|
|
17
|
+
vendor_route = find_or_create_vendor_route(vendor: vendor, route: route)
|
|
18
|
+
vendor_route.update!(trip_count: vendor_route.trip_count + 1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
success(trip: trip)
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
failure(nil, e.message)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def find_or_create_vendor_route(vendor:, route:)
|
|
29
|
+
vendor.vendor_routes.find_or_create_by!(route_id: route.id)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Users
|
|
3
|
+
class IncompleteGuestCheckerService
|
|
4
|
+
attr_reader :user
|
|
5
|
+
|
|
6
|
+
def initialize(user)
|
|
7
|
+
@user = user
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
return if user.blank? || incomplete_guest_info?
|
|
12
|
+
|
|
13
|
+
user.public_metadata['has_incomplete_guest_info'] = false
|
|
14
|
+
user.save!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def incomplete_guest_info?
|
|
20
|
+
guests =
|
|
21
|
+
SpreeCmCommissioner::Guest
|
|
22
|
+
.joins(line_item: :order)
|
|
23
|
+
.includes(:guest_dynamic_fields, line_item: { product: :dynamic_fields })
|
|
24
|
+
.where(spree_orders: { user_id: user.id, state: 'complete' })
|
|
25
|
+
|
|
26
|
+
guests.any? do |guest|
|
|
27
|
+
guest.pre_registration_completed? &&
|
|
28
|
+
guest.post_registration_fields? &&
|
|
29
|
+
!guest.post_registration_completed?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
|
|
11
11
|
<% if @success %>
|
|
12
12
|
<div id="results" style="font-family: 'Poppins', sans-serif;"></div>
|
|
13
|
-
<p class="text-muted">Loading...</p>
|
|
14
|
-
</div>
|
|
15
13
|
|
|
16
14
|
<script>
|
|
17
15
|
function showRun(data) {
|
|
@@ -82,4 +80,8 @@
|
|
|
82
80
|
height: 15px;
|
|
83
81
|
fill: <%= theme_colors[:text] %>;
|
|
84
82
|
}
|
|
83
|
+
|
|
84
|
+
#results table thead.tableFloatingHeaderOriginal {
|
|
85
|
+
top: 60px !important;
|
|
86
|
+
}
|
|
85
87
|
</style>
|