spree_cm_commissioner 2.5.0.pre.pre13 → 2.5.0.pre.pre14

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +4 -0
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/admin/base_controller_decorator.rb +7 -33
  5. data/app/controllers/spree/api/v2/storefront/guests_controller.rb +7 -51
  6. data/app/controllers/spree/api/v2/storefront/intercity_taxi/distance_calculator_controller.rb +47 -0
  7. data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +7 -5
  8. data/app/controllers/spree/api/v2/storefront/trip_search_controller.rb +2 -2
  9. data/app/controllers/spree/api/v2/tenant/intercity_taxi/distance_calculator_controller.rb +47 -0
  10. data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +7 -5
  11. data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +2 -2
  12. data/app/interactors/spree_cm_commissioner/google_routes_distance_calculator.rb +0 -13
  13. data/app/interactors/spree_cm_commissioner/invalidate_cache_request.rb +3 -3
  14. data/app/interactors/spree_cm_commissioner/vehicle_type_updater.rb +41 -0
  15. data/app/jobs/spree_cm_commissioner/invalidate_cache_request_job.rb +1 -1
  16. data/app/mailers/spree/order_mailer_decorator.rb +22 -1
  17. data/app/models/concerns/spree_cm_commissioner/line_item_seat_selection.rb +40 -0
  18. data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +1 -1
  19. data/app/models/concerns/spree_cm_commissioner/vehicle_kind.rb +21 -0
  20. data/app/models/spree_cm_commissioner/adjustable/adjuster/pricing_action.rb +25 -0
  21. data/app/models/spree_cm_commissioner/guest.rb +1 -1
  22. data/app/models/spree_cm_commissioner/guest_dynamic_field.rb +3 -0
  23. data/app/models/spree_cm_commissioner/homepage_background_app_image.rb +1 -1
  24. data/app/models/spree_cm_commissioner/invite_guest.rb +2 -5
  25. data/app/models/spree_cm_commissioner/line_item_decorator.rb +2 -2
  26. data/app/models/spree_cm_commissioner/option_type_decorator.rb +4 -1
  27. data/app/models/spree_cm_commissioner/option_value_decorator.rb +4 -1
  28. data/app/models/spree_cm_commissioner/{option_value_vehicle.rb → option_value_vehicle_type.rb} +2 -2
  29. data/app/models/spree_cm_commissioner/trip.rb +7 -6
  30. data/app/models/spree_cm_commissioner/vehicle.rb +8 -20
  31. data/app/models/spree_cm_commissioner/vehicle_type.rb +28 -0
  32. data/app/models/spree_cm_commissioner/vehicle_type_option_type.rb +6 -0
  33. data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -0
  34. data/app/overrides/spree/admin/products/edit/clear_cache_button.html.erb.deface +1 -1
  35. data/app/overrides/spree/admin/taxons/edit/clear_cache_button.html.erb.deface +3 -2
  36. data/app/queries/spree_cm_commissioner/trip_query.rb +5 -5
  37. data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +2 -0
  38. data/app/serializers/spree/v2/storefront/line_item_serializer_decorator.rb +12 -0
  39. data/app/serializers/spree/v2/tenant/line_item_serializer.rb +14 -1
  40. data/app/serializers/spree_cm_commissioner/v2/storefront/distance_serializer.rb +26 -0
  41. data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb +3 -3
  42. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_result_serializer.rb +2 -2
  43. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_serializer.rb +1 -1
  44. data/app/serializers/spree_cm_commissioner/v2/storefront/{trip_vehicle_serializer.rb → trip_vehicle_type_serializer.rb} +2 -4
  45. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +98 -0
  46. data/app/services/spree_cm_commissioner/calculate_distance.rb +279 -0
  47. data/app/services/spree_cm_commissioner/cart/recalculate_decorator.rb +28 -1
  48. data/app/services/spree_cm_commissioner/guests/claim_invite_guest_service.rb +65 -0
  49. data/app/services/spree_cm_commissioner/intercity_taxi_order/calculate_distance.rb +224 -0
  50. data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +30 -3
  51. data/app/services/spree_cm_commissioner/line_items/apply_pricing_models.rb +27 -0
  52. data/app/services/spree_cm_commissioner/pricing_models/apply.rb +4 -12
  53. data/app/services/spree_cm_commissioner/signing/sign_data.rb +42 -0
  54. data/app/services/spree_cm_commissioner/signing/verify_signature.rb +52 -0
  55. data/app/services/spree_cm_commissioner/transit/export_order.rb +146 -0
  56. data/app/services/spree_cm_commissioner/update_guest_service.rb +129 -0
  57. data/app/views/spree/admin/homepage_section/edit.html.erb +7 -2
  58. data/app/views/spree/order_mailer/cancel_email.html.erb +19 -0
  59. data/app/views/spree_cm_commissioner/cancel_order_mailer/_cancel_email.html.erb +12 -0
  60. data/app/views/spree_cm_commissioner/cancel_order_mailer/_footer.html.erb +8 -0
  61. data/app/views/spree_cm_commissioner/cancel_order_mailer/_header.html.erb +3 -0
  62. data/app/views/spree_cm_commissioner/cancel_order_mailer/_mailer_stylesheets.html.erb +139 -0
  63. data/app/views/spree_cm_commissioner/cancel_order_mailer/_your_booking.html.erb +8 -0
  64. data/app/views/spree_cm_commissioner/cancel_order_mailer/purchased_items/_items.html.erb +53 -0
  65. data/app/views/spree_cm_commissioner/cancel_order_mailer/purchased_items/_summary.html.erb +108 -0
  66. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_cancel_email.html.erb +12 -0
  67. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_footer.html.erb +8 -0
  68. data/app/views/spree_cm_commissioner/cancel_order_mailer/tenant/_header.html.erb +15 -0
  69. data/app/views/spree_cm_commissioner/layouts/cancel_order_mailer.html.erb +16 -0
  70. data/config/locales/en.yml +15 -0
  71. data/config/locales/km.yml +27 -2
  72. data/config/routes.rb +2 -0
  73. data/db/migrate/20251127074809_change_guest_dynamic_fields_value_from_jsonb_to_text.rb +40 -0
  74. data/db/migrate/20251219035243_add_migrations_to_support_vehicle_types.rb +36 -0
  75. data/lib/spree_cm_commissioner/distance.rb +88 -0
  76. data/lib/spree_cm_commissioner/engine.rb +3 -0
  77. data/lib/spree_cm_commissioner/test_helper/factories/guest_factory.rb +2 -2
  78. data/lib/spree_cm_commissioner/test_helper/factories/line_item_factory.rb +3 -3
  79. data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_variant_factory.rb +6 -0
  80. data/lib/spree_cm_commissioner/test_helper/factories/seat_layout_factory.rb +1 -1
  81. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +1 -0
  82. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_factory.rb +4 -1
  83. data/lib/spree_cm_commissioner/test_helper/factories/vehicle_type_factory.rb +7 -0
  84. data/lib/spree_cm_commissioner/trip_query_result.rb +1 -1
  85. data/lib/spree_cm_commissioner/trip_result.rb +7 -1
  86. data/lib/spree_cm_commissioner/version.rb +1 -1
  87. data/lib/spree_cm_commissioner.rb +1 -1
  88. metadata +39 -10
  89. data/app/interactors/spree_cm_commissioner/trip_distance_calculator.rb +0 -165
  90. data/app/interactors/spree_cm_commissioner/vehicle_updater.rb +0 -41
  91. data/app/models/concerns/spree_cm_commissioner/vehicle_type.rb +0 -11
  92. data/app/models/spree_cm_commissioner/vehicle_option_type.rb +0 -6
  93. data/lib/spree_cm_commissioner/intercity_taxi/distance.rb +0 -38
  94. data/lib/spree_cm_commissioner/test_helper/factories/vendor_route_factory.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a830fb7087d89f6789c9a4e0cad9c3d98d782e00d4c79cd6d4a47f9bb17cdb7f
