spree_cm_commissioner 2.3.2.pre.pre2 → 2.3.2.pre.pre4

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +36 -36
  4. data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +63 -0
  5. data/app/controllers/spree/api/v2/storefront/route_places_controller.rb +68 -0
  6. data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +37 -37
  7. data/app/controllers/spree/api/v2/tenant/trip_places_controller.rb +5 -11
  8. data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +1 -1
  9. data/app/controllers/spree/api/v2/tenant/trips_controller.rb +8 -8
  10. data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +12 -0
  11. data/app/finders/spree_cm_commissioner/places/find_with_route.rb +43 -0
  12. data/app/finders/spree_cm_commissioner/routes/find_popular.rb +25 -0
  13. data/app/mailers/spree/order_mailer_decorator.rb +8 -6
  14. data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +34 -89
  15. data/app/models/concerns/spree_cm_commissioner/service_recommendations.rb +2 -2
  16. data/app/models/spree_cm_commissioner/adjustment_decorator.rb +1 -1
  17. data/app/models/spree_cm_commissioner/calculators/extra_drop_off_distance.rb +15 -0
  18. data/app/models/spree_cm_commissioner/calculators/extra_pick_up_distance.rb +15 -0
  19. data/app/models/spree_cm_commissioner/place.rb +7 -0
  20. data/app/models/spree_cm_commissioner/pricing_action.rb +27 -0
  21. data/app/models/spree_cm_commissioner/pricing_model.rb +18 -0
  22. data/app/models/spree_cm_commissioner/pricing_model_variant.rb +6 -0
  23. data/app/models/spree_cm_commissioner/pricing_rule.rb +15 -0
  24. data/app/models/spree_cm_commissioner/pricing_rule_group.rb +20 -0
  25. data/app/models/spree_cm_commissioner/pricing_rules/extra_drop_off_distance.rb +10 -0
  26. data/app/models/spree_cm_commissioner/pricing_rules/extra_pick_up_distance.rb +12 -0
  27. data/app/models/spree_cm_commissioner/pricing_rules/nationality.rb +35 -0
  28. data/app/models/spree_cm_commissioner/tenant.rb +4 -0
  29. data/app/models/spree_cm_commissioner/variant_decorator.rb +3 -0
  30. data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -0
  31. data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +22 -3
  32. data/app/request_schemas/spree_cm_commissioner/route_places_request_schema.rb +12 -0
  33. data/app/serializers/spree/v2/storefront/place_serializer.rb +3 -2
  34. data/app/serializers/spree/v2/tenant/cart_serializer.rb +1 -0
  35. data/app/serializers/spree/v2/tenant/intercity_taxi_cart_serializer.rb +12 -0
  36. data/app/serializers/spree/v2/tenant/line_item_serializer.rb +1 -0
  37. data/app/serializers/spree/v2/tenant/trip_place_serializer.rb +8 -0
  38. data/app/serializers/spree/v2/tenant/trip_query_result_serializer.rb +8 -0
  39. data/app/serializers/spree/v2/tenant/trip_serializer.rb +8 -0
  40. data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb +17 -18
  41. data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +12 -0
  42. data/app/services/spree_cm_commissioner/intercity_taxi_order/create.rb +11 -22
  43. data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +32 -11
  44. data/app/services/spree_cm_commissioner/pricing_models/apply.rb +51 -0
  45. data/app/services/spree_cm_commissioner/pricing_rules/build_params.rb +53 -0
  46. data/app/services/spree_cm_commissioner/pricing_rules/build_template.rb +27 -0
  47. data/app/services/spree_cm_commissioner/pricing_rules/create.rb +10 -0
  48. data/app/services/spree_cm_commissioner/pricing_rules/update.rb +11 -0
  49. data/app/services/spree_cm_commissioner/vendor_places/base.rb +24 -0
  50. data/app/services/spree_cm_commissioner/vendor_places/bulk_create.rb +70 -0
  51. data/app/services/spree_cm_commissioner/vendor_places/create_with_google_map_data.rb +85 -0
  52. data/app/services/spree_cm_commissioner/vendor_places/update.rb +56 -0
  53. data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
  54. data/app/views/spree_cm_commissioner/order_mailer/_mailer_stylesheets.html.erb +14 -13
  55. data/app/views/spree_cm_commissioner/order_mailer/tenant/_greeting.html.erb +6 -4
  56. data/config/routes.rb +4 -10
  57. data/db/migrate/20250915075830_create_spree_cm_commissioner_pricing_models.rb +17 -0
  58. data/db/migrate/20250915080733_create_spree_cm_commissioner_pricing_model_variants.rb +14 -0
  59. data/db/migrate/20251003082144_create_spree_cm_commissioner_pricing_rule_groups.rb +16 -0
  60. data/db/migrate/20251003093408_create_spree_cm_commissioner_pricing_rule.rb +19 -0
  61. data/db/migrate/20251003093643_create_spree_cm_commissioner_pricing_action.rb +14 -0
  62. data/db/migrate/20251119093000_add_tenant_id_to_cm_vendor_places.rb +2 -1
  63. data/lib/spree_cm_commissioner/intercity_taxi/distance.rb +38 -0
  64. data/lib/spree_cm_commissioner/intercity_taxi/map_place.rb +31 -0
  65. data/lib/spree_cm_commissioner/test_helper/factories/pricing_action_factory.rb +5 -0
  66. data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_factory.rb +8 -0
  67. data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_factory.rb +17 -0
  68. data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_group_factory.rb +8 -0
  69. data/lib/spree_cm_commissioner/version.rb +1 -1
  70. data/lib/spree_cm_commissioner.rb +2 -0
  71. metadata +42 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4090589caa9f83b8b2020e1ed20383647e2671b87692bcecf57f2f455bee7a58
