spree_cm_commissioner 2.5.1 → 2.5.2.pre.pre1
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/.github/workflows/test_and_build_gem.yml +2 -2
- data/Gemfile.lock +1 -1
- data/app/controllers/spree/admin/inventory_items_controller.rb +36 -56
- data/app/controllers/spree/admin/stock_managements_controller.rb +14 -3
- data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +2 -2
- data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +4 -4
- data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +60 -0
- data/app/controllers/spree/api/v2/tenant/routes_controller.rb +50 -0
- data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +46 -0
- data/app/controllers/spree/transit/trips_controller.rb +3 -3
- data/app/finders/spree_cm_commissioner/places/find_with_route.rb +12 -12
- data/app/finders/spree_cm_commissioner/route_metrics/find_popular.rb +44 -0
- data/app/finders/spree_cm_commissioner/routes/find.rb +94 -0
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +19 -35
- data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +11 -4
- data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +10 -1
- data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +4 -3
- data/app/interactors/spree_cm_commissioner/trip_stops_creator.rb +2 -2
- data/app/jobs/spree_cm_commissioner/route_metrics/decrease_trip_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_fulfilled_order_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_order_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/route_metrics/increase_trip_count_job.rb +10 -0
- data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +2 -2
- data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +2 -2
- data/app/models/spree_cm_commissioner/place.rb +5 -8
- data/app/models/spree_cm_commissioner/product_decorator.rb +1 -0
- data/app/models/spree_cm_commissioner/route.rb +45 -5
- data/app/models/spree_cm_commissioner/route_metric.rb +21 -0
- data/app/models/spree_cm_commissioner/route_photo.rb +12 -0
- data/app/models/spree_cm_commissioner/trip.rb +8 -33
- data/app/models/spree_cm_commissioner/trip_stop.rb +16 -2
- data/app/models/spree_cm_commissioner/vendor_decorator.rb +3 -1
- data/app/queries/spree_cm_commissioner/trip_query.rb +2 -2
- data/app/serializers/spree/v2/tenant/transit_cart_serializer.rb +11 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_metric_serializer.rb +13 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +2 -1
- data/app/serializers/spree_cm_commissioner/v2/storefront/transit_line_item_serializer.rb +17 -0
- data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -1
- data/app/services/spree_cm_commissioner/route_metrics/decrease_trip_count.rb +31 -0
- data/app/services/spree_cm_commissioner/{routes/increment_fulfilled_order_count.rb → route_metrics/increase_fulfilled_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/{routes/increment_order_count.rb → route_metrics/increase_order_count.rb} +3 -3
- data/app/services/spree_cm_commissioner/route_metrics/increase_trip_count.rb +31 -0
- data/app/services/spree_cm_commissioner/{routes/base_update_order_metrics.rb → route_metrics/update_route_metrics.rb} +11 -16
- data/app/services/spree_cm_commissioner/routes/create.rb +51 -0
- data/app/services/spree_cm_commissioner/routes/update.rb +25 -0
- data/app/{interactors/spree_cm_commissioner/transit/draft_order_creator.rb → services/spree_cm_commissioner/transit_order/create.rb} +13 -16
- data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +123 -0
- data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +54 -0
- data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +88 -0
- data/app/services/spree_cm_commissioner/trips/variants/create.rb +103 -0
- data/app/views/spree/admin/inventory_items/prices.html.erb +45 -0
- data/app/views/spree/admin/inventory_items/stocks.html.erb +36 -0
- data/app/views/spree/admin/stock_managements/calendar.html.erb +105 -12
- data/app/views/spree/admin/stock_managements/index.html.erb +9 -8
- data/app/views/spree/transit/trip_stops/index.html.erb +4 -2
- data/app/views/spree_cm_commissioner/guest_mailer/send_ticket_to_guest.html.erb +0 -1
- data/config/initializers/spree_permitted_attributes.rb +11 -0
- data/config/routes.rb +12 -7
- data/db/migrate/20251224033103_migrate_cm_routes_to_cm_route_metrics.rb +17 -0
- data/db/migrate/20251224033910_migrate_cm_vendor_routes_to_cm_routes.rb +30 -0
- data/db/migrate/20260105072450_migrate_cm_trip_stops_to_support_trip_connection.rb +12 -0
- data/db/migrate/20260108101406_add_allow_booking_to_cm_trips.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +7 -6
- data/lib/spree_cm_commissioner/test_helper/factories/route_metric_factory.rb +12 -0
- data/lib/spree_cm_commissioner/test_helper/factories/route_photo_factory.rb +5 -0
- data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +4 -1
- data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +3 -1
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +2 -0
- data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +22 -0
- data/lib/spree_cm_commissioner/transit/route_stop.rb +61 -0
- data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +175 -0
- data/lib/spree_cm_commissioner/transit/trip_form.rb +81 -0
- data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +65 -0
- data/lib/spree_cm_commissioner/version.rb +1 -1
- data/lib/spree_cm_commissioner.rb +4 -0
- metadata +42 -21
- data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +0 -13
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +0 -10
- data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +0 -10
- data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +0 -48
- data/app/models/spree_cm_commissioner/trip_connection.rb +0 -36
- data/app/models/spree_cm_commissioner/vendor_route.rb +0 -9
- data/app/services/spree_cm_commissioner/routes/decrement_previous_trip_count.rb +0 -30
- data/app/services/spree_cm_commissioner/routes/decrement_trip_count.rb +0 -33
- data/app/services/spree_cm_commissioner/routes/increment_trip_count.rb +0 -33
- data/app/views/spree/admin/inventory_items/show.html.erb +0 -72
- data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +0 -6
|
@@ -1,46 +1,30 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
|
|
8
|
-
#
|
|
9
|
-
# @return [ActiveRecord::Relation<SpreeCmCommissioner::Route>] Routes with origin and destination places loaded,
|
|
10
|
-
# ordered from most to least popular
|
|
11
|
-
#
|
|
12
|
-
# @example Get all popular routes
|
|
13
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
14
|
-
# finder.execute # => Returns all routes sorted by fulfillment and order counts
|
|
15
|
-
#
|
|
16
|
-
# @example Get popular ferry routes
|
|
17
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
18
|
-
# finder.execute(route_type: :ferry) # => Returns ferry routes sorted by popularity
|
|
19
|
-
#
|
|
20
|
-
# @example Get popular bus routes
|
|
21
|
-
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
22
|
-
# finder.execute(route_type: :bus) # => Returns bus routes sorted by popularity
|
|
1
|
+
# Finder to get popular route metrics scoped to a vendor or tenant.
|
|
2
|
+
# Orders by fulfilled_order_count DESC, then order_count DESC.
|
|
3
|
+
# Usage:
|
|
4
|
+
# finder = SpreeCmCommissioner::Routes::FindPopular.new(vendor: vendor, route_type: :bus, limit: 10)
|
|
5
|
+
# finder.execute
|
|
23
6
|
|
|
24
7
|
module SpreeCmCommissioner
|
|
25
8
|
module Routes
|
|
26
9
|
class FindPopular
|
|
27
|
-
def
|
|
28
|
-
|
|
10
|
+
def initialize(vendor: nil, tenant: nil, route_type: nil, limit: 20)
|
|
11
|
+
@vendor = vendor
|
|
12
|
+
@tenant = tenant
|
|
13
|
+
@route_type = route_type
|
|
14
|
+
@limit = limit.to_i
|
|
29
15
|
end
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
result = result.joins(:trips).where(cm_trips: { route_type: route_type.to_sym }) if route_type.present?
|
|
17
|
+
def execute
|
|
18
|
+
scope = SpreeCmCommissioner::Route.includes(:origin_place, :destination_place, :route_photos)
|
|
19
|
+
scope = scope.where(vendor_id: vendor.id) if vendor.present?
|
|
20
|
+
scope = scope.where(tenant_id: tenant.id) if tenant.present?
|
|
21
|
+
scope = scope.where(route_type: route_type.to_s) if route_type.present?
|
|
22
|
+
scope.order(fulfilled_order_count: :desc, order_count: :desc).limit(limit)
|
|
23
|
+
end
|
|
39
24
|
|
|
40
|
-
|
|
25
|
+
private
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
end
|
|
27
|
+
attr_reader :vendor, :tenant, :route_type, :limit
|
|
44
28
|
end
|
|
45
29
|
end
|
|
46
30
|
end
|
|
@@ -33,11 +33,18 @@ module SpreeCmCommissioner
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Returns dates for which inventory should be generated, based on calendar or default period.
|
|
36
37
|
def inventory_dates_for(variant)
|
|
37
|
-
|
|
38
|
-
end_date = Time.zone.today + pre_inventory_days_for(variant)
|
|
38
|
+
calendar = variant.product&.service_calendar
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
if calendar.present?
|
|
41
|
+
calendar.start_date.upto(calendar.end_date).select { |date| calendar.service_available?(date) }
|
|
42
|
+
else
|
|
43
|
+
start_date = Time.zone.tomorrow
|
|
44
|
+
end_date = Time.zone.today + pre_inventory_days_for(variant)
|
|
45
|
+
|
|
46
|
+
start_date.upto(end_date).to_a
|
|
47
|
+
end
|
|
41
48
|
end
|
|
42
49
|
|
|
43
50
|
def inventory_exist?(variant, inventory_date)
|
|
@@ -68,7 +75,7 @@ module SpreeCmCommissioner
|
|
|
68
75
|
def variants
|
|
69
76
|
scope = Spree::Variant.active.with_permanent_stock.where(is_master: false)
|
|
70
77
|
scope = scope.where(id: variant_ids) if variant_ids.present?
|
|
71
|
-
scope
|
|
78
|
+
scope.includes(product: :service_calendar)
|
|
72
79
|
end
|
|
73
80
|
end
|
|
74
81
|
end
|
|
@@ -8,7 +8,8 @@ module SpreeCmCommissioner
|
|
|
8
8
|
|
|
9
9
|
return context.fail!(message: Spree.t(:doesnt_track_inventory)) unless variant.track_inventory?
|
|
10
10
|
|
|
11
|
-
stock_location =
|
|
11
|
+
stock_location = resolve_stock_location(variant, stock_location_id)
|
|
12
|
+
|
|
12
13
|
stock_movement = stock_location.stock_movements.build(stock_movement_params)
|
|
13
14
|
stock_movement.stock_item = stock_location.set_up_stock_item(variant)
|
|
14
15
|
|
|
@@ -22,6 +23,14 @@ module SpreeCmCommissioner
|
|
|
22
23
|
|
|
23
24
|
private
|
|
24
25
|
|
|
26
|
+
def resolve_stock_location(variant, stock_location_id = nil)
|
|
27
|
+
if stock_location_id.present?
|
|
28
|
+
Spree::StockLocation.find(stock_location_id)
|
|
29
|
+
else
|
|
30
|
+
variant.vendor.stock_locations.first || variant.vendor.send(:create_stock_location)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
25
34
|
def adjust_inventory_items_async(variant_id, quantity)
|
|
26
35
|
args = { variant_id: variant_id, quantity: quantity }
|
|
27
36
|
CmAppLogger.log(label: "#{self.class.name}#adjust_inventory_items_async", data: args) do
|
|
@@ -91,9 +91,10 @@ module SpreeCmCommissioner
|
|
|
91
91
|
stop_params = params.dig(:trip_stops_attributes, index.to_s)
|
|
92
92
|
return unless stop_params
|
|
93
93
|
|
|
94
|
-
cloned_stop.departure_time
|
|
95
|
-
cloned_stop.arrival_time
|
|
96
|
-
cloned_stop.
|
|
94
|
+
cloned_stop.departure_time = parse_time(stop_params[:departure_time])
|
|
95
|
+
cloned_stop.arrival_time = parse_time(stop_params[:arrival_time])
|
|
96
|
+
cloned_stop.allow_boarding = stop_params[:allow_boarding] if stop_params.key?(:allow_boarding)
|
|
97
|
+
cloned_stop.allow_drop_off = stop_params[:allow_drop_off] if stop_params.key?(:allow_drop_off)
|
|
97
98
|
cloned_stop.location_place_id = stop_params[:location_place_id].presence
|
|
98
99
|
end
|
|
99
100
|
|
|
@@ -26,7 +26,7 @@ module SpreeCmCommissioner
|
|
|
26
26
|
trip: trip,
|
|
27
27
|
stop_place: origin_place,
|
|
28
28
|
location_place: origin_place,
|
|
29
|
-
|
|
29
|
+
allow_boarding: true
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
attributes[:departure_time] = departure_time
|
|
@@ -42,7 +42,7 @@ module SpreeCmCommissioner
|
|
|
42
42
|
trip: trip,
|
|
43
43
|
stop_place: destination_place,
|
|
44
44
|
location_place: destination_place,
|
|
45
|
-
|
|
45
|
+
allow_drop_off: true
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
attributes[:arrival_time] = departure_time + duration_seconds.seconds
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class DecreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
trip = SpreeCmCommissioner::Trip.find(options[:trip_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::DecreaseTripCount.call(trip: trip)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseFulfilledOrderCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
order = Spree::Order.find(options[:order_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseFulfilledOrderCount.call(order: order)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseOrderCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
order = Spree::Order.find(options[:order_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseOrderCount.call(order: order)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseTripCountJob < ApplicationUniqueJob
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
trip = SpreeCmCommissioner::Trip.find(options[:trip_id])
|
|
6
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseTripCount.call(trip: trip)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
module Stock
|
|
3
3
|
class PermanentInventoryItemsGeneratorJob < ApplicationUniqueJob
|
|
4
|
-
def perform
|
|
5
|
-
SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call
|
|
4
|
+
def perform(options = {})
|
|
5
|
+
SpreeCmCommissioner::Stock::PermanentInventoryItemsGenerator.call(options)
|
|
6
6
|
end
|
|
7
7
|
end
|
|
8
8
|
end
|
|
@@ -7,13 +7,13 @@ module SpreeCmCommissioner
|
|
|
7
7
|
def increment_route_fulfilled_order_count
|
|
8
8
|
return unless has_trip_ids?
|
|
9
9
|
|
|
10
|
-
SpreeCmCommissioner::
|
|
10
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseFulfilledOrderCountJob.perform_later(order_id: id)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def increment_route_order_count
|
|
14
14
|
return unless has_trip_ids?
|
|
15
15
|
|
|
16
|
-
SpreeCmCommissioner::
|
|
16
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseOrderCountJob.perform_later(order_id: id)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -21,15 +21,12 @@ module SpreeCmCommissioner
|
|
|
21
21
|
has_many :taxon_places, class_name: 'SpreeCmCommissioner::TaxonPlace', dependent: :destroy
|
|
22
22
|
has_many :taxons, through: :taxon_places
|
|
23
23
|
|
|
24
|
-
has_many :
|
|
25
|
-
has_many :
|
|
24
|
+
has_many :route_metrics_as_origin, class_name: 'SpreeCmCommissioner::RouteMetric', foreign_key: :origin_place_id
|
|
25
|
+
has_many :route_metrics_as_destination, class_name: 'SpreeCmCommissioner::RouteMetric', foreign_key: :destination_place_id
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# Scopes for route-based filtering
|
|
31
|
-
scope :with_routes_as_origin, -> { joins(:routes_as_origin).distinct }
|
|
32
|
-
scope :with_routes_as_destination, -> { joins(:routes_as_destination).distinct }
|
|
27
|
+
# Scopes for route metric filtering
|
|
28
|
+
scope :with_route_metrics_as_origin, -> { joins(:route_metrics_as_origin).distinct }
|
|
29
|
+
scope :with_route_metrics_as_destination, -> { joins(:route_metrics_as_destination).distinct }
|
|
33
30
|
|
|
34
31
|
def self.ransackable_attributes(auth_object = nil)
|
|
35
32
|
super & %w[name code]
|
|
@@ -48,6 +48,7 @@ module SpreeCmCommissioner
|
|
|
48
48
|
base.accepts_nested_attributes_for :product_places, allow_destroy: true
|
|
49
49
|
|
|
50
50
|
base.has_one :trip, class_name: 'SpreeCmCommissioner::Trip', dependent: :destroy
|
|
51
|
+
base.has_one :service_calendar, as: :calendarable, class_name: 'SpreeCmCommissioner::ServiceCalendar', dependent: :destroy
|
|
51
52
|
|
|
52
53
|
base.belongs_to :event, class_name: 'Spree::Taxon', optional: true
|
|
53
54
|
|
|
@@ -1,12 +1,52 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class Route < Base
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
include SpreeCmCommissioner::RouteType
|
|
4
|
+
|
|
5
|
+
belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
|
|
6
|
+
belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant', optional: true
|
|
7
|
+
|
|
8
|
+
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
9
|
+
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
10
|
+
|
|
11
|
+
has_one :primary_photo, -> { order(position: :asc) }, class_name: 'SpreeCmCommissioner::RoutePhoto',
|
|
12
|
+
as: :viewable,
|
|
13
|
+
dependent: :destroy
|
|
14
|
+
has_many :route_photos, class_name: 'SpreeCmCommissioner::RoutePhoto', as: :viewable, dependent: :destroy
|
|
15
|
+
|
|
5
16
|
has_many :trips, inverse_of: :route
|
|
6
17
|
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
validates :route_name, presence: true
|
|
19
|
+
|
|
20
|
+
validate :validate_route_stops
|
|
21
|
+
|
|
22
|
+
delegate :multi_leg?, to: :route_stops
|
|
23
|
+
|
|
24
|
+
def self.ransackable_attributes(_auth_object = nil)
|
|
25
|
+
%w[route_type short_name route_name]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def route_stops
|
|
29
|
+
Transit::RouteStopCollection.from_hash(read_attribute(:route_stops))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def route_stops=(value)
|
|
33
|
+
if value.is_a?(Transit::RouteStopCollection)
|
|
34
|
+
@route_stops = value
|
|
35
|
+
self[:route_stops] = value.to_h
|
|
36
|
+
elsif value.is_a?(Array)
|
|
37
|
+
@route_stops = Transit::RouteStopCollection.from_hash(value)
|
|
38
|
+
self[:route_stops] = @route_stops.to_h
|
|
39
|
+
else
|
|
40
|
+
raise ArgumentError, 'route_stops must be an Array or RouteStopCollection'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def validate_route_stops
|
|
47
|
+
return if route_stops.valid?
|
|
9
48
|
|
|
10
|
-
|
|
49
|
+
route_stops.errors.each { |error| errors.add(:route_stops, error) }
|
|
50
|
+
end
|
|
11
51
|
end
|
|
12
52
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
class RouteMetric < Base
|
|
3
|
+
include SpreeCmCommissioner::RouteType
|
|
4
|
+
|
|
5
|
+
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
6
|
+
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
7
|
+
|
|
8
|
+
validates :route_type, presence: true
|
|
9
|
+
validates :route_type, uniqueness: { scope: %i[origin_place_id destination_place_id] }
|
|
10
|
+
|
|
11
|
+
validate :origin_and_destination_cannot_be_the_same
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def origin_and_destination_cannot_be_the_same
|
|
16
|
+
return unless origin_place_id == destination_place_id
|
|
17
|
+
|
|
18
|
+
errors.add(:base, 'Origin and destination cannot be the same')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -2,7 +2,6 @@ module SpreeCmCommissioner
|
|
|
2
2
|
class Trip < Base
|
|
3
3
|
include SpreeCmCommissioner::StoreMetadata
|
|
4
4
|
include SpreeCmCommissioner::RouteType
|
|
5
|
-
include SpreeCmCommissioner::RouteTripCountCallbacks
|
|
6
5
|
|
|
7
6
|
attr_accessor :hours, :minutes, :seconds
|
|
8
7
|
|
|
@@ -15,7 +14,9 @@ module SpreeCmCommissioner
|
|
|
15
14
|
|
|
16
15
|
# before create, duplicate seat layout from vehicle_type if present
|
|
17
16
|
before_validation :duplicate_seat_layout_from_vehicle, if: -> { seat_layout.nil? && vehicle_type&.seat_layout.present? }
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
after_create :increment_route_metric_trip_count
|
|
19
|
+
before_destroy :decrement_route_metric_trip_count
|
|
19
20
|
|
|
20
21
|
belongs_to :vendor, class_name: 'Spree::Vendor', inverse_of: :trips, optional: false
|
|
21
22
|
belongs_to :product, class_name: 'Spree::Product', inverse_of: :trip, optional: false
|
|
@@ -26,7 +27,7 @@ module SpreeCmCommissioner
|
|
|
26
27
|
|
|
27
28
|
belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
28
29
|
belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
29
|
-
belongs_to :route, inverse_of: :trips
|
|
30
|
+
belongs_to :route, class_name: 'SpreeCmCommissioner::Route', inverse_of: :trips, optional: true
|
|
30
31
|
|
|
31
32
|
has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
|
|
32
33
|
has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
|
|
@@ -48,10 +49,6 @@ module SpreeCmCommissioner
|
|
|
48
49
|
self.whitelisted_ransackable_associations = %w[product vehicle vehicle_type]
|
|
49
50
|
self.whitelisted_ransackable_attributes = %w[origin_place_id destination_place_id]
|
|
50
51
|
|
|
51
|
-
def changed_route_attributes?
|
|
52
|
-
origin_place_id_changed? || destination_place_id_changed? || vendor_id_changed?
|
|
53
|
-
end
|
|
54
|
-
|
|
55
52
|
def convert_duration_to_seconds
|
|
56
53
|
return if hours.blank? && minutes.blank? && seconds.blank?
|
|
57
54
|
|
|
@@ -104,34 +101,12 @@ module SpreeCmCommissioner
|
|
|
104
101
|
)
|
|
105
102
|
end
|
|
106
103
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# Returns true if any of the route context attributes will change on save.
|
|
110
|
-
will_save_change_to_route_id? ||
|
|
111
|
-
will_save_change_to_origin_place_id? ||
|
|
112
|
-
will_save_change_to_destination_place_id? ||
|
|
113
|
-
will_save_change_to_vendor_id?
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def changed_route_context?
|
|
117
|
-
saved_change_to_route_id? ||
|
|
118
|
-
saved_change_to_origin_place_id? ||
|
|
119
|
-
saved_change_to_destination_place_id? ||
|
|
120
|
-
saved_change_to_vendor_id?
|
|
104
|
+
def increment_route_metric_trip_count
|
|
105
|
+
SpreeCmCommissioner::RouteMetrics::IncreaseTripCountJob.perform_later(trip_id: id)
|
|
121
106
|
end
|
|
122
107
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# product when the route is new/blank.
|
|
126
|
-
def assign_route_and_vendor_route
|
|
127
|
-
route = SpreeCmCommissioner::Route.find_or_create_by!(
|
|
128
|
-
origin_place_id: origin_place_id,
|
|
129
|
-
destination_place_id: destination_place_id
|
|
130
|
-
)
|
|
131
|
-
route.route_name = product.name if route.route_name.blank?
|
|
132
|
-
route.save! if route.changed?
|
|
133
|
-
|
|
134
|
-
self.route_id = route.id
|
|
108
|
+
def decrement_route_metric_trip_count
|
|
109
|
+
SpreeCmCommissioner::RouteMetrics::DecreaseTripCountJob.perform_later(trip_id: id)
|
|
135
110
|
end
|
|
136
111
|
end
|
|
137
112
|
end
|
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
2
|
class TripStop < Base
|
|
3
3
|
acts_as_list column: :sequence, scope: :trip_id
|
|
4
|
-
enum :stop_type, { boarding: 0, drop_off: 1 }
|
|
5
4
|
|
|
6
5
|
belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: false
|
|
7
6
|
belongs_to :stop_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
8
7
|
belongs_to :location_place, class_name: 'SpreeCmCommissioner::Place', optional: false
|
|
9
8
|
|
|
9
|
+
belongs_to :board_to_trip, class_name: 'SpreeCmCommissioner::Trip', optional: true
|
|
10
|
+
|
|
10
11
|
before_validation :set_stop_name
|
|
12
|
+
|
|
11
13
|
validates :stop_place_id, uniqueness: { scope: :trip_id }
|
|
14
|
+
validate :boarding_trip_requires_allow_boarding
|
|
15
|
+
|
|
16
|
+
scope :boarding, -> { where(allow_boarding: true) }
|
|
17
|
+
scope :drop_off, -> { where(allow_drop_off: true) }
|
|
12
18
|
|
|
13
19
|
def set_stop_name
|
|
14
20
|
self.stop_name = stop_place.name if stop_place.present?
|
|
15
21
|
end
|
|
16
22
|
|
|
17
23
|
def self.ransackable_attributes(_auth_object = nil)
|
|
18
|
-
%w[stop_name
|
|
24
|
+
%w[stop_name allow_boarding allow_drop_off]
|
|
19
25
|
end
|
|
20
26
|
|
|
21
27
|
def self.ransackable_associations(_auth_object = nil)
|
|
22
28
|
['stop_place']
|
|
23
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def boarding_trip_requires_allow_boarding
|
|
34
|
+
return unless board_to_trip_id.present? && !allow_boarding?
|
|
35
|
+
|
|
36
|
+
errors.add(:board_to_trip, 'requires allow_boarding to be true')
|
|
37
|
+
end
|
|
24
38
|
end
|
|
25
39
|
end
|
|
@@ -82,7 +82,9 @@ module SpreeCmCommissioner
|
|
|
82
82
|
base.has_many :subscriptions, through: :customers, class_name: 'SpreeCmCommissioner::Subscription'
|
|
83
83
|
base.has_many :subscription_orders, through: :subscriptions, class_name: 'Spree::Order', source: :orders
|
|
84
84
|
base.has_many :promotion_categories, class_name: 'Spree::PromotionCategory', foreign_key: :vendor_id, dependent: :destroy
|
|
85
|
-
base.has_many :
|
|
85
|
+
base.has_many :routes, class_name: 'SpreeCmCommissioner::Route', dependent: :destroy
|
|
86
|
+
base.has_many :popular_routes, -> { order(fulfilled_order_count: :desc, order_count: :desc) },
|
|
87
|
+
class_name: 'SpreeCmCommissioner::Route'
|
|
86
88
|
|
|
87
89
|
base.has_many :homepage_section_relatables,
|
|
88
90
|
as: :relatable,
|
|
@@ -77,8 +77,8 @@ module SpreeCmCommissioner
|
|
|
77
77
|
|
|
78
78
|
scope
|
|
79
79
|
.joins(<<~SQL.squish)
|
|
80
|
-
INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = cm_trips.id AND boarding.
|
|
81
|
-
INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.
|
|
80
|
+
INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = cm_trips.id AND boarding.allow_boarding = true
|
|
81
|
+
INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.allow_drop_off = true
|
|
82
82
|
INNER JOIN cm_places AS origin_places ON origin_places.id = cm_trips.origin_place_id
|
|
83
83
|
INNER JOIN cm_places AS dest_places ON dest_places.id = cm_trips.destination_place_id
|
|
84
84
|
INNER JOIN spree_variants AS master ON master.product_id = cm_trips.product_id AND master.is_master = true
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class RouteMetricSerializer < BaseSerializer
|
|
5
|
+
set_type :route
|
|
6
|
+
attributes :route_name
|
|
7
|
+
|
|
8
|
+
has_one :origin_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
9
|
+
has_one :destination_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -2,8 +2,9 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class RouteSerializer < BaseSerializer
|
|
5
|
-
attributes :route_name
|
|
5
|
+
attributes :route_name, :route_type, :short_name
|
|
6
6
|
|
|
7
|
+
has_many :route_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer
|
|
7
8
|
has_one :origin_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
8
9
|
has_one :destination_place, serializer: ::Spree::V2::Storefront::PlaceSerializer
|
|
9
10
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class TransitLineItemSerializer < BaseSerializer
|
|
5
|
+
# Nested-only API shape
|
|
6
|
+
attributes :from_date,
|
|
7
|
+
:to_date,
|
|
8
|
+
:trip_id,
|
|
9
|
+
:direction,
|
|
10
|
+
:boarding_trip_stop_id,
|
|
11
|
+
:drop_off_trip_stop_id
|
|
12
|
+
|
|
13
|
+
cache_options store: nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module V2
|
|
3
3
|
module Storefront
|
|
4
4
|
class TripStopSerializer < BaseSerializer
|
|
5
|
-
attributes :id, :
|
|
5
|
+
attributes :id, :allow_boarding, :allow_drop_off, :stop_name, :arrival_time, :departure_time
|
|
6
6
|
|
|
7
7
|
has_one :stop_place, serializer: ::SpreeCmCommissioner::V2::Storefront::TripPlaceSerializer
|
|
8
8
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module SpreeCmCommissioner
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class DecreaseTripCount
|
|
4
|
+
prepend ::Spree::ServiceModule::Base
|
|
5
|
+
|
|
6
|
+
def call(trip:)
|
|
7
|
+
return failure(nil, 'Trip not found') unless trip
|
|
8
|
+
|
|
9
|
+
route_metric = find_or_create_route_metric(trip)
|
|
10
|
+
|
|
11
|
+
route_metric.with_lock do
|
|
12
|
+
route_metric.update!(trip_count: [route_metric.trip_count - 1, 0].max)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
success(trip: trip)
|
|
16
|
+
rescue StandardError => e
|
|
17
|
+
failure(nil, e.message)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def find_or_create_route_metric(trip)
|
|
23
|
+
SpreeCmCommissioner::RouteMetric.find_or_create_by!(
|
|
24
|
+
origin_place_id: trip.origin_place_id,
|
|
25
|
+
destination_place_id: trip.destination_place_id,
|
|
26
|
+
route_type: trip.route_type
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
-
module
|
|
3
|
-
class
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseFulfilledOrderCount
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
6
|
def call(order:)
|
|
7
7
|
return failure(nil, 'Order not found') unless order
|
|
8
8
|
|
|
9
|
-
SpreeCmCommissioner::
|
|
9
|
+
SpreeCmCommissioner::RouteMetrics::UpdateRouteMetrics
|
|
10
10
|
.call(order: order, attribute: :fulfilled_order_count)
|
|
11
11
|
|
|
12
12
|
success(order: order)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module SpreeCmCommissioner
|
|
2
|
-
module
|
|
3
|
-
class
|
|
2
|
+
module RouteMetrics
|
|
3
|
+
class IncreaseOrderCount
|
|
4
4
|
prepend ::Spree::ServiceModule::Base
|
|
5
5
|
|
|
6
6
|
def call(order:)
|
|
7
7
|
return failure(nil, 'Order not found') unless order
|
|
8
8
|
|
|
9
|
-
SpreeCmCommissioner::
|
|
9
|
+
SpreeCmCommissioner::RouteMetrics::UpdateRouteMetrics
|
|
10
10
|
.new(order: order, attribute: :order_count)
|
|
11
11
|
.call
|
|
12
12
|
|