4
- data.tar.gz: 29f6d23d6c75f2770f021c162167b67c2fadac360887ec88302b8ede9aab9d31
3
+ metadata.gz: 1a284ecbc4094b414d407ecc02ee8892b199ff39ea3049b79c9ab9feb1f56545
4
+ data.tar.gz: 0df5f1a16d68f0927ccbff0e265c7984343853f31e2c6d9473b0818f5013a9ef
5
5
  SHA512:
6
- metadata.gz: 88d46953b8a89c7240b78a3f546813617bd3787212a304b20a9d9b62ded7a00e7fb9e4089c22452969e6bf713463eddc3a954137c13b8cf2f11e95d809f7eb01
7
- data.tar.gz: cef3c5a4743aacd9e4833700b6fef3ef9078d58f58122aaec68e8903c0f1a79681dff5a41126ecffcf2d04f5ec3892fc5a92e1bb421a277587db5f4213a78f64
6
+ metadata.gz: fb468b68eaeeecb491b899a84f5bb6153bb26d7bd29dfe13713dc98226e7e0e1e67ba33646212193b30730ba6bfbb250f652a2f4b4ca6f7ba783d252fd8d0de2
7
+ data.tar.gz: 5e9cfb969a2d94bda9a23f2ef5bfe8382705d5df502458eba98ad5eb9082e1f9bfee08ef258555548834240ae2c5241b9091c00e0bd39cab58c8c975fa2dc2f7
data/.env.example CHANGED
@@ -27,3 +27,7 @@ VATTANAC_PUBLIC_KEY=""
27
27
 
