spree_cm_commissioner 2.3.0.pre.pre20 → 2.3.1.pre.pre1

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 (45) 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/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +0 -1
  6. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +3 -0
  7. data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +41 -0
  8. data/app/controllers/spree/api/v2/tenant/trip_places_controller.rb +48 -0
  9. data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +103 -0
  10. data/app/controllers/spree/api/v2/tenant/trips_controller.rb +32 -0
  11. data/app/interactors/spree_cm_commissioner/create_ticket.rb +2 -0
  12. data/app/interactors/spree_cm_commissioner/google_routes_distance_calculator.rb +211 -0
  13. data/app/interactors/spree_cm_commissioner/host_matcher.rb +1 -1
  14. data/app/interactors/spree_cm_commissioner/intercity_taxi_order_creator.rb +52 -12
  15. data/app/interactors/spree_cm_commissioner/trip_distance_calculator.rb +165 -0
  16. data/app/interactors/spree_cm_commissioner/vendor_creation_telegram_alert_sender.rb +1 -1
  17. data/app/mailers/spree/order_mailer_decorator.rb +8 -9
  18. data/app/models/concerns/spree_cm_commissioner/order_scopes.rb +4 -5
  19. data/app/models/concerns/spree_cm_commissioner/service_recommendations.rb +2 -2
  20. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
  21. data/app/models/spree_cm_commissioner/product_completion_step.rb +4 -1
  22. data/app/models/spree_cm_commissioner/product_completion_step_banner.rb +12 -0
  23. data/app/models/spree_cm_commissioner/product_completion_steps/social_entry_url.rb +86 -19
  24. data/app/models/spree_cm_commissioner/tenant.rb +10 -1
  25. data/app/models/spree_cm_commissioner/vendor_place.rb +10 -1
  26. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +9 -0
  27. data/app/queries/spree_cm_commissioner/trip_query.rb +51 -33
  28. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +1 -1
  29. data/app/services/spree_cm_commissioner/intercity_taxi_order/create.rb +8 -0
  30. data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +35 -45
  31. data/app/services/spree_cm_commissioner/orders/bulk_archive_inactive_orders.rb +10 -2
  32. data/app/services/spree_cm_commissioner/orders/daily_archive_inactive_orders.rb +8 -0
  33. data/app/services/spree_cm_commissioner/trips/search.rb +7 -11
  34. data/app/views/shared/_asset_field.html.erb +13 -0
  35. data/app/views/spree/admin/product_completion_steps/_form.html.erb +28 -2
  36. data/app/views/spree/admin/product_completion_steps/_supported_fields.html.erb +33 -0
  37. data/app/views/spree/admin/tenants/_form.html.erb +1 -1
  38. data/config/initializers/spree_permitted_attributes.rb +1 -0
  39. data/config/locales/en.yml +4 -0
  40. data/config/locales/km.yml +65 -61
  41. data/config/routes.rb +16 -0
  42. data/db/migrate/20251119093000_add_tenant_id_to_cm_vendor_places.rb +7 -0
  43. data/lib/spree_cm_commissioner/test_helper/factories/product_completion_step_banner_factory.rb +7 -0
  44. data/lib/spree_cm_commissioner/version.rb +1 -1
  45. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f73314825dc2db092e1255e49203ceb8e1071e0dac1c11803d8343b8dc37fafa
4
- data.tar.gz: 32c25a52977a2f20591b8650ece48a69e517845b3431b79e3c57da63cf8ddb50
3
+ metadata.gz: 7791d1c6474375315937bc020c5bb5274abecbddc5e9b9f5df5a73e53576d626
4
+ data.tar.gz: 03c0f3ccc698d12697c135f4daaab5718b1a72117288de9270069397660d62a5
5
5
  SHA512:
6
- metadata.gz: 1bd90e765ad3bdd9f28b6199900a7ea4f587dd11732af3a03c8bd3b13d234eb83765740de6619223dd03cbe77cb4b865c82c12c8a1845bc05157977463bf17d6
7
- data.tar.gz: ef7380007df4d1ee55da2fea5d2d9cb2b3b5d931c44d17a955b9925c338f8a6285df1df59459b643152cd03fda819bcdd264075bc87eaaff5961e93d08b84606
6
+ metadata.gz: 5cb82c98aac134ebedbdb29c1daf2cc22035ac4e5856c548b22015ffb9a574e6873e897407b8043d899164a9a1ffa941ec04fc25df2263e255e5fd1ad362531c
7
+ data.tar.gz: 4ff0e66a420d2e7f589f2c63d55ca18acc0065aa015033b3917a5dbf603440df4f8a56a32834a02135f5e216a2e5f7d82e8e78f5c6d1df571dc7b261ddb90a79
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.3.0.pre.pre20)
37
+ spree_cm_commissioner (2.3.1.pre.pre1)
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
@@ -20,7 +20,6 @@ module Spree
20
20
 
