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
@@ -14,7 +14,10 @@ module SpreeCmCommissioner
14
14
 
15
15
  base.has_many :variants, through: :products
16
16
 
17
- base.has_many :vehicle_option_types, class_name: 'SpreeCmCommissioner::VehicleOptionType', foreign_key: :option_type_id, dependent: :destroy
17
+ base.has_many :vehicle_type_option_types,
18
+ class_name: 'SpreeCmCommissioner::VehicleTypeOptionType',
19
+ foreign_key: :option_type_id,
20
+ dependent: :destroy
18
21
 
19
22
  base.scope :promoted, -> { where(promoted: true) }
20
23
  base.whitelisted_ransackable_attributes = %w[kind]
@@ -2,7 +2,10 @@ module SpreeCmCommissioner
2
2
  module OptionValueDecorator
3
3
  def self.prepended(base)
4
4
  base.include SpreeCmCommissioner::OptionValueAttrType
5
- base.has_many :option_value_vehicles, class_name: 'SpreeCmCommissioner::OptionValueVehicle', foreign_key: :option_value_id, dependent: :destroy
5
+ base.has_many :option_value_vehicle_types,
6
+ class_name: 'SpreeCmCommissioner::OptionValueVehicleType',
7
+ foreign_key: :option_value_id,
8
+ dependent: :destroy
6
9
 
7
10
  base.validates :name, presence: true, uniqueness: { scope: :option_type_id, case_sensitive: -> { name_case_sensitive? } }
8
11
 
@@ -1,8 +1,8 @@
1
1
  require_dependency 'spree_cm_commissioner'
2
2
 
3
3
  module SpreeCmCommissioner
4
- class OptionValueVehicle < ApplicationRecord
5
- belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle'
4
+ class OptionValueVehicleType < ApplicationRecord
5
+ belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType'
6
6
  belongs_to :option_value, class_name: 'Spree::OptionValue'
7
7
  end
8
8
  end
@@ -13,10 +13,15 @@ module SpreeCmCommissioner
13
13
 
14
14
  before_validation :convert_duration_to_seconds
15
15
 
16
+ # before create, duplicate seat layout from vehicle_type if present
17
+ before_validation :duplicate_seat_layout_from_vehicle, if: -> { seat_layout.nil? && vehicle_type&.seat_layout.present? }
18
+ before_validation :assign_route_and_vendor_route, if: :changed_route_attributes?
19
+
16
20
  belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
17
21
  belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
18
22
 
19
- belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle', optional: false
23
+ belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType', optional: false
24
+ belongs_to :vehicle, class_name: 'SpreeCmCommissioner::Vehicle', optional: true
20
25
  has_one :seat_layout, as: :layoutable, class_name: 'SpreeCmCommissioner::SeatLayout', dependent: :destroy
21
26
 
22
27
  belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
@@ -38,10 +43,6 @@ module SpreeCmCommissioner
38
43
  validates :duration, numericality: { greater_than: 0 }
39
44
  validate :origin_and_destination_cannot_be_the_same
40
45
 
41
- # before create, duplicate seat layout from vehicle if present
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?
44
-
45
46
  accepts_nested_attributes_for :trip_stops, :product, :seat_layout, allow_destroy: true
46
47
 
47
48
  self.whitelisted_ransackable_associations = %w[product vehicle]
@@ -84,7 +85,7 @@ module SpreeCmCommissioner
84
85
  # Duplicate the vehicle's seat layout into this trip. Copies nested
85
86
  # seat sections and blocks while excluding identifying columns and
86
87
  # timestamps to avoid collisions when creating new records.
87
- original = vehicle.seat_layout
88
+ original = vehicle_type.seat_layout
88
89
 
89
90
  self.seat_layout_attributes =
90
91
  original.attributes.except('id', 'created_at', 'updated_at', 'layoutable_id', 'layoutable_type')
@@ -1,34 +1,22 @@
1
1
  module SpreeCmCommissioner
2
2
  class Vehicle < Base
3
- include SpreeCmCommissioner::StoreMetadata
4
- include SpreeCmCommissioner::VehicleType
5
-
6
- # This model has no seat_layout column (polymorphic association), so we store the preload_seat_layout_id ID in public_metadata.
7
- # This lets us check if a seat layout exists without triggering a database query.
8
- # The ID is automatically updated whenever the seat_layout is saved.
9
- store_public_metadata :preload_seat_layout_id, :integer
3
+ include SpreeCmCommissioner::VehicleKind
10
4
 
11
5
  belongs_to :vendor, class_name: 'Spree::Vendor'