4
- data.tar.gz: 8cd128d3b2764b6ba6b309c8e0c9b71108b7057ccdd4e03fe92ec4e9d8702d9a
3
+ metadata.gz: 89bec2bb51dcea55ff8caa274aec5a2c84a3efc81a7d65b5474c4f418095ff4e
4
+ data.tar.gz: cf555b28cfebc09740e0f4f656d8ab2105e0398eb38534855ddfccfa4d3ffd0b
5
5
  SHA512:
6
- metadata.gz: 692a3b8079e7f44d84556a826f8519ece6226334b28f78b194829f908f005902a4aaa34f18683aecbb161450197f3c808a82d84545fcc868975306ae45b396a5
7
- data.tar.gz: e5e0191151979f1476f7ba43fe46ce13dd346c9d44d729d18da9fcff587004c21c7f028bc814356fa2c08a3a32dc867e3bd155ccc0f7659761ab7a9a6201b9b9
6
+ metadata.gz: 53add1b0da7b9a4ac41f39475132c136a20d7028ff6c99231f0f053f47128a6b9f9a08cb768d8342a5fc4e199cbaa17d7a02a6e5a9210aea0442fda723fe8a2e
7
+ data.tar.gz: 5815669bc5bed8c7a3e76024fd82d4299f46145c52030b1aa797c6fc61f4ac65d69bc2afbceb9967eb096331552e485cfa5ae82f9d1a4127b2ff66cbbc9b48eb
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.3.2.pre.pre2)
37
+ spree_cm_commissioner (2.3.2.pre.pre4)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -8,8 +8,9 @@ module Spree
8
8
  include SpreeCmCommissioner::OrderConcern
9
9
 
10
10
  before_action :ensure_order, only: :update
11
+
11
12
  def create
12
- order = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
13
+ result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
13
14
  trip_id: params[:trip_id],
14
15
  from_date: params[:from_date],
15
16
  to_date: params[:to_date],
@@ -17,51 +18,50 @@ module Spree
17
18
  quantity: params[:quantity]
18
19
  )
19
20
 
20
- render_serialized_payload { serialize_resource(order) }
21
- rescue StandardError => e
22
- render_error_payload(e.message)
21
+ if result.success?
22
+ render_serialized_payload { serialize_resource(result.value) }
23
+ else
24
+ render_error_payload(result.error)
25
+ end
23
26
  end
24
27
 
25
28
  def update
26
- order = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
29
+ result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
27
30
  order: spree_current_order,