21
21
  def update
22
22
  order = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
23
- order_number: params[:id],
24
23
  params: params
25
24
  )
26
25
 
@@ -12,6 +12,7 @@ module Spree
12
12
  origin_id: params[:origin_id],
13
13
  destination_id: params[:destination_id],
14
14
  date: params[:date],
15
+ route_type: params[:route_type],
15
16
  vendor_id: params[:vendor_id],
16
17
  number_of_guests: params[:number_of_guests],
17
18
  params: params
@@ -36,6 +37,7 @@ module Spree
36
37
  params[:origin_id],
37
38
  params[:destination_id],
38
39
  params[:date],
40
+ params[:route_type],
39
41
  params[:vendor_id],
40
42
  params[:number_of_guests],
41
43
  resource_includes&.sort&.join(','),
@@ -62,6 +64,7 @@ module Spree
62
64
  'trips.vendor',
63
65
  'trips.vendor.logo',
64
66
  'trips.vehicle',
67
+ 'trips.vehicle.vehicle_photos',
65
68
  'trips.amenities'
66
69
  ]
67
70
  end
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ module IntercityTaxi
6
+ class DraftOrdersController < BaseController
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
+ params: params
24
+ )
25
+
26
+ render_serialized_payload { serialize_resource(order) }
27
+ rescue StandardError => e
28
+ render_error_payload(e.message)
29
+ end
30
+
31
+ private
32
+
33
+ def resource_serializer
34
+ SpreeCmCommissioner::V2::Storefront::IntercityTaxiCartSerializer
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class TripPlacesController < BaseController
6
+ def index
7
+ render_serialized_payload do
8
+ serialize_collection(paginated_collection)
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def paginated_collection
15
+ collection.page(params[:page]).per(params[:per_page])
16
+ end
17
+
18
+ def collection
19
+ @collection ||= begin
20
+ base_scope = scope
21
+
22
+ if params[:q].present?
23
+ @search = base_scope.ransack(params[:q])
24
+ @search.result
25
+ else
26
+ base_scope
27
+ end
28
+ end
29
+ end
30
+
31
+ def scope
32
+ model_class.joins(:location_vendor_places)
33
+ .where(location_vendor_places: { tenant_id: @tenant.id })
34
+ .distinct
35
+ end
36
+
37
+ def model_class
38
+ SpreeCmCommissioner::Place
39
+ end
40
+
41
+ def collection_serializer
42
+ SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,103 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class TripSearchController < BaseController
6
+ CACHE_EXPIRES_IN = 5.minutes
7
+
8
+ def index
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
+ route_type: params[:route_type],
16
+ number_of_guests: params[:number_of_guests],
17
+ tenant_id: @tenant.id,
18
+ params: params
19
+ ).call
20
+
21
+ serialize_collection(trips)
22
+ end
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # override from ContentCacheable
29
+ def max_age
30
+ CACHE_EXPIRES_IN.to_i
31
+ end
32
+
33
+ # override
34
+ def collection_cache_key
35
+ cache_key_parts = [
36
+ 'trip_search',
37
+ @tenant.id,
38
+ params[:origin_id],
39
+ params[:destination_id],
40
+ params[:date],
41
+ params[:route_type],
42
+ params[:number_of_guests],
43
+ resource_includes&.sort&.join(','),
44
+ sparse_fields&.sort&.join(','),
45
+ serializer_params.to_json,
46
+ params[:page]&.to_s&.strip,
47
+ params[:per_page]&.to_s&.strip
48
+ ].compact.join('-')
49
+
50
+ Digest::MD5.hexdigest(cache_key_parts)
51
+ end
52
+
53
+ # override
54
+ def collection_cache_opts
55
+ {
56
+ namespace: Spree::Api::Config[:api_v2_collection_cache_namespace],
57
+ expires_in: CACHE_EXPIRES_IN
58
+ }
59
+ end
60
+
61
+ # override
62
+ def default_resource_includes
63
+ [
64
+ 'trips.vendor',
65
+ 'trips.vendor.logo',
66
+ 'trips.vehicle',
67
+ 'trips.vehicle.vehicle_photos',
68
+ 'trips.amenities'
69
+ ]
70
+ end
71
+
72
+ def serialize_collection(collection)
73
+ serialized_data = SpreeCmCommissioner::V2::Storefront::TripQueryResultSerializer.new(
74
+ collection,
75
+ include: default_resource_includes,
76
+ params: serializer_params
77
+ ).serializable_hash
78
+ serialized_data[:meta] = {
79
+ count: collection.size,
80
+ total_count: collection.respond_to?(:total_count) ? collection.total_count : collection.size,
81
+ current_page: collection.respond_to?(:current_page) ? collection.current_page : 1,
82
+ per_page: collection.respond_to?(:limit_value) ? collection.limit_value : collection.size,
83
+ pages: collection.respond_to?(:total_pages) ? collection.total_pages : 1,
84
+ next_page: collection.respond_to?(:next_page) ? collection.next_page : nil,
85
+ prev_page: collection.respond_to?(:prev_page) ? collection.prev_page : nil
86
+ }
87
+ serialized_data
88
+ end
89
+
90
+ # override
91
+ def serializer_params
92
+ params.permit(:include).to_hash
93
+ end
94
+
95
+ # override
96
+ def required_schema
97
+ SpreeCmCommissioner::TripSearchRequestSchema
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,32 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ class TripsController < BaseController
6
+ def show
7
+ resource = scope.find(params[:id])
8
+
9
+ render_serialized_payload do
10
+ serialize_resource(resource)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def scope
17
+ model_class.joins(:vendor)
18
+ .where(spree_vendors: { tenant_id: @tenant.id })
19
+ end
20
+
21
+ def model_class
22
+ SpreeCmCommissioner::Trip
23
+ end
24
+
25
+ def resource_serializer
26
+ SpreeCmCommissioner::V2::Storefront::TripSerializer
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -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,
@@ -0,0 +1,211 @@
1
+ module SpreeCmCommissioner
2
+ # Interactor wrapper around Google Routes API v2 to compute trip details.
3
+ #
4
+ # Inputs via context:
5
+ # - origin: { lat:, lng: } | "lat,lng" | [lat, lng]
6
+ # - destination: { lat:, lng: } | "lat,lng" | [lat, lng]
7
+ # - Optional arrays: waypoints, pickups, dropoffs (same point formats)
8
+ # - Optional boolean: optimize (default: true) to let Google reorder waypoints
9
+ #
10
+ # Outputs via context on success:
11
+ # - distance_km (Float)
12
+ # - ordered_waypoints (Array<Hash>)
13
+ # - ordered_points (Array<Hash>)
14
+ # - directions_url (String)
15
+ # - estimated_time_minutes (Integer) when optimize is true
16
+ #
17
+ # On failure, context.fail!(message: ...) is called.
18
+ class GoogleRoutesDistanceCalculator < BaseInteractor
19
+ delegate :origin, :destination, :waypoints, :pickups, :dropoffs, :optimize, to: :context
20
+
21
+ def call
22
+ initialize_points_and_options
23
+
24
+ via_points = combined_via_points
25
+ json = fetch_routes_for(@origin, @destination, via_points)
26
+
27
+ if json.nil?
28
+ context.fail!(message: 'Failed to fetch routes from Google API')
29
+ return
30
+ end
31
+
32
+ if json['error']
33
+ error_message = json.dig('error', 'message') || json.dig('error', 'status')
34
+ context.fail!(message: "Google Routes API error: #{error_message}")
35
+ return
36
+ end
37
+
38
+ route = json['routes']&.first
39
+ context.fail!(message: 'No route found') if route.nil?
40
+
41
+ distance_meters = total_distance_of(route)
42
+ duration_seconds = total_duration_of(route)
43
+ ordered_waypoints = build_ordered_waypoints_from(route, via_points)
44
+
45
+ ordered_points = [@origin] + ordered_waypoints + [@destination]
46
+
47
+ context.distance_km = (distance_meters / 1000.0).round(3)
48
+ context.ordered_waypoints = ordered_waypoints
49
+ context.ordered_points = ordered_points
50
+ context.directions_url = build_human_link(ordered_waypoints)
51
+ # Only expose estimated time when optimize is enabled (per requirement)
52
+ context.estimated_time_minutes = (@optimize ? (duration_seconds / 60.0).round : nil)
53
+ end
54
+
55
+ private
56
+
57
+ def initialize_points_and_options
58
+ context.fail!(message: 'origin is required') if origin.blank?
59
+ context.fail!(message: 'destination is required') if destination.blank?
60
+
61
+ begin
62
+ @origin = normalize_point(origin)
63
+ @destination = normalize_point(destination)
64
+
65
+ extra_waypoints = Array(waypoints).compact
66
+ pickups_points = Array(pickups).compact
67
+ dropoff_points = Array(dropoffs).compact
68
+
69
+ @extra_waypoints = extra_waypoints.map { |p| normalize_point(p) }
70
+ @pickups_points = pickups_points.map { |p| normalize_point(p) }
71
+ @dropoff_points = dropoff_points.map { |p| normalize_point(p) }
72
+ rescue ArgumentError => e
73
+ context.fail!(message: e.message)
74
+ end
75
+
76
+ @optimize = optimize.nil? ? true : !!optimize
77
+ end
78
+
79
+ def google_map_key
80
+ @google_map_key ||= ENV.fetch('GOOGLE_MAP_KEY')
81
+ end
82
+
83
+ def fetch_routes_for(from, to_point, via_waypoints)
84
+ url = "https://routes.googleapis.com/directions/v2:computeRoutes?key=#{google_map_key}"
85
+ headers = {
86
+ 'Content-Type' => 'application/json',
87
+ 'X-Goog-FieldMask' => [
88
+ 'routes.duration',
89
+ 'routes.distanceMeters',
90
+ 'routes.legs',
91
+ 'routes.optimizedIntermediateWaypointIndex',
92
+ 'routes.legs.duration',
93
+ 'routes.legs.distanceMeters'
94
+ ].join(',')
95
+ }
96
+ body = build_request_body(from: from, to: to_point, via_waypoints: via_waypoints)
97
+
98
+ response = Faraday.post(url, body.to_json, headers)
99
+ return nil unless response.success?
100
+
101
+ JSON.parse(response.body)
102
+ end
103
+
104
+ def build_request_body(from:, to:, via_waypoints: [])
105
+ request = {
106
+ origin: {
107
+ location: {
108
+ latLng: {
109
+ latitude: from[:lat],
110
+ longitude: from[:lng]
111
+ }
112
+ }
113
+ },
114
+ destination: {
115
+ location: {
116
+ latLng: {
117
+ latitude: to[:lat],
118
+ longitude: to[:lng]
119
+ }
120
+ }
121
+ },
122
+ travelMode: 'DRIVE',
123
+ routingPreference: 'TRAFFIC_AWARE',
124
+ computeAlternativeRoutes: false,
125
+ units: 'METRIC'
126
+ }
127
+
128
+ if via_waypoints.any?
129
+ intermediates = via_waypoints.map do |point|
130
+ {
131
+ location: {
132
+ latLng: {
133
+ latitude: point[:lat],
134
+ longitude: point[:lng]
135
+ }
136
+ }
137
+ }
138
+ end
139
+
140
+ request[:optimizeWaypointOrder] = true if @optimize
141
+
142
+ request[:intermediates] = intermediates
143
+ end
144
+
145
+ request
146
+ end
147
+
148
+ def normalize_point(point)
149
+ return { lat: point[:lat].to_f, lng: point[:lng].to_f } if point.is_a?(Hash)
150
+ return { lat: point[0].to_f, lng: point[1].to_f } if point.is_a?(Array) && point.size == 2
151
+
152
+ if point.is_a?(String) && point.include?(',')
153
+ lat_str, lng_str = point.split(',', 2)
154
+ return { lat: lat_str.to_f, lng: lng_str.to_f }
155
+ end
156
+ raise ArgumentError, 'Invalid point format; expected { lat:, lng: }'
157
+ end
158
+
159
+ def same_point?(point_a, point_b)
160
+ (point_a[:lat].to_f - point_b[:lat].to_f).abs < 1e-9 && (point_a[:lng].to_f - point_b[:lng].to_f).abs < 1e-9
161
+ end
162
+
163
+ def combined_via_points
164
+ points = @extra_waypoints + @pickups_points + @dropoff_points
165
+ points.reject { |p| same_point?(p, @origin) || same_point?(p, @destination) }
166
+ end
167
+
168
+ def total_distance_of(route)
169
+ # Routes API v2 provides distanceMeters at route level
170
+ distance = route['distanceMeters']
171
+ return distance.to_i if distance
172
+
173
+ # Fallback: sum from legs if route-level distance not available
174
+ route.fetch('legs', []).sum { |leg| leg['distanceMeters'].to_i }
175
+ end
176
+
177
+ def total_duration_of(route)
178
+ # Routes API v2 provides duration at route level as string "3600s"
179
+ duration_str = route['duration']
180
+ return duration_str.to_s.gsub('s', '').to_i if duration_str
181
+
182
+ # Fallback: sum from legs if route-level duration not available
183
+ route.fetch('legs', []).sum do |leg|
184
+ leg_duration = leg['duration']
185
+ leg_duration ? leg_duration.to_s.gsub('s', '').to_i : 0
186
+ end
187
+ end
188
+
189
+ def build_ordered_waypoints_from(route, base_waypoints)
190
+ optimized_indices = route['optimizedIntermediateWaypointIndex'] || []
191
+ return base_waypoints if optimized_indices.empty? || !@optimize
192
+
193
+ optimized_indices.map { |idx| base_waypoints[idx] }
194
+ end
195
+
196
+ def format_point(point)
197
+ "#{point[:lat]},#{point[:lng]}"
198
+ end
199
+
200
+ def build_human_link(ordered_waypoints)
201
+ params = {
202
+ api: 1,
203
+ origin: format_point(@origin),
204
+ destination: format_point(@destination),
205
+ travelmode: 'driving'
206
+ }
207
+ params[:waypoints] = ordered_waypoints.map { |p| format_point(p) }.join('|') if ordered_waypoints.any?
208
+ "https://www.google.com/maps/dir/?#{URI.encode_www_form(params)}"
209
+ end
210
+ end
211
+ end
@@ -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
@@ -49,10 +49,21 @@ module SpreeCmCommissioner
49
49
  :passenger_count,
