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.
Files changed (90) 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/admin/inventory_items_controller.rb +36 -56
  5. data/app/controllers/spree/admin/stock_managements_controller.rb +14 -3
  6. data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +2 -2
  7. data/app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb +4 -4
  8. data/app/controllers/spree/api/v2/tenant/popular_route_places_controller.rb +60 -0
  9. data/app/controllers/spree/api/v2/tenant/routes_controller.rb +50 -0
  10. data/app/controllers/spree/api/v2/tenant/transit/draft_orders_controller.rb +46 -0
  11. data/app/controllers/spree/transit/trips_controller.rb +3 -3
  12. data/app/finders/spree_cm_commissioner/places/find_with_route.rb +12 -12
  13. data/app/finders/spree_cm_commissioner/route_metrics/find_popular.rb +44 -0
  14. data/app/finders/spree_cm_commissioner/routes/find.rb +94 -0
  15. data/app/finders/spree_cm_commissioner/routes/find_popular.rb +19 -35
  16. data/app/interactors/spree_cm_commissioner/stock/permanent_inventory_items_generator.rb +11 -4
  17. data/app/interactors/spree_cm_commissioner/stock/stock_movement_creator.rb +10 -1
  18. data/app/interactors/spree_cm_commissioner/trip_clone_creator.rb +4 -3
  19. data/app/interactors/spree_cm_commissioner/trip_stops_creator.rb +2 -2
  20. data/app/jobs/spree_cm_commissioner/route_metrics/decrease_trip_count_job.rb +10 -0
  21. data/app/jobs/spree_cm_commissioner/route_metrics/increase_fulfilled_order_count_job.rb +10 -0
  22. data/app/jobs/spree_cm_commissioner/route_metrics/increase_order_count_job.rb +10 -0
  23. data/app/jobs/spree_cm_commissioner/route_metrics/increase_trip_count_job.rb +10 -0
  24. data/app/jobs/spree_cm_commissioner/stock/permanent_inventory_items_generator_job.rb +2 -2
  25. data/app/models/concerns/spree_cm_commissioner/route_order_countable.rb +2 -2
  26. data/app/models/spree_cm_commissioner/place.rb +5 -8
  27. data/app/models/spree_cm_commissioner/product_decorator.rb +1 -0
  28. data/app/models/spree_cm_commissioner/route.rb +45 -5
  29. data/app/models/spree_cm_commissioner/route_metric.rb +21 -0
  30. data/app/models/spree_cm_commissioner/route_photo.rb +12 -0
  31. data/app/models/spree_cm_commissioner/trip.rb +8 -33
  32. data/app/models/spree_cm_commissioner/trip_stop.rb +16 -2
  33. data/app/models/spree_cm_commissioner/vendor_decorator.rb +3 -1
  34. data/app/queries/spree_cm_commissioner/trip_query.rb +2 -2
  35. data/app/serializers/spree/v2/tenant/transit_cart_serializer.rb +11 -0
  36. data/app/serializers/spree_cm_commissioner/v2/storefront/route_metric_serializer.rb +13 -0
  37. data/app/serializers/spree_cm_commissioner/v2/storefront/route_serializer.rb +2 -1
  38. data/app/serializers/spree_cm_commissioner/v2/storefront/transit_line_item_serializer.rb +17 -0
  39. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_stop_serializer.rb +1 -1
  40. data/app/services/spree_cm_commissioner/route_metrics/decrease_trip_count.rb +31 -0
  41. data/app/services/spree_cm_commissioner/{routes/increment_fulfilled_order_count.rb → route_metrics/increase_fulfilled_order_count.rb} +3 -3
  42. data/app/services/spree_cm_commissioner/{routes/increment_order_count.rb → route_metrics/increase_order_count.rb} +3 -3
  43. data/app/services/spree_cm_commissioner/route_metrics/increase_trip_count.rb +31 -0
  44. data/app/services/spree_cm_commissioner/{routes/base_update_order_metrics.rb → route_metrics/update_route_metrics.rb} +11 -16
  45. data/app/services/spree_cm_commissioner/routes/create.rb +51 -0
  46. data/app/services/spree_cm_commissioner/routes/update.rb +25 -0
  47. data/app/{interactors/spree_cm_commissioner/transit/draft_order_creator.rb → services/spree_cm_commissioner/transit_order/create.rb} +13 -16
  48. data/app/services/spree_cm_commissioner/trips/create_single_leg.rb +123 -0
  49. data/app/services/spree_cm_commissioner/trips/service_calendars/create_or_update.rb +54 -0
  50. data/app/services/spree_cm_commissioner/trips/update_single_leg.rb +88 -0
  51. data/app/services/spree_cm_commissioner/trips/variants/create.rb +103 -0
  52. data/app/views/spree/admin/inventory_items/prices.html.erb +45 -0
  53. data/app/views/spree/admin/inventory_items/stocks.html.erb +36 -0
  54. data/app/views/spree/admin/stock_managements/calendar.html.erb +105 -12
  55. data/app/views/spree/admin/stock_managements/index.html.erb +9 -8
  56. data/app/views/spree/transit/trip_stops/index.html.erb +4 -2
  57. data/app/views/spree_cm_commissioner/guest_mailer/send_ticket_to_guest.html.erb +0 -1
  58. data/config/initializers/spree_permitted_attributes.rb +11 -0
  59. data/config/routes.rb +12 -7
  60. data/db/migrate/20251224033103_migrate_cm_routes_to_cm_route_metrics.rb +17 -0
  61. data/db/migrate/20251224033910_migrate_cm_vendor_routes_to_cm_routes.rb +30 -0
  62. data/db/migrate/20260105072450_migrate_cm_trip_stops_to_support_trip_connection.rb +12 -0
  63. data/db/migrate/20260108101406_add_allow_booking_to_cm_trips.rb +5 -0
  64. data/lib/spree_cm_commissioner/test_helper/factories/route_factory.rb +7 -6
  65. data/lib/spree_cm_commissioner/test_helper/factories/route_metric_factory.rb +12 -0
  66. data/lib/spree_cm_commissioner/test_helper/factories/route_photo_factory.rb +5 -0
  67. data/lib/spree_cm_commissioner/test_helper/factories/trip_factory.rb +4 -1
  68. data/lib/spree_cm_commissioner/test_helper/factories/trip_stop_factory.rb +3 -1
  69. data/lib/spree_cm_commissioner/test_helper/factories/vendor_factory.rb +2 -0
  70. data/lib/spree_cm_commissioner/test_helper/factories/vendor_place_factory.rb +22 -0
  71. data/lib/spree_cm_commissioner/transit/route_stop.rb +61 -0
  72. data/lib/spree_cm_commissioner/transit/route_stop_collection.rb +175 -0
  73. data/lib/spree_cm_commissioner/transit/trip_form.rb +81 -0
  74. data/lib/spree_cm_commissioner/transit/trip_stop_form.rb +65 -0
  75. data/lib/spree_cm_commissioner/version.rb +1 -1
  76. data/lib/spree_cm_commissioner.rb +4 -0
  77. metadata +42 -21
  78. data/app/jobs/spree_cm_commissioner/transit/route_fulfilled_order_count_incrementer_job.rb +0 -10
  79. data/app/jobs/spree_cm_commissioner/transit/route_order_count_incrementer_job.rb +0 -10
  80. data/app/jobs/spree_cm_commissioner/transit/route_previous_trip_count_decrementer_job.rb +0 -13
  81. data/app/jobs/spree_cm_commissioner/transit/route_trip_count_decrementer_job.rb +0 -10
  82. data/app/jobs/spree_cm_commissioner/transit/route_trip_count_incrementer_job.rb +0 -10
  83. data/app/models/concerns/spree_cm_commissioner/route_trip_count_callbacks.rb +0 -48
  84. data/app/models/spree_cm_commissioner/trip_connection.rb +0 -36
  85. data/app/models/spree_cm_commissioner/vendor_route.rb +0 -9
  86. data/app/services/spree_cm_commissioner/routes/decrement_previous_trip_count.rb +0 -30
  87. data/app/services/spree_cm_commissioner/routes/decrement_trip_count.rb +0 -33
  88. data/app/services/spree_cm_commissioner/routes/increment_trip_count.rb +0 -33
  89. data/app/views/spree/admin/inventory_items/show.html.erb +0 -72
  90. data/lib/spree_cm_commissioner/test_helper/factories/trip_connection_factory.rb +0 -6