28
28
  # Organizer URL
29
29
  ORGANIZER_URL=http://127.0.0.1:4000/organizer
30
+
31
+ # Use to sign & verify Distance object:
32
+ # spree_cm_commissioner/distance.rb
33
+ DISTANCE_SIGNING_KEY=hei********************VY
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.5.0.pre.pre13)
37
+ spree_cm_commissioner (2.5.0.pre.pre14)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -32,42 +32,16 @@ module Spree
32
32
  end
33
33
 
34
34
  # POST
35
- def invalidate_api_caches # rubocop:disable Metrics/CyclomaticComplexity
35
+ def invalidate_api_caches
36
36
  if params[:model].present?
37
- model = case params[:model]
38
- when 'SpreeCmCommissioner::HomepageSection'
39
- SpreeCmCommissioner::HomepageSection
40
- when 'SpreeCmCommissioner::HomepageBackground'
41
- SpreeCmCommissioner::HomepageBackground
42
- when 'Spree::Menu'
43
- Spree::Menu
44
- when 'Spree::Taxon'
45
- Spree::Taxon
46
- when 'Spree::Product'
47
- Spree::Product
48
- else
49
- flash[:error] = 'Invalid model provided' # rubocop:disable Rails/I18nLocaleTexts
50
- redirect_back fallback_location: admin_root_path and return
51
- end
37
+ result = SpreeCmCommissioner::ApiCaches::Invalidate.call(
38
+ model: params[:model],
39
+ id: params[:id]
40
+ )
52
41
 
53
- model.find_each(&:touch)
54
- api_patterns_map = {
55
- SpreeCmCommissioner::HomepageSection => '/api/v2/storefront/homepage/*',
56
- SpreeCmCommissioner::HomepageBackground => '/api/v2/storefront/homepage/*',
57
- Spree::Menu => '/api/v2/storefront/menus*',
58
- Spree::Taxon => '/api/v2/storefront/taxons*',
59
- Spree::Product => '/api/v2/storefront/products*'
60
- }
61
- api_patterns = api_patterns_map[model]
62
-
63
- if api_patterns.is_a?(Array)
64
- api_patterns.each { |pattern| SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(pattern: pattern) }
65
- elsif api_patterns.is_a?(String)
66
- SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(pattern: api_patterns)
67
- elsif params[:api_pattern].present?
68
- SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(pattern: params[:api_pattern])
69
- end
42
+ flash[:error] = result.error if result.failure?
70
43
  end
44
+
71
45
  redirect_back fallback_location: admin_root_path
72
46
  end
73
47
 
@@ -42,18 +42,11 @@ module Spree
42
42
  end
43
43
 
44
44
  def update
45
- spree_authorize! :update, spree_current_order, order_token
45
+ spree_authorize! :update, spree_current_order, order_token unless guest_for_update.pre_registration?
46
46
 
47
47
  begin
