spree_cm_commissioner 2.5.1.pre.pre5 → 2.5.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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test_and_build_gem.yml +2 -2
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +2 -2
  5. data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +4 -4
  6. data/app/controllers/spree/transit/trips_controller.rb +3 -3
  7. data/app/finders/spree_cm_commissioner/places/find_with_route.rb +12 -12
  8. data/app/finders/spree_cm_commissioner/routes/find_popular.rb +35 -19
  9. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +4 -11
  10. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +1 -10
  11. data/app/{services/spree_cm_commissioner/transit_order/create.rb → interactors/spree_cm_commissioner/transit/draft_order_creator.rb} +16 -13
  12. data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +3 -4
  13. data/app/interactors/spree_cm_commissioner/trip_stops_creator.rb +2 -2
  14. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +2 -2
  15. data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +10 -0
  16. data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +10 -0
  17. data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +13 -0
  18. data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +10 -0
  19. data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +10 -0
  20. data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +2 -2
  21. data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +48 -0
  22. data/app/models/spree_cm_commissioner/place.rb +8 -5
  23. data/app/models/spree_cm_commissioner/product_decorator.rb +0 -1
  24. data/app/models/spree_cm_commissioner/route.rb +5 -45
  25. data/app/models/spree_cm_commissioner/trip.rb +33 -8
  26. data/app/models/spree_cm_commissioner/trip_connection.rb +36 -0
  27. data/app/models/spree_cm_commissioner/trip_stop.rb +2 -16
  28. data/app/models/spree_cm_commissioner/vendor_decorator.rb +1 -3
  29. data/app/models/spree_cm_commissioner/vendor_route.rb +9 -0
  30. data/app/queries/spree_cm_commissioner/trip_query.rb +2 -2
  31. data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +1 -2
  32. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -1
  33. data/app/services/spree_cm_commissioner/{route_metrics/update_route_metrics.rb → routes/base_update_order_metrics.rb} +16 -11
  34. data/app/services/spree_cm_commissioner/routes/decrement_previous_trip_count.rb +30 -0
  35. data/app/services/spree_cm_commissioner/routes/decrement_trip_count.rb +33 -0
  36. data/app/services/spree_cm_commissioner/{route_metrics/increase_fulfilled_order_count.rb → routes/increment_fulfilled_order_count.rb} +3 -3
  37. data/app/services/spree_cm_commissioner/{route_metrics/increase_order_count.rb → routes/increment_order_count.rb} +3 -3
  38. data/app/services/spree_cm_commissioner/routes/increment_trip_count.rb +33 -0
  39. data/app/views/spree/transit/trip_stops/index.html.erb +2 -4
  40. data/app/views/spree_cm_commissioner/guest_mailer/send_ticket_to_guest.html.erb +1 -0
  41. data/config/initializers/spree_permitted_attributes.rb +0 -11
  42. data/config/routes.rb +0 -6
  43. data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +6 -7
  44. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +6 -0
  45. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +1 -4
  46. data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +1 -3
  47. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +0 -2
  48. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +0 -22
  49. data/lib/spree_cm_commissioner/version.rb +1 -1
  50. data/lib/spree_cm_commissioner.rb +0 -4
  51. metadata +19 -39
  52. data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +0 -60
  53. data/app/controllers/spree/api/v2/tenant/routes_controller.rb +0 -50
  54. data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +0 -46
  55. data/app/finders/spree_cm_commissioner/route_metrics/find_popular.rb +0 -44
  56. data/app/finders/spree_cm_commissioner/routes/find.rb +0 -94
  57. data/app/jobs/spree_cm_commissioner/route_metrics/decrease_trip_count_job.rb +0 -10
  58. data/app/jobs/spree_cm_commissioner/route_metrics/increase_fulfilled_order_count_job.rb +0 -10
  59. data/app/jobs/spree_cm_commissioner/route_metrics/increase_order_count_job.rb +0 -10
  60. data/app/jobs/spree_cm_commissioner/route_metrics/increase_trip_count_job.rb +0 -10
  61. data/app/models/spree_cm_commissioner/route_metric.rb +0 -21
  62. data/app/models/spree_cm_commissioner/route_photo.rb +0 -12
  63. data/app/serializers/spree/v2/tenant/transit_cart_serializer.rb +0 -11
  64. data/app/serializers/spree_cm_commissioner/v2/storefront/route_metric_serializer.rb +0 -13
  65. data/app/serializers/spree_cm_commissioner/v2/storefront/transit_line_item_serializer.rb +0 -17
  66. data/app/services/spree_cm_commissioner/route_metrics/decrease_trip_count.rb +0 -31
  67. data/app/services/spree_cm_commissioner/route_metrics/increase_trip_count.rb +0 -31
  68. data/app/services/spree_cm_commissioner/routes/create.rb +0 -51
  69. data/app/services/spree_cm_commissioner/routes/update.rb +0 -25
  70. data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +0 -128
  71. data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +0 -54
  72. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +0 -88
  73. data/app/services/spree_cm_commissioner/trips/variants/create.rb +0 -103
  74. data/db/migrate/20251224033103_migrate_cm_routes_to_cm_route_metrics.rb +0 -17
  75. data/db/migrate/20251224033910_migrate_cm_vendor_routes_to_cm_routes.rb +0 -30
  76. data/db/migrate/20260105072450_migrate_cm_trip_stops_to_support_trip_connection.rb +0 -12
  77. data/db/migrate/20260108101406_add_allow_booking_to_cm_trips.rb +0 -5
  78. data/lib/spree_cm_commissioner/test_helper/factories/route_metric_factory.rb +0 -12
  79. data/lib/spree_cm_commissioner/test_helper/factories/route_photo_factory.rb +0 -5
  80. data/lib/spree_cm_commissioner/transit/route_stop.rb +0 -61
  81. data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +0 -203
  82. data/lib/spree_cm_commissioner/transit/trip_form.rb +0 -105
  83. data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +0 -67