12
-
13
- has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
14
- has_one :seat_layout, as: :layoutable, class_name: 'SpreeCmCommissioner::SeatLayout', dependent: :destroy
15
-
16
- has_many :vehicle_photos, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
17
-
18
- has_many :vehicle_option_types, class_name: 'SpreeCmCommissioner::VehicleOptionType', dependent: :destroy
19
- has_many :option_value_vehicles, class_name: 'SpreeCmCommissioner::OptionValueVehicle', dependent: :destroy
20
- has_many :option_values, through: :option_value_vehicles, class_name: 'Spree::OptionValue'
21
- has_many :amenities, through: :option_value_vehicles, source: :option_value
6
+ belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType'
22
7
 
23
8
  has_many :trips, class_name: 'SpreeCmCommissioner::Trip', dependent: :nullify
9
+ has_many :amenities, through: :vehicle_type
10
+ has_many :option_values, through: :vehicle_type
11
+ has_many :vehicle_photos, through: :vehicle_type, source: :vehicle_photos
24
12
 
25
- validates :code, uniqueness: { scope: :vendor_id }, presence: true
13
+ validates :name, presence: true
26
14
  validates :license_plate, uniqueness: true, allow_blank: true
27
15
 
28
- self.whitelisted_ransackable_attributes = %w[license_plate code vehicle_type]
16
+ self.whitelisted_ransackable_attributes = %w[name license_plate kind]
29
17
 
30
18
  def display_name
31
- "#{code} (#{license_plate})"
19
+ license_plate.present? ? "#{name} (#{license_plate})" : name
32
20
  end
33
21
  end
34
22
  end
@@ -0,0 +1,28 @@
1
+ module SpreeCmCommissioner
2
+ class VehicleType < Base
3
+ include SpreeCmCommissioner::StoreMetadata
4
+ include SpreeCmCommissioner::VehicleKind
5
+
6
+ # This model has no seat_layout column (polymorphic association), so we store the preload_seat_layout_id ID in public_metadata.
7
+ # This lets us check if a seat layout exists without triggering a database query.
8
+ # The ID is automatically updated whenever the seat_layout is saved.
9
+ store_public_metadata :preload_seat_layout_id, :integer
10
+
11
+ belongs_to :vendor, class_name: 'Spree::Vendor'
12
+
13
+ has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
14
+ has_one :seat_layout, as: :layoutable, class_name: 'SpreeCmCommissioner::SeatLayout', dependent: :destroy
15
+
16
+ has_many :vehicles, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
17
+ has_many :vehicle_photos, class_name: 'SpreeCmCommissioner::VehiclePhoto', as: :viewable, dependent: :destroy
18
+
19
+ has_many :vehicle_type_option_types, class_name: 'SpreeCmCommissioner::VehicleTypeOptionType', dependent: :destroy
20
+ has_many :option_value_vehicle_types, class_name: 'SpreeCmCommissioner::OptionValueVehicleType', dependent: :destroy
21
+ has_many :option_values, through: :option_value_vehicle_types, class_name: 'Spree::OptionValue'
22
+ has_many :amenities, through: :option_value_vehicle_types, source: :option_value
23
+
24
+ validates :name, uniqueness: { scope: :vendor_id }, presence: true
25
+
26
+ self.whitelisted_ransackable_attributes = %w[name kind]
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ module SpreeCmCommissioner
2
+ class VehicleTypeOptionType < ApplicationRecord
3
+ belongs_to :vehicle_type, class_name: 'SpreeCmCommissioner::VehicleType'
4
+ belongs_to :option_type, class_name: 'Spree::OptionType'
5
+ end
6
+ end
@@ -90,6 +90,7 @@ module SpreeCmCommissioner
90
90
  dependent: :destroy,
91
91
  inverse_of: :relatable
92
92
 
93
+ base.has_many :vehicle_types, class_name: 'SpreeCmCommissioner::VehicleType', dependent: :destroy
93
94
  base.has_many :vehicles, class_name: 'SpreeCmCommissioner::Vehicle', dependent: :destroy
94
95
  base.has_many :trips, class_name: 'SpreeCmCommissioner::Trip', dependent: :destroy
95
96
  base.has_many :trip_stops, through: :trips, class_name: 'SpreeCmCommissioner::TripStop'
@@ -1,3 +1,3 @@
1
1
  <!-- insert_after "erb[silent]:contains('content_for :page_actions')" -->
2
2
 
