@fleetbase/fleetops-engine 0.6.11 → 0.6.13
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.
- package/addon/components/customer/orders.js +6 -8
- package/addon/components/layout/fleet-ops-sidebar.js +8 -0
- package/addon/components/leaflet-tracking-marker.js +19 -0
- package/addon/components/live-map.hbs +5 -0
- package/addon/components/live-map.js +6 -8
- package/addon/components/order-config/fields-editor.js +1 -1
- package/addon/components/route-optimization-engine-select-button.hbs +25 -0
- package/addon/components/route-optimization-engine-select-button.js +13 -0
- package/addon/controllers/operations/orders/index/new.js +78 -171
- package/addon/controllers/operations/orders/index/view.js +30 -29
- package/addon/controllers/settings/routing.js +47 -0
- package/addon/engine.js +27 -0
- package/addon/routes/application.js +8 -0
- package/addon/routes/operations/orders/index/view.js +12 -3
- package/addon/routes/settings/routing.js +3 -0
- package/addon/routes.js +1 -0
- package/addon/services/leaflet-router-control.js +64 -0
- package/addon/services/osrm.js +46 -0
- package/addon/services/route-optimization-interface.js +9 -0
- package/addon/services/route-optimization.js +67 -0
- package/addon/templates/operations/orders/index/new.hbs +21 -16
- package/addon/templates/settings/routing.hbs +32 -0
- package/app/components/route-optimization-engine-select-button.js +1 -0
- package/app/controllers/settings/routing.js +1 -0
- package/app/routes/settings/routing.js +1 -0
- package/app/services/leaflet-router-control.js +1 -0
- package/app/services/osrm.js +1 -0
- package/app/services/route-optimization-interface.js +1 -0
- package/app/services/route-optimization.js +1 -0
- package/app/templates/settings/routing.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/config/api.php +10 -1
- package/server/src/Auth/Schemas/FleetOps.php +5 -0
- package/server/src/Console/Commands/DebugOrderTracker.php +5 -5
- package/server/src/Events/EntityActivityChanged.php +118 -0
- package/server/src/Events/EntityCompleted.php +118 -0
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +3 -1
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +16 -13
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +31 -1
- package/server/src/Http/Resources/v1/Waypoint.php +1 -1
- package/server/src/Models/Order.php +44 -8
- package/server/src/Models/Payload.php +29 -10
- package/server/src/Models/Place.php +49 -13
- package/server/src/Support/Geocoding.php +0 -2
- package/server/src/routes.php +2 -0
- package/translations/en-us.yaml +5 -0
|
@@ -6,8 +6,7 @@ import { isArray } from '@ember/array';
|
|
|
6
6
|
import { later } from '@ember/runloop';
|
|
7
7
|
import { debug } from '@ember/debug';
|
|
8
8
|
import { task, timeout } from 'ember-concurrency';
|
|
9
|
-
import {
|
|
10
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
9
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
11
10
|
import engineService from '@fleetbase/ember-core/decorators/engine-service';
|
|
12
11
|
import registerComponent from '@fleetbase/ember-core/utils/register-component';
|
|
13
12
|
import OrderProgressCardComponent from '../order-progress-card';
|
|
@@ -26,6 +25,7 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
26
25
|
@service modalsManager;
|
|
27
26
|
@service customerSession;
|
|
28
27
|
@service hostRouter;
|
|
28
|
+
@service leafletRouterControl;
|
|
29
29
|
@engineService('@fleetbase/fleetops-engine') movementTracker;
|
|
30
30
|
@engineService('@fleetbase/fleetops-engine') location;
|
|
31
31
|
@tracked orders = [];
|
|
@@ -226,7 +226,6 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
226
226
|
|
|
227
227
|
@action displayOrderRoute() {
|
|
228
228
|
const waypoints = this.getRouteCoordinatesFromOrder(this.selectedOrder);
|
|
229
|
-
const routingHost = getRoutingHost();
|
|
230
229
|
if (this.cannotRouteWaypoints(waypoints)) {
|
|
231
230
|
return;
|
|
232
231
|
}
|
|
@@ -240,15 +239,14 @@ export default class CustomerOrdersComponent extends Component {
|
|
|
240
239
|
debug(`Leaflet Map Error: ${error.message}`);
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
profile: 'driving',
|
|
246
|
-
});
|
|
242
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
243
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
247
244
|
|
|
248
245
|
this.routeControl = new RoutingControl({
|
|
249
|
-
fitSelectedRoutes: false,
|
|
250
246
|
router,
|
|
247
|
+
formatter,
|
|
251
248
|
waypoints,
|
|
249
|
+
fitSelectedRoutes: false,
|
|
252
250
|
alternativeClassName: 'hidden',
|
|
253
251
|
addWaypoints: false,
|
|
254
252
|
markerOptions: {
|
|
@@ -162,6 +162,14 @@ export default class LayoutFleetOpsSidebarComponent extends Component {
|
|
|
162
162
|
permission: 'fleet-ops view notification-settings',
|
|
163
163
|
visible: this.abilities.can('fleet-ops see notification-settings'),
|
|
164
164
|
},
|
|
165
|
+
{
|
|
166
|
+
intl: 'fleet-ops.component.layout.fleet-ops-sidebar.routing',
|
|
167
|
+
title: this.intl.t('fleet-ops.component.layout.fleet-ops-sidebar.routing'),
|
|
168
|
+
icon: 'route',
|
|
169
|
+
route: 'settings.routing',
|
|
170
|
+
permission: 'fleet-ops view routing-settings',
|
|
171
|
+
visible: this.abilities.can('fleet-ops see routing-settings'),
|
|
172
|
+
},
|
|
165
173
|
];
|
|
166
174
|
|
|
167
175
|
const createPanel = (intl, routePrefix, items = []) => ({
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import MarkerLayer from 'ember-leaflet/components/marker-layer';
|
|
2
2
|
import { isArray } from '@ember/array';
|
|
3
3
|
|
|
4
|
+
const __draggingHotfix = (layer) => {
|
|
5
|
+
if (!layer.dragging) {
|
|
6
|
+
layer.dragging = {
|
|
7
|
+
enabled: () => false,
|
|
8
|
+
enable: () => (layer.options.draggable = true),
|
|
9
|
+
disable: () => (layer.options.draggable = false),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
4
14
|
const arrayFromLatLng = (latlng) => {
|
|
5
15
|
if (isArray(latlng)) {
|
|
6
16
|
return latlng;
|
|
@@ -52,6 +62,7 @@ L.TrackingMarker = L.Marker.extend({
|
|
|
52
62
|
this._slideKeepAtCenter = false;
|
|
53
63
|
this._slideDraggingWasAllowed = false;
|
|
54
64
|
this._slideFrame = 0;
|
|
65
|
+
__draggingHotfix(this);
|
|
55
66
|
},
|
|
56
67
|
|
|
57
68
|
slideTo: function (latlng, options = {}) {
|
|
@@ -69,6 +80,7 @@ L.TrackingMarker = L.Marker.extend({
|
|
|
69
80
|
this._nextPosition = arrayFromLatLng(latlng);
|
|
70
81
|
this._slideKeepAtCenter = !!options.keepAtCenter;
|
|
71
82
|
this._slideDraggingWasAllowed = this._slideDraggingWasAllowed !== undefined ? this._slideDraggingWasAllowed : this._map.dragging.enabled();
|
|
83
|
+
__draggingHotfix(this);
|
|
72
84
|
|
|
73
85
|
if (this._slideKeepAtCenter) {
|
|
74
86
|
this._map.dragging.disable();
|
|
@@ -245,6 +257,13 @@ export default class LeafletTrackingMarkerComponent extends MarkerLayer {
|
|
|
245
257
|
*/
|
|
246
258
|
rotationAngle = 0;
|
|
247
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Default value for the draggable prop.
|
|
262
|
+
*
|
|
263
|
+
* @memberof LeafletTrackingMarkerComponent
|
|
264
|
+
*/
|
|
265
|
+
draggable = false;
|
|
266
|
+
|
|
248
267
|
getOption(key, defaultValue = null) {
|
|
249
268
|
const value = this.options[key] ?? this[key];
|
|
250
269
|
if (value === undefined) {
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
@icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}}
|
|
39
39
|
@onAdd={{fn this.triggerAction "onDriverAdded" driver}}
|
|
40
40
|
@onClick={{fn this.triggerAction "onDriverClicked" driver}}
|
|
41
|
+
@draggable={{false}}
|
|
41
42
|
as |marker|
|
|
42
43
|
>
|
|
43
44
|
<marker.popup @maxWidth="500" @minWidth="225">
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
@icon={{icon iconUrl=driver.vehicle_avatar iconSize=(array 24 24)}}
|
|
78
79
|
@onAdd={{fn this.triggerAction "onDriverAdded" driver}}
|
|
79
80
|
@onClick={{fn this.triggerAction "onDriverClicked" driver}}
|
|
81
|
+
@draggable={{false}}
|
|
80
82
|
as |marker|
|
|
81
83
|
>
|
|
82
84
|
<marker.popup @maxWidth="500" @minWidth="225">
|
|
@@ -114,6 +116,7 @@
|
|
|
114
116
|
@icon={{icon iconUrl=vehicle.avatar_url iconSize=(array 24 24)}}
|
|
115
117
|
@onAdd={{fn this.triggerAction "onVehicleAdded" vehicle}}
|
|
116
118
|
@onClick={{fn this.triggerAction "onVehicleClicked" vehicle}}
|
|
119
|
+
@draggable={{false}}
|
|
117
120
|
as |marker|
|
|
118
121
|
>
|
|
119
122
|
<marker.popup @permanent={{false}} @sticky={{true}}>
|
|
@@ -150,6 +153,7 @@
|
|
|
150
153
|
@icon={{icon iconUrl=vehicle.avatar_url iconSize=(array 24 24)}}
|
|
151
154
|
@onAdd={{fn this.triggerAction "onVehicleAdded" vehicle}}
|
|
152
155
|
@onClick={{fn this.triggerAction "onVehicleClicked" vehicle}}
|
|
156
|
+
@draggable={{false}}
|
|
153
157
|
as |marker|
|
|
154
158
|
>
|
|
155
159
|
<marker.popup @permanent={{false}} @sticky={{true}}>
|
|
@@ -189,6 +193,7 @@
|
|
|
189
193
|
@alt={{place.address}}
|
|
190
194
|
@onAdd={{fn this.triggerAction "onPlaceAdded" place}}
|
|
191
195
|
@onClick={{fn this.triggerAction "onPlaceClicked" place}}
|
|
196
|
+
@draggable={{false}}
|
|
192
197
|
as |marker|
|
|
193
198
|
>
|
|
194
199
|
<marker.popup>
|
|
@@ -9,8 +9,7 @@ import { later } from '@ember/runloop';
|
|
|
9
9
|
import { debug } from '@ember/debug';
|
|
10
10
|
import { allSettled } from 'rsvp';
|
|
11
11
|
import { task } from 'ember-concurrency';
|
|
12
|
-
import {
|
|
13
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
12
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
14
13
|
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
|
|
15
14
|
|
|
16
15
|
/**
|
|
@@ -37,6 +36,7 @@ export default class LiveMapComponent extends Component {
|
|
|
37
36
|
@service contextPanel;
|
|
38
37
|
@service leafletMapManager;
|
|
39
38
|
@service leafletContextmenuManager;
|
|
39
|
+
@service leafletRouterControl;
|
|
40
40
|
@service theme;
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -1126,7 +1126,6 @@ export default class LiveMapComponent extends Component {
|
|
|
1126
1126
|
|
|
1127
1127
|
// create order route preview
|
|
1128
1128
|
const waypoints = this.getRouteCoordinatesFromOrder(order);
|
|
1129
|
-
const routingHost = getRoutingHost();
|
|
1130
1129
|
if (this.cannotRouteWaypoints(waypoints)) {
|
|
1131
1130
|
return;
|
|
1132
1131
|
}
|
|
@@ -1140,15 +1139,14 @@ export default class LiveMapComponent extends Component {
|
|
|
1140
1139
|
debug(`Leaflet Map Error: ${error.message}`);
|
|
1141
1140
|
}
|
|
1142
1141
|
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
profile: 'driving',
|
|
1146
|
-
});
|
|
1142
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
1143
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
1147
1144
|
|
|
1148
1145
|
this.routeControl = new RoutingControl({
|
|
1149
|
-
fitSelectedRoutes: false,
|
|
1150
1146
|
router,
|
|
1147
|
+
formatter,
|
|
1151
1148
|
waypoints,
|
|
1149
|
+
fitSelectedRoutes: false,
|
|
1152
1150
|
alternativeClassName: 'hidden',
|
|
1153
1151
|
addWaypoints: false,
|
|
1154
1152
|
markerOptions: {
|
|
@@ -197,7 +197,7 @@ export default class OrderConfigFieldsEditorComponent extends Component {
|
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
/* eslint no-unused-vars
|
|
200
|
+
/* eslint-disable no-unused-vars */
|
|
201
201
|
@action sortMetaFieldOptions(metaField, el, target) {
|
|
202
202
|
// const { fields } = this;
|
|
203
203
|
// const { index } = el.dataset;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<DropdownButton
|
|
2
|
+
@text="Optimize Route"
|
|
3
|
+
@icon="caret-down"
|
|
4
|
+
@type="magic"
|
|
5
|
+
@size="sm"
|
|
6
|
+
@iconPrefix="fas"
|
|
7
|
+
@triggerClass="hidden md:flex"
|
|
8
|
+
@helpText="Optimize route using an integrated optimization engine"
|
|
9
|
+
@disabled={{@disabled}}
|
|
10
|
+
@isLoading={{@isLoading}}
|
|
11
|
+
@renderInPlace={{true}}
|
|
12
|
+
as |dd|
|
|
13
|
+
>
|
|
14
|
+
<div class="next-dd-menu" aria-labelledby="user-menu">
|
|
15
|
+
<div class="px-1">
|
|
16
|
+
{{#each this.routeOptimization.availableServices as |service|}}
|
|
17
|
+
<a href="javascript:;" class="next-dd-item" {{on "click" (dropdown-fn dd this.handleClick service)}}>
|
|
18
|
+
<div >
|
|
19
|
+
<div class="text-sm">{{service.name}}</div>
|
|
20
|
+
</div>
|
|
21
|
+
</a>
|
|
22
|
+
{{/each}}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</DropdownButton>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Component from '@glimmer/component';
|
|
2
|
+
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
|
|
5
|
+
export default class RouteOptimizationEngineSelectButtonComponent extends Component {
|
|
6
|
+
@service routeOptimization;
|
|
7
|
+
|
|
8
|
+
@action handleClick({ key }) {
|
|
9
|
+
if (typeof this.args.onClick === 'function') {
|
|
10
|
+
this.args.onClick(key);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -9,11 +9,9 @@ import { isBlank } from '@ember/utils';
|
|
|
9
9
|
import { dasherize } from '@ember/string';
|
|
10
10
|
import { next } from '@ember/runloop';
|
|
11
11
|
import { task } from 'ember-concurrency';
|
|
12
|
-
import {
|
|
12
|
+
import { Control as RoutingControl } from '@fleetbase/leaflet-routing-machine';
|
|
13
13
|
import { debug } from '@ember/debug';
|
|
14
|
-
import polyline from '@fleetbase/ember-core/utils/polyline';
|
|
15
14
|
import isNotEmpty from '@fleetbase/ember-core/utils/is-not-empty';
|
|
16
|
-
import getRoutingHost from '@fleetbase/ember-core/utils/get-routing-host';
|
|
17
15
|
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
|
|
18
16
|
import isModel from '@fleetbase/ember-core/utils/is-model';
|
|
19
17
|
|
|
@@ -31,95 +29,22 @@ L.Bounds.prototype.intersects = function (bounds) {
|
|
|
31
29
|
export default class OperationsOrdersIndexNewController extends BaseController {
|
|
32
30
|
@controller('operations.orders.index') ordersController;
|
|
33
31
|
|
|
34
|
-
/**
|
|
35
|
-
* Inject the `modalsManager` service
|
|
36
|
-
*
|
|
37
|
-
* @var {Service}
|
|
38
|
-
*/
|
|
39
32
|
@service modalsManager;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Inject the `notifications` service
|
|
43
|
-
*
|
|
44
|
-
* @var {Service}
|
|
45
|
-
*/
|
|
46
33
|
@service notifications;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Inject the `loader` service
|
|
50
|
-
*
|
|
51
|
-
* @var {Service}
|
|
52
|
-
*/
|
|
53
34
|
@service loader;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Inject the `currentUser` service
|
|
57
|
-
*
|
|
58
|
-
* @var {Service}
|
|
59
|
-
*/
|
|
60
35
|
@service currentUser;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Inject the `hostRouter` service
|
|
64
|
-
*
|
|
65
|
-
* @var {Service}
|
|
66
|
-
*/
|
|
67
36
|
@service hostRouter;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Inject the `fileQueue` service
|
|
71
|
-
*
|
|
72
|
-
* @var {Service}
|
|
73
|
-
*/
|
|
74
37
|
@service fileQueue;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Inject the `intl` service
|
|
78
|
-
*
|
|
79
|
-
* @var {Service}
|
|
80
|
-
*/
|
|
81
38
|
@service intl;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Inject the `fetch` service
|
|
85
|
-
*
|
|
86
|
-
* @var {Service}
|
|
87
|
-
*/
|
|
88
39
|
@service fetch;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Inject the `store` service
|
|
92
|
-
*
|
|
93
|
-
* @var {Service}
|
|
94
|
-
*/
|
|
95
40
|
@service store;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Inject the `contextPanel` service
|
|
99
|
-
*
|
|
100
|
-
* @var {Service}
|
|
101
|
-
*/
|
|
102
41
|
@service contextPanel;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Inject the `universe` service
|
|
106
|
-
*
|
|
107
|
-
* @var {Service}
|
|
108
|
-
*/
|
|
109
42
|
@service universe;
|
|
43
|
+
@service routeOptimization;
|
|
44
|
+
@service leafletRouterControl;
|
|
45
|
+
@service osrm;
|
|
110
46
|
|
|
111
|
-
/**
|
|
112
|
-
* Create an OrderModel instance.
|
|
113
|
-
*
|
|
114
|
-
* @var {OrderModel}
|
|
115
|
-
*/
|
|
116
47
|
@tracked order = this.store.createRecord('order', { meta: [] });
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Create an PayloadModel instance.
|
|
120
|
-
*
|
|
121
|
-
* @var {OrderModel}
|
|
122
|
-
*/
|
|
123
48
|
@tracked payload = this.store.createRecord('payload');
|
|
124
49
|
@tracked driversQuery = {};
|
|
125
50
|
@tracked vehiclesQuery = {};
|
|
@@ -139,7 +64,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
139
64
|
@tracked isCreatingOrder = false;
|
|
140
65
|
@tracked isMultipleDropoffOrder = false;
|
|
141
66
|
@tracked isViewingRoutePreview = false;
|
|
142
|
-
@tracked isOptimizingRoute = false;
|
|
143
67
|
@tracked optimizedRouteMarkers = [];
|
|
144
68
|
@tracked optimizedRoutePolyline;
|
|
145
69
|
@tracked isFetchingQuotes = false;
|
|
@@ -191,14 +115,8 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
191
115
|
'application/x-tar',
|
|
192
116
|
];
|
|
193
117
|
|
|
194
|
-
get
|
|
195
|
-
|
|
196
|
-
return renderableComponents;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
get renderableEntityInputComponents() {
|
|
200
|
-
const renderableComponents = this.universe.getRenderableComponentsFromRegistry('fleet-ops:template:operations:orders:new:entities-input');
|
|
201
|
-
return renderableComponents;
|
|
118
|
+
get hasRouteOptimizationEngines() {
|
|
119
|
+
return this.routeOptimization.availableEngines.length > 1;
|
|
202
120
|
}
|
|
203
121
|
|
|
204
122
|
@not('isServicable') isNotServicable;
|
|
@@ -677,7 +595,7 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
677
595
|
|
|
678
596
|
@action resetInterface() {
|
|
679
597
|
if (this.leafletMap && this.leafletMap.liveMap) {
|
|
680
|
-
this.leafletMap.liveMap.
|
|
598
|
+
this.leafletMap.liveMap.showAll();
|
|
681
599
|
}
|
|
682
600
|
}
|
|
683
601
|
|
|
@@ -709,31 +627,35 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
709
627
|
}
|
|
710
628
|
|
|
711
629
|
@action removeRoutingControlPreview() {
|
|
712
|
-
|
|
713
|
-
|
|
630
|
+
return new Promise((resolve) => {
|
|
631
|
+
const leafletMap = this.leafletMap;
|
|
632
|
+
const previewRouteControl = this.previewRouteControl;
|
|
714
633
|
|
|
715
|
-
|
|
634
|
+
let removed = false;
|
|
716
635
|
|
|
717
|
-
|
|
718
|
-
try {
|
|
719
|
-
previewRouteControl.remove();
|
|
720
|
-
removed = true;
|
|
721
|
-
} catch (e) {
|
|
722
|
-
// silent
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
if (!removed) {
|
|
636
|
+
if (leafletMap && previewRouteControl instanceof RoutingControl) {
|
|
726
637
|
try {
|
|
727
|
-
|
|
638
|
+
previewRouteControl.remove();
|
|
639
|
+
removed = true;
|
|
728
640
|
} catch (e) {
|
|
729
641
|
// silent
|
|
730
642
|
}
|
|
643
|
+
|
|
644
|
+
if (!removed) {
|
|
645
|
+
try {
|
|
646
|
+
leafletMap.removeControl(previewRouteControl);
|
|
647
|
+
} catch (e) {
|
|
648
|
+
// silent
|
|
649
|
+
}
|
|
650
|
+
}
|
|
731
651
|
}
|
|
732
|
-
}
|
|
733
652
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
653
|
+
if (!removed) {
|
|
654
|
+
this.forceRemoveRoutePreview();
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
resolve(true);
|
|
658
|
+
});
|
|
737
659
|
}
|
|
738
660
|
|
|
739
661
|
@action forceRemoveRoutePreview() {
|
|
@@ -869,13 +791,12 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
869
791
|
const canPreviewRoute = this.routePreviewArray.length > 0;
|
|
870
792
|
|
|
871
793
|
if (canPreviewRoute) {
|
|
872
|
-
const
|
|
873
|
-
const router =
|
|
874
|
-
serviceUrl: `${routingHost}/route/v1`,
|
|
875
|
-
profile: 'driving',
|
|
876
|
-
});
|
|
794
|
+
const routingService = this.currentUser.getOption('routing', { router: 'osrm' }).router;
|
|
795
|
+
const { router, formatter } = this.leafletRouterControl.get(routingService);
|
|
877
796
|
|
|
878
797
|
this.previewRouteControl = new RoutingControl({
|
|
798
|
+
router,
|
|
799
|
+
formatter,
|
|
879
800
|
waypoints: this.routePreviewCoordinates,
|
|
880
801
|
alternativeClassName: 'hidden',
|
|
881
802
|
addWaypoints: false,
|
|
@@ -888,7 +809,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
888
809
|
iconAnchor: [12, 41],
|
|
889
810
|
}),
|
|
890
811
|
},
|
|
891
|
-
router,
|
|
892
812
|
}).addTo(leafletMap);
|
|
893
813
|
|
|
894
814
|
this.previewRouteControl.on('routesfound', (event) => {
|
|
@@ -935,68 +855,54 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
935
855
|
}
|
|
936
856
|
}
|
|
937
857
|
|
|
938
|
-
@task *
|
|
939
|
-
this.isOptimizingRoute = true;
|
|
940
|
-
|
|
941
|
-
// Build the coordinate list we’ll send to OSRM
|
|
942
|
-
const driverAssigned = this.order.driver_assigned;
|
|
943
|
-
const driverPosition = driverAssigned?.location?.coordinates; // [lon,lat] | undefined
|
|
944
|
-
const originalCoords = this.getCoordinatesFromPayload(); // [[lon,lat], …]
|
|
945
|
-
const coordinates = driverPosition ? [driverPosition, ...originalCoords] : [...originalCoords];
|
|
946
|
-
const hasDriverStart = Boolean(driverPosition);
|
|
947
|
-
const source = 'first';
|
|
948
|
-
const destination = 'any';
|
|
949
|
-
const roundtrip = false; // don’t loop back
|
|
950
|
-
const routingHost = getRoutingHost(this.payload, this.waypoints);
|
|
951
|
-
|
|
952
|
-
// Call the OSRM /trip service
|
|
858
|
+
@task *optimizeRouteWithService(service) {
|
|
953
859
|
try {
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
}));
|
|
967
|
-
|
|
968
|
-
// Drop the driver start if present
|
|
969
|
-
const payloadPairs = hasDriverStart ? pairs.slice(1) : pairs;
|
|
970
|
-
|
|
971
|
-
// Sort by the optimised order
|
|
972
|
-
payloadPairs.sort((a, b) => a.wp.waypoint_index - b.wp.waypoint_index);
|
|
973
|
-
|
|
974
|
-
// Extract the Ember models (null-safe)
|
|
975
|
-
const sortedWaypoints = payloadPairs.map((p) => p.model).filter(Boolean);
|
|
860
|
+
const result = yield this.routeOptimization.optimize(service, {
|
|
861
|
+
context: 'create_order',
|
|
862
|
+
order: this.order,
|
|
863
|
+
payload: this.payload,
|
|
864
|
+
waypoints: this.waypoints,
|
|
865
|
+
coordinates: this.getCoordinatesFromPayload(),
|
|
866
|
+
});
|
|
867
|
+
this.handleRouteOptimization(result);
|
|
868
|
+
} catch (err) {
|
|
869
|
+
this.notifications.error(err.message ?? this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
976
872
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
this.
|
|
980
|
-
|
|
873
|
+
@task *optimizeRoute() {
|
|
874
|
+
try {
|
|
875
|
+
const result = yield this.osrm.optimize({
|
|
876
|
+
context: 'create_order',
|
|
877
|
+
order: this.order,
|
|
878
|
+
payload: this.payload,
|
|
879
|
+
waypoints: this.waypoints,
|
|
880
|
+
coordinates: this.getCoordinatesFromPayload(),
|
|
881
|
+
});
|
|
981
882
|
|
|
982
|
-
|
|
983
|
-
|
|
883
|
+
this.handleRouteOptimization(result);
|
|
884
|
+
} catch (err) {
|
|
885
|
+
this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
984
888
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
889
|
+
handleRouteOptimization({ sortedWaypoints, route, trip, result }) {
|
|
890
|
+
// Update map layers & UI
|
|
891
|
+
this.removeRoutingControlPreview();
|
|
892
|
+
this.removeOptimizedRoute(this.leafletMap);
|
|
893
|
+
this.clearLayers();
|
|
989
894
|
|
|
990
|
-
|
|
895
|
+
// Update controller state
|
|
896
|
+
this.waypoints = sortedWaypoints;
|
|
897
|
+
if (route) {
|
|
898
|
+
this.setOptimizedRoute(route, trip, result.waypoints);
|
|
899
|
+
}
|
|
900
|
+
this.previewDraftOrderRoute(this.payload, this.waypoints, this.isMultipleDropoffOrder);
|
|
901
|
+
this.updatePayloadCoordinates();
|
|
991
902
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
} catch (err) {
|
|
996
|
-
debug('Error optimizing route', err);
|
|
997
|
-
this.notifications.error(this.intl.t('fleet-ops.operations.orders.index.new.route-error'));
|
|
998
|
-
} finally {
|
|
999
|
-
this.isOptimizingRoute = false;
|
|
903
|
+
this.order.set('is_route_optimized', true);
|
|
904
|
+
if (this.isUsingIntegratedVendor) {
|
|
905
|
+
this.getQuotes();
|
|
1000
906
|
}
|
|
1001
907
|
}
|
|
1002
908
|
|
|
@@ -1060,7 +966,7 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
1060
966
|
}
|
|
1061
967
|
}
|
|
1062
968
|
|
|
1063
|
-
@action resetForm() {
|
|
969
|
+
@action async resetForm() {
|
|
1064
970
|
const order = this.store.createRecord('order', { meta: [] });
|
|
1065
971
|
const payload = this.store.createRecord('payload');
|
|
1066
972
|
const driversQuery = {};
|
|
@@ -1080,8 +986,6 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
1080
986
|
const customFields = [];
|
|
1081
987
|
const customFieldValues = {};
|
|
1082
988
|
|
|
1083
|
-
this.removeRoutingControlPreview();
|
|
1084
|
-
this.removeOptimizedRoute();
|
|
1085
989
|
this.setProperties({
|
|
1086
990
|
order,
|
|
1087
991
|
payload,
|
|
@@ -1102,6 +1006,9 @@ export default class OperationsOrdersIndexNewController extends BaseController {
|
|
|
1102
1006
|
customFields,
|
|
1103
1007
|
customFieldValues,
|
|
1104
1008
|
});
|
|
1009
|
+
|
|
1010
|
+
await this.removeRoutingControlPreview();
|
|
1011
|
+
this.removeOptimizedRoute();
|
|
1105
1012
|
this.resetInterface();
|
|
1106
1013
|
}
|
|
1107
1014
|
|