28
- order_params: intercity_taxi_update_order_params
31
+ remark: params[:remark],
32
+ pickup_map_place_attributes: params.require(:pickup_map_place_attributes).permit(
33
+ :place_name,
34
+ :lat,
35
+ :lng,
36
+ :oob_confirmed
37
+ ),
38
+ dropoff_map_place_attributes: params.require(:dropoff_map_place_attributes).permit(
39
+ :place_name,
40
+ :lat,
41
+ :lng,
42
+ :oob_confirmed
43
+ ),
44
+ distance_attributes: params.require(:distance_attributes).permit(
45
+ :distance_km,
46
+ :ordered_points,
47
+ :estimated_time_minutes,
48
+ :base_km,
49
+ :detour_pickup_km,
50
+ :detour_dropoff_km,
51
+ :extra_pickup_km,
52
+ :extra_dropoff_km
53
+ )
29
54
  )
30
55
 
31
- render_serialized_payload { serialize_resource(order) }
32
- rescue StandardError => e
33
- render_error_payload(e.message)
56
+ if result.success?
57
+ render_serialized_payload { serialize_resource(result.value) }
58
+ else
59
+ render_error_payload(result.error)
60
+ end
34
61
  end
35
62
 
36
63
  private
37
64
 
38
- def intercity_taxi_update_order_params
39
- params.require(:order).permit(
40
- line_items_attributes: %i[
41
- id
42
- pickup_place_name
43
- drop_off_place_name
44
- from_date
45
- pickup_lat
46
- pickup_lng
47
- pickup_oob_confirmed
48
- drop_off_lat
49
- drop_off_lng
50
- drop_off_oob_confirmed
51
- distance_km
52
- ordered_points
53
- base_km
54
- detour_pickup_km
55
- detour_dropoff_km
56
- extra_pickup_km
57
- extra_dropoff_km
58
- estimated_time_minutes
59
- remark
60
- passenger_count
61
- ]
62
- )
63
- end
64
-
65
65
  def required_schema
66
66
  if action_name == 'create'
67
67
  SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
@@ -0,0 +1,63 @@
1
+ # API endpoint for retrieving popular routes ordered by order metrics.
2
+ #
3
+ # GET /api/v2/storefront/popular_route_places
4
+ #
5
+ # Returns routes sorted by popularity (fulfilled_order_count, then order_count) with their
6
+ # origin and destination places. Supports optional includes for vendors and nearby_places.
7
+ #
8
+ # Response: Collection of routes serialized with RouteSerializer
9
+ #
10
+ # @example
11
+ # GET /api/v2/storefront/popular_route_places
12
+ module Spree
13
+ module Api
14
+ module V2
15
+ module Storefront
16
+ class PopularRoutePlacesController < ::Spree::Api::V2::ResourceController
17
+ private
18
+
19
+ # override
20
+ def collection
21
+ @collection ||= collection_finder.new.execute
22
+ end
23
+
24
+ # override
25
+ def default_resource_includes
26
+ %w[origin_place destination_place]
27
+ end
28
+
29
+ # override
30
+ def collection_finder
31
+ SpreeCmCommissioner::Routes::FindPopular
32
+ end
33
+
34
+ # override
35
+ def collection_serializer
36
+ resource_serializer
37
+ end
38
+
39
+ # override
40
+ def resource_serializer
41
+ SpreeCmCommissioner::V2::Storefront::RouteSerializer
42
+ end
43
+
44
+ # override
45
+ def serializer_params
46
+ super.merge(
47
+ include_vendors: include_vendors?,
48
+ include_nearby_places: include_nearby_places?
49
+ )
50
+ end
51
+
52
+ def include_vendors?
53
+ resource_includes.include?(:vendors) || false
54
+ end
55
+
56
+ def include_nearby_places?
57
+ resource_includes.include?(:nearby_places) || false
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,68 @@
1
+ # API endpoint for searching places that are used in routes by keyword.
2
+ #
3
+ # GET /api/v2/storefront/route_places
4
+ #
5
+ # Searches for places (origins or destinations) that are used in existing routes,
6
+ # filtered by keyword search on place names.
7
+ #
8
+ # Query Parameters:
9
+ # - query: [String] Required. Keyword to search in place names (case-insensitive)
10
+ # - place_type: [String] Required. Type of route place: 'origin' or 'destination'
11
+ # - include: Optional comma-separated list (e.g., 'vendors,nearby_places')
12
+ #
13
+ # Response: Collection of places serialized with PlaceSerializer
14
+ #
15
+ # Behavior:
16
+ # - Returns empty collection if query is blank or place_type is invalid
17
+ # - UI should only call this endpoint when user has input a search term
18
+ # - For initial/empty state, use GET /api/v2/storefront/popular_route_places instead
19
+ #
20
+ # @example
21
+ # GET /api/v2/storefront/route_places?query=Phnom&place_type=origin
22
+ module Spree
23
+ module Api
24
+ module V2
25
+ module Storefront
26
+ class RoutePlacesController < ::Spree::Api::V2::ResourceController
27
+ private
28
+
29
+ # override
30
+ def collection
31
+ @collection ||= collection_finder.new(keyword: params[:query], place_type: params[:place_type]).execute
32
+ end
33
+
34
+ # override
35
+ def collection_finder
36
+ SpreeCmCommissioner::Places::FindWithRoute
37
+ end
38
+
39
+ # override
40
+ def collection_serializer
41
+ Spree::V2::Storefront::PlaceSerializer
42
+ end
43
+
44
+ # override
45
+ def required_schema
46
+ SpreeCmCommissioner::RoutePlacesRequestSchema
47
+ end
48
+
49
+ # override
50
+ def serializer_params
51
+ super.merge(
52
+ include_vendors: include_vendors?,
53
+ include_nearby_places: include_nearby_places?
54
+ )
55
+ end
56
+
57
+ def include_vendors?
58
+ resource_includes.include?(:vendors) || false
59
+ end
60
+
61
+ def include_nearby_places?
62
+ resource_includes.include?(:nearby_places) || false
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -8,8 +8,9 @@ module Spree
8
8
  include SpreeCmCommissioner::OrderConcern