3
- <%= button_link_to Spree.t(:clear_cache), admin_invalidate_api_caches_path(model: Spree::Product.name),method: :post, class: "btn btn-outline-primary" %>
3
+ <%= button_link_to Spree.t(:clear_cache), admin_invalidate_api_caches_path(model: Spree::Product.name, id: @product.id), method: :post, class: "btn btn-outline-primary" %>
@@ -2,6 +2,7 @@
2
2
 
3
3
  <% if @taxon.depth == 1 %>
4
4
  <%= button_link_to Spree.t(:clear_cache),
5
- admin_invalidate_api_caches_path(model: Spree::Taxon.name),
6
- method: :post, class: "btn btn-outline-primary" %>
5
+ admin_invalidate_api_caches_path(model: Spree::Taxon.name, id: @taxon.id),
6
+ method: :post,
7
+ class: "btn btn-outline-primary" %>
7
8
  <% end %>
@@ -71,7 +71,7 @@ module SpreeCmCommissioner
71
71
  prices.amount AS amount, prices.compare_at_amount AS compare_at_amount,
72
72
  prices.currency AS currency
73
73
  SQL
74
- .includes(vendor: :logo, vehicle: :option_values)
74
+ .includes(vendor: :logo, vehicle_type: :option_values)
75
75
 
76
76
  scope = scope.joins(:vendor) if tenant_id.present?
77
77
 
@@ -100,7 +100,7 @@ module SpreeCmCommissioner
100
100
  end
101
101
 
102
102
  def build_trip_result(trip)
103
- vehicle = trip&.vehicle
103
+ vehicle_type = trip&.vehicle_type
104
104
  vendor = trip&.vendor
105
105
  trip_result_options = {
106
106
  id: trip&.id,
@@ -117,8 +117,8 @@ module SpreeCmCommissioner
117
117
  id: trip&.destination_place_id,
118
118
  name: trip&.destination_place_name
119
119
  },
120
- vehicle_id: trip&.vehicle_id,
121
- vehicle: vehicle,
120
+ vehicle_type_id: trip&.vehicle_type_id,
121
+ vehicle_type: vehicle_type,
122
122
  vendor_id: trip&.vendor_id,
123
123
  vendor: vendor,
124
124
  product_id: trip&.product_id,
@@ -129,7 +129,7 @@ module SpreeCmCommissioner
129
129
  drop_off: build_drop_off_info(trip),
130
130
  quantity_available: trip&.quantity_available,
131
131
  max_capacity: trip&.max_capacity,
132
- amenities: (trip.vehicle&.option_values || [])
132
+ amenities: (trip.vehicle_type&.option_values || [])
133
133
  }
134
134
  SpreeCmCommissioner::TripResult.new(trip_result_options)
135
135
  end
@@ -23,6 +23,8 @@ module SpreeCmCommissioner
23
23
  optional(:detour_dropoff_km).filled(:float)
24
24
  optional(:extra_pickup_km).filled(:float)
25
25
  optional(:extra_dropoff_km).filled(:float)
26
+ optional(:signed_at).filled(:string)
27
+ optional(:signature).filled(:string)
26
28
  end
27
29
  end
28
30
  end
@@ -17,6 +17,18 @@ module Spree
17
17
  base.attribute :delivery_required, &:delivery_required?
18
18
  base.attribute :sufficient_stock, &:sufficient_stock?
19
19
 
20
+ base.attribute :pickup_map_place do |line_item|
21
+ line_item.pickup_map_place&.to_h
22
+ end
23
+
24
+ base.attribute :dropoff_map_place do |line_item|
25
+ line_item.dropoff_map_place&.to_h
26
+ end
27
+
28
+ base.attribute :distance do |line_item|
29
+ line_item.distance&.to_h
30
+ end
31
+
20
32
  base.has_one :vendor
21
33
 
22
34
  base.belongs_to :order, serializer: Spree::V2::Storefront::OrderSerializer
@@ -10,7 +10,8 @@ module Spree
10
10
  :from_date, :to_date, :need_confirmation, :product_type, :event_status, :amount,
11
11
  :display_amount, :number, :qr_data, :kyc, :kyc_fields, :remaining_total_guests, :number_of_guests,
12
12
  :completion_steps, :available_social_contact_platforms, :allow_anonymous_booking,
13
- :discontinue_on, :high_demand, :jwt_token, :pre_tax_amount, :display_pre_tax_amount, :public_metadata
13
+ :discontinue_on, :high_demand, :jwt_token, :pre_tax_amount, :display_pre_tax_amount, :public_metadata,
14
+ :direction, :trip_id, :boarding_trip_stop_id, :drop_off_trip_stop_id
14
15
 
15
16
  attribute :required_self_check_in_location, &:required_self_check_in_location?
16
17
  attribute :allowed_self_check_in, &:allowed_self_check_in?