50
50
  :pickup_oob_confirmed,
51
51
  :drop_off_oob_confirmed,
52
+ :distance_km,
53
+ :directions_url,
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,
52
63
  { guests_attributes: %i[
53
64
  first_name last_name gender age nationality_id
54
65
  ]
55
- }
66
+ }
56
67
  ]
57
68
  )
58
69
 
@@ -61,25 +72,54 @@ module SpreeCmCommissioner
61
72
  end
62
73
 
63
74
  def process_line_items_attributes(params)
64
- date_param = raw_params[:date] || Date.current.to_s
75
+ date_param = request_date
65
76
 
66
77
  load_variant
67
78
 
68
79
  params[:line_items_attributes].each_value do |item|
80
+ normalize_coordinate_group!(item, :pickup, :pickup_lat, :pickup_lng)
81
+ normalize_coordinate_group!(item, :drop_off, :drop_off_lat, :drop_off_lng)
82
+
69
83
  pickup_time_minutes = item[:from_date]
70
- combined_datetime = "#{date_param} #{pickup_time_minutes}"
71
-
72
- item[:from_date] = combined_datetime
73
- item[:to_date] = combined_datetime
74
- item[:variant_id] = @variant.id
75
- item[:vendor_id] = current_vendor.id
76
- item[:quantity] = 1
77
- item[:currency] = 'USD'
78
- item[:direction] = 'outbound'
79
- item[:trip_id] = trip.id
84
+ combined_datetime = build_datetime(date_param, pickup_time_minutes)
85
+
86
+ assign_default_line_item_fields!(item, combined_datetime)
80
87
  end