9
9
 
10
10
  before_action :ensure_order, only: :update
11
+
11
12
  def create
12
- order = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
13
+ result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
13
14
  trip_id: params[:trip_id],
14
15
  from_date: params[:from_date],
15
16
  to_date: params[:to_date],
@@ -17,51 +18,50 @@ module Spree
17
18
  user: spree_current_user
18
19
  )
19
20
 
20
- render_serialized_payload { serialize_resource(order) }
21
- rescue StandardError => e
22
- render_error_payload(e.message)
21
+ if result.success?
22
+ render_serialized_payload { serialize_resource(result.value) }
23
+ else
24
+ render_error_payload(result.error)
25
+ end
23
26
  end
24
27
 
25
28
  def update
26
- order = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
29
+ result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
27
30
  order: spree_current_order,
28
- order_params: intercity_taxi_update_order_params
31
+ remark: params[:remark],
32
+ pickup_map_place_attributes: params.require(:pickup_map_place_attributes).permit(
33
+ :place_name,
34
+ :lat,
35
+ :lng,
36
+ :oob_confirmed
37
+ ),
38
+ dropoff_map_place_attributes: params.require(:dropoff_map_place_attributes).permit(
39
+ :place_name,
40
+ :lat,
41
+ :lng,
42
+ :oob_confirmed
43
+ ),
44
+ distance_attributes: params.require(:distance_attributes).permit(
45
+ :distance_km,
46
+ :ordered_points,
47
+ :estimated_time_minutes,
48
+ :base_km,
49
+ :detour_pickup_km,
50
+ :detour_dropoff_km,
51
+ :extra_pickup_km,
52
+ :extra_dropoff_km
53
+ )
29
54
  )
30
55
 
31
- render_serialized_payload { serialize_resource(order) }
32
- rescue StandardError => e
33
- render_error_payload(e.message)
56
+ if result.success?
57
+ render_serialized_payload { serialize_resource(result.value) }
58
+ else
59
+ render_error_payload(result.error)
60
+ end
34
61
  end
35
62
 
36
63
  private
37
64
 
38
- def intercity_taxi_update_order_params
39
- params.require(:order).permit(
40
- line_items_attributes: %i[
41
- id
42
- pickup_place_name
43
- drop_off_place_name
44
- from_date
45
- pickup_lat
46
- pickup_lng
47
- pickup_oob_confirmed
48
- drop_off_lat
49
- drop_off_lng
50
- drop_off_oob_confirmed
51
- distance_km
52
- ordered_points
53
- base_km
54
- detour_pickup_km
55
- detour_dropoff_km
56
- extra_pickup_km
57
- extra_dropoff_km
58
- estimated_time_minutes
59
- remark
60
- passenger_count
61
- ]
62
- )
63
- end
64
-
65
65
  def required_schema
66
66
  if action_name == 'create'
67
67
  SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