@@ -18,6 +19,18 @@ module Spree
18
19
  attribute :delivery_required, &:delivery_required?
19
20
  attribute :sufficient_stock, &:sufficient_stock?
20
21
 
22
+ attribute :pickup_map_place do |line_item|
23
+ line_item.pickup_map_place&.to_h
24
+ end
25
+
26
+ attribute :dropoff_map_place do |line_item|
27
+ line_item.dropoff_map_place&.to_h
28
+ end
29
+
30
+ attribute :distance do |line_item|
31
+ line_item.distance&.to_h
32
+ end
33
+
21
34
  has_one :vendor, serializer: Spree::V2::Tenant::VendorSerializer
22
35
  has_one :product, serializer: Spree::V2::Storefront::ProductSerializer
23
36
  has_many :saved_guests, serializer: SpreeCmCommissioner::V2::Storefront::SavedGuestSerializer
@@ -0,0 +1,26 @@
1
+ module SpreeCmCommissioner
2
+ module V2
3
+ module Storefront
4
+ class DistanceSerializer < BaseSerializer
5
+ attributes :distance_km,
6
+ :ordered_points,
7
+ :estimated_time_minutes,
8
+ :extra_pickup_km,
9
+ :extra_dropoff_km,
10
+ :base_km,
11
+ :detour_pickup_km,
12
+ :detour_dropoff_km
13
+
14
+ attribute :signed_at do |object|
15
+ object.to_signed_h['signed_at'] if object.respond_to?(:to_signed_h)
16
+ end
17
+
18
+ attribute :signature do |object|
19
+ object.to_signed_h['signature'] if object.respond_to?(:to_signed_h)
20
+ end
21
+
22
+ cache_options store: nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -13,15 +13,15 @@ module SpreeCmCommissioner
13
13
 
14
14
  cache_options store: nil
15
15
 
16
- def pickup_map_place
16
+ attribute :pickup_map_place do |object|
17
17
  object.pickup_map_place&.to_h
18
18
  end
19
19
 
20
- def dropoff_map_place
20
+ attribute :dropoff_map_place do |object|
21
21
  object.dropoff_map_place&.to_h
22
22
  end
23
23
 
24
- def distance
24
+ attribute :distance do |object|
25
25
  object.distance&.to_h
26
26
  end
27
27
  end
@@ -16,11 +16,11 @@ module SpreeCmCommissioner
16
16
  )
17
17
 
18
18
  attributes :origin_place, :destination_place, :boarding, :drop_off, :price, :compare_at_amount, :currency, :quantity_available, :max_capacity,
19
- :allow_seat_selection, :departure_time, :route_type, :distance
19
+ :allow_seat_selection, :departure_time, :route_type, :distance, :display_price
20
20
  attribute :duration_in_hms, &:duration_in_hms
21
21
  attribute :arrival_time, &:arrival_time
22
22
  belongs_to :vendor, serializer: ::SpreeCmCommissioner::V2::Storefront::TripVendorSerializer
23
- belongs_to :vehicle, serializer: ::SpreeCmCommissioner::V2::Storefront::TripVehicleSerializer
23
+ belongs_to :vehicle_type, serializer: ::SpreeCmCommissioner::V2::Storefront::TripVehicleTypeSerializer
24
24
  has_many :amenities, serializer: ::SpreeCmCommissioner::V2::Storefront::AmenitySerializer
25
25
  end
26
26
  end
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
4
4
  class TripSerializer < BaseSerializer
5
5
  attributes :departure_time, :arrival_time, :duration_in_hms, :route_type, :allow_seat_selection
6
6
 
7
- has_one :vehicle, serializer: ::SpreeCmCommissioner::V2::Storefront::TripVehicleSerializer
7
+ has_one :vehicle_type, serializer: ::SpreeCmCommissioner::V2::Storefront::TripVehicleTypeSerializer
8
8
  has_one :seat_layout, serializer: SpreeCmCommissioner::V2::Storefront::SeatLayoutSerializer
9
9
 
10
10
  has_one :origin_place, serializer: ::SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
@@ -1,10 +1,8 @@
1
1
  module SpreeCmCommissioner
2
2
  module V2
3
3
  module Storefront
4
- class TripVehicleSerializer < BaseSerializer
5
- set_type :vehicle
6
-
7
- attributes :id, :code, :vehicle_type, :number_of_seats
4
+ class TripVehicleTypeSerializer < BaseSerializer
5
+ attributes :id, :name, :kind
8
6
 
9
7
  has_many :vehicle_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer
10
8
  has_many :amenities, serializer: ::SpreeCmCommissioner::V2::Storefront::AmenitySerializer
