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,9 +1,50 @@
|
|
|
1
1
|
<%= turbo_frame_tag "calendar" do %>
|
|
2
|
-
|
|
3
|
-
<%=
|
|
4
|
-
|
|
2
|
+
<div class="flex-wrap gap-3 mt-4 d-flex align-items-center justify-content-between">
|
|
3
|
+
<%= form_with url: admin_product_stock_managements_path, method: :get, class: "d-flex flex-wrap align-items-center gap-2" do |f| %>
|
|
4
|
+
<%= select_year(params[:year]&.to_i || @year, { start_year: 2025, end_year: @year + 3 }, { class: 'form-control', style: "width: auto;", name: "year", onchange: "this.form.submit()" }) %>
|
|
5
|
+
<%= f.select :selected_variant_id,
|
|
6
|
+
options_for_select([["All Variants", ""]] + @variants.map { |v| [v.options_text.presence || "N/A", v.id] }, params[:selected_variant_id]), {},
|
|
7
|
+
{ class: "form-control", style: "width: auto;", onchange: "this.form.submit()" } %>
|
|
8
|
+
<% end %>
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
<div id="bulkActionsBar" class="flex-wrap gap-2 align-items-center" style="display:none;">
|
|
11
|
+
<button class="btn btn-primary"
|
|
12
|
+
onclick="setModalUrl('prices')"
|
|
13
|
+
data-toggle="modal"
|
|
14
|
+
data-target="#inventoryItemModal">
|
|
15
|
+
<%= svg_icon name: "money.svg", width: '18', height: '18' %>
|
|
16
|
+
<span class="ml-1">Bulk Update Prices</span>
|
|
17
|
+
</button>
|
|
18
|
+
<button class="btn btn-success"
|
|
19
|
+
onclick="setModalUrl('stocks')"
|
|
20
|
+
data-toggle="modal"
|
|
21
|
+
data-target="#inventoryItemModal">
|
|
22
|
+
<%= svg_icon name: "box-seam.svg", width: '18', height: '18' %>
|
|
23
|
+
<span class="ml-1">Bulk Update Stocks</span>
|
|
24
|
+
</button>
|
|
25
|
+
<button class="btn btn-outline-secondary" onclick="clearSelection()">
|
|
26
|
+
Clear (<span id="selectedCount">0</span>)
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.gap-2 {
|
|
33
|
+
gap: 0.5rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.gap-3 {
|
|
37
|
+
gap: 1rem;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.day-selected {
|
|
41
|
+
background-color: #e3f2fd !important;
|
|
42
|
+
border: 2px solid #2196F3 !important;
|
|
43
|
+
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
|
|
47
|
+
<div class="mt-2 c-annual-calendar-container">
|
|
7
48
|
<div class="c-annual-calendar">
|
|
8
49
|
<% (1..12).each do |month| %>
|
|
9
50
|
<%= month_calendar(start_date: Date.new(@year, month, 1), attribute: :from_date, end_attribute: :to_date, events: @events) do |date, events| %>
|
|
@@ -20,10 +61,8 @@
|
|
|
20
61
|
<% classes += 'badge-success ' if inventory_item.prices.any? %>
|
|
21
62
|
|
|
22
63
|
<li class="<%= classes %>"
|
|
23
|
-
data-
|
|
24
|
-
data-
|
|
25
|
-
role="button"
|
|
26
|
-
onclick="updateModalTurboFrameSrc('<%= admin_product_variant_inventory_item_path(@product, inventory_item.variant_id, inventory_item.id) %>')">
|
|
64
|
+
data-variant-id="<%= inventory_item.variant_id %>"
|
|
65
|
+
data-inventory-id="<%= inventory_item.id %>">
|
|
27
66
|
<%= inventory_item.quantity_available %>
|
|
28
67
|
<%= inventory_item.prices.first.display_amount if inventory_item.prices.any? %>
|
|
29
68
|
</li>
|
|
@@ -46,11 +85,65 @@
|
|
|
46
85
|
</div>
|
|
47
86
|
|
|
48
87
|
<script>
|
|
49
|
-
|
|
88
|
+
let selectedInventoryIds = new Set();
|
|
89
|
+
|
|
90
|
+
// Use event delegation for day clicks - works after Turbo updates
|
|
91
|
+
document.addEventListener('click', function(e) {
|
|
92
|
+
const dayElement = e.target.closest('.c-annual-day');
|
|
93
|
+
if (dayElement) {
|
|
94
|
+
handleDayClick(e, dayElement);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
function handleDayClick(event, dayElement) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
|
|
102
|
+
const badges = dayElement.querySelectorAll('.badge[data-inventory-id]');
|
|
103
|
+
const inventoryIds = Array.from(badges).map(b => b.getAttribute('data-inventory-id'));
|
|
104
|
+
|
|
105
|
+
if (inventoryIds.length === 0) return;
|
|
106
|
+
|
|
107
|
+
const allSelected = inventoryIds.every(id => selectedInventoryIds.has(id));
|
|
108
|
+
|
|
109
|
+
if (allSelected) {
|
|
110
|
+
// Deselect
|
|
111
|
+
inventoryIds.forEach(id => selectedInventoryIds.delete(id));
|
|
112
|
+
dayElement.parentElement.classList.remove('day-selected');
|
|
113
|
+
updateBulkButton();
|
|
114
|
+
} else {
|
|
115
|
+
// Select
|
|
116
|
+
inventoryIds.forEach(id => selectedInventoryIds.add(id));
|
|
117
|
+
dayElement.parentElement.classList.add('day-selected');
|
|
118
|
+
updateBulkButton();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function updateBulkButton() {
|
|
125
|
+
const count = selectedInventoryIds.size;
|
|
126
|
+
document.getElementById('selectedCount').textContent = count;
|
|
127
|
+
const bar = document.getElementById('bulkActionsBar');
|
|
128
|
+
bar.style.display = count > 0 ? 'flex' : 'none';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function clearSelection() {
|
|
132
|
+
selectedInventoryIds.clear();
|
|
133
|
+
document.querySelectorAll('.day-selected').forEach(element => {
|
|
134
|
+
element.classList.remove('day-selected');
|
|
135
|
+
});
|
|
136
|
+
updateBulkButton();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function setModalUrl(type) {
|
|
140
|
+
const ids = Array.from(selectedInventoryIds).join(',');
|
|
141
|
+
const url = type === 'prices'
|
|
142
|
+
? '<%= prices_admin_product_inventory_items_path(@product) %>?inventory_ids=' + ids
|
|
143
|
+
: '<%= stocks_admin_product_inventory_items_path(@product) %>?inventory_ids=' + ids;
|
|
50
144
|
let frame = document.getElementById('inventory_item');
|
|
51
|
-
frame.
|
|
52
|
-
frame.
|
|
53
|
-
frame.src = url
|
|
145
|
+
frame.innerHTML = `<div class="mt-2 spinner-border" role="status"></div>`;
|
|
146
|
+
frame.setAttribute('src', url);
|
|
54
147
|
}
|
|
55
148
|
</script>
|
|
56
149
|
<% end %>
|
|
@@ -2,23 +2,24 @@
|
|
|
2
2
|
<%= render partial: 'spree/admin/shared/error_messages', locals: { target: @product } %>
|
|
3
3
|
|
|
4
4
|
<% if false && can?(:create, Spree::StockMovement) %>
|
|
5
|
-
<div id="add_stock_form" class="
|
|
5
|
+
<div id="add_stock_form" class="mb-3 card">
|
|
6
6
|
<%= render 'add_stock_form' %>
|
|
7
7
|
</div>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
10
|
-
<div class="
|
|
10
|
+
<div class="bg-white border rounded table-responsive">
|
|
11
11
|
<table class="table" id="listing_product_stock">
|
|
12
12
|
<thead class="text-muted">
|
|
13
13
|
<tr data-hook="admin_product_stock_management_index_headers">
|
|
14
14
|
<th colspan="2"><%= Spree.t(:variant) %></th>
|
|
15
15
|
<th colspan="3"><%= Spree.t(:stock) %></th>
|
|
16
|
+
<th colspan="2"><%= Spree.t(:price) %></th>
|
|
16
17
|
</tr>
|
|
17
18
|
</thead>
|
|
18
19
|
<tbody>
|
|
19
20
|
<% @variants.each do |variant| %>
|
|
20
21
|
<tr id="<%= spree_dom_id variant %>" data-hook="admin_product_stock_management_index_rows">
|
|
21
|
-
<td class="
|
|
22
|
+
<td class="text-center image">
|
|
22
23
|
<%= small_image(variant) %>
|
|
23
24
|
</td>
|
|
24
25
|
<td>
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
<div>
|
|
41
42
|
<%= svg_icon name: "cart-check.svg", width: '14', height: '14' %>
|
|
42
43
|
<%= label_tag "reserved_stock#{variant.id}", "Reserved Stock: #{@reserved_stocks[variant.id] || 0}", class: "m-0" %>
|
|
43
|
-
<%= link_to_with_icon('capture.svg', "Create Inventory Item",
|
|
44
|
+
<%= link_to_with_icon('capture.svg', "Create Inventory Item", admin_product_inventory_items_path(@product.slug, variant_id: variant.id),
|
|
44
45
|
method: :post,
|
|
45
46
|
remote: false,
|
|
46
47
|
class: 'icon_link btn btn-sm btn-outline-primary ml-2',
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
<% end %>
|
|
78
79
|
</span>
|
|
79
80
|
|
|
80
|
-
<%= link_to_with_icon('arrow-counterclockwise.svg', "Reset Inventory Item",
|
|
81
|
+
<%= link_to_with_icon('arrow-counterclockwise.svg', "Reset Inventory Item", reset_admin_product_inventory_item_path(@product.slug, inventory_item.id),
|
|
81
82
|
method: :patch,
|
|
82
83
|
remote: false,
|
|
83
84
|
class: 'icon_link btn btn-sm outline text-dark',
|
|
@@ -87,18 +88,18 @@
|
|
|
87
88
|
<% end %>
|
|
88
89
|
<% end %>
|
|
89
90
|
</td>
|
|
90
|
-
|
|
91
91
|
<td colspan="3" class="stock_location_info">
|
|
92
92
|
<%= render partial: 'variant_stock_items', locals: { variant: variant } if variant.vendor.stock_locations.any? %>
|
|
93
93
|
</td>
|
|
94
|
+
<td><%= variant.display_price %></td>
|
|
94
95
|
</tr>
|
|
95
96
|
<% end %>
|
|
96
97
|
</tbody>
|
|
97
98
|
</table>
|
|
98
99
|
</div>
|
|
99
100
|
|
|
100
|
-
<%= turbo_frame_tag "calendar", src: calendar_admin_product_stock_managements_path(@product.
|
|
101
|
-
<div class="spinner-border
|
|
101
|
+
<%= turbo_frame_tag "calendar", src: calendar_admin_product_stock_managements_path(@product.slug, year: params[:year], selected_variant_id: params[:selected_variant_id]) do %>
|
|
102
|
+
<div class="mt-2 spinner-border" role="status">
|
|
102
103
|
<span class="sr-only">Loading...</span>
|
|
103
104
|
</div>
|
|
104
105
|
<% end if @product.permanent_stock? %>
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
<tr data-hook="option_header">
|
|
10
10
|
<th class="no-border handel-head"></th>
|
|
11
11
|
<th><%= Spree.t(:name) %></th>
|
|
12
|
-
<th><%= Spree.t(:
|
|
12
|
+
<th><%= Spree.t(:allow_boarding) %></th>
|
|
13
|
+
<th><%= Spree.t(:allow_drop_off) %></th>
|
|
13
14
|
<th><%= Spree.t(:sequence) %></th>
|
|
14
15
|
<th><%= Spree.t(:created_at) %></th>
|
|
15
16
|
</tr>
|
|
@@ -21,7 +22,8 @@
|
|
|
21
22
|
<%= svg_icon name: "grip-vertical.svg", width: '18', height: '18' %>
|
|
22
23
|
</td>
|
|
23
24
|
<td><%= stop.stop_name %></td>
|
|
24
|
-
<td> <%= stop.
|
|
25
|
+
<td> <%= stop.allow_boarding ? 'Yes' : 'No' %></td>
|
|
26
|
+
<td> <%= stop.allow_drop_off ? 'Yes' : 'No' %></td>
|
|
25
27
|
<td> <%= stop.sequence %></td>
|
|
26
28
|
<td> <%= stop.created_at.to_date %></td>
|
|
27
29
|
</tr>
|
|
@@ -151,7 +151,6 @@
|
|
|
151
151
|
<% @trip.trip_stops.order(:sequence).each do |stop| %>
|
|
152
152
|
<tr>
|
|
153
153
|
<td>
|
|
154
|
-
<span class="stop-type <%= stop.stop_type %>"><%= stop.stop_type.to_s.titleize %></span>
|
|
155
154
|
<div class="stop-name"><strong><%= stop.stop_name %></strong></div>
|
|
156
155
|
<% if stop.respond_to?(:location_place) && stop.location_place.present? %>
|
|
157
156
|
<% if stop.location_place.respond_to?(:address) && stop.location_place.address.present? %>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module PermittedAttributes
|
|
3
|
+
ATTRIBUTES << :route_attributes unless ATTRIBUTES.include?(:route_attributes)
|
|
4
|
+
mattr_reader :route_attributes
|
|
5
|
+
|
|
3
6
|
@@vendor_attributes << :logo
|
|
4
7
|
|
|
5
8
|
# Permitted all guest attributes for now as permitting only some guest attributes is not working by design
|
|
@@ -86,5 +89,13 @@ module Spree
|
|
|
86
89
|
originator_type
|
|
87
90
|
originator_id
|
|
88
91
|
]
|
|
92
|
+
|
|
93
|
+
@@route_attributes = %i[
|
|
94
|
+
route_name
|
|
95
|
+
short_name
|
|
96
|
+
vendor_id
|
|
97
|
+
route_type
|
|
98
|
+
route_stops
|
|
99
|
+
]
|
|
89
100
|
end
|
|
90
101
|
end
|
data/config/routes.rb
CHANGED
|
@@ -149,13 +149,12 @@ Spree::Core::Engine.add_routes do
|
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
resources :
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
end
|
|
152
|
+
resources :inventory_items, only: [] do
|
|
153
|
+
collection do
|
|
154
|
+
get :prices
|
|
155
|
+
get :stocks
|
|
156
|
+
patch :bulk_update_prices
|
|
157
|
+
patch :bulk_update_stocks
|
|
159
158
|
end
|
|
160
159
|
end
|
|
161
160
|
|
|
@@ -560,6 +559,12 @@ Spree::Core::Engine.add_routes do
|
|
|
560
559
|
resources :trip_places, only: :index
|
|
561
560
|
resources :trip_search, only: [:index]
|
|
562
561
|
resources :trips, only: %i[show]
|
|
562
|
+
resources :popular_route_places, only: [:index]
|
|
563
|
+
resources :routes, only: [:index]
|
|
564
|
+
|
|
565
|
+
namespace :transit do
|
|
566
|
+
resources :draft_orders, only: %i[create]
|
|
567
|
+
end
|
|
563
568
|
|
|
564
569
|
namespace :intercity_taxi do
|
|
565
570
|
resource :draft_orders, only: %i[create update]
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1,12 @@
|
|
|
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,10 +1,11 @@
|
|
|
1
1
|
FactoryBot.define do
|
|
2
|
-
factory :cm_route, class:
|
|
2
|
+
factory :cm_route, class: SpreeCmCommissioner::Route do
|
|
3
|
+
association :vendor, factory: :vendor
|
|
4
|
+
association :tenant, factory: :cm_tenant
|
|
5
|
+
association :origin_place, factory: :cm_place
|
|
6
|
+
association :destination_place, factory: :cm_place
|
|
7
|
+
|
|
3
8
|
sequence(:route_name) { |n| "Route #{n}" }
|
|
4
|
-
|
|
5
|
-
destination_place_id { create(:cm_place).id }
|
|
6
|
-
trip_count { 0 }
|
|
7
|
-
order_count { 0 }
|
|
8
|
-
fulfilled_order_count { 0 }
|
|
9
|
+
sequence(:short_name) { |n| "R#{n}" }
|
|
9
10
|
end
|
|
10
11
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
|
@@ -11,7 +11,10 @@ FactoryBot.define do
|
|
|
11
11
|
association :vehicle, factory: :cm_vehicle
|
|
12
12
|
association :origin_place, factory: :cm_place
|
|
13
13
|
association :destination_place, factory: :cm_place
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
# Don't create route by default to avoid uniqueness conflicts
|
|
16
|
+
# Tests that need these should create them explicitly
|
|
17
|
+
route { nil }
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
factory :cm_trip_with_seat_counts, parent: :cm_trip do
|
|
@@ -4,7 +4,9 @@ FactoryBot.define do
|
|
|
4
4
|
association :stop_place, factory: :cm_place
|
|
5
5
|
association :location_place, factory: :cm_place
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
allow_boarding { true }
|
|
8
|
+
allow_drop_off { true }
|
|
9
|
+
|
|
8
10
|
stop_name { stop_place.name }
|
|
9
11
|
|
|
10
12
|
arrival_time { Time.current }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
FactoryBot.define do
|
|
2
2
|
factory :cm_transit_vendor, parent: :vendor do
|
|
3
|
+
from_email { FFaker::Internet.email } # required only when tenant exist.
|
|
3
4
|
sequence(:name) { |n| "#{FFaker::Company.name} #{n}#{Kernel.rand(9999)}" }
|
|
4
5
|
state { :active }
|
|
5
6
|
primary_product_type { :transit }
|
|
@@ -7,6 +8,7 @@ FactoryBot.define do
|
|
|
7
8
|
|
|
8
9
|
factory :cm_vendor, parent: :vendor do
|
|
9
10
|
sequence(:name) { |n| "#{FFaker::Company.name} #{n}#{Kernel.rand(9999)}" }
|
|
11
|
+
from_email { FFaker::Internet.email } # required only when tenant exist.
|
|
10
12
|
state { :active }
|
|
11
13
|
default_state_id { Spree::State.first&.id }
|
|
12
14
|
primary_product_type { :ecommerce }
|
|
@@ -7,6 +7,17 @@ FactoryBot.define do
|
|
|
7
7
|
position { FFaker::Number.number }
|
|
8
8
|
|
|
9
9
|
place_type { :location }
|
|
10
|
+
|
|
11
|
+
transient do
|
|
12
|
+
place_name { nil }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
before :create do |vendor_place, evaluator|
|
|
16
|
+
if evaluator.place_name.present?
|
|
17
|
+
vendor_place.place.name = evaluator.place_name
|
|
18
|
+
vendor_place.place.save!
|
|
19
|
+
end
|
|
20
|
+
end
|
|
10
21
|
end
|
|
11
22
|
|
|
12
23
|
factory :cm_vendor_place, class: SpreeCmCommissioner::VendorPlace do
|
|
@@ -30,5 +41,16 @@ FactoryBot.define do
|
|
|
30
41
|
place_type { :location }
|
|
31
42
|
location { nil }
|
|
32
43
|
end
|
|
44
|
+
|
|
45
|
+
transient do
|
|
46
|
+
place_name { nil }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
before :create do |vendor_place, evaluator|
|
|
50
|
+
if evaluator.place_name.present?
|
|
51
|
+
vendor_place.place.name = evaluator.place_name
|
|
52
|
+
vendor_place.place.save!
|
|
53
|
+
end
|
|
54
|
+
end
|
|
33
55
|
end
|
|
34
56
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|