spree_cm_commissioner 2.3.2 → 2.4.0
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 +68 -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 +43 -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 +12 -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
data/app/serializers/spree_cm_commissioner/v2/storefront/intercity_taxi_line_item_serializer.rb
CHANGED
|
@@ -2,29 +2,28 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class IntercityTaxiLineItemSerializer < BaseSerializer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
:
|
|
8
|
-
:
|
|
9
|
-
:drop_off_lat,
|
|
10
|
-
:drop_off_lng,
|
|
11
|
-
:passenger_count,
|
|
12
|
-
:distance_km,
|
|
13
|
-
:ordered_points,
|
|
14
|
-
:base_km,
|
|
15
|
-
:detour_pickup_km,
|
|
16
|
-
:detour_dropoff_km,
|
|
17
|
-
:extra_pickup_km,
|
|
18
|
-
:extra_dropoff_km,
|
|
19
|
-
:extra_pickup_charge_usd,
|
|
20
|
-
:extra_dropoff_charge_usd,
|
|
21
|
-
:estimated_time_minutes,
|
|
5
|
+
# Nested-only API shape
|
|
6
|
+
attributes :pickup_map_place,
|
|
7
|
+
:dropoff_map_place,
|
|
8
|
+
:distance,
|
|
22
9
|
:from_date,
|
|
23
10
|
:to_date,
|
|
24
|
-
:
|
|
11
|
+
:remark,
|
|
25
12
|
:trip_id
|
|
26
13
|
|
|
27
14
|
cache_options store: nil
|
|
15
|
+
|
|
16
|
+
def pickup_map_place
|
|
17
|
+
object.pickup_map_place&.to_h
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def dropoff_map_place
|
|
21
|
+
object.dropoff_map_place&.to_h
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def distance
|
|
25
|
+
object.distance&.to_h
|
|
26
|
+
end
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class RouteSerializer < BaseSerializer
|
|
5
|
+
attributes :route_name
|
|
6
|
+
|
|
7
|
+
has_one :origin_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
8
|
+
has_one :destination_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -1,35 +1,19 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module IntercityTaxiOrder
|
|
3
3
|
class Create
|
|
4
|
-
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
|
-
def
|
|
7
|
-
|
|
8
|
-
trip_id: trip_id,
|
|
9
|
-
from_date: from_date,
|
|
10
|
-
to_date: to_date,
|
|
11
|
-
user_id: user_id,
|
|
12
|
-
quantity: quantity
|
|
13
|
-
).call
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def initialize(trip_id:, from_date:, to_date:, user_id:, quantity:)
|
|
17
|
-
@trip_id = trip_id
|
|
18
|
-
@from_date = from_date
|
|
19
|
-
@to_date = to_date
|
|
20
|
-
@user_id = user_id
|
|
21
|
-
@quantity = quantity.to_i <= 0 ? 1 : quantity.to_i
|
|
22
|
-
end
|
|
6
|
+
def call(trip_id:, from_date:, to_date:, user: nil, quantity: 1)
|
|
7
|
+
validate!(trip_id: trip_id, from_date: from_date, to_date: to_date, quantity: quantity)
|
|
23
8
|
|
|
24
|
-
|
|
25
|
-
validate!
|
|
9
|
+
quantity = quantity.to_i
|
|
26
10
|
|
|
27
|
-
ActiveRecord::Base.transaction do
|
|
11
|
+
order = ActiveRecord::Base.transaction do
|
|
28
12
|
trip = SpreeCmCommissioner::Trip.find(trip_id)
|
|
29
13
|
variant = trip.product.variants.first
|
|
30
14
|
raise ArgumentError, 'No variant found for trip' if variant.blank?
|
|
31
15
|
|
|
32
|
-
order = Spree::Order.new(state: 'cart', use_billing: true,
|
|
16
|
+
order = Spree::Order.new(state: 'cart', use_billing: true, user: user)
|
|
33
17
|
|
|
34
18
|
line_item = order.line_items.new(
|
|
35
19
|
variant_id: variant.id,
|
|
@@ -42,26 +26,39 @@ module SpreeCmCommissioner
|
|
|
42
26
|
)
|
|
43
27
|
|
|
44
28
|
build_guests_for!(line_item, quantity)
|
|
29
|
+
assign_saved_guests_to_line_item(line_item)
|
|
45
30
|
|
|
46
31
|
order.save!
|
|
47
32
|
order.update_with_updater!
|
|
48
33
|
|
|
49
34
|
order
|
|
50
35
|
end
|
|
36
|
+
|
|
37
|
+
success(order)
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
failure(nil, e.message)
|
|
51
40
|
end
|
|
52
41
|
|
|
53
42
|
private
|
|
54
43
|
|
|
55
|
-
def validate!
|
|
44
|
+
def validate!(trip_id:, from_date:, to_date:, quantity:)
|
|
56
45
|
raise ArgumentError, 'trip_id is required' if trip_id.blank?
|
|
57
46
|
raise ArgumentError, 'from_date is required' if from_date.blank?
|
|
58
47
|
raise ArgumentError, 'to_date is required' if to_date.blank?
|
|
59
48
|
raise ArgumentError, 'to_date must be after from_date' if from_date.present? && to_date.present? && to_date < from_date
|
|
49
|
+
raise ArgumentError, 'quantity must be 1' if quantity.to_i != 1
|
|
60
50
|
end
|
|
61
51
|
|
|
62
52
|
def build_guests_for!(line_item, qty)
|
|
63
53
|
Array.new(qty) { line_item.guests.new }
|
|
64
54
|
end
|
|
55
|
+
|
|
56
|
+
def assign_saved_guests_to_line_item(line_item)
|
|
57
|
+
line_item.guests.each do |guest|
|
|
58
|
+
saved_guest = SpreeCmCommissioner::SavedGuest.new
|
|
59
|
+
guest.saved_guest = saved_guest
|
|
60
|
+
end
|
|
61
|
+
end
|
|
65
62
|
end
|
|
66
63
|
end
|
|
67
64
|
end
|
|
@@ -1,78 +1,42 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module IntercityTaxiOrder
|
|
3
3
|
class Update
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
:intel_phone_number,
|
|
41
|
-
:email,
|
|
42
|
-
line_items_attributes: [
|
|
43
|
-
:id,
|
|
44
|
-
:pickup_place_name,
|
|
45
|
-
:drop_off_place_name,
|
|
46
|
-
:from_date,
|
|
47
|
-
:pickup_lat,
|
|
48
|
-
:pickup_lng,
|
|
49
|
-
:pickup_oob_confirmed,
|
|
50
|
-
:drop_off_lat,
|
|
51
|
-
:drop_off_lng,
|
|
52
|
-
:drop_off_oob_confirmed,
|
|
53
|
-
:distance_km,
|
|
54
|
-
:ordered_points,
|
|
55
|
-
:base_km,
|
|
56
|
-
:detour_pickup_km,
|
|
57
|
-
:detour_dropoff_km,
|
|
58
|
-
:extra_pickup_km,
|
|
59
|
-
:extra_dropoff_km,
|
|
60
|
-
:extra_pickup_charge_usd,
|
|
61
|
-
:extra_dropoff_charge_usd,
|
|
62
|
-
:estimated_time_minutes,
|
|
63
|
-
:remark,
|
|
64
|
-
:passenger_count,
|
|
65
|
-
{ guests_attributes: %i[
|
|
66
|
-
id
|
|
67
|
-
first_name
|
|
68
|
-
last_name
|
|
69
|
-
gender
|
|
70
|
-
nationality_id
|
|
71
|
-
age
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
]
|
|
75
|
-
)
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(
|
|
7
|
+
order:,
|
|
8
|
+
remark: nil,
|
|
9
|
+
pickup_map_place_attributes: nil,
|
|
10
|
+
dropoff_map_place_attributes: nil,
|
|
11
|
+
distance_attributes: nil
|
|
12
|
+
)
|
|
13
|
+
line_item = order.line_items.first
|
|
14
|
+
|
|
15
|
+
raise ArgumentError, 'Order has no line items' if line_item.blank?
|
|
16
|
+
|
|
17
|
+
if pickup_map_place_attributes.present?
|
|
18
|
+
line_item.pickup_map_place = SpreeCmCommissioner::IntercityTaxi::MapPlace.new(
|
|
19
|
+
pickup_map_place_attributes
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if dropoff_map_place_attributes.present?
|
|
24
|
+
line_item.dropoff_map_place = SpreeCmCommissioner::IntercityTaxi::MapPlace.new(
|
|
25
|
+
dropoff_map_place_attributes
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if distance_attributes.present?
|
|
30
|
+
line_item.distance = SpreeCmCommissioner::IntercityTaxi::Distance.new(
|
|
31
|
+
distance_attributes
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
line_item.update!(remark: remark)
|
|
36
|
+
|
|
37
|
+
success(order.reload)
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
failure(nil, e.message)
|
|
76
40
|
end
|
|
77
41
|
end
|
|
78
42
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingModels
|
|
3
|
+
class Apply
|
|
4
|
+
def initialize(line_item_id:, pricing_model_id:)
|
|
5
|
+
@line_item_id = line_item_id
|
|
6
|
+
@pricing_model_id = pricing_model_id
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call
|
|
10
|
+
pricing_model.pricing_rule_groups.each do |group|
|
|
11
|
+
apply_group(group, line_item)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
attr_reader :line_item_id, :pricing_model_id
|
|
18
|
+
|
|
19
|
+
def line_item
|
|
20
|
+
@line_item ||= Spree::LineItem.find(line_item_id)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def pricing_model
|
|
24
|
+
@pricing_model ||= SpreeCmCommissioner::PricingModel.find(pricing_model_id)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def apply_group(group, line_item)
|
|
28
|
+
eligible_rules = group.pricing_rules.select { |r| r.eligible?(line_item) }
|
|
29
|
+
|
|
30
|
+
return if eligible_rules.empty?
|
|
31
|
+
|
|
32
|
+
if group_eligible?(group, eligible_rules)
|
|
33
|
+
group.pricing_action&.perform(line_item)
|
|
34
|
+
else
|
|
35
|
+
Rails.logger.info("Group #{group.id} not eligible for line_item #{line_item.id}")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def group_eligible?(group, eligible_rules)
|
|
40
|
+
case group.match_type
|
|
41
|
+
when 'all'
|
|
42
|
+
eligible_rules.size == group.pricing_rules.size
|
|
43
|
+
when 'any'
|
|
44
|
+
eligible_rules.any?
|
|
45
|
+
else
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingRules
|
|
3
|
+
module BuildParams
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
def permitted_group_params(params)
|
|
7
|
+
group_params = params[:spree_cm_commissioner_pricing_rule_group] || params
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
name: group_params[:name],
|
|
11
|
+
status: group_params[:status],
|
|
12
|
+
template_key: group_params[:template_key],
|
|
13
|
+
match_type: group_params[:match_type],
|
|
14
|
+
pricing_rules_attributes: build_rules_attributes(params[:pricing_rules]),
|
|
15
|
+
pricing_action_attributes: build_action_attributes(params[:pricing_action])
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def build_rules_attributes(rules_array)
|
|
20
|
+
return [] unless rules_array
|
|
21
|
+
|
|
22
|
+
rules_array.map do |r|
|
|
23
|
+
{
|
|
24
|
+
id: r[:id],
|
|
25
|
+
type: r[:type],
|
|
26
|
+
private_metadata: r[:private_metadata],
|
|
27
|
+
_destroy: r[:_destroy]
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_action_attributes(action_data)
|
|
33
|
+
return nil unless action_data&.dig(:calculator)
|
|
34
|
+
|
|
35
|
+
calc_data = action_data[:calculator]
|
|
36
|
+
calculator = calc_data[:type].safe_constantize&.new
|
|
37
|
+
set_preferences(calculator, calc_data[:preferences]) if calculator
|
|
38
|
+
|
|
39
|
+
{ id: action_data[:id], calculator: calculator }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def set_preferences(calculator, preferences)
|
|
43
|
+
return unless preferences && calculator
|
|
44
|
+
|
|
45
|
+
prefs = preferences.respond_to?(:to_unsafe_h) ? preferences.to_unsafe_h : preferences
|
|
46
|
+
prefs.each do |key, value|
|
|
47
|
+
setter = "preferred_#{key}="
|
|
48
|
+
calculator.public_send(setter, value) if calculator.respond_to?(setter)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module PricingRules
|
|
3
|
+
class BuildTemplate
|
|
4
|
+
def self.call(pricing_model:, template_key:)
|
|
5
|
+
template = PRICING_RULE_GROUP_TEMPLATES[template_key.to_sym]
|
|
6
|
+
return unless template
|
|
7
|
+
|
|
8
|
+
group = pricing_model.pricing_rule_groups.build
|
|
9
|
+
|
|
10
|
+
template[:rules]&.each do |rule_data|
|
|
11
|
+
group.pricing_rules.where(type: rule_data[:type]).build(
|
|
12
|
+
type: rule_data[:type],
|
|
13
|
+
private_metadata: rule_data[:private_metadata] || {}
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if template[:action]
|
|
18
|
+
calculator = template[:action][:calculator_type].constantize.new
|
|
19
|
+
BuildParams.set_preferences(calculator, template[:action][:preferences])
|
|
20
|
+
group.build_pricing_action(calculator: calculator)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
group
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module VendorPlaces
|
|
3
|
+
class Base
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
ALLOWED_PLACE_TYPES = %w[venue stop branch location].freeze
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def validate_place_type!(place_type)
|
|
11
|
+
return I18n.t('controller.vendor_places.place_type_required', default: 'Place type is required') if place_type.nil?
|
|
12
|
+
|
|
13
|
+
unless ALLOWED_PLACE_TYPES.include?(place_type.to_s)
|
|
14
|
+
return I18n.t(
|
|
15
|
+
'controller.vendor_places.invalid_place_type',
|
|
16
|
+
default: "Place type must be one of: #{ALLOWED_PLACE_TYPES.join(', ')}"
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
nil
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Bulk creates vendor places of any valid type (e.g., :stop, :branch, :location) with validation and transaction support.
|
|
2
|
+
#
|
|
3
|
+
# Validates place_ids count and creates vendor places atomically.
|
|
4
|
+
# Rolls back all changes if any creation fails.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# result = SpreeCmCommissioner::VendorPlaces::BulkCreate.call(
|
|
8
|
+
# vendor: current_vendor,
|
|
9
|
+
# place_ids: [1, 2, 3],
|
|
10
|
+
# place_type: :location, # or :stop, :branch, etc.
|
|
11
|
+
# description: "Sample description",
|
|
12
|
+
# max_count: 10,
|
|
13
|
+
# location: parent_location # required for :stop and :branch
|
|
14
|
+
# )
|
|
15
|
+
#
|
|
16
|
+
# if result.success?
|
|
17
|
+
# created_vendor_places = result.value
|
|
18
|
+
# else
|
|
19
|
+
# error_message = result.error
|
|
20
|
+
|
|
21
|
+
module SpreeCmCommissioner
|
|
22
|
+
module VendorPlaces
|
|
23
|
+
class BulkCreate < Base
|
|
24
|
+
def call(vendor:, place_ids:, place_type:, description: nil, max_count: 10, location: nil) # rubocop:disable Metrics/ParameterLists
|
|
25
|
+
error_message = validate_inputs!(place_ids: place_ids, place_type: place_type, max_count: max_count, location: location)
|
|
26
|
+
return failure(nil, error_message) if error_message
|
|
27
|
+
|
|
28
|
+
created_vendor_places = []
|
|
29
|
+
|
|
30
|
+
ActiveRecord::Base.transaction do
|
|
31
|
+
place_ids.each do |place_id|
|
|
32
|
+
vendor_place = build_vendor_place(
|
|
33
|
+
vendor: vendor,
|
|
34
|
+
place_id: place_id,
|
|
35
|
+
place_type: place_type,
|
|
36
|
+
description: description,
|
|
37
|
+
location: location
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
vendor_place.save!
|
|
41
|
+
created_vendor_places << vendor_place
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
success(created_vendor_places)
|
|
46
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
47
|
+
failure(nil, e.record.errors.full_messages.to_sentence)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def validate_inputs!(place_ids:, place_type:, max_count:, location: nil)
|
|
53
|
+
validate_place_type!(place_type) ||
|
|
54
|
+
(place_ids.blank? && I18n.t('controller.vendor_places.place_ids_blank')) ||
|
|
55
|
+
(place_ids.size > max_count && I18n.t('controller.vendor_places.selection_limit', count: max_count)) ||
|
|
56
|
+
(requires_location?(place_type: place_type) && location.blank? && I18n.t('controller.vendor_places.location_required'))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def build_vendor_place(vendor:, place_id:, place_type:, description:, location: nil)
|
|
60
|
+
attrs = { vendor_id: vendor.id, place_type: place_type.to_sym, place_id: place_id, description: description }
|
|
61
|
+
attrs[:location_id] = location&.id if requires_location?(place_type: place_type)
|
|
62
|
+
SpreeCmCommissioner::VendorPlace.new(attrs)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def requires_location?(place_type:)
|
|
66
|
+
%i[stop branch].include?(place_type.to_sym)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Creates a vendor place from Google Map base64-encoded place data.
|
|
2
|
+
# Processes place data and creates both Place and VendorPlace records atomically.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# result = SpreeCmCommissioner::VendorPlaces::CreateWithGoogleMapData.call(
|
|
6
|
+
# vendor: current_vendor,
|
|
7
|
+
# place_type: 'stop',
|
|
8
|
+
# google_place_base_64_content: google_place_base_64_content,
|
|
9
|
+
# description: 'Main Warehouse',
|
|
10
|
+
# location: parent_vendor_location
|
|
11
|
+
# )
|
|
12
|
+
#
|
|
13
|
+
# if result.success?
|
|
14
|
+
# vendor_place = result.value
|
|
15
|
+
# else
|
|
16
|
+
# error_message = result.error
|
|
17
|
+
# end
|
|
18
|
+
|
|
19
|
+
module SpreeCmCommissioner
|
|
20
|
+
module VendorPlaces
|
|
21
|
+
class CreateWithGoogleMapData < Base
|
|
22
|
+
def call(vendor:, place_type:, google_place_base_64_content:, description: nil, location: nil)
|
|
23
|
+
error_message = validate_place_type!(place_type)
|
|
24
|
+
return failure(nil, error_message) if error_message.present?
|
|
25
|
+
|
|
26
|
+
# Only require location for stop and branch types
|
|
27
|
+
return failure(nil, I18n.t('controller.vendor_places.location_required')) if %w[stop branch].include?(place_type) && location.nil?
|
|
28
|
+
|
|
29
|
+
place = process_place(google_place_base_64_content: google_place_base_64_content)
|
|
30
|
+
return failure(nil, place) unless place.is_a?(SpreeCmCommissioner::Place)
|
|
31
|
+
|
|
32
|
+
vendor_place = create_vendor_place(vendor: vendor, place: place, place_type: place_type, description: description, location: location)
|
|
33
|
+
return failure(nil, vendor_place) if vendor_place.nil? || vendor_place.is_a?(String)
|
|
34
|
+
|
|
35
|
+
success(vendor_place)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def process_place(google_place_base_64_content:)
|
|
41
|
+
place = SpreeCmCommissioner::PlaceDecoder.process_place(google_place_base_64_content)
|
|
42
|
+
return I18n.t('controller.vendor_places.process_place_failed', default: 'Failed to process place') if place.nil? || !place.save
|
|
43
|
+
|
|
44
|
+
place
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
e.message
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create_vendor_place(vendor:, place:, place_type:, description:, location:)
|
|
50
|
+
error_message = nil
|
|
51
|
+
result = ActiveRecord::Base.transaction do
|
|
52
|
+
vendor_place = build_vendor_place(vendor: vendor, place: place, type: place_type, description: description, location: location)
|
|
53
|
+
unless vendor_place.save
|
|
54
|
+
error_message = vendor_place.errors.full_messages.to_sentence
|
|
55
|
+
raise ActiveRecord::Rollback
|
|
56
|
+
end
|
|
57
|
+
vendor_place
|
|
58
|
+
end
|
|
59
|
+
error_message || result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def build_vendor_place(vendor:, place:, type:, description: nil, location: nil)
|
|
63
|
+
case type
|
|
64
|
+
when 'stop'
|
|
65
|
+
vendor.stops.new(
|
|
66
|
+
place_id: place.id,
|
|
67
|
+
description: description,
|
|
68
|
+
location_id: location&.id
|
|
69
|
+
)
|
|
70
|
+
when 'branch'
|
|
71
|
+
vendor.branches.new(
|
|
72
|
+
place_id: place.id,
|
|
73
|
+
description: description,
|
|
74
|
+
location_id: location&.id
|
|
75
|
+
)
|
|
76
|
+
when 'location'
|
|
77
|
+
vendor.locations.new(
|
|
78
|
+
place_id: place.id,
|
|
79
|
+
description: description
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Updates all vendor places matching the same vendor, place_id, and location_id.
|
|
2
|
+
# Only updates description and location_id. Rolls back if any update fails.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# result = SpreeCmCommissioner::VendorPlaces::Update.call(
|
|
6
|
+
# vendor: current_vendor,
|
|
7
|
+
# vendor_place: existing_vendor_place,
|
|
8
|
+
# location_id: new_location_id,
|
|
9
|
+
# description: 'Updated description'
|
|
10
|
+
# )
|
|
11
|
+
#
|
|
12
|
+
# if result.success?
|
|
13
|
+
# updated_place = result.value
|
|
14
|
+
# else
|
|
15
|
+
# error_message = result.error
|
|
16
|
+
# end
|
|
17
|
+
module SpreeCmCommissioner
|
|
18
|
+
module VendorPlaces
|
|
19
|
+
class Update < Base
|
|
20
|
+
def call(vendor:, vendor_place:, description: nil, location_id: nil)
|
|
21
|
+
if vendor_place.blank?
|
|
22
|
+
return failure(
|
|
23
|
+
nil,
|
|
24
|
+
I18n.t('controller.vendor_places.vendor_place_not_found', default: 'Vendor place not found')
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ActiveRecord::Base.transaction do
|
|
29
|
+
related_places = SpreeCmCommissioner::VendorPlace.where(
|
|
30
|
+
vendor_id: vendor.id,
|
|
31
|
+
place_id: vendor_place.place_id,
|
|
32
|
+
location_id: vendor_place.location_id
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
update_attrs = {}
|
|
36
|
+
update_attrs[:description] = description unless description.nil?
|
|
37
|
+
update_attrs[:location_id] = location_id unless location_id.nil?
|
|
38
|
+
|
|
39
|
+
related_places.each do |vp|
|
|
40
|
+
unless vp.update(update_attrs)
|
|
41
|
+
error_message = vp.errors.full_messages.to_sentence
|
|
42
|
+
raise ActiveRecord::Rollback, error_message
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
vendor_place.reload
|
|
48
|
+
success(vendor_place)
|
|
49
|
+
rescue ActiveRecord::RecordInvalid => e
|
|
50
|
+
failure(nil, e.record.errors.full_messages.to_sentence)
|
|
51
|
+
rescue ActiveRecord::Rollback => e
|
|
52
|
+
failure(nil, e.message)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
<% else %>
|
|
18
18
|
<div class="f-fallback">
|
|
19
|
-
<%= render 'spree_cm_commissioner/order_mailer/tenant/greeting', order: @order %>
|
|
19
|
+
<%= render 'spree_cm_commissioner/order_mailer/tenant/greeting', order: @order, logo: @logo, name: @name%>
|
|
20
20
|
<div class="content">
|
|
21
21
|
<%= render 'spree_cm_commissioner/order_mailer/order_total', order: @order %>
|
|
22
22
|
<%= render 'spree_cm_commissioner/order_mailer/your_booking', order: @order, show_booking_header: true %>
|