48
- ActiveRecord::Base.transaction do
49
- process_dynamic_fields(guest_params[:guest_dynamic_fields_attributes]) if guest_params[:guest_dynamic_fields_attributes]
50
-
51
- resource.assign_attributes(guest_params.except(:guest_dynamic_fields_attributes))
52
- resource.save!
53
- end
54
-
55
- resource.save_and_move_to_next_stage
56
- render_serialized_payload { serialize_resource(resource) }
48
+ SpreeCmCommissioner::UpdateGuestService.new(guest_for_update, guest_params).call
49
+ render_serialized_payload { serialize_resource(guest_for_update) }
57
50
  rescue ActiveRecord::RecordInvalid => e
58
51
  render_error_payload(e.record.errors, 422)
59
52
  end
@@ -70,6 +63,10 @@ module Spree
70
63
  @parent ||= spree_current_order.line_items.find(params[:line_item_id])
71
64
  end
72
65
 
66
+ def guest_for_update
67
+ @guest_for_update ||= SpreeCmCommissioner::Guest.find(params[:id])
68
+ end
69
+
73
70
  def guest_params
74
71
  params.permit(
75
72
  :first_name,
@@ -93,47 +90,6 @@ module Spree
93
90
  guest_dynamic_fields_attributes: %i[id dynamic_field_id dynamic_field_option_id value _destroy]
94
91
  )
95
92
  end
96
-
97
- def process_dynamic_fields(dynamic_fields_attributes)
98
- dynamic_fields_attributes.each do |raw_attributes|
99
- attributes = raw_attributes.to_h.symbolize_keys
100
-
101
- if ActiveModel::Type::Boolean.new.cast(attributes[:_destroy])
102
- destroy_guest_dynamic_field(attributes[:id])
103
- next
104
- end
105
-
106
- upsert_guest_dynamic_field(attributes)
107
- end
108
- end
109
-
110
- def associate_dynamic_field_option(field)
111
- return unless field.dynamic_field&.requires_dynamic_field_options?
112
-
113
- option = field.dynamic_field.dynamic_field_options.find_by(id: field.value)
114
- field.dynamic_field_option = option
115
- end
116
-
117
- def destroy_guest_dynamic_field(id)
118
- return if id.blank?
119
-
120
- resource.guest_dynamic_fields.find(id).destroy!
121
- end
122
-
123
- def upsert_guest_dynamic_field(attributes)
124
- field = find_or_initialize_guest_dynamic_field(attributes)
125
- field.assign_attributes(attributes.except(:id, :_destroy))
126
- associate_dynamic_field_option(field) if field.dynamic_field.present?
127
- field.save!
128
- end
129
-
130
- def find_or_initialize_guest_dynamic_field(attributes)
131
- if attributes[:id].present?
132
- resource.guest_dynamic_fields.find(attributes[:id])
133
- else
134
- resource.guest_dynamic_fields.find_or_initialize_by(dynamic_field_id: attributes[:dynamic_field_id])
135
- end
136
- end
137
93
  end
138
94
  end
139
95
  end
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module IntercityTaxi
6
+ class DistanceCalculatorController < ::Spree::Api::V2::ResourceController
7
+ include Spree::Api::V2::Storefront::OrderConcern
8
+ include SpreeCmCommissioner::OrderConcern
9
+
10
+ def create
11
+ result = SpreeCmCommissioner::CalculateDistance.call(
12
+ pickups_attributes: Array(params[:pickups_attributes]).map do |pickup|
13
+ {
14
+ lat: pickup[:lat],
15
+ lng: pickup[:lng]
16
+ }
17
+ end,
18
+ dropoffs_attributes: Array(params[:dropoffs_attributes]).map do |dropoff|
19
+ {
20
+ lat: dropoff[:lat],
21
+ lng: dropoff[:lng]
22
+ }
23
+ end,
24
+ pickup_oob_confirmed: params[:pickup_oob_confirmed],
25
+ dropoff_oob_confirmed: params[:dropoff_oob_confirmed],
26
+ trip_id: params[:trip_id]
27
+ )
28
+
29
+ if result.success?
30
+ render_serialized_payload { serialize_resource(result.value) }
31
+ else
32
+ render_error_payload(result.error)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # override
39
+ def resource_serializer
40
+ SpreeCmCommissioner::V2::Storefront::DistanceSerializer
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -29,27 +29,29 @@ module Spree
29
29
  result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
30
30
  order: spree_current_order,
31
31
  remark: params[:remark],
