spree_cm_commissioner 2.3.2 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +52 -11
- data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +63 -0
- data/app/controllers/spree/api/v2/storefront/route_places_controller.rb +90 -0
- data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +81 -0
- data/app/controllers/spree/api/v2/tenant/trip_places_controller.rb +42 -0
- data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +103 -0
- data/app/controllers/spree/api/v2/tenant/trips_controller.rb +32 -0
- data/app/factory/spree_cm_commissioner/order_telegram_message_factory.rb +12 -0
- data/app/finders/spree_cm_commissioner/places/find_with_route.rb +50 -0
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +25 -0
- data/app/mailers/spree/order_mailer_decorator.rb +8 -6
- data/app/models/concerns/spree_cm_commissioner/line_item_transitable.rb +34 -89
- data/app/models/concerns/spree_cm_commissioner/service_recommendations.rb +2 -2
- data/app/models/spree_cm_commissioner/adjustment_decorator.rb +1 -1
- data/app/models/spree_cm_commissioner/calculators/extra_drop_off_distance.rb +15 -0
- data/app/models/spree_cm_commissioner/calculators/extra_pick_up_distance.rb +15 -0
- data/app/models/spree_cm_commissioner/order_decorator.rb +1 -1
- data/app/models/spree_cm_commissioner/place.rb +7 -0
- data/app/models/spree_cm_commissioner/pricing_action.rb +27 -0
- data/app/models/spree_cm_commissioner/pricing_model.rb +18 -0
- data/app/models/spree_cm_commissioner/pricing_model_variant.rb +6 -0
- data/app/models/spree_cm_commissioner/pricing_rule.rb +15 -0
- data/app/models/spree_cm_commissioner/pricing_rule_group.rb +20 -0
- data/app/models/spree_cm_commissioner/pricing_rules/extra_drop_off_distance.rb +10 -0
- data/app/models/spree_cm_commissioner/pricing_rules/extra_pick_up_distance.rb +12 -0
- data/app/models/spree_cm_commissioner/pricing_rules/nationality.rb +35 -0
- data/app/models/spree_cm_commissioner/tenant.rb +4 -0
- data/app/models/spree_cm_commissioner/variant_decorator.rb +3 -0
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/vendor_place.rb +9 -0
- data/app/queries/spree_cm_commissioner/trip_query.rb +23 -17
- data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_schema.rb +10 -0
- data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +29 -0
- data/app/request_schemas/spree_cm_commissioner/route_places_request_schema.rb +13 -0
- data/app/serializers/spree/v2/storefront/place_serializer.rb +3 -2
- data/app/serializers/spree/v2/tenant/cart_serializer.rb +1 -0
- data/app/serializers/spree/v2/tenant/intercity_taxi_cart_serializer.rb +12 -0
- data/app/serializers/spree/v2/tenant/line_item_serializer.rb +1 -0
- data/app/serializers/spree/v2/tenant/trip_place_serializer.rb +8 -0
- data/app/serializers/spree/v2/tenant/trip_query_result_serializer.rb +8 -0
- data/app/serializers/spree/v2/tenant/trip_serializer.rb +8 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb +17 -18
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +12 -0
- data/app/services/spree_cm_commissioner/intercity_taxi_order/create.rb +20 -23
- data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +36 -72
- data/app/services/spree_cm_commissioner/pricing_models/apply.rb +51 -0
- data/app/services/spree_cm_commissioner/pricing_rules/build_params.rb +53 -0
- data/app/services/spree_cm_commissioner/pricing_rules/build_template.rb +27 -0
- data/app/services/spree_cm_commissioner/pricing_rules/create.rb +10 -0
- data/app/services/spree_cm_commissioner/pricing_rules/update.rb +11 -0
- data/app/services/spree_cm_commissioner/vendor_places/base.rb +24 -0
- data/app/services/spree_cm_commissioner/vendor_places/bulk_create.rb +70 -0
- data/app/services/spree_cm_commissioner/vendor_places/create_with_google_map_data.rb +85 -0
- data/app/services/spree_cm_commissioner/vendor_places/update.rb +56 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
- data/app/views/spree_cm_commissioner/order_mailer/_mailer_stylesheets.html.erb +14 -13
- data/app/views/spree_cm_commissioner/order_mailer/tenant/_greeting.html.erb +6 -4
- data/config/routes.rb +11 -1
- data/db/migrate/20250915075830_create_spree_cm_commissioner_pricing_models.rb +17 -0
- data/db/migrate/20250915080733_create_spree_cm_commissioner_pricing_model_variants.rb +14 -0
- data/db/migrate/20251003082144_create_spree_cm_commissioner_pricing_rule_groups.rb +16 -0
- data/db/migrate/20251003093408_create_spree_cm_commissioner_pricing_rule.rb +19 -0
- data/db/migrate/20251003093643_create_spree_cm_commissioner_pricing_action.rb +14 -0
- data/db/migrate/20251119093000_add_tenant_id_to_cm_vendor_places.rb +8 -0
- data/lib/spree_cm_commissioner/intercity_taxi/distance.rb +38 -0
- data/lib/spree_cm_commissioner/intercity_taxi/map_place.rb +31 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_action_factory.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_factory.rb +8 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_factory.rb +17 -0
- data/lib/spree_cm_commissioner/test_helper/factories/pricing_rule_group_factory.rb +8 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +2 -0
- metadata +49 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e32e4116a234b679abcfe3a9d7005bd61890d80936839c32330ecf2b421dadf
|
|
4
|
+
data.tar.gz: a7759f377e09d49281150257a496e44af61e4cabb67a8bec0bacb20a73ea439d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5119cedb570e77e3417bf723ea84926231e4d72000686312a194c1cfb4764061b5df9801923a5db629d8b8adccfd8a70378a238a0c099af46abe8f5c6a2da1d0
|
|
7
|
+
data.tar.gz: adeee4909603c0294be64345ea3de5b310e2dcaa174669137794b56dd23b800ddd9a1f429da636df23953d80154a3b96835cb19a81274fc0908edaae1ebd13d9
|
data/Gemfile.lock
CHANGED
|
@@ -4,29 +4,70 @@ module Spree
|
|
|
4
4
|
module Storefront
|
|
5
5
|
module IntercityTaxi
|
|
6
6
|
class DraftOrdersController < ::Spree::Api::V2::ResourceController
|
|
7
|
+
include Spree::Api::V2::Storefront::OrderConcern
|
|
8
|
+
include SpreeCmCommissioner::OrderConcern
|
|
9
|
+
|
|
10
|
+
before_action :ensure_order, only: :update
|
|
11
|
+
|
|
7
12
|
def create
|
|
8
|
-
|
|
13
|
+
result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
|
|
9
14
|
trip_id: params[:trip_id],
|
|
10
15
|
from_date: params[:from_date],
|
|
11
16
|
to_date: params[:to_date],
|
|
12
|
-
|
|
17
|
+
user: spree_current_user,
|
|
13
18
|
quantity: params[:quantity]
|
|
14
19
|
)
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
if result.success?
|
|
22
|
+
render_serialized_payload { serialize_resource(result.value) }
|
|
23
|
+
else
|
|
24
|
+
render_error_payload(result.error)
|
|
25
|
+
end
|
|
19
26
|
end
|
|
20
27
|
|
|
21
28
|
def update
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
|
|
30
|
+
order: spree_current_order,
|
|
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
|
+
)
|
|
25
54
|
)
|
|
26
55
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
56
|
+
if result.success?
|
|
57
|
+
render_serialized_payload { serialize_resource(result.value) }
|
|
58
|
+
else
|
|
59
|
+
render_error_payload(result.error)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def required_schema
|
|
66
|
+
if action_name == 'create'
|
|
67
|
+
SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
|
|
68
|
+
else
|
|
69
|
+
SpreeCmCommissioner::IntercityTaxiDraftOrderUpdateSchema
|
|
70
|
+
end
|
|
30
71
|
end
|
|
31
72
|
|
|
32
73
|
def resource_serializer
|
|
@@ -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,90 @@
|
|
|
1
|
+
# API endpoint for searching places connected to a specific place via routes.
|
|
2
|
+
#
|
|
3
|
+
# GET /api/v2/storefront/route_places
|
|
4
|
+
#
|
|
5
|
+
# Finds places (origins or destinations) that are connected to a given place through existing routes.
|
|
6
|
+
# Optionally filters results by keyword. Supports two modes:
|
|
7
|
+
#
|
|
8
|
+
# Usage 1: Connected places (requires place_id)
|
|
9
|
+
# - Finds places connected to a specific place via routes
|
|
10
|
+
# - Returns origins/destinations that have routes with the specified place
|
|
11
|
+
#
|
|
12
|
+
# Usage 2: Keyword search (requires query, place_id optional)
|
|
13
|
+
# - Searches all route places by keyword
|
|
14
|
+
# - Returns all origins/destinations matching the keyword
|
|
15
|
+
#
|
|
16
|
+
# Query Parameters:
|
|
17
|
+
# - place_type: [String] Required. Type of route place: 'origin' or 'destination'
|
|
18
|
+
# * 'origin': returns origins that have routes TO the specified place (if place_id provided)
|
|
19
|
+
# * 'destination': returns destinations that have routes FROM the specified place (if place_id provided)
|
|
20
|
+
# - place_id: [Integer] Optional. The place ID to find connected places for
|
|
21
|
+
# - query: [String] Optional. Keyword to filter place names (case-insensitive)
|
|
22
|
+
# - include: Optional comma-separated list (e.g., 'vendors,nearby_places')
|
|
23
|
+
#
|
|
24
|
+
# Response: Collection of places serialized with PlaceSerializer
|
|
25
|
+
#
|
|
26
|
+
# Behavior:
|
|
27
|
+
# - Returns empty collection if place_type is invalid
|
|
28
|
+
# - If place_id provided: returns places connected to that place
|
|
29
|
+
# - If place_id blank: returns all origins/destinations
|
|
30
|
+
# - Filters results by keyword if provided
|
|
31
|
+
#
|
|
32
|
+
# @example Mode 1: Find destinations connected to origin place ID 123
|
|
33
|
+
# GET /api/v2/storefront/route_places?place_id=123&place_type=destination
|
|
34
|
+
#
|
|
35
|
+
# @example Mode 2: Search all destination places by keyword
|
|
36
|
+
# GET /api/v2/storefront/route_places?place_type=destination&query=Phnom
|
|
37
|
+
#
|
|
38
|
+
# @example Combined: Find origins connected to place 456, filtered by keyword
|
|
39
|
+
# GET /api/v2/storefront/route_places?place_id=456&place_type=origin&query=Siem
|
|
40
|
+
module Spree
|
|
41
|
+
module Api
|
|
42
|
+
module V2
|
|
43
|
+
module Storefront
|
|
44
|
+
class RoutePlacesController < ::Spree::Api::V2::ResourceController
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# override
|
|
48
|
+
def collection
|
|
49
|
+
@collection ||= collection_finder.new(
|
|
50
|
+
place_type: params[:place_type],
|
|
51
|
+
place_id: params[:place_id],
|
|
52
|
+
keyword: params[:query]
|
|
53
|
+
).execute
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# override
|
|
57
|
+
def collection_finder
|
|
58
|
+
SpreeCmCommissioner::Places::FindWithRoute
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# override
|
|
62
|
+
def collection_serializer
|
|
63
|
+
Spree::V2::Storefront::PlaceSerializer
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# override
|
|
67
|
+
def required_schema
|
|
68
|
+
SpreeCmCommissioner::RoutePlacesRequestSchema
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# override
|
|
72
|
+
def serializer_params
|
|
73
|
+
super.merge(
|
|
74
|
+
include_vendors: include_vendors?,
|
|
75
|
+
include_nearby_places: include_nearby_places?
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def include_vendors?
|
|
80
|
+
resource_includes.include?(:vendors) || false
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def include_nearby_places?
|
|
84
|
+
resource_includes.include?(:nearby_places) || false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
module IntercityTaxi
|
|
6
|
+
class DraftOrdersController < BaseController
|
|
7
|
+
include Spree::Api::V2::Storefront::OrderConcern
|
|
8
|
+
include SpreeCmCommissioner::OrderConcern
|
|
9
|
+
|
|
10
|
+
before_action :ensure_order, only: :update
|
|
11
|
+
|
|
12
|
+
def create
|
|
13
|
+
result = SpreeCmCommissioner::IntercityTaxiOrder::Create.call(
|
|
14
|
+
trip_id: params[:trip_id],
|
|
15
|
+
from_date: params[:from_date],
|
|
16
|
+
to_date: params[:to_date],
|
|
17
|
+
quantity: params[:quantity],
|
|
18
|
+
user: spree_current_user
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if result.success?
|
|
22
|
+
render_serialized_payload { serialize_resource(result.value) }
|
|
23
|
+
else
|
|
24
|
+
render_error_payload(result.error)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def update
|
|
29
|
+
result = SpreeCmCommissioner::IntercityTaxiOrder::Update.call(
|
|
30
|
+
order: spree_current_order,
|
|
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
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if result.success?
|
|
57
|
+
render_serialized_payload { serialize_resource(result.value) }
|
|
58
|
+
else
|
|
59
|
+
render_error_payload(result.error)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def required_schema
|
|
66
|
+
if action_name == 'create'
|
|
67
|
+
SpreeCmCommissioner::IntercityTaxiDraftOrderSchema
|
|
68
|
+
else
|
|
69
|
+
SpreeCmCommissioner::IntercityTaxiDraftOrderUpdateSchema
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def resource_serializer
|
|
74
|
+
Spree::V2::Tenant::IntercityTaxiCartSerializer
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Tenant
|
|
5
|
+
class TripPlacesController < BaseController
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# override
|
|
9
|
+
def collection
|
|
10
|
+
@collection ||= begin
|
|
11
|
+
base_scope = scope
|
|
12
|
+
|
|
13
|
+
if params[:q].present?
|
|
14
|
+
@search = base_scope.ransack(params[:q])
|
|
15
|
+
@search.result
|
|
16
|
+
else
|
|
17
|
+
base_scope
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# override
|
|
23
|
+
def scope
|
|
24
|
+
model_class.joins(:location_vendor_places)
|
|
25
|
+
.where(location_vendor_places: { tenant_id: @tenant.id })
|
|
26
|
+
.distinct
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# override
|
|
30
|
+
def model_class
|
|
31
|
+
SpreeCmCommissioner::Place
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# override
|
|
35
|
+
def collection_serializer
|
|
36
|
+
Spree::V2::Tenant::TripPlaceSerializer
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
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 = Spree::V2::Tenant::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
|
+
private
|
|
7
|
+
|
|
8
|
+
# override
|
|
9
|
+
def resource
|
|
10
|
+
scope.find(params[:id])
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# override
|
|
14
|
+
def scope
|
|
15
|
+
model_class.joins(:vendor)
|
|
16
|
+
.where(spree_vendors: { tenant_id: @tenant.id })
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# override
|
|
20
|
+
def model_class
|
|
21
|
+
SpreeCmCommissioner::Trip
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# override
|
|
25
|
+
def resource_serializer
|
|
26
|
+
Spree::V2::Tenant::TripSerializer
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
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,50 @@
|
|
|
1
|
+
# Finds places connected via routes with optional filtering.
|
|
2
|
+
#
|
|
3
|
+
# @param place_type [String] Required. 'origin' or 'destination'
|
|
4
|
+
# @param place_id [Integer] Optional. Filter by connected place ID
|
|
5
|
+
# @param keyword [String] Optional. Filter by place name (case-insensitive)
|
|
6
|
+
#
|
|
7
|
+
# @return [ActiveRecord::Relation<SpreeCmCommissioner::Place>]
|
|
8
|
+
#
|
|
9
|
+
# Modes:
|
|
10
|
+
# - place_id only: returns connected places (origins TO or destinations FROM given place)
|
|
11
|
+
# - keyword only: filters by name
|
|
12
|
+
# - both: connected places filtered by name
|
|
13
|
+
# - neither: all origins/destinations in routes
|
|
14
|
+
#
|
|
15
|
+
# @example Origins with routes to place 123
|
|
16
|
+
# FindWithRoute.new(place_type: 'origin', place_id: 123).execute
|
|
17
|
+
#
|
|
18
|
+
# @example Destinations filtered by keyword
|
|
19
|
+
# FindWithRoute.new(place_type: 'destination', keyword: 'Phnom').execute
|
|
20
|
+
module SpreeCmCommissioner
|
|
21
|
+
module Places
|
|
22
|
+
class FindWithRoute
|
|
23
|
+
attr_reader :place_type, :place_id, :keyword
|
|
24
|
+
|
|
25
|
+
def initialize(place_type:, place_id: nil, keyword: nil)
|
|
26
|
+
@place_type = place_type
|
|
27
|
+
@place_id = place_id
|
|
28
|
+
@keyword = keyword
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def execute
|
|
32
|
+
return SpreeCmCommissioner::Place.none if place_type.blank?
|
|
33
|
+
|
|
34
|
+
result = if place_type == 'origin'
|
|
35
|
+
scope = SpreeCmCommissioner::Place.with_routes_as_origin
|
|
36
|
+
place_id.present? ? scope.where(cm_routes: { destination_place_id: place_id }) : scope
|
|
37
|
+
else
|
|
38
|
+
scope = SpreeCmCommissioner::Place.with_routes_as_destination
|
|
39
|
+
place_id.present? ? scope.where(cm_routes: { origin_place_id: place_id }) : scope
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return SpreeCmCommissioner::Place.none if place_id.present? && !SpreeCmCommissioner::Place.exists?(place_id)
|
|
43
|
+
|
|
44
|
+
# Apply keyword filter if provided
|
|
45
|
+
result = result.where('cm_places.name ILIKE ?', "%#{keyword}%") if keyword.present?
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
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,
|
|
25
|
+
from: from_email_address,
|
|
26
|
+
subject: subject,
|
|
21
27
|
store_url: store_url
|
|
22
|
-
|
|
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)
|