spree_cm_commissioner 2.3.0.pre.pre16 → 2.3.0.pre.pre18
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/line_items_controller.rb +1 -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/user_registration_with_id_token.rb +72 -36
- 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/line_item_decorator.rb +5 -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/v2/storefront/line_item_serializer_decorator.rb +2 -0
- 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
|
@@ -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
|
|
@@ -22,6 +22,11 @@ module SpreeCmCommissioner
|
|
|
22
22
|
base.has_many :pending_guests, pending_guests_query, class_name: 'SpreeCmCommissioner::Guest', dependent: :destroy
|
|
23
23
|
base.has_many :product_completion_steps, class_name: 'SpreeCmCommissioner::ProductCompletionStep', through: :product
|
|
24
24
|
|
|
25
|
+
base.has_many :saved_guests,
|
|
26
|
+
through: :guests,
|
|
27
|
+
source: :saved_guest,
|
|
28
|
+
class_name: 'SpreeCmCommissioner::SavedGuest'
|
|
29
|
+
|
|
25
30
|
base.before_validation -> { self.product_type = variant.product_type || product.product_type }, if: -> { product_type.nil? }
|
|
26
31
|
|
|
27
32
|
base.before_save :update_vendor_id
|
|
@@ -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
|
|
@@ -68,11 +68,6 @@ module SpreeCmCommissioner
|
|
|
68
68
|
)
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
# override
|
|
72
|
-
def options_text
|
|
73
|
-
@options_text ||= Spree::Variants::VisableOptionsPresenter.new(self).to_sentence
|
|
74
|
-
end
|
|
75
|
-
|
|
76
71
|
def selected_option_value_ids
|
|
77
72
|
option_value_variants.pluck(:option_value_id)
|
|
78
73
|
end
|
|
@@ -139,6 +139,14 @@ module SpreeCmCommissioner
|
|
|
139
139
|
@seat_number_layouts ||= option_value_name_for(option_type_name: 'seat-number-layouts')&.split(',')
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
def color
|
|
143
|
+
@color ||= option_value_name_for(option_type_name: 'color')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ticket_type
|
|
147
|
+
@ticket_type ||= option_value_name_for(option_type_name: 'ticket-type')
|
|
148
|
+
end
|
|
149
|
+
|
|
142
150
|
def seat_type
|
|
143
151
|
@seat_type ||= option_value_name_for(option_type_name: 'seat-type')
|
|
144
152
|
end
|
|
@@ -16,6 +16,8 @@ module SpreeCmCommissioner
|
|
|
16
16
|
|
|
17
17
|
base.before_save :generate_code, if: :code.nil?
|
|
18
18
|
|
|
19
|
+
base.after_create :send_telegram_vendor_creation_alert
|
|
20
|
+
|
|
19
21
|
base.has_many :photos, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'SpreeCmCommissioner::VendorPhoto'
|
|
20
22
|
base.has_many :option_values, through: :products
|
|
21
23
|
base.has_many :vendor_option_types, class_name: 'SpreeCmCommissioner::VendorOptionType'
|
|
@@ -201,6 +203,10 @@ module SpreeCmCommissioner
|
|
|
201
203
|
vendor_kind_option_values.where(option_type_id: rules_option_type.id)
|
|
202
204
|
end
|
|
203
205
|
|
|
206
|
+
def send_telegram_vendor_creation_alert
|
|
207
|
+
VendorCreationTelegramAlertSenderJob.perform_later(id)
|
|
208
|
+
end
|
|
209
|
+
|
|
204
210
|
delegate :present?, to: :tenant, prefix: true
|
|
205
211
|
end
|
|
206
212
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<!-- insert_bottom "[data-hook='admin_variant_form_additional_fields']" -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<%# deprecated no longer used, should be removed entirely along with table column, just hide for now. %>
|
|
4
|
+
<div class="form-group d-none">
|
|
4
5
|
<%= f.field_container :permanent_stock do %>
|
|
5
6
|
<%= f.label :permanent_stock, Spree.t(:permanent_stock) %>
|
|
6
7
|
<%= f.number_field :permanent_stock, class: 'form-control' %>
|
|
@@ -25,6 +25,8 @@ module Spree
|
|
|
25
25
|
|
|
26
26
|
base.has_many :guests, serializer: SpreeCmCommissioner::V2::Storefront::GuestSerializer
|
|
27
27
|
|
|
28
|
+
base.has_many :saved_guests, serializer: SpreeCmCommissioner::V2::Storefront::SavedGuestSerializer
|
|
29
|
+
|
|
28
30
|
base.has_many :pending_guests, serializer: SpreeCmCommissioner::V2::Storefront::GuestSerializer
|
|
29
31
|
|
|
30
32
|
base.has_one :product, serializer: Spree::V2::Storefront::ProductSerializer
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class IntercityTaxiCartSerializer < Spree::V2::Storefront::CartSerializer
|
|
5
|
+
has_many :line_items,
|
|
6
|
+
serializer: SpreeCmCommissioner::V2::Storefront::IntercityTaxiLineItemSerializer
|
|
7
|
+
|
|
8
|
+
cache_options store: nil
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class IntercityTaxiLineItemSerializer < BaseSerializer
|
|
5
|
+
attributes :pickup_place_name,
|
|
6
|
+
:drop_off_place_name,
|
|
7
|
+
:pickup_lat,
|
|
8
|
+
:pickup_lng,
|
|
9
|
+
:drop_off_lat,
|
|
10
|
+
:drop_off_lng,
|
|
11
|
+
:passenger_count,
|
|
12
|
+
:distance_km,
|
|
13
|
+
:ordered_points,
|
|
14
|
+
:base_km,
|
|
15
|
+
:detour_pickup_km,
|
|
16
|
+
:detour_dropoff_km,
|
|
17
|
+
:extra_pickup_km,
|
|
18
|
+
:extra_dropoff_km,
|
|
19
|
+
:extra_pickup_charge_usd,
|
|
20
|
+
:extra_dropoff_charge_usd,
|
|
21
|
+
:estimated_time_minutes,
|
|
22
|
+
:from_date,
|
|
23
|
+
:to_date,
|
|
24
|
+
:direction,
|
|
25
|
+
:trip_id
|
|
26
|
+
|
|
27
|
+
cache_options store: nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -4,6 +4,17 @@ module SpreeCmCommissioner
|
|
|
4
4
|
class TripResultSerializer < BaseSerializer
|
|
5
5
|
set_type :trip_result
|
|
6
6
|
|
|
7
|
+
# Enable serializer-level caching
|
|
8
|
+
# Longer TTL than controller cache (5min) because:
|
|
9
|
+
# - Individual trips change less frequently
|
|
10
|
+
# - Same trip can be reused across multiple searches
|
|
11
|
+
# - Cache invalidation handled by cache_key_with_version
|
|
12
|
+
cache_options(
|
|
13
|
+
store: Rails.cache,
|
|
14
|
+
namespace: 'trip-result-serializer',
|
|
15
|
+
expires_in: 30.minutes
|
|
16
|
+
)
|
|
17
|
+
|
|
7
18
|
attributes :origin_place, :destination_place, :boarding, :drop_off, :price, :compare_at_amount, :currency, :quantity_available, :max_capacity,
|
|
8
19
|
:allow_seat_selection, :departure_time, :route_type, :distance
|
|
9
20
|
attribute :duration_in_hms, &:duration_in_hms
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module IntercityTaxiOrder
|
|
3
|
+
class Create
|
|
4
|
+
attr_reader :trip_id, :from_date, :to_date, :user_id, :quantity
|
|
5
|
+
|
|
6
|
+
def self.call(trip_id:, from_date:, to_date:, user_id: nil, quantity: 1)
|
|
7
|
+
new(
|
|
8
|
+
trip_id: trip_id,
|
|
9
|
+
from_date: from_date,
|
|
10
|
+
to_date: to_date,
|
|
11
|
+
user_id: user_id,
|
|
12
|
+
quantity: quantity
|
|
13
|
+
).call
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(trip_id:, from_date:, to_date:, user_id:, quantity:)
|
|
17
|
+
@trip_id = trip_id
|
|
18
|
+
@from_date = from_date
|
|
19
|
+
@to_date = to_date
|
|
20
|
+
@user_id = user_id
|
|
21
|
+
@quantity = quantity.to_i <= 0 ? 1 : quantity.to_i
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
validate!
|
|
26
|
+
|
|
27
|
+
ActiveRecord::Base.transaction do
|
|
28
|
+
trip = SpreeCmCommissioner::Trip.find(trip_id)
|
|
29
|
+
variant = trip.product.variants.first
|
|
30
|
+
raise ArgumentError, 'No variant found for trip' if variant.blank?
|
|
31
|
+
|
|
32
|
+
order = Spree::Order.new(state: 'cart', use_billing: true, user_id: user_id)
|
|
33
|
+
|
|
34
|
+
line_item = order.line_items.new(
|
|
35
|
+
variant_id: variant.id,
|
|
36
|
+
quantity: quantity,
|
|
37
|
+
from_date: from_date,
|
|
38
|
+
to_date: to_date,
|
|
39
|
+
private_metadata: {
|
|
40
|
+
trip_id: trip.id
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
build_guests_for!(line_item, quantity)
|
|
45
|
+
|
|
46
|
+
order.save!
|
|
47
|
+
order.update_with_updater!
|
|
48
|
+
|
|
49
|
+
order
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def validate!
|
|
56
|
+
raise ArgumentError, 'trip_id is required' if trip_id.blank?
|
|
57
|
+
raise ArgumentError, 'from_date is required' if from_date.blank?
|
|
58
|
+
raise ArgumentError, 'to_date is required' if to_date.blank?
|
|
59
|
+
raise ArgumentError, 'to_date must be after from_date' if from_date.present? && to_date.present? && to_date < from_date
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def build_guests_for!(line_item, qty)
|
|
63
|
+
Array.new(qty) { line_item.guests.new }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module IntercityTaxiOrder
|
|
3
|
+
class Update
|
|
4
|
+
attr_reader :order_number, :params
|
|
5
|
+
|
|
6
|
+
def self.call(order_number:, params:)
|
|
7
|
+
new(order_number: order_number, params: params).call
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(order_number:, params:)
|
|
11
|
+
@order_number = order_number
|
|
12
|
+
@params = ensure_parameters(params)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
order = Spree::Order.find_by!(number: order_number)
|
|
17
|
+
order.update!(order_params)
|
|
18
|
+
|
|
19
|
+
create_billing_address_if_missing(order)
|
|
20
|
+
|
|
21
|
+
order
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def ensure_parameters(params)
|
|
27
|
+
return params if params.is_a?(::ActionController::Parameters)
|
|
28
|
+
|
|
29
|
+
::ActionController::Parameters.new(params)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def create_billing_address_if_missing(order)
|
|
33
|
+
return if order.bill_address.present?
|
|
34
|
+
|
|
35
|
+
SpreeCmCommissioner::BillingAddressCreator.call(order: order)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def order_params
|
|
39
|
+
params.require(:order).permit(
|
|
40
|
+
:intel_phone_number,
|
|
41
|
+
:email,
|
|
42
|
+
line_items_attributes: [
|
|
43
|
+
:id,
|
|
44
|
+
:pickup_place_name,
|
|
45
|
+
:drop_off_place_name,
|
|
46
|
+
:from_date,
|
|
47
|
+
:pickup_lat,
|
|
48
|
+
:pickup_lng,
|
|
49
|
+
:pickup_oob_confirmed,
|
|
50
|
+
:drop_off_lat,
|
|
51
|
+
:drop_off_lng,
|
|
52
|
+
:drop_off_oob_confirmed,
|
|
53
|
+
:distance_km,
|
|
54
|
+
:ordered_points,
|
|
55
|
+
:base_km,
|
|
56
|
+
:detour_pickup_km,
|
|
57
|
+
:detour_dropoff_km,
|
|
58
|
+
:extra_pickup_km,
|
|
59
|
+
:extra_dropoff_km,
|
|
60
|
+
:extra_pickup_charge_usd,
|
|
61
|
+
:extra_dropoff_charge_usd,
|
|
62
|
+
:estimated_time_minutes,
|
|
63
|
+
:remark,
|
|
64
|
+
:passenger_count,
|
|
65
|
+
{ guests_attributes: %i[
|
|
66
|
+
id
|
|
67
|
+
first_name
|
|
68
|
+
last_name
|
|
69
|
+
gender
|
|
70
|
+
nationality_id
|
|
71
|
+
age
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
-
module
|
|
3
|
-
class
|
|
2
|
+
module Routes
|
|
3
|
+
class DecrementPreviousTripCount
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
6
|
def call(previous_route_id:)
|
|
@@ -12,7 +12,7 @@ module SpreeCmCommissioner
|
|
|
12
12
|
# Build a trip-like object that responds to :route and :vendor so the delegated
|
|
13
13
|
# service can locate vendor routes correctly.
|
|
14
14
|
trip_like = Struct.new(:route, :vendor).new(previous_route, previous_route.vendors.first)
|
|
15
|
-
result = SpreeCmCommissioner::
|
|
15
|
+
result = SpreeCmCommissioner::Routes::DecrementTripCount.call(trip: trip_like)
|
|
16
16
|
return result if result.failure?
|
|
17
17
|
else
|
|
18
18
|
previous_route.with_lock do
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
-
module
|
|
3
|
-
class
|
|
2
|
+
module Routes
|
|
3
|
+
class IncrementFulfilledOrderCount
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
6
|
def call(order:)
|
|
7
7
|
return failure(nil, 'Order not found') unless order
|
|
8
8
|
|
|
9
|
-
SpreeCmCommissioner::
|
|
9
|
+
SpreeCmCommissioner::Routes::BaseUpdateOrderMetrics
|
|
10
10
|
.call(order: order, attribute: :fulfilled_order_count)
|
|
11
11
|
|
|
12
12
|
success(order: order)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
-
module
|
|
3
|
-
class
|
|
2
|
+
module Routes
|
|
3
|
+
class IncrementOrderCount
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
6
|
def call(order:)
|
|
7
7
|
return failure(nil, 'Order not found') unless order
|
|
8
8
|
|
|
9
|
-
SpreeCmCommissioner::
|
|
9
|
+
SpreeCmCommissioner::Routes::BaseUpdateOrderMetrics
|
|
10
10
|
.new(order: order, attribute: :order_count)
|
|
11
11
|
.call
|
|
12
12
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module Trips
|
|
3
|
+
class Search
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
REFRESH_RATE_LIMIT_INTERVAL = 30.seconds
|
|
7
|
+
SEARCH_CACHE_TTL = 5.minutes
|
|
8
|
+
|
|
9
|
+
def self.cache_key_for(params, vendor)
|
|
10
|
+
new.send(:cache_key, params, vendor)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(params:, vendor:, is_intercity_taxi: false)
|
|
14
|
+
return success(results: []) unless valid_search?(params)
|
|
15
|
+
|
|
16
|
+
cache_key = cache_key(params, vendor)
|
|
17
|
+
|
|
18
|
+
results = Rails.cache.fetch(cache_key, expires_in: SEARCH_CACHE_TTL) do
|
|
19
|
+
trips = SpreeCmCommissioner::TripQuery.new(
|
|
20
|
+
origin_id: origin_id(params),
|
|
21
|
+
destination_id: destination_id(params),
|
|
22
|
+
vendor_id: vendor.id,
|
|
23
|
+
number_of_guests: params[:number_of_guests] || 1,
|
|
24
|
+
date: search_date(params).to_date
|
|
25
|
+
).call
|
|
26
|
+
|
|
27
|
+
is_intercity_taxi || taxi?(params) ? filter_intercity(trips) : trips
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
success(results: results)
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
failure(nil, e.message)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def valid_search?(params)
|
|
38
|
+
params[:origin].present? && params[:destination].present? && params[:outbound_date].present?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def filter_intercity(results)
|
|
42
|
+
Array(results).select do |res|
|
|
43
|
+
res.respond_to?(:trips) && res.trips.any? { |t| t.route_type.to_s == 'intercity_taxi' }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def origin_id(params) = outbound?(params) ? params[:origin] : params[:destination]
|
|
48
|
+
def destination_id(params) = outbound?(params) ? params[:destination] : params[:origin]
|
|
49
|
+
def search_date(params) = outbound?(params) ? params[:outbound_date] : params[:inbound_date]
|
|
50
|
+
def outbound?(params) = params[:direction] == 'outbound' || params[:direction].blank?
|
|
51
|
+
def taxi?(params) = params[:service_type] == 'intercity_taxi'
|
|
52
|
+
|
|
53
|
+
def cache_key(params, vendor)
|
|
54
|
+
[
|
|
55
|
+
'search_trips',
|
|
56
|
+
"vendor:#{vendor.id}",
|
|
57
|
+
"dir:#{params[:direction] || 'outbound'}",
|
|
58
|
+
"o:#{origin_id(params)}",
|
|
59
|
+
"d:#{destination_id(params)}",
|
|
60
|
+
"date:#{search_date(params)}"
|
|
61
|
+
].join(':')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|