81
88
  end
82
89
 
90
+ def request_date
91
+ raw_params[:date] || Date.current.to_s
92
+ end
93
+
94
+ def build_datetime(date_string, time_string)
95
+ "#{date_string} #{time_string}"
96
+ end
97
+
98
+ def normalize_coordinate_group!(item, group_key, lat_key, lng_key)
99
+ group = item[group_key]
100
+ return if group.blank?
101
+
102
+ if group.is_a?(Array)
103
+ item[lat_key], item[lng_key] = group
104
+ elsif group.is_a?(ActionController::Parameters) || group.is_a?(Hash)
105
+ item[lat_key] = group[:lat] || group['lat']
106
+ item[lng_key] = group[:lng] || group['lng']
107
+ end
108
+
109
+ item.delete(group_key)
110
+ end
111
+
112
+ def assign_default_line_item_fields!(item, combined_datetime)
113
+ item[:from_date] = combined_datetime
114
+ item[:to_date] = combined_datetime
115
+ item[:variant_id] = @variant.id
116
+ item[:vendor_id] = current_vendor.id
117
+ item[:quantity] = 1
118
+ item[:currency] = 'USD'
119
+ item[:direction] = 'outbound'
120
+ item[:trip_id] = trip.id
121
+ end
122
+
83
123
  def create_order
84
124
  context.order = Spree::Order.create(context.order_params)
85
125