@@ -71,7 +71,7 @@ module Spree
71
71
  end
72
72
 
73
73
  def resource_serializer
74
- SpreeCmCommissioner::V2::Storefront::IntercityTaxiCartSerializer
74
+ Spree::V2::Tenant::IntercityTaxiCartSerializer
75
75
  end
76
76
  end
77
77
  end
@@ -3,18 +3,9 @@ module Spree
3
3
  module V2
4
4
  module Tenant
5
5
  class TripPlacesController < BaseController
6
- def index
7
- render_serialized_payload do
8
- serialize_collection(paginated_collection)
9
- end
10
- end
11
-
12
6
  private
13
7
 
14
- def paginated_collection
15
- collection.page(params[:page]).per(params[:per_page])
16
- end
17
-
8
+ # override
18
9
  def collection
19
10
  @collection ||= begin
20
11
  base_scope = scope
@@ -28,18 +19,21 @@ module Spree
28
19
  end
29
20
  end
30
21
 
22
+ # override
31
23
  def scope
32
24
  model_class.joins(:location_vendor_places)
33
25
  .where(location_vendor_places: { tenant_id: @tenant.id })
34
26
  .distinct
35
27
  end
36
28
 
29
+ # override
37
30
  def model_class
38
31
  SpreeCmCommissioner::Place
39
32
  end
40
33
 
34
+ # override
41
35
  def collection_serializer
42
- SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
36
+ Spree::V2::Tenant::TripPlaceSerializer
43
37
  end
44
38
  end
45
39
  end
@@ -70,7 +70,7 @@ module Spree
70
70
  end
71
71
 
72
72
  def serialize_collection(collection)