32
- pickup_map_place_attributes: params.require(:pickup_map_place_attributes).permit(
32
+ pickup_map_place_attributes: params[:pickup_map_place_attributes]&.permit(
33
33
  :place_name,
34
34
  :lat,
35
35
  :lng,
36
36
  :oob_confirmed
37
37
  ),
38
- dropoff_map_place_attributes: params.require(:dropoff_map_place_attributes).permit(
38
+ dropoff_map_place_attributes: params[:dropoff_map_place_attributes]&.permit(
39
39
  :place_name,
40
40
  :lat,
41
41
  :lng,
42
42
  :oob_confirmed
43
43
  ),
44
- distance_attributes: params.require(:distance_attributes).permit(
44
+ distance_attributes: params[:distance_attributes]&.permit(
45
45
  :distance_km,
46
- :ordered_points,
46
+ { ordered_points: %i[lat lng] },
47
47
  :estimated_time_minutes,
48
48
  :base_km,
49
49
  :detour_pickup_km,
50
50
  :detour_dropoff_km,
51
51
  :extra_pickup_km,
52
- :extra_dropoff_km
52
+ :extra_dropoff_km,
53
+ :signed_at,
54
+ :signature
53
55
  )
54
56
  )
55
57
 
@@ -63,8 +63,8 @@ module Spree
63
63
  [
64
64
  'trips.vendor',
65
65
  'trips.vendor.logo',
66
- 'trips.vehicle',
67
- 'trips.vehicle.vehicle_photos',
66
+ 'trips.vehicle_type',
67
+ 'trips.vehicle_type.vehicle_photos',
68
68
  'trips.amenities'
69
69
  ]
70
70
  end
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ module IntercityTaxi
6
+ class DistanceCalculatorController < BaseController
7
+ include Spree::Api::V2::Storefront::OrderConcern
8
+ include SpreeCmCommissioner::OrderConcern
9
+
10
+ def create
11
+ result = SpreeCmCommissioner::CalculateDistance.call(
12
+ pickups_attributes: Array(params[:pickups_attributes]).map do |pickup|
13
+ {
14
+ lat: pickup[:lat],
15
+ lng: pickup[:lng]
16
+ }
17
+ end,
18
+ dropoffs_attributes: Array(params[:dropoffs_attributes]).map do |dropoff|
19
+ {
20
+ lat: dropoff[:lat],
21
+ lng: dropoff[:lng]
22
+ }
23
+ end,
24
+ pickup_oob_confirmed: params[:pickup_oob_confirmed],
25
+ dropoff_oob_confirmed: params[:dropoff_oob_confirmed],
26
+ trip_id: params[:trip_id]
27
+ )
28
+
29
+ if result.success?
30
+ render_serialized_payload { serialize_resource(result.value) }
31
+ else
32
+ render_error_payload(result.error)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # override
39
+ def resource_serializer
40
+ SpreeCmCommissioner::V2::Storefront::DistanceSerializer
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -29,27 +29,29 @@ module Spree
29
29
  result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
30
30
  order: spree_current_order,
31
31
  remark: params[:remark],
32
- pickup_map_place_attributes: params.require(:pickup_map_place_attributes).permit(
32
+ pickup_map_place_attributes: params[:pickup_map_place_attributes]&.permit(
33
33
  :place_name,
34
34
  :lat,
35
35
  :lng,
36
36
  :oob_confirmed
37
37
  ),
38
- dropoff_map_place_attributes: params.require(:dropoff_map_place_attributes).permit(
38
+ dropoff_map_place_attributes: params[:dropoff_map_place_attributes]&.permit(
39
39
  :place_name,
40
40
  :lat,
41
41
  :lng,
42
42
  :oob_confirmed
43
43
  ),
44
- distance_attributes: params.require(:distance_attributes).permit(
44
+ distance_attributes: params[:distance_attributes]&.permit(
45
45
  :distance_km,
46
- :ordered_points,
46
+ { ordered_points: %i[lat lng] },
47
47
  :estimated_time_minutes,
48
48
  :base_km,
49
49
  :detour_pickup_km,
50
50
  :detour_dropoff_km,
51
51
  :extra_pickup_km,
52
- :extra_dropoff_km
52
+ :extra_dropoff_km,
53
+ :signed_at,
54
+ :signature
53
55
  )
54
56
  )
55
57
 
@@ -63,8 +63,8 @@ module Spree
63
63
  [
64
64
  'trips.vendor',
65
65
  'trips.vendor.logo',
66
- 'trips.vehicle',
67
- 'trips.vehicle.vehicle_photos',
66
+ 'trips.vehicle_type',
67
+ 'trips.vehicle_type.vehicle_photos',
68
68
  'trips.amenities'
69
69
  ]
70
70
  end
@@ -11,7 +11,6 @@ module SpreeCmCommissioner
11
11
  # - distance_km (Float)
12
12
  # - ordered_waypoints (Array<Hash>)
13
13
  # - ordered_points (Array<Hash>)
14
- # - directions_url (String)
15
14
  # - estimated_time_minutes (Integer) when optimize is true
16
15
  #
17
16
  # On failure, context.fail!(message: ...) is called.
@@ -47,7 +46,6 @@ module SpreeCmCommissioner
47
46
  context.distance_km = (distance_meters / 1000.0).round(3)
48
47
  context.ordered_waypoints = ordered_waypoints
49
48
  context.ordered_points = ordered_points
50
- context.directions_url = build_human_link(ordered_waypoints)
51
49
  # Only expose estimated time when optimize is enabled (per requirement)
52
50
  context.estimated_time_minutes = (@optimize ? (duration_seconds / 60.0).round : nil)
53
51
  end
@@ -196,16 +194,5 @@ module SpreeCmCommissioner
196
194
  def format_point(point)
197
195
  "#{point[:lat]},#{point[:lng]}"
198
196
  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
197
  end
211
198
  end
@@ -5,7 +5,7 @@ require 'aws-sdk-cloudfront'
5
5
 
6
6
  module SpreeCmCommissioner
7
7
  class InvalidateCacheRequest < BaseInteractor
8
- delegate :pattern, to: :context
8
+ delegate :patterns, to: :context
9
9
 
10
10
  def call
11
11
  client = ::Aws::CloudFront::Client.new(
@@ -21,8 +21,8 @@ module SpreeCmCommissioner
21
21
  invalidation_batch: {
22
22
  caller_reference: Time.now.to_i.to_s,
23
23
  paths: {
24
- quantity: 1,
25
- items: [pattern]
24
+ quantity: patterns.size,
25
+ items: patterns
26
26
  }
27
27
  }
28
28
  )
@@ -0,0 +1,41 @@
1
+ module SpreeCmCommissioner
2
+ class VehicleTypeUpdater < BaseInteractor
3
+ def call
4
+ vehicle_type = context.vehicle_type
5
+ data = context.data
6
+
7
+ option_value_ids = data[:option_value_ids] || []
8
+ option_type_ids = data[:option_type_ids] || []
9
+
10
+ ActiveRecord::Base.transaction do
11
+ update_vehicle_type!(vehicle_type, data[:vehicle_type_attributes])
12
+ update_amenities!(vehicle_type, option_value_ids, option_type_ids)
13
+ end
14
+
15
+ context.message = I18n.t('views.transit.vehicle_types.update_success')
16
+ rescue StandardError => e
17
+ context.fail!(message: "Failed to update vehicle type: #{e.message}")
18
+ end
19
+
20
+ private
21
+
22
+ def update_vehicle_type!(vehicle_type, attributes)
23
+ return if attributes.blank? || vehicle_type.update(attributes.slice(:name, :kind, :number_of_seats))
24
+
25
+ raise vehicle_type.errors.full_messages.to_sentence
26
+ end
27
+
28
+ def update_amenities!(vehicle_type, option_value_ids, option_type_ids)
29
+ vehicle_type.option_value_vehicle_types.where.not(option_value_id: option_value_ids).destroy_all
30
+ vehicle_type.vehicle_type_option_types.where.not(option_type_id: option_type_ids).destroy_all
31
+
32
+ option_value_ids.each do |option_value_id|
33
+ vehicle_type.option_value_vehicle_types.find_or_create_by(option_value_id: option_value_id)
34
+ end
35
+
36
+ option_type_ids.each do |option_type_id|
37
+ vehicle_type.vehicle_type_option_types.find_or_create_by(option_type_id: option_type_id)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class InvalidateCacheRequestJob < ApplicationUniqueJob
3
3
  def perform(options = {})
4
- SpreeCmCommissioner::InvalidateCacheRequest.call(pattern: options[:pattern])
4
+ SpreeCmCommissioner::InvalidateCacheRequest.call(patterns: options[:patterns])
5
5
  end
6
6
  end
7
7
  end
@@ -4,7 +4,22 @@ module Spree
4
4
  @order = order.respond_to?(:id) ? order : Spree::Order.find(order)
5
5
  return false if @order.email.blank?
6
6
 
7
- super
7
+ setup_tenant_and_store
8
+ if @tenant.present?
9
+ @name = @order.tenant.name
10
+ @logo = @order.tenant.active_vendor&.logo&.original_url
11
+ @brand_color = @order.tenant.preferred_brand_primary_color
12
+ end
13
+ @product_type = @order.products.first&.product_type || 'accommodation'
14
+
15
+ mail(to: @order.email,
16
+ from: from_email_address,
17
+ subject: cancel_subject(resend),
18
+ store_url: store_url
19
+ ) do |format|
20
+ format.html { render layout: 'spree_cm_commissioner/layouts/cancel_order_mailer' }
21
+ format.text
22
+ end
8
23
  end
9
24
 
10
25
  def confirm_email(order, resend: false)
@@ -52,6 +67,12 @@ module Spree
52
67
  "#{prefix}#{store_name} Booking Confirmation ##{@order.number}"
53
68
  end
54
69
 
70
+ def cancel_subject(resend)
71
+ prefix = resend ? "[#{Spree.t(:resend).upcase}] " : ''
72
+ store_name = @tenant.present? ? @tenant.name : @current_store&.name
73
+ "#{prefix}#{store_name} Cancellation of Order ##{@order.number}"
74
+ end
75
+
55
76
  def store_url
56
77
  @tenant.present? ? @tenant.url : @current_store.url
57
78
  end
@@ -0,0 +1,40 @@
1
+ module SpreeCmCommissioner
2
+ module LineItemSeatSelection
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_create :set_required_seat_selection_flag
7
+ end
8
+
9
+ # Caches the required_seat_selection flag from the trip in private_metadata.
10
+ #
11
+ # Why cache?
12
+ # Most line items don't have trips, so checking `product.trip.required_seat_selection`
13
+ # on every read would cause N+1 queries. Caching avoids this performance hit.
14
+ #
15
+ # Behavior:
16
+ # The flag is set once at line item creation and remains immutable. This preserves
17
+ # the historical seat selection state of the order, even if the trip's settings
18
+ # change later. We don't have use cases requiring dynamic updates of this yet,
19
+ # so we keep it constant for simplicity and consistency.
20
+
21
+ def required_seat_selection?
22
+ return false if private_metadata['required_seat_selection'].nil?
23
+
24
+ ActiveModel::Type::Boolean.new.cast(private_metadata['required_seat_selection'])
25
+ end
26
+
27
+ private
28
+
29
+ def set_required_seat_selection_flag
30
+ return if private_metadata.key?('required_seat_selection')
31
+ return if product.nil?
32
+
33
+ private_metadata['required_seat_selection'] = if product.trip.present?
34
+ product.trip.allow_seat_selection && variant.blocks.any?
35
+ else
36
+ variant.blocks.any?
37
+ end
38
+ end
39
+ end
40
+ end
@@ -76,7 +76,7 @@ module SpreeCmCommissioner
76
76
  distance_hash = public_metadata&.dig('distance')
77
77
  return nil if distance_hash.blank?
78
78
 
79
- SpreeCmCommissioner::IntercityTaxi::Distance.from_hash(distance_hash)
79
+ SpreeCmCommissioner::Distance.from_hash(distance_hash)
80
80
  end
81
81
 
82
82
  def pickup_map_place=(value)
@@ -0,0 +1,21 @@
1
+ module SpreeCmCommissioner
2
+ module VehicleKind
3
+ extend ActiveSupport::Concern
4
+
5
+ VEHICLE_KINDS = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus ferry].freeze
6
+
7
+ included do
8
+ enum kind: VEHICLE_KINDS if table_exists? && column_names.include?('kind')
9
+ end
10
+
11
+ def self.options
12
+ VEHICLE_KINDS.map { |k| [I18n.t("vehicle_type.#{k}"), k] }
13
+ end
14
+
15
+ def display_vehicle_kind
16
+ return nil if kind.blank?
17
+
18
+ I18n.t("vehicle_type.#{kind}")
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module SpreeCmCommissioner
2
+ module Adjustable
3
+ module Adjuster
4
+ class PricingAction < Spree::Adjustable::Adjuster::Base
5
+ def update
6
+ pricing_adjustments = adjustments.pricing_action.eligible.reload
7
+ pricing_total = pricing_adjustments.sum(&:amount)
8
+
9
+ update_totals(pricing_total)
10
+ end
11
+
12
+ private
13
+
14
+ def adjustments
15
+ adjustable.try(:all_adjustments) || adjustable.adjustments
16
+ end
17
+
18
+ def update_totals(pricing_total)
19
+ pricing_total ||= 0.0
20
+ @totals[:non_taxable_adjustment_total] += pricing_total
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -67,7 +67,7 @@ module SpreeCmCommissioner
67
67
 
68
68
  validate :validate_block, if: :should_validate_block?
69
69
 
70
- self.whitelisted_ransackable_associations = %w[id_card event line_item]
70
+ self.whitelisted_ransackable_associations = %w[id_card event line_item guest_dynamic_fields]
71
71
  self.whitelisted_ransackable_attributes = %w[first_name last_name phone_number gender contact occupation_id card_type created_at check_in_status]
72
72
 
73
73
  scope :with_dynamic_fields_and_options, lambda {
@@ -16,6 +16,9 @@ module SpreeCmCommissioner
16
16
  includes(:dynamic_field, :dynamic_field_option)
17
17
  }
18
18
 
19
+ self.whitelisted_ransackable_associations = %w[guest]
20
+ self.whitelisted_ransackable_attributes = %w[value]
21
+
19
22
  private
20
23
 
21
24
  def validate_value_format
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
4
4
  {
5
5
  mini: '187x67>',
6
6
  small: '375x135>',
7
- large: '750x270>'
7
+ large: '960x346>'
8
8
  }
9
9
  end
10
10
  end
@@ -20,12 +20,9 @@ module SpreeCmCommissioner
20
20
  line_item.guests.count >= order.line_items.first.number_of_guests
21
21
  end
22
22
 
23
- def invitation_link
24
- "#{Spree::Store.default.formatted_url}/invite_guests/#{token}?utm_source=email"
25
- end
26
-
27
23
  def ticket_url(guest_token)
28
- "#{Spree::Store.default.formatted_url}/invite_guests/#{token}/guest_invitations/#{guest_token}"
24
+ # Build URL string directly since route is defined in cm-market-server
25
+ "/invite_guests/#{token}/guest_invitations/#{guest_token}"
29
26
  end
30
27
  end
31
28
  end
@@ -32,9 +32,8 @@ module SpreeCmCommissioner
32
32
  base.before_save :update_vendor_id
33
33
 
34
34
  base.before_create :add_due_date, if: :subscription?
35
-
36
35
  base.validate :ensure_not_exceed_max_quantity_per_order, if: -> { variant&.max_quantity_per_order.present? }
37
- base.validate :ensure_all_guests_have_blocks, if: -> { variant&.blocks&.any? }
36
+ base.validate :ensure_all_guests_have_blocks, if: :required_seat_selection?
38
37
 
39
38
  base.whitelisted_ransackable_associations |= %w[guests order]
40
39
  base.whitelisted_ransackable_attributes |= %w[number to_date from_date vendor_id]
@@ -77,6 +76,7 @@ module SpreeCmCommissioner
77
76
  base.include SpreeCmCommissioner::LineItemsFilterScope
78
77
  base.include SpreeCmCommissioner::LineItemGuestsConcern
79
78
  base.include SpreeCmCommissioner::LineItemTransitable
79
+ base.include SpreeCmCommissioner::LineItemSeatSelection
80
80
  base.include SpreeCmCommissioner::Integrations::IntegrationMappable
81
81
  base.include SpreeCmCommissioner::ProductType
82
82
  base.include SpreeCmCommissioner::ProductDelegation