@@ -1,48 +0,0 @@
1
- module SpreeCmCommissioner
2
- module RouteTripCountCallbacks
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- after_create :increase_trip_count
7
- before_destroy :decrease_trip_count
8
-
9
- # When route context may change, enqueue an async job after commit that
10
- # reads the previous values from `previous_changes` to decrement counts
11
- # on the old route/vendor. This avoids keeping in-memory state and keeps
12
- # the Trip update fast.
13
- after_commit :enqueue_decrement_previous_route_trip_counts, on: :update
14
-
15
- after_commit :increment_new_route_trip_counts, on: :update, if: :changed_route_context?
16
- end
17
-
18
- private
19
-
20
- def decrease_trip_count
21
- SpreeCmCommissioner::Transit::RouteTripCountDecrementerJob.perform_now(trip_id: id)
22
- end
23
-
24
- def increase_trip_count
25
- SpreeCmCommissioner::Transit::RouteTripCountIncrementerJob.perform_later(trip_id: id)
26
- end
27
-
28
- # Read previous_changes to find prior route context and enqueue the job.
29
- def enqueue_decrement_previous_route_trip_counts
30
- prev_route_id = previous_changes['route_id']&.first
31
- prev_origin_id = previous_changes['origin_place_id']&.first
32
- prev_destination_id = previous_changes['destination_place_id']&.first
33
-
34
- return unless prev_route_id.present? && prev_origin_id.present? && prev_destination_id.present?
35
-
36
- SpreeCmCommissioner::Transit::RoutePreviousTripCountDecrementerJob.perform_later(
37
- previous_route_id: prev_route_id
38
- )
39
- end
40
-
41
- # Safely increment the trip_count for the trip's (new) route after update.
42
- def increment_new_route_trip_counts
43
- return if origin_place_id.blank? || destination_place_id.blank? || route_id.blank?
44
-
45
- SpreeCmCommissioner::Transit::RouteTripCountIncrementerJob.perform_later(trip_id: id)
46
- end
47
- end
48
- end
@@ -1,36 +0,0 @@
1
- module SpreeCmCommissioner
2
- class TripConnection < Base
3
- belongs_to :from_trip, class_name: 'SpreeCmCommissioner::Trip'
4
- belongs_to :to_trip, class_name: 'SpreeCmCommissioner::Trip'
5
-
6
- validate :both_trip_cannot_be_the_same
7
- before_validation :calculate_connection_time_minutes
8
- validates :from_trip_id, uniqueness: { scope: :to_trip_id }
9
-
10
- private
11
-
12
- def calculate_connection_time_minutes
13
- return if from_trip.nil? || to_trip.nil?
14
-
15
- arrival_seconds = from_trip.arrival_time.seconds_since_midnight
16
- departure_seconds = to_trip.departure_time.seconds_since_midnight
17
-
18
- layover_seconds = departure_seconds - arrival_seconds
19
- layover_seconds += 86_400 if layover_seconds.negative?
20
-
21
- connection_time_in_minutes = layover_seconds / 60
22
-
23
- if connection_time_in_minutes.between?(0, 180)
24
- self.connection_time_minutes = connection_time_in_minutes.to_i
25
- else
26
- errors.add(:base, 'Connection time must be less than 3 hours')
27
- end
28
- end
29
-
30
- def both_trip_cannot_be_the_same
31
- return unless from_trip.id == to_trip.id
32
-
33
- errors.add(:base, 'Both trip cannot be the same')
34
- end
35
- end
36
- end
@@ -1,9 +0,0 @@
1
- module SpreeCmCommissioner
2
- class VendorRoute < Base
3
- belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
4
- belongs_to :route, class_name: 'SpreeCmCommissioner::Route', optional: false
5
-
6
- has_many :trips, -> (vr) { where(vendor_id: vr.vendor_id, route_id: vr.route_id) }, class_name: 'SpreeCmCommissioner::Trip'
7
- validates :vendor_id, uniqueness: { scope: :route_id }
8
- end
9
- end
@@ -1,30 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Routes
3
- class DecrementPreviousTripCount
4
- prepend ::Spree::ServiceModule::Base
5
-
6
- def call(previous_route_id:)
7
- previous_route = SpreeCmCommissioner::Route.find_by(id: previous_route_id)
8
- return failure(nil, 'Route not found') unless previous_route
9
-
10
- if previous_route.vendors.exists?
11
-
12
- # Build a trip-like object that responds to :route and :vendor so the delegated
13
- # service can locate vendor routes correctly.
14
- trip_like = Struct.new(:route, :vendor).new(previous_route, previous_route.vendors.first)
15
- result = SpreeCmCommissioner::Routes::DecrementTripCount.call(trip: trip_like)
16
- return result if result.failure?
17
- else
18
- previous_route.with_lock do
19
- new_count = [previous_route.trip_count.to_i - 1, 0].max
20
- previous_route.update!(trip_count: new_count)
21
- end
22
- end
23
-
24
- success(previous_route: previous_route)
25
- rescue StandardError => e
26
- failure(nil, e.message)
27
- end
28
- end
29
- end
30
- end
@@ -1,33 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Routes
3
- class DecrementTripCount
4
- prepend ::Spree::ServiceModule::Base
5
-
6
- def call(trip:)
7
- return failure(nil, 'Trip not found') unless trip
8
-
9
- vendor = trip.vendor
10
- return failure(nil, 'Vendor not found') unless vendor
11
-
12
- ActiveRecord::Base.transaction do
13
- route = trip.route
14
-
15
- route.update!(trip_count: [route.trip_count - 1, 0].max)
16
-
17
- vendor_route = locate_vendor_route(vendor: vendor, route: route)
18
- vendor_route&.update!(trip_count: [vendor_route.trip_count - 1, 0].max)
19
- end
20
-
21
- success(trip: trip)
22
- rescue StandardError => e
23
- failure(nil, e.message)
24
- end
25
-
26
- private
27
-
28
- def locate_vendor_route(vendor:, route:)
29
- vendor.vendor_routes.find_by(route_id: route.id)
30
- end
31
- end
32
- end
33
- end
@@ -1,33 +0,0 @@
1
- module SpreeCmCommissioner
2
- module Routes
3
- class IncrementTripCount
4
- prepend ::Spree::ServiceModule::Base
5
-
6
- def call(trip:)
7
- return failure(nil, 'Trip not found') unless trip
8
-
9
- vendor = trip.vendor
10
- return failure(nil, 'Vendor not found') unless vendor
11
-
12
- ActiveRecord::Base.transaction do
13
- route = trip.route
14
-
15
- route.update!(trip_count: route.trip_count + 1)
16
-
17
- vendor_route = find_or_create_vendor_route(vendor: vendor, route: route)
18
- vendor_route.update!(trip_count: vendor_route.trip_count + 1)
19
- end
20
-
21
- success(trip: trip)
22
- rescue StandardError => e
23
- failure(nil, e.message)
24
- end
25
-
26
- private
27
-
28
- def find_or_create_vendor_route(vendor:, route:)
29
- vendor.vendor_routes.find_or_create_by!(route_id: route.id)
30
- end
31
- end
32
- end
33
- end
@@ -1,72 +0,0 @@
1
- <%= turbo_frame_tag "inventory_item" do %>
2
- <div>
3
- <h5 class="modal-title">Inventory</h5>
4
- <p>
5
- <% if @inventory_item.inventory_date.present? %>
6
- <span><%= @inventory_item.inventory_date %></span><br>
7
- <% end %>
8
- <%= @inventory_item.variant.options_text %>
9
- </p>
10
-
11
- <ul class="list-group mb-1">
12
- <li class="list-group-item d-flex justify-content-between align-items-center">
13
- Max capacity
14
- <span class="badge badge-primary badge-pill"><%= @inventory_item.max_capacity %></span>
15
- </li>
16
- <li class="list-group-item d-flex justify-content-between align-items-center">
17
- Quantity available
18
- <span class="badge badge-primary badge-pill"><%= @inventory_item.quantity_available %></span>
19
- </li>
20
- </ul>
21
-
22
- <small class="text-muted">
23
- <%= inventory_item_message(@inventory_item, @cached_inventory_item) %>
24
- </small>
25
-
26
- <%= link_to_with_icon('arrow-counterclockwise.svg', "Reset Inventory Item", reset_admin_product_variant_inventory_item_path(@product, @inventory_item.variant_id, @inventory_item.id),
27
- method: :patch,
28
- remote: false,
29
- class: 'icon_link btn btn-sm outline text-dark',
30
- data: { confirm: "Only reset if stock is incorrect. Are you sure?" }, no_text: true
31
- ) %>
32
- </div>
33
-
34
- <hr />
35
-
36
- <div class="mt-2">
37
- <h6 class="modal-title mb-1 d-flex flex-row justify-content-between">
38
- Custom Prices
39
- <%= link_to_with_icon('delete.svg', Spree.t(:remove),
40
- delete_prices_admin_product_variant_inventory_item_path(@product, @inventory_item.variant_id, @inventory_item.id),
41
- method: :delete, remote: false, class: 'icon_link btn btn-sm text-danger', data: { action: :remove, confirm: "Are you sure to remove these prices & use base price?" }, no_text: true
42
- ) if @prices.any?(&:persisted?) %>
43
- </h6>
44
-
45
- <%= form_with url: update_prices_admin_product_variant_inventory_item_path(@product, @inventory_item.variant_id, @inventory_item.id), method: :patch, local: true, html: { class: 'inventory-item-prices-form' } do |form| %>
46
- <table class="table">
47
- <tbody>
48
- <% @prices.each do |price| %>
49
- <% currency = price.currency %>
50
- <% default_price = @default_prices[currency] || Spree::Price.new %>
51
- <tr id="inventory-item_prices_row_<%= currency %>" data-hook="inventory-item_prices_row">
52
- <td><%= form.label "price_#{currency}", currency, class: 'form-label' %></td>
53
- <td>
54
- <%= form.hidden_field "prices[#{currency}][currency]", value: currency %>
55
- <%= form.text_field "prices[#{currency}][price]", value: price.amount, placeholder: "Default: #{default_price.amount || 'N/A'}", class: 'form-control' %>
56
- </td>
57
- <td>
58
- <%= form.text_field "prices[#{currency}][compare_at_price]", value: price.compare_at_price, placeholder: "Default: #{default_price.compare_at_price || 'N/A'}", class: 'form-control' %>
59
- </td>
60
- </tr>
61
- <% end %>
62
- </tbody>
63
- </table>
64
-
65
- <div class="d-flex flex-row justify-content-end mt-2">
66
- <div class="form-actions" data-hook="buttons">
67
- <%= button Spree.t('actions.save'), 'save.svg', 'submit', { class: 'btn-outline-success', data: { disable_with: "#{ Spree.t(:saving) }...", turbo_frame: "_top" } } %>
68
- </div>
69
- </div>
70
- <% end %>
71
- </div>
72
- <% end %>
@@ -1,6 +0,0 @@
1
- FactoryBot.define do
2
- factory :cm_trip_connection, class: SpreeCmCommissioner::TripConnection do
3
- from_trip_id { create(:cm_trip) }
4
- to_trip_id { create(:cm_trip) }
5
- end
6
- end