73
- serialized_data = SpreeCmCommissioner::V2::Storefront::TripQueryResultSerializer.new(
73
+ serialized_data = Spree::V2::Tenant::TripQueryResultSerializer.new(
74
74
  collection,
75
75
  include: default_resource_includes,
76
76
  params: serializer_params
@@ -3,27 +3,27 @@ module Spree
3
3
  module V2
4
4
  module Tenant
5
5
  class TripsController < BaseController
6
- def show
7
- resource = scope.find(params[:id])
6
+ private
8
7
 
9
- render_serialized_payload do
10
- serialize_resource(resource)
11
- end
8
+ # override
9
+ def resource
10
+ scope.find(params[:id])
12
11
  end
13
12
 
14
- private
15
-
13
+ # override
16
14
  def scope
17
15
  model_class.joins(:vendor)
18
16
  .where(spree_vendors: { tenant_id: @tenant.id })
19
17
  end
20
18
 
19
+ # override
21
20
  def model_class
22
21
  SpreeCmCommissioner::Trip
23
22
  end
24
23
 
24
+ # override
25
25
  def resource_serializer
26
- SpreeCmCommissioner::V2::Storefront::TripSerializer
26
+ Spree::V2::Tenant::TripSerializer
27
27
  end
28
28
  end
29
29
  end
@@ -19,6 +19,7 @@
19
19
  # Name: Vaneath Awesome
20
20
  # Tel: <code>+85570760548</code>
21
21
  # Email: <code>admin_dev@cm.com</code>
22
+ # Delivery Address: #123, St. 456, Phnom Penh
22
23
 
23
24
  module SpreeCmCommissioner
24
25
  class OrderTelegramMessageFactory < TelegramMessageFactory
@@ -79,6 +80,7 @@ module SpreeCmCommissioner
79
80
  text << "Name: #{order.name}"
80
81
  text << "Tel: #{inline_code(order.intel_phone_number || order.phone_number)}"
81
82
  text << "Email: #{inline_code(order.email)}" if order.email.present?
83
+ text << "Delivery Address: #{formatted_shipping_address.presence || 'N/A'}" if order.delivery_required?
82
84
 
83
85
  if show_details_link && order.guests.any?
84
86
  text << ''
@@ -89,6 +91,16 @@ module SpreeCmCommissioner
89
91
  text.compact.join("\n")
90
92
  end
91
93
 
94
+ def formatted_shipping_address
95
+ return 'N/A' if order.shipping_address.blank?
96
+
97
+ @formatted_shipping_address ||= [
98
+ order.shipping_address.address1,
99
+ order.shipping_address.address2,
100
+ order.shipping_address.state&.name
101
+ ].compact.compact_blank.join(', ')
102
+ end
103
+
92
104
  # Result:
93
105
  # | No. A24 | No. A24 |
94
106
  # | No. A24 | No. A24 |
@@ -0,0 +1,43 @@
1
+ # Finds places that are used in routes (as origin or destination) by keyword search.
2
+ #
3
+ # @param keyword [String] Search term to match against place names (case-insensitive)
4
+ # @param place_type [String] Type of route place: 'origin' or 'destination'
5
+ #
6
+ # @return [ActiveRecord::Relation<SpreeCmCommissioner::Place>] Places matching the keyword
7
+ # that are used as origins or destinations in existing routes. Returns empty relation if
8
+ # keyword is blank or place_type is invalid.
9
+ #
10
+ # @note For default/empty state display, use SpreeCmCommissioner::Routes::FindPopular instead
11
+ # to show popular routes to users before they enter a search term.
12
+ #
13
+ # @example
14
+ # finder = SpreeCmCommissioner::Places::FindWithRoute.new(keyword: 'Phnom', place_type: 'origin')
15
+ # finder.execute # => Returns places with "Phnom" in name that are used as route origins
16
+ module SpreeCmCommissioner
17
+ module Places
18
+ class FindWithRoute
19
+ attr_reader :keyword, :place_type
20
+
21
+ def initialize(keyword:, place_type:)
22
+ @keyword = keyword
23
+ @place_type = place_type
24
+ end
25
+
26
+ def execute
27
+ return SpreeCmCommissioner::Place.none if keyword.blank? || place_type.blank?
28
+
29
+ scope.where('cm_places.name ILIKE ?', "%#{keyword}%")
30
+ end
31
+
32
+ private
33
+
34
+ def scope
35
+ if place_type == 'origin'
36
+ SpreeCmCommissioner::Place.with_routes_as_origin
37
+ else
38
+ SpreeCmCommissioner::Place.with_routes_as_destination
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # Finds routes ordered by popularity based on order metrics.
2
+ #
3
+ # Routes are sorted by:
4
+ # 1. fulfilled_order_count (completed orders) - DESC
5
+ # 2. order_count (total orders including incomplete) - DESC
6
+ #
7
+ # @return [ActiveRecord::Relation<SpreeCmCommissioner::Route>] Routes with origin and destination places loaded,
8
+ # ordered from most to least popular
9
+ #
10
+ # @example
11
+ # finder = SpreeCmCommissioner::Routes::FindPopular.new
12
+ # finder.execute # => Returns routes sorted by fulfillment and order counts
13
+
14
+ module SpreeCmCommissioner
15
+ module Routes
16
+ class FindPopular
17
+ def execute
18
+ SpreeCmCommissioner::Route
19
+ .includes(:origin_place, :destination_place)
20
+ .order(Arel.sql('COALESCE(fulfilled_order_count, 0) DESC'))
21
+ .order(Arel.sql('COALESCE(order_count, 0) DESC'))
22
+ end
23
+ end
24
+ end
25
+ end
@@ -12,14 +12,20 @@ module Spree
12
12
  return false if @order.email.blank?
13
13
 
14
14
  setup_tenant_and_store
15
+ if @tenant.present?
16
+ @name = @order.tenant.name
17
+ @logo = @order.tenant.active_vendor&.logo&.original_url
18
+ @brand_color = @order.tenant.preferred_brand_primary_color
19
+ end
15
20
  @product_type = @order.products.first&.product_type || 'accommodation'
16
21
 
17
22
  subject = build_subject(resend)
18
23
 
19
24
  mail(to: @order.email,
20
- from: from_email_address, subject: subject,
25
+ from: from_email_address,
26
+ subject: subject,
21
27
  store_url: store_url
22
- ) do |format|
28
+ ) do |format|
23
29
  format.html { render layout: 'spree_cm_commissioner/layouts/order_mailer' }
24
30
  format.text
25
31
  end
@@ -38,10 +44,6 @@ module Spree
38
44
  def setup_tenant_and_store
39
45
  @tenant = @order.tenant
40
46
  @current_store = @order.store
41
- return if @tenant.blank?
42
-
43
- @brand_color = @tenant.preferences[:brand_primary_color]
44
- @vendor_logo_url = @tenant.active_vendor&.logo&.original_url
45
47
  end
46
48
 
47
49
  def build_subject(resend)