@@ -0,0 +1,98 @@
1
+ module SpreeCmCommissioner
2
+ module ApiCaches
3
+ class Invalidate
4
+ prepend ::Spree::ServiceModule::Base
5
+
6
+ SUPPORTED_MODELS = {
7
+ 'SpreeCmCommissioner::HomepageSection' => SpreeCmCommissioner::HomepageSection,
8
+ 'SpreeCmCommissioner::HomepageBackground' => SpreeCmCommissioner::HomepageBackground,
9
+ 'Spree::Menu' => Spree::Menu,
10
+ 'Spree::Taxon' => Spree::Taxon,
11
+ 'Spree::Product' => Spree::Product
12
+ }.freeze
13
+
14
+ SINGLE_API_PATTERN = {
15
+ SpreeCmCommissioner::HomepageSection => ['/api/v2/storefront/homepage/:id'],
16
+ SpreeCmCommissioner::HomepageBackground => ['/api/v2/storefront/homepage/:id'],
17
+ Spree::Menu => ['/api/v2/storefront/menus/:id'],
18
+ Spree::Taxon => ['/api/v2/storefront/taxons/:id'],
19
+ Spree::Product => [
20
+ '/api/v2/tenant/products/:id',
21
+ '/api/v2/tenant/products/:slug',
22
+ '/api/v2/storefront/products/:id',
23
+ '/api/v2/storefront/products/:slug'
24
+ ]
25
+ }.freeze
26
+
27
+ ALL_API_PATTERNS = {
28
+ SpreeCmCommissioner::HomepageSection => ['/api/v2/storefront/homepage/*'],
29
+ SpreeCmCommissioner::HomepageBackground => ['/api/v2/storefront/homepage/*'],
30
+ Spree::Menu => ['/api/v2/storefront/menus/*'],
31
+ Spree::Taxon => ['/api/v2/storefront/taxons/*'],
32
+ Spree::Product => [
33
+ '/api/v2/tenant/products/*',
34
+ '/api/v2/storefront/products/*'
35
+ ]
36
+ }.freeze
37
+
38
+ # eg. 'Spree::Product', 123 or 'Spree::Product', nil
39
+ def call(model:, id: nil)
40
+ model_class = SUPPORTED_MODELS[model]
41
+
42
+ return failure(nil, 'Model is not supported') if model_class.nil?
43
+
44
+ if id.present?
45
+ invalidate_single_record(model_class: model_class, id: id)
46
+ else
47
+ invalidate_all_records(model_class: model_class)
48
+ end
49
+ end
50
+
51
+ # eg. 'Spree::Product', 123 => ['/api/v2/tenant/products/123', '/api/v2/tenant/products/slug-of-product', ...]
52
+ def invalidate_single_record(model_class:, id:)
53
+ return failure(nil, "#{model_class.name} ID is required") if id.blank?
54
+
55
+ record = model_class.find_by(id: id)
56
+ return failure(nil, "#{model_class.name} #{id} record not found") if record.nil?
57
+
58
+ refresh_record_cache_key(record)
59
+
60
+ api_patterns = SINGLE_API_PATTERN[record.class].map do |pattern|
61
+ pattern.gsub(/:(\w+)/) do
62
+ key = Regexp.last_match(1)
63
+ record.public_send(key).to_s
64
+ end
65
+ end
66
+
67
+ enqueue_invalidate(api_patterns)
68
+
69
+ success(record: record, api_patterns: api_patterns)
70
+ end
71
+
72
+ # eg. 'Spree::Product' => ['/api/v2/tenant/products/*', '/api/v2/storefront/products/*']
73
+ def invalidate_all_records(model_class:)
74
+ model_class.find_each do |record|
75
+ refresh_record_cache_key(record)
76
+ end
77
+
78
+ enqueue_invalidate(ALL_API_PATTERNS[model_class])
79
+
80
+ success(api_patterns: ALL_API_PATTERNS[model_class])
81
+ end
82
+
83
+ private
84
+
85
+ # eg. updated_at: Fri, 26 Dec 2025 10:40:56.505450000 +07 +07:00
86
+ def refresh_record_cache_key(record)
87
+ # rubocop:disable Rails/SkipsModelValidations
88
+ record.touch
89
+ # rubocop:enable Rails/SkipsModelValidations
90
+ end
91
+
92
+ # Enqueue cache invalidation jobs
93
+ def enqueue_invalidate(patterns)
94
+ SpreeCmCommissioner::InvalidateCacheRequestJob.perform_later(patterns: patterns)
95
+ end
96
+ end
97
+ end
98
+ end