@@ -1,31 +0,0 @@
1
- module SpreeCmCommissioner
2
- module RouteMetrics
3
- class IncreaseTripCount
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)
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,51 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Routes
3
- class Create
4
- prepend ::Spree::ServiceModule::Base
5
-
6
- # @param vendor [Spree::Vendor]
7
- # @param route_params [Hash]
8
- # @param route_stops [SpreeCmCommissioner::Transit::RouteStopCollection]
9
- def call(vendor:, route_params:, route_stops:)
10
- # vendor_id, tenant_id, origin_place_id, destination_place_id are excluded and set automatically.
11
- attrs = ::Spree::PermittedAttributes.route_attributes
12
- .index_with { |attr_key| route_params[attr_key] }
13
- .except(:vendor_id, :tenant_id, :route_stops, :origin_place_id, :destination_place_id)
14
- .compact
15
-
16
- return failure(nil, 'Route stops are required') if route_stops.length < 2
17
-
18
- origin_place = find_place_for(route_stops, route_stops.first)
19
- destination_place = find_place_for(route_stops, route_stops.last)
20
-
21
- return failure(nil, 'Origin or destination place could not be determined') if origin_place.nil? || destination_place.nil?
22
- return failure(nil, 'Route with origin & destination already exists') if vendor.routes.exists?(
23
- origin_place_id: origin_place.id,
24
- destination_place_id: destination_place.id
25
- )
26
-
27
- attrs = attrs.merge(
28
- tenant_id: vendor.tenant_id,
29
- route_name: attrs[:route_name] || "#{origin_place.name} -> #{destination_place.name}",
30
- origin_place_id: origin_place.id,
31
- destination_place_id: destination_place.id,
32
- route_stops: route_stops
33
- )
34
-
35
- route = vendor.routes.new(attrs)
36
- if route.save
37
- success(route: route)
38
- else
39
- failure(route.errors)
40
- end
41
- end
42
-
43
- def find_place_for(route_stops, route_stop)
44
- return nil if route_stop.nil?
45
-
46
- @locations ||= SpreeCmCommissioner::VendorPlace.where(id: route_stops.map(&:location_id).uniq).index_by(&:id)
47
- @locations[route_stop.location_id]&.place
48
- end
49
- end
50
- end
51
- end
@@ -1,25 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Routes
3
- class Update
4
- prepend ::Spree::ServiceModule::Base
5
-
6
- def call(vendor:, route:, route_params:)
7
- # vendor_id, tenant_id, origin_place_id, destination_place_id are excluded and set automatically.
8
- # route_stops is not updateable.
9
- attrs = ::Spree::PermittedAttributes.route_attributes
10
- .index_with { |attr_key| route_params[attr_key] }
11
- .except(:vendor_id, :tenant_id, :route_stops, :origin_place_id, :destination_place_id)
12
- .compact
13
-
14
- route = vendor.routes.find_by(id: route.id)
15
- return failure(nil, :route_not_found) if route.nil?
16
-
17
- if route.update(attrs)
18
- success(route: route)
19
- else
20
- failure(route.errors)
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,128 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Trips
3
- # Service class responsible for creating a single-leg trip in the booking system.
4
- # Orchestrates the creation of product variant, trip record, stops, calendar, and inventory.
5
- class CreateSingleLeg
6
- prepend Spree::ServiceModule::Base
7
-
8
- # Main service method that validates the trip form and orchestrates creation.
9
- # Creates variant, trip, calendar, and inventory within a database transaction.
10
- # Returns success with created objects or failure with error message on exceptions.
11
- def call(vendor:, trip_form:)
12
- return failure(nil, trip_form.errors.full_messages.to_sentence) unless trip_form.valid_data?
13
-
14
- ApplicationRecord.transaction do
15
- variant = create_variant!(vendor, trip_form)
16
- trip = create_trip!(vendor, trip_form, variant)
17
- calendar = setup_calendar!(variant.product, trip_form.service_calendar)
18
- generate_inventory!(variant)
19
-
20
- success(trip: trip, variant: variant, calendar: calendar)
21
- rescue StandardError => e
22
- CmAppLogger.error(
23
- label: 'SpreeCmCommissioner::Trips::CreateSingleLeg#call',
24
- data: {
25
- error_class: e.class.name,
26
- error_message: e.message
27
- }
28
- )
29
- failure(nil, e.message)
30
- end
31
- end
32
-
33
- private
34
-
35
- # Creates a product variant for the trip using the vehicle type's seat capacity.
36
- def create_variant!(vendor, trip_form)
37
- first_stop = trip_form.trip_stops.first
38
- vehicle_type = SpreeCmCommissioner::VehicleType.find(first_stop.vehicle_type_id)
39
- capacity = vehicle_type.number_of_seats.to_i
40
-
41
- raise StandardError, 'Vehicle type must be selected' if vehicle_type.blank?
42
- raise StandardError, 'Vehicle type seat capacity must be greater than 0' if capacity <= 0
43
-
44
- result = SpreeCmCommissioner::Trips::Variants::Create.call(
45
- vendor: vendor,
46
- trip_form: trip_form,
47
- price: trip_form.price || 0.0,
48
- capacity: capacity
49
- )
50
-
51
- raise StandardError, result.error unless result.success?
52
-
53
- result.value[:variant]
54
- end
55
-
56
- # Creates the main trip record with details extracted from the trip form.
57
- # Associates the trip with vendor, product, vehicle, route, and time.
58
- def create_trip!(vendor, trip_form, variant)
59
- first_stop = trip_form.trip_stops.first
60
- last_stop = trip_form.trip_stops.last
61
-
62
- locations = vendor.locations.where(id: trip_form.trip_stops.map(&:location_id)).index_by(&:id)
63
-
64
- # vendor_places are either stops or branches
65
- vendor_places = SpreeCmCommissioner::VendorPlace
66
- .where(id: trip_form.trip_stops.map(&:stop_id), vendor: vendor)
67
- .index_by(&:id)
68
-
69
- trip = vendor.trips.create!(
70
- product: variant.product,
71
- vehicle_type: SpreeCmCommissioner::VehicleType.find_by(id: first_stop.vehicle_type_id),
72
- vehicle: SpreeCmCommissioner::Vehicle.find_by(id: first_stop.vehicle_id),
73
- origin_place_id: locations[first_stop.location_id.to_i].place_id,
74
- destination_place_id: locations[last_stop.location_id.to_i].place_id,
75
- route: vendor.routes.find(trip_form.route_id),
76
- departure_time: first_stop.departure_time,
77
- duration: trip_form.total_duration_seconds,
78
- allow_booking: trip_form.allow_booking,
79
- allow_seat_selection: trip_form.allow_seat_selection,
80
- route_type: trip_form.route_type || vendor.routes.find(trip_form.route_id)&.route_type
81
- )
82
-
83
- create_trip_stops(trip, trip_form, locations, vendor_places)
84
- trip
85
- end
86
-
87
- # Creates individual trip stops based on the trip form stops.
88
- # Each stop includes arrival/departure times and boarding/drop-off permissions.
89
- def create_trip_stops(trip, trip_form, locations, vendor_places)
90
- previous_departure = nil
91
- Array(trip_form.trip_stops).each do |ts|
92
- arrival_time = previous_departure || ts.departure_time
93
- trip.trip_stops.create!(
94
- stop_place: vendor_places[ts.stop_id.to_i].place,
95
- location_place: locations[ts.location_id.to_i].place,
96
- allow_boarding: ts.allow_boarding?,
97
- allow_drop_off: ts.allow_drop_off?,
98
- arrival_time: arrival_time,
99
- departure_time: ts.departure_time
100
- )
101
-
102
- previous_departure = ts.departure_time
103
- end
104
- end
105
-
106
- # Sets up the service calendar for the trip, defining operational dates, weekdays, and exceptions.
107
- def setup_calendar!(product, calendar_form)
108
- res = SpreeCmCommissioner::Trips::ServiceCalendars::CreateOrUpdate.call(
109
- calendarable: product,
110
- name: calendar_form.name,
111
- start_date: calendar_form.start_date,
112
- end_date: calendar_form.end_date,
113
- weekdays: calendar_form.weekdays,
114
- exception_rules: calendar_form.exception_rules || []
115
- )
116
-
117
- raise StandardError, res.error unless res.success?
118
-
119
- res.value[:calendar]
120
- end
121
-
122
- # Triggers asynchronous generation of permanent inventory items for the variant.
123
- def generate_inventory!(variant)
124
- SpreeCmCommissioner::Stock::PermanentInventoryItemsGeneratorJob.perform_later(variant_ids: [variant[:id]])
125
- end
126
- end
127
- end
128
- end
@@ -1,54 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Trips
3
- module ServiceCalendars
4
- # Service to create or update a service calendar for a calendarable entity.
5
- class CreateOrUpdate
6
- prepend ::Spree::ServiceModule::Base
7
-
8
- def call(calendarable:, name:, start_date:, end_date:, weekdays:, exception_rules: []) # rubocop:disable Metrics/ParameterLists
9
- return failure(nil, 'calendarable must be present') if calendarable.blank?
10
- return failure(nil, 'name must be present') if name.blank?
11
- return failure(nil, 'start_date must be present') if start_date.blank?
12
- return failure(nil, 'end_date must be present') if end_date.blank?
13
- return failure(nil, 'weekdays must be present') if weekdays.blank?
14
-
15
- ApplicationRecord.transaction do
16
- calendar = create_or_update_calendar(calendarable, name, start_date, end_date, weekdays, exception_rules)
17
- success(calendar: calendar)
18
- end
19
- rescue StandardError => e
20
- CmAppLogger.error(
21
- label: 'SpreeCmCommissioner::Trips::ServiceCalendars::CreateOrUpdate#call',
22
- data: {
23
- error_class: e.class.name,
24
- error_message: e.message
25
- }
26
- )
27
- failure(nil, 'Failed to create service calendar')
28
- end
29
-
30
- private
31
-
32
- # Create or update calendar
33
- def create_or_update_calendar(calendarable, name, start_date, end_date, weekdays, exception_rules) # rubocop:disable Metrics/ParameterLists
34
- calendar = calendarable.service_calendar || calendarable.build_service_calendar
35
-
36
- calendar.assign_attributes(
37
- name: name,
38
- start_date: start_date,
39
- end_date: end_date,
40
- exception_rules: exception_rules
41
- )
42
-
43
- # Set weekday attributes
44
- weekdays.each do |day, enabled|
45
- calendar.send("#{day}=", enabled)
46
- end
47
-
48
- calendar.save!
49
- calendar
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,88 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Trips
3
- # Service class responsible for updating an existing single-leg trip.
4
- # Handles updates to trip, product, variant, stops, and calendar in the booking system.
5
- class UpdateSingleLeg
6
- prepend Spree::ServiceModule::Base
7
-
8
- # Main service method that orchestrates trip updates within a database transaction.
9
- # Updates trip/product, variant if price provided, stops, and calendar conditionally.
10
- # Returns success with updated objects or failure with error message.
11
- def call(trip:, trip_form:)
12
- ApplicationRecord.transaction do
13
- update_trip_and_product(trip, trip_form)
14
- update_variant(trip, trip_form) if trip_form.price.present?
15
- update_trip_stops(trip, trip_form)
16
- calendar = update_calendar(trip.product, trip_form.service_calendar) if trip_form.service_calendar.present?
17
-
18
- success(trip: trip, variant: trip.product.variants.first, calendar: calendar)
19
- rescue StandardError => e
20
- CmAppLogger.error(
21
- label: 'SpreeCmCommissioner::Trips::UpdateSingleLeg#call',
22
- data: {
23
- error_class: e.class.name,
24
- error_message: e.message
25
- }
26
- )
27
- failure(nil, 'Failed to update trip')
28
- end
29
- end
30
-
31
- private
32
-
33
- # Updates the trip record and associated product with attributes from the form.
34
- def update_trip_and_product(trip, trip_form)
35
- trip.update!(trip_attrs(trip_form))
36
- trip.product.update!(product_attrs(trip_form))
37
- end
38
-
39
- # Updates the variant's price if a new price is provided in the form.
40
- def update_variant(trip, trip_form)
41
- variant = trip.product.variants.where.not(id: trip.product.master.id).first
42
- return unless variant
43
-
44
- variant.update!(price: trip_form.price)
45
- trip.product.update!(price: trip_form.price)
46
- end
47
-
48
- # Iterates through stops and applies updates only if attributes are present.
49
- def update_trip_stops(trip, trip_form)
50
- trip.trip_stops.each_with_index do |stop, idx|
51
- ts_form = trip_form.trip_stops[idx]
52
- next unless ts_form
53
-
54
- updates = {
55
- allow_boarding: ts_form.allow_boarding?,
56
- allow_drop_off: ts_form.allow_drop_off
57
- }.compact
58
-
59
- stop.update!(updates) if updates.any?
60
- end
61
- end
62
-
63
- # Updates or creates the service calendar for the product using calendar form data.
64
- def update_calendar(product, calendar_form)
65
- result = SpreeCmCommissioner::Trips::ServiceCalendars::CreateOrUpdate.call(
66
- calendarable: product,
67
- name: calendar_form.name,
68
- start_date: calendar_form.start_date,
69
- end_date: calendar_form.end_date,
70
- weekdays: calendar_form.weekdays,
71
- exception_rules: calendar_form.exception_rules || []
72
- )
73
-
74
- raise result.error unless result.success?
75
-
76
- result.value[:calendar]
77
- end
78
-
79
- def trip_attrs(form)
80
- { allow_booking: form.allow_booking, allow_seat_selection: form.allow_seat_selection }.compact
81
- end
82
-
83
- def product_attrs(form)
84
- { name: form.name, short_name: form.short_name }.compact
85
- end
86
- end
87
- end
88
- end
@@ -1,103 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Trips
3
- module Variants
4
- # Service class responsible for creating product and variant for a trip.
5
- # Handles validations, product creation with options, variant pricing, and stock setup.
6
- class Create
7
- prepend ::Spree::ServiceModule::Base
8
-
9
- # Main method that validates inputs and creates product, options, variant, and stock in transaction.
10
- # Returns success with product and variant or failure with error message.
11
- def call(vendor:, trip_form:, price:, capacity:)
12
- return failure(nil, 'vendor must be present') if vendor.blank?
13
- return failure(nil, 'trip_form must be present') if trip_form.blank?
14
- return failure(nil, 'price must be present') if price.blank?
15
- return failure(nil, 'capacity must be present') if capacity.blank?
16
- return failure(nil, 'capacity must be greater than 0') if capacity <= 0
17
-
18
- ApplicationRecord.transaction do
19
- product = create_product!(vendor, trip_form)
20
- option_values = setup_option_values!(product)
21
- variant = create_variant!(product, price, option_values)
22
-
23
- create_stock_item!(variant, capacity)
24
-
25
- success(product: product, variant: variant)
26
- end
27
- rescue StandardError => e
28
- CmAppLogger.error(
29
- label: 'SpreeCmCommissioner::Trips::Variants::Create#call',
30
- data: {
31
- error_class: e.class.name,
32
- error_message: e.message
33
- }
34
- )
35
- failure(nil, e.message)
36
- end
37
-
38
- private
39
-
40
- # Creates a transit product with name based on stops, assigns vendor, type, and options.
41
- # Includes seat-type option type and default store association.
42
- def create_product!(vendor, trip_form)
43
- vendor_stops = branches(vendor, trip_form)
44
- first_stop = vendor_stops[trip_form.trip_stops.first.stop_id]
45
- last_stop = vendor_stops[trip_form.trip_stops.last.stop_id]
46
-
47
- Spree::Product.create!(
48
- name: trip_form.name.presence || "#{first_stop.id} - #{last_stop.id}",
49
- short_name: trip_form.short_name,
50
- vendor: vendor,
51
- product_type: 'transit',
52
- status: 'active',
53
- available_on: Time.current,
54
- price: trip_form.price,
55
- shipping_category: Spree::ShippingCategory.find_or_create_by!(name: 'Default'),
56
- option_types: [Spree::OptionType.find_by(name: 'seat-type')].compact,
57
- stores: [Spree::Store.default].compact
58
- )
59
- end
60
-
61
- # Retrieves and indexes stops from the vendor for the trip form's stop IDs.
62
- def branches(vendor, trip_form)
63
- vendor.branches.where(id: trip_form.trip_stops.map(&:stop_id)).index_by(&:id)
64
- end
65
-
66
- # Sets up or finds the seat-type option type and creates normal option value.
67
- # Associates the option type with the product if not already present.
68
- def setup_option_values!(product)
69
- option_type = Spree::OptionType.find_or_create_by!(
70
- name: 'seat-type',
71
- presentation: 'Seat Type'
72
- )
73
-
74
- product.option_types << option_type unless product.option_types.include?(option_type)
75
-
76
- option_value = option_type.option_values.find_or_create_by_name!(option_type, 'Normal')
77
-
78
- [option_value]
79
- end
80
-
81
- # Creates a product variant with the given price and associated option values.
82
- # Saves the variant to the database.
83
- def create_variant!(product, price, option_values)
84
- variant = product.variants.new(price: price)
85
- variant.option_values = option_values
86
- variant.save!
87
- variant
88
- end
89
-
90
- # Creates a stock item for the variant with the specified capacity.
91
- # Uses stock movement creator to initialize inventory.
92
- def create_stock_item!(variant, capacity)
93
- result = SpreeCmCommissioner::Stock::StockMovementCreator.call(
94
- variant_id: variant.id,
95
- current_store: variant.product.stores.first,
96
- stock_movement_params: { quantity: capacity }
97
- )
98
- result.stock_item
99
- end
100
- end
101
- end
102
- end
103
- end
@@ -1,17 +0,0 @@
1
- class MigrateCmRoutesToCmRouteMetrics < ActiveRecord::Migration[7.0]
2
- def change
3
- # 1. Rename cm_routes which we use to track order, trip count to cm_route_metrics instead.
4
- remove_index :cm_routes, [:origin_place_id, :destination_place_id] if index_exists?(:cm_routes, [:origin_place_id, :destination_place_id])
5
- rename_table :cm_routes, :cm_route_metrics unless table_exists?(:cm_route_metrics)
6
- add_column :cm_route_metrics, :route_type, :integer, default: 0, null: false unless column_exists?(:cm_route_metrics, :route_type)
7
-
8
- # 2. Drop the old index before renaming table to avoid index name length issues & re-add the index with a shorter name
9
- unless index_exists?(:cm_route_metrics, [:route_type, :origin_place_id, :destination_place_id], name: 'index_cm_route_metrics_on_origin_dest')
10
- add_index :cm_route_metrics, [:route_type, :origin_place_id, :destination_place_id], unique: true, name: 'index_cm_route_metrics_on_origin_dest'
11
- end
12
-
13
- # 3. Remove old route_id column from trips.
14
- # it has route_metrics value, and trip don't need to have association with metric directly.
15
- remove_column :cm_trips, :route_id if column_exists?(:cm_trips, :route_id)
16
- end
17
- end
@@ -1,30 +0,0 @@
1
- class MigrateCmVendorRoutesToCmRoutes < ActiveRecord::Migration[7.0]
2
- def change
3
- # 1. Remove cm_vendor_routes and add cm_routes instead for routes that associated with vendor.
4
- drop_table :cm_vendor_routes, if_exists: true
5
- create_table :cm_routes, if_not_exists: true do |t|
6
- t.string :route_name
7
- t.string :short_name
8
- t.integer :order_count
9
- t.integer :fulfilled_order_count
10
- t.integer :route_type
11
- t.jsonb :route_stops, default: "[]"
12
- t.references :vendor, foreign_key: { to_table: :spree_vendors }, null: false
13
- t.references :tenant, foreign_key: { to_table: :cm_tenants }, null: true
14
-
15
- t.references :origin_place, foreign_key: { to_table: :cm_places }, null: false
16
- t.references :destination_place, foreign_key: { to_table: :cm_places }, null: false
17
-
18
- t.integer :lock_version, default: 0, null: false
19
- t.timestamps
20
- end
21
-
22
- # 2. Add indexes for popular_routes association (vendor_id + order by fulfilled_order_count, order_count)
23
- add_index :cm_routes, [:vendor_id, :route_type, :fulfilled_order_count, :order_count],
24
- order: { fulfilled_order_count: :desc, order_count: :desc },
25
- name: 'index_cm_routes_on_vendor_id_route_type_and_popular' unless index_exists?(:cm_routes, [:vendor_id, :route_type, :fulfilled_order_count, :order_count])
26
-
27
- # 3. Add new route reference to cm_trips
28
- add_reference :cm_trips, :route, foreign_key: { to_table: :cm_routes }, null: true unless column_exists?(:cm_trips, :route_id)
29
- end
30
- end
@@ -1,12 +0,0 @@
1
- class MigrateCmTripStopsToSupportTripConnection < ActiveRecord::Migration[7.0]
2
- def change
3
- # 1. Replace stop_type with allow_boarding & allow_drop_off instead, it is more flexible.
4
- # Useful for middle stops where both boarding & drop off can be allowed or both can be disallowed.
5
- remove_column :cm_trip_stops, :stop_type if column_exists?(:cm_trip_stops, :stop_type)
6
- add_column :cm_trip_stops, :allow_boarding, :boolean unless column_exists?(:cm_trip_stops, :allow_boarding)
7
- add_column :cm_trip_stops, :allow_drop_off, :boolean unless column_exists?(:cm_trip_stops, :allow_drop_off)
8
-
9
- # 2. Add references to link trip stops to trips for boarding, this will enable trip connections. Where current trip is main trip.
10
- add_reference :cm_trip_stops, :board_to_trip, foreign_key: { to_table: :cm_trips }, null: true unless column_exists?(:cm_trip_stops, :board_to_trip_id)
11
- end
12
- end
@@ -1,5 +0,0 @@
1
- class AddAllowBookingToCmTrips < ActiveRecord::Migration[7.0]
2
- def change
3
- add_column :cm_trips, :allow_booking, :boolean, default: true, if_not_exists: true
4
- end
5
- end
@@ -1,12 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_route_metric, class: SpreeCmCommissioner::RouteMetric do
3
- association :origin_place, factory: :cm_place
4
- association :destination_place, factory: :cm_place
5
-
6
- route_name { "#{origin_place.name} - #{destination_place.name}" }
7
- route_type { :bus }
8
- trip_count { 0 }
9
- order_count { 0 }
10
- fulfilled_order_count { 0 }
11
- end
12
- end
@@ -1,5 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_route_photo, class: SpreeCmCommissioner::RoutePhoto do
3
- attachment { Rack::Test::UploadedFile.new(SpreeMultiVendor::Engine.root.join('spec', 'fixtures', 'thinking-cat.jpg'), 'image/jpeg') }
4
- end
5
- end
@@ -1,61 +0,0 @@
1
- module SpreeCmCommissioner::Transit
2
- class RouteStop
3
- attr_accessor :location_id, :vendor_place_id, :type, :sequence, :vendor_place
4
-
5
- STOP_TYPES = %i[branch stop].freeze
6
-
7
- def initialize(options = {})
8
- @location_id = options[:location_id]
9
- @type = options[:type]&.to_sym
10
- @sequence = options[:sequence]
11
-
12
- # vendor place is either stop or branch
13
- @vendor_place_id = options[:vendor_place_id]
14
- end
15
-
16
- def self.from_hash(hash)
17
- hash = hash.symbolize_keys
18
-
19
- new(
20
- location_id: hash[:location_id],
21
- vendor_place_id: hash[:vendor_place_id],
22
- type: hash[:type],
23
- sequence: hash[:sequence]
24
- )
25
- end
26
-
27
- def to_h
28
- {
29
- location_id: @location_id,
30
- vendor_place_id: @vendor_place_id,
31
- type: @type,
32
- sequence: @sequence
33
- }
34
- end
35
-
36
- def branch?
37
- @type == :branch
38
- end
39
-
40
- def stop?
41
- @type == :stop
42
- end
43
-
44
- def valid?
45
- errors.empty?
46
- end
47
-
48
- def errors
49
- @errors ||= []
50
- @errors.clear
51
-
52
- @errors << 'location_id is required' if @location_id.blank?
53
- @errors << 'vendor_place_id is required' if @vendor_place_id.blank?
54
- @errors << 'type is required' if @type.blank?
55
- @errors << 'type must be :branch or :stop' if @type.present? && STOP_TYPES.exclude?(@type)
56
- @errors << 'sequence is required' if @sequence.nil?
57
-
58
- @errors
59
- end
60
- end
61
- end