@fleetbase/fleetops-engine 0.6.44 → 0.6.45
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/entity/form.hbs +24 -0
- package/addon/components/entity/form.js +30 -0
- package/addon/components/leaflet-draw-control.js +1 -1
- package/addon/components/map/drawer/geofence-event-listing.js +5 -2
- package/addon/components/order/form/service-rate.hbs +1 -1
- package/addon/components/service-rate/details.hbs +6 -6
- package/addon/components/service-rate/form.hbs +28 -15
- package/addon/controllers/operations/orders/index/details/virtual.js +5 -0
- package/addon/controllers/operations/orders/index/details.js +27 -2
- package/addon/controllers/operations/service-rates/index/edit.js +1 -0
- package/addon/controllers/operations/service-rates/index/new.js +1 -0
- package/addon/helpers/f-to-int.js +6 -0
- package/addon/routes/application.js +3 -3
- package/addon/routes/operations/orders/index/details/virtual.js +1 -0
- package/addon/routes/operations/orders/index/details.js +19 -3
- package/addon/services/equipment-actions.js +4 -6
- package/addon/services/leaflet-map-manager.js +4 -0
- package/addon/services/maintenance-actions.js +4 -6
- package/addon/services/part-actions.js +4 -6
- package/addon/services/service-rate-actions.js +8 -3
- package/addon/templates/operations/orders/index/details/virtual.hbs +7 -1
- package/addon/utils/fleet-ops-options.js +33 -0
- package/addon/utils/serialize-payload.js +1 -1
- package/app/controllers/operations/orders/index/details/virtual.js +1 -0
- package/app/helpers/f-to-int.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/src/Http/Controllers/Api/v1/OrderController.php +5 -5
- package/server/src/Http/Controllers/Api/v1/PurchaseRateController.php +11 -3
- package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +4 -0
- package/server/src/Http/Controllers/Api/v1/ServiceRateController.php +6 -1
- package/server/src/Http/Requests/CreateServiceRateRequest.php +2 -2
- package/server/src/Http/Resources/v1/Order.php +0 -1
- package/server/src/Http/Resources/v1/ServiceQuote.php +2 -2
- package/server/src/Models/Entity.php +7 -0
- package/server/src/Models/Order.php +39 -7
- package/server/src/Models/ServiceRate.php +122 -30
- package/server/src/Observers/PurchaseRateObserver.php +3 -5
- package/server/src/Rules/ComputableAlgo.php +1 -3
- package/server/src/Support/Algo.php +87 -1
- package/server/src/Support/Utils.php +35 -0
|
@@ -23,6 +23,30 @@
|
|
|
23
23
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
|
|
24
24
|
<InputGroup @name={{t "common.name"}} @value={{@resource.name}} @helpText={{t "modals.entity-form.name-text"}} />
|
|
25
25
|
<InputGroup @name={{t "common.internal-id"}} @value={{@resource.internal_id}} @helpText={{t "modals.entity-form.id-text"}} />
|
|
26
|
+
<div class="space-y-2">
|
|
27
|
+
<InputGroup @name={{t "common.type"}} @helpText="Choose a common entity type or switch to a custom value.">
|
|
28
|
+
{{#if this.useCustomType}}
|
|
29
|
+
<Input @value={{@resource.type}} @type="text" class="w-full form-input" placeholder="Enter custom entity type" />
|
|
30
|
+
{{else}}
|
|
31
|
+
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
|
32
|
+
<PowerSelect
|
|
33
|
+
@options={{this.entityTypes}}
|
|
34
|
+
@selected={{this.selectedEntityType}}
|
|
35
|
+
@onChange={{this.selectEntityType}}
|
|
36
|
+
@placeholder="Select entity type"
|
|
37
|
+
@triggerClass="form-select"
|
|
38
|
+
as |type|
|
|
39
|
+
>
|
|
40
|
+
<div class="text-sm">
|
|
41
|
+
<div class="font-semibold normalize-in-trigger">{{type.label}}</div>
|
|
42
|
+
<div class="hide-from-trigger">{{type.description}}</div>
|
|
43
|
+
</div>
|
|
44
|
+
</PowerSelect>
|
|
45
|
+
</div>
|
|
46
|
+
{{/if}}
|
|
47
|
+
</InputGroup>
|
|
48
|
+
<Checkbox @value={{this.useCustomType}} @label="Use custom type" @onToggle={{this.toggleCustomType}} @alignItems="center" @labelClass="mb-0i" />
|
|
49
|
+
</div>
|
|
26
50
|
<InputGroup @name={{t "modals.entity-form.sku"}} @value={{@resource.sku}} @helpText={{t "modals.entity-form.sku-text"}} />
|
|
27
51
|
<div></div>
|
|
28
52
|
<InputGroup @name={{t "modals.entity-form.description"}} @helpText={{t "modals.entity-form.description-text"}} @wrapperClass="col-span-2">
|
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
2
|
import { inject as service } from '@ember/service';
|
|
3
|
+
import { action } from '@ember/object';
|
|
4
|
+
import { tracked } from '@glimmer/tracking';
|
|
3
5
|
import { task } from 'ember-concurrency';
|
|
6
|
+
import fleetOpsOptions from '../../utils/fleet-ops-options';
|
|
4
7
|
|
|
5
8
|
export default class EntityFormComponent extends Component {
|
|
6
9
|
@service fetch;
|
|
7
10
|
@service currentUser;
|
|
8
11
|
@service notifications;
|
|
12
|
+
@tracked useCustomType = false;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.useCustomType = this.selectedEntityType === undefined && Boolean(this.args.resource?.type);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get entityTypes() {
|
|
20
|
+
return fleetOpsOptions('entityTypes');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get selectedEntityType() {
|
|
24
|
+
return this.entityTypes.find((option) => option.value === this.args.resource?.type);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@action selectEntityType(option) {
|
|
28
|
+
this.useCustomType = false;
|
|
29
|
+
this.args.resource.type = option?.value ?? null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@action toggleCustomType(value) {
|
|
33
|
+
this.useCustomType = value;
|
|
34
|
+
|
|
35
|
+
if (!value && !this.selectedEntityType) {
|
|
36
|
+
this.args.resource.type = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
9
39
|
|
|
10
40
|
@task *handlePhotoUpload(file) {
|
|
11
41
|
try {
|
|
@@ -27,7 +27,7 @@ export default class LeafletDrawControl extends BaseLayer {
|
|
|
27
27
|
leafletOptions = ['draw', 'edit', 'remove', 'poly', 'position'];
|
|
28
28
|
|
|
29
29
|
@computed('leafletEvents.[]', 'args') get usedLeafletEvents() {
|
|
30
|
-
const leafletEvents = [...this.leafletEvents, ...Object.values(L.Draw
|
|
30
|
+
const leafletEvents = [...this.leafletEvents, ...Object.values(L.Draw?.Event ?? {})];
|
|
31
31
|
return leafletEvents.filter((eventName) => {
|
|
32
32
|
eventName = camelize(eventName.replace(':', ' '));
|
|
33
33
|
let methodName = `_${eventName}`;
|
|
@@ -2,6 +2,7 @@ import Component from '@glimmer/component';
|
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
4
|
import { action } from '@ember/object';
|
|
5
|
+
import { next } from '@ember/runloop';
|
|
5
6
|
|
|
6
7
|
export default class MapDrawerGeofenceEventListingComponent extends Component {
|
|
7
8
|
@service fetch;
|
|
@@ -12,8 +13,10 @@ export default class MapDrawerGeofenceEventListingComponent extends Component {
|
|
|
12
13
|
|
|
13
14
|
constructor() {
|
|
14
15
|
super(...arguments);
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
next(() => {
|
|
17
|
+
this.geofenceEventBus.subscribe(this.currentUser.companyId);
|
|
18
|
+
this.loadRecentEvents();
|
|
19
|
+
});
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
get events() {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
@name="serviceQuote"
|
|
59
59
|
@changed={{fn (mut @resource.service_quote_uuid)}}
|
|
60
60
|
/>
|
|
61
|
-
<label for={{serviceQuote.uuid}} class="ml-3 flex-1">{{serviceQuote.public_id}}</label>
|
|
61
|
+
<label for={{serviceQuote.uuid}} class="ml-3 flex-1 text-sm">{{serviceQuote.public_id}} ({{serviceQuote.service_rate_name}})</label>
|
|
62
62
|
<Badge @hideStatusDot={{true}} @status="info">
|
|
63
63
|
{{serviceQuote.request_id}}
|
|
64
64
|
</Badge>
|
|
@@ -106,11 +106,11 @@
|
|
|
106
106
|
</tr>
|
|
107
107
|
</thead>
|
|
108
108
|
<tbody>
|
|
109
|
-
{{#each @resource.
|
|
109
|
+
{{#each @resource.rateFees as |rateFee|}}
|
|
110
110
|
<tr>
|
|
111
111
|
<td>{{n-a rateFee.min}}</td>
|
|
112
112
|
<td>{{n-a rateFee.max}}</td>
|
|
113
|
-
<td>{{format-currency rateFee.fee @resource.currency}}</td>
|
|
113
|
+
<td>{{format-currency (f-to-int rateFee.fee) @resource.currency}}</td>
|
|
114
114
|
</tr>
|
|
115
115
|
{{else}}
|
|
116
116
|
<tr>
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
<div class="field-info-container">
|
|
159
159
|
<div class="field-name">Algorithm Code</div>
|
|
160
160
|
<div class="field-value">
|
|
161
|
-
<pre class="bg-gray-100 dark:bg-gray-
|
|
161
|
+
<pre class="bg-gray-100 dark:bg-gray-900 font-mono p-3 rounded text-xs overflow-x-auto"><code class="font-mono text-xs">{{n-a @resource.algorithm}}</code></pre>
|
|
162
162
|
</div>
|
|
163
163
|
</div>
|
|
164
164
|
</ContentPanel>
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
{{#if @resource.isParcelService}}
|
|
168
168
|
<ContentPanel @title={{t "service-rate.fields.percel-fee-title"}} @open={{true}} @wrapperClass="bordered-top">
|
|
169
169
|
<div class="space-y-2">
|
|
170
|
-
{{#each @resource.
|
|
170
|
+
{{#each @resource.parcelFees as |parcelFee|}}
|
|
171
171
|
<div class="dark:text-gray-100">
|
|
172
172
|
<div class="grid grid-cols-7">
|
|
173
173
|
<div class="flex flex-col items-start justify-start pt-3">
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
{{t "service-rate.fields.additional-fee"}}
|
|
243
243
|
</div>
|
|
244
244
|
<div class="field-value">
|
|
245
|
-
{{n-a (format-currency parcelFee.fee @resource.currency)}}
|
|
245
|
+
{{n-a (format-currency (f-to-int parcelFee.fee) @resource.currency)}}
|
|
246
246
|
</div>
|
|
247
247
|
</div>
|
|
248
248
|
</div>
|
|
@@ -323,4 +323,4 @@
|
|
|
323
323
|
</ContentPanel>
|
|
324
324
|
|
|
325
325
|
<CustomField::Yield @subject={{@resource}} @viewMode={{true}} @wrapperClass="bordered-top" />
|
|
326
|
-
</div>
|
|
326
|
+
</div>
|
|
@@ -176,7 +176,7 @@
|
|
|
176
176
|
</tr>
|
|
177
177
|
</thead>
|
|
178
178
|
<tbody>
|
|
179
|
-
{{#each @resource.
|
|
179
|
+
{{#each @resource.rateFees as |rateFee|}}
|
|
180
180
|
<tr>
|
|
181
181
|
<td>
|
|
182
182
|
<Input
|
|
@@ -252,25 +252,38 @@
|
|
|
252
252
|
{{else if @resource.isAlgorithm}}
|
|
253
253
|
<ContentPanel @title={{t "service-rate.fields.custom-algorithm-title"}} @open={{true}} @wrapperClass="bordered-top">
|
|
254
254
|
<InfoBlock class="mb-5">
|
|
255
|
-
|
|
256
|
-
{{t "service-rate.fields.custom-algorithm-info-second-message"}}
|
|
255
|
+
Define a custom formula for this service rate using variables wrapped in a single pair of curly braces.
|
|
257
256
|
|
|
258
257
|
<div class="block my-4 break-text">
|
|
259
258
|
<ul class="list-disc space-y-2 pl-16">
|
|
260
|
-
<li class="leading-5"><
|
|
261
|
-
|
|
262
|
-
<li class="leading-5"><
|
|
263
|
-
|
|
259
|
+
<li class="leading-5"><code>{distance}</code>, <code>{distance_m}</code>, <code>{distance_km}</code>, <code>{distance_mi}</code>: route distance in meters, kilometers, or miles.</li>
|
|
260
|
+
<li class="leading-5"><code>{time}</code>, <code>{time_s}</code>, <code>{time_min}</code>: route time in seconds or minutes.</li>
|
|
261
|
+
<li class="leading-5"><code>{stops}</code>: total service stops including pickup, dropoff, and intermediate waypoints.</li>
|
|
262
|
+
<li class="leading-5"><code>{waypoints}</code>: intermediate waypoint count only.</li>
|
|
263
|
+
<li class="leading-5"><code>{parcels}</code>, <code>{entities}</code>: parcel count and total payload entity count.</li>
|
|
264
|
+
<li class="leading-5"><code>{base_fee}</code>: the configured base fee for this service rate.</li>
|
|
265
|
+
<li class="leading-5">Supported functions: <code>max(a,b)</code>, <code>min(a,b)</code>, <code>ceil(x)</code>, <code>floor(x)</code>, <code>round(x)</code>.</li>
|
|
264
266
|
</ul>
|
|
265
267
|
</div>
|
|
266
268
|
|
|
267
|
-
<div>
|
|
268
|
-
<
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
269
|
+
<div class="space-y-3">
|
|
270
|
+
<div>
|
|
271
|
+
<h4 class="mb-1 text-sm font-semibold">{{t "service-rate.fields.example"}} 1</h4>
|
|
272
|
+
<div class="mb-2 text-inherit">Charge by distance in miles after the first 15 miles, plus the base fee.</div>
|
|
273
|
+
<code class="text-xs font-mono">max({distance_mi} - 15, 0) * 5 + {base_fee}</code>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div>
|
|
277
|
+
<h4 class="mb-1 text-sm font-semibold">{{t "service-rate.fields.example"}} 2</h4>
|
|
278
|
+
<div class="mb-2 text-inherit">Charge extra for stops after the first 2 service stops.</div>
|
|
279
|
+
<code class="text-xs font-mono">max({stops} - 2, 0) * 10 + {base_fee}</code>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div>
|
|
283
|
+
<h4 class="mb-1 text-sm font-semibold">{{t "service-rate.fields.example"}} 3</h4>
|
|
284
|
+
<div class="mb-2 text-inherit">Combine waypoint, parcel, and distance surcharges in one formula.</div>
|
|
285
|
+
<code class="text-xs font-mono">max({waypoints}, 0) * 15 + max({parcels} - 3, 0) * 2 + max({distance_km} - 25, 0) * 0.5 + {base_fee}</code>
|
|
272
286
|
</div>
|
|
273
|
-
<code class="text-xs font-mono">(( {distance} / 50 ) * .05 ) + (( {time} / 60 ) * .01)</code>
|
|
274
287
|
</div>
|
|
275
288
|
</InfoBlock>
|
|
276
289
|
|
|
@@ -287,7 +300,7 @@
|
|
|
287
300
|
{{else if @resource.isParcelService}}
|
|
288
301
|
<ContentPanel @title={{t "service-rate.fields.percel-fee-title"}} @open={{true}} @wrapperClass="bordered-top">
|
|
289
302
|
<div class="space-y-2">
|
|
290
|
-
{{#each @resource.
|
|
303
|
+
{{#each @resource.parcelFees as |parcelFee|}}
|
|
291
304
|
<div class="dark:text-gray-100">
|
|
292
305
|
<div class="grid grid-cols-7">
|
|
293
306
|
<div class="flex flex-col items-start justify-start pt-3">
|
|
@@ -541,4 +554,4 @@
|
|
|
541
554
|
</InputGroup>
|
|
542
555
|
{{/if}}
|
|
543
556
|
</ContentPanel>
|
|
544
|
-
</div>
|
|
557
|
+
</div>
|
|
@@ -16,6 +16,7 @@ export default class OperationsOrdersIndexDetailsController extends Controller {
|
|
|
16
16
|
@service universe;
|
|
17
17
|
@service sidebar;
|
|
18
18
|
@tracked routingControl;
|
|
19
|
+
@tracked routingCompleted = false;
|
|
19
20
|
|
|
20
21
|
get tabs() {
|
|
21
22
|
const registeredTabs = this.menuService.getMenuItems('fleet-ops:component:order:details');
|
|
@@ -109,7 +110,19 @@ export default class OperationsOrdersIndexDetailsController extends Controller {
|
|
|
109
110
|
@action async setup() {
|
|
110
111
|
// Change to map layout and display order route
|
|
111
112
|
this.index.changeLayout('map');
|
|
112
|
-
this.
|
|
113
|
+
this.routingCompleted = false;
|
|
114
|
+
this.routingControl = await this.leafletMapManager.addRoutingControl(this.model.routeWaypoints, {
|
|
115
|
+
onRouteFound: () => {
|
|
116
|
+
this.routingCompleted = true;
|
|
117
|
+
},
|
|
118
|
+
onRoutingError: () => {
|
|
119
|
+
this.routingCompleted = true;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
if (!this.routingControl) {
|
|
124
|
+
this.routingCompleted = true;
|
|
125
|
+
}
|
|
113
126
|
|
|
114
127
|
// Hide sidebar
|
|
115
128
|
this.sidebar.hideNow();
|
|
@@ -125,7 +138,19 @@ export default class OperationsOrdersIndexDetailsController extends Controller {
|
|
|
125
138
|
async (_msg, { reloadable }) => {
|
|
126
139
|
if (reloadable) {
|
|
127
140
|
await this.hostRouter.refresh();
|
|
128
|
-
this.
|
|
141
|
+
this.routingCompleted = false;
|
|
142
|
+
this.routingControl = await this.leafletMapManager.replaceRoutingControl(this.model.routeWaypoints, this.routingControl, {
|
|
143
|
+
onRouteFound: () => {
|
|
144
|
+
this.routingCompleted = true;
|
|
145
|
+
},
|
|
146
|
+
onRoutingError: () => {
|
|
147
|
+
this.routingCompleted = true;
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (!this.routingControl) {
|
|
152
|
+
this.routingCompleted = true;
|
|
153
|
+
}
|
|
129
154
|
}
|
|
130
155
|
},
|
|
131
156
|
{ debounceMs: 250 }
|
|
@@ -20,14 +20,14 @@ export default class ApplicationRoute extends Route {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
beforeModel() {
|
|
23
|
+
async beforeModel() {
|
|
24
24
|
if (this.abilities.cannot('fleet-ops see extension')) {
|
|
25
25
|
this.notifications.warning(this.intl.t('common.unauthorized-access'));
|
|
26
26
|
return this.hostRouter.transitionTo('console');
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
this.location.getUserLocation();
|
|
30
|
-
this.#loadRoutingSettings();
|
|
29
|
+
await this.location.getUserLocation();
|
|
30
|
+
await this.#loadRoutingSettings();
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async #loadRoutingSettings() {
|
|
@@ -19,5 +19,6 @@ export default class OperationsOrdersIndexDetailsVirtualRoute extends Route {
|
|
|
19
19
|
setupController(controller) {
|
|
20
20
|
super.setupController(...arguments);
|
|
21
21
|
controller.resource = this.modelFor('operations.orders.index.details');
|
|
22
|
+
controller.detailsController = this.controllerFor('operations.orders.index.details');
|
|
22
23
|
}
|
|
23
24
|
}
|
|
@@ -15,9 +15,12 @@ export default class OperationsOrdersIndexDetailsRoute extends Route {
|
|
|
15
15
|
@action willTransition(transition) {
|
|
16
16
|
const fromName = transition.from?.name;
|
|
17
17
|
const toName = transition.to?.name;
|
|
18
|
+
const orderDetailsRoute = 'console.fleet-ops.operations.orders.index.details';
|
|
18
19
|
|
|
19
|
-
// only cleanup when
|
|
20
|
-
|
|
20
|
+
// only cleanup when leaving the order details route tree entirely
|
|
21
|
+
const isLeavingOrderDetails = fromName?.startsWith(orderDetailsRoute) && !toName?.startsWith(orderDetailsRoute);
|
|
22
|
+
|
|
23
|
+
if (isLeavingOrderDetails) {
|
|
21
24
|
const controller = this.controllerFor('operations.orders.index.details');
|
|
22
25
|
const rc = controller.routingControl;
|
|
23
26
|
|
|
@@ -55,7 +58,20 @@ export default class OperationsOrdersIndexDetailsRoute extends Route {
|
|
|
55
58
|
return this.store.queryRecord('order', {
|
|
56
59
|
public_id,
|
|
57
60
|
single: true,
|
|
58
|
-
with: [
|
|
61
|
+
with: [
|
|
62
|
+
'payload',
|
|
63
|
+
'driverAssigned',
|
|
64
|
+
'orderConfig',
|
|
65
|
+
'customer',
|
|
66
|
+
'facilitator',
|
|
67
|
+
'trackingStatuses',
|
|
68
|
+
'trackingNumber',
|
|
69
|
+
'purchaseRate',
|
|
70
|
+
'purchaseRate.serviceQuote',
|
|
71
|
+
'purchaseRate.serviceQuote.items',
|
|
72
|
+
'comments',
|
|
73
|
+
'files',
|
|
74
|
+
],
|
|
59
75
|
});
|
|
60
76
|
}
|
|
61
77
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import ResourceActionService
|
|
1
|
+
import ResourceActionService from '@fleetbase/ember-core/services/resource-action';
|
|
2
2
|
|
|
3
3
|
export default class EquipmentActionsService extends ResourceActionService {
|
|
4
|
-
|
|
4
|
+
get defaultCurrency() {
|
|
5
|
+
return this.currentUser?.company?.currency || this.currentUser.currency || 'USD';
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
constructor() {
|
|
7
9
|
super(...arguments);
|
|
@@ -12,10 +14,6 @@ export default class EquipmentActionsService extends ResourceActionService {
|
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
get defaultCurrency() {
|
|
16
|
-
return this.currentUser?.company?.currency || 'USD';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
17
|
transition = {
|
|
20
18
|
view: (equipment) => this.transitionTo('maintenance.equipment.index.details', equipment),
|
|
21
19
|
edit: (equipment) => this.transitionTo('maintenance.equipment.index.edit', equipment),
|
|
@@ -270,6 +270,10 @@ export default class LeafletMapManagerService extends Service {
|
|
|
270
270
|
this.route = routes[0];
|
|
271
271
|
});
|
|
272
272
|
|
|
273
|
+
routingControl.on('routingerror', (event) => {
|
|
274
|
+
options?.onRoutingError?.(event);
|
|
275
|
+
});
|
|
276
|
+
|
|
273
277
|
this.positionWaypoints(waypoints);
|
|
274
278
|
|
|
275
279
|
return routingControl;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import ResourceActionService
|
|
1
|
+
import ResourceActionService from '@fleetbase/ember-core/services/resource-action';
|
|
2
2
|
|
|
3
3
|
export default class MaintenanceActionsService extends ResourceActionService {
|
|
4
|
-
|
|
4
|
+
get defaultCurrency() {
|
|
5
|
+
return this.currentUser?.company?.currency || this.currentUser.currency || 'USD';
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
constructor() {
|
|
7
9
|
super(...arguments);
|
|
@@ -12,10 +14,6 @@ export default class MaintenanceActionsService extends ResourceActionService {
|
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
get defaultCurrency() {
|
|
16
|
-
return this.currentUser?.company?.currency || 'USD';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
17
|
transition = {
|
|
20
18
|
view: (maintenance) => this.transitionTo('maintenance.maintenances.index.details', maintenance),
|
|
21
19
|
edit: (maintenance) => this.transitionTo('maintenance.maintenances.index.edit', maintenance),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import ResourceActionService
|
|
1
|
+
import ResourceActionService from '@fleetbase/ember-core/services/resource-action';
|
|
2
2
|
|
|
3
3
|
export default class PartActionsService extends ResourceActionService {
|
|
4
|
-
|
|
4
|
+
get defaultCurrency() {
|
|
5
|
+
return this.currentUser?.company?.currency || this.currentUser.currency || 'USD';
|
|
6
|
+
}
|
|
5
7
|
|
|
6
8
|
constructor() {
|
|
7
9
|
super(...arguments);
|
|
@@ -12,10 +14,6 @@ export default class PartActionsService extends ResourceActionService {
|
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
get defaultCurrency() {
|
|
16
|
-
return this.currentUser?.company?.currency || 'USD';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
17
|
transition = {
|
|
20
18
|
view: (part) => this.transitionTo('maintenance.parts.index.details', part),
|
|
21
19
|
edit: (part) => this.transitionTo('maintenance.parts.index.edit', part),
|
|
@@ -6,13 +6,17 @@ import serializePayload from '../utils/serialize-payload';
|
|
|
6
6
|
export default class ServiceRateActionsService extends ResourceActionService {
|
|
7
7
|
modelNamePath = 'service_name';
|
|
8
8
|
|
|
9
|
+
get defaultCurrency() {
|
|
10
|
+
return this.currentUser?.company?.currency || this.currentUser.currency || 'USD';
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
constructor() {
|
|
10
14
|
super(...arguments);
|
|
11
15
|
this.initialize('service-rate', {
|
|
12
16
|
defaultAttributes: {
|
|
13
17
|
rate_calculation_method: 'per_meter',
|
|
14
18
|
per_meter_unit: 'm',
|
|
15
|
-
currency: this.
|
|
19
|
+
currency: this.defaultCurrency,
|
|
16
20
|
parcel_fees: this.#getDefaultParcelFees(),
|
|
17
21
|
},
|
|
18
22
|
});
|
|
@@ -118,12 +122,13 @@ export default class ServiceRateActionsService extends ResourceActionService {
|
|
|
118
122
|
|
|
119
123
|
const hasFacilitator = !isNone(order.facilitator);
|
|
120
124
|
const facilitatorServiceType = order.facilitator?.get('service_types.firstObject.key') ?? order.type;
|
|
125
|
+
const routeSummary = order.route?.summary ?? order.route?.details?.summary ?? {};
|
|
121
126
|
|
|
122
127
|
try {
|
|
123
128
|
const serviceQuotes = yield this.fetch.post('service-quotes/preliminary', {
|
|
124
129
|
payload: serializePayload(order.payload),
|
|
125
|
-
distance:
|
|
126
|
-
time:
|
|
130
|
+
distance: routeSummary.totalDistance,
|
|
131
|
+
time: routeSummary.totalTime,
|
|
127
132
|
service_type: hasFacilitator ? facilitatorServiceType : order.type,
|
|
128
133
|
facilitator: order.facilitator?.public_id,
|
|
129
134
|
scheduled_at: order.scheduled_at,
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{{
|
|
1
|
+
{{#if this.parent.routingCompleted}}
|
|
2
|
+
{{component (lazy-engine-component @model.component) resource=this.resource order=this.resource params=@model.componentParams}}
|
|
3
|
+
{{else}}
|
|
4
|
+
<div class="flex items-center justify-center py-12">
|
|
5
|
+
<Spinner />
|
|
6
|
+
</div>
|
|
7
|
+
{{/if}}
|
|
@@ -123,6 +123,38 @@ export const contactTypes = [
|
|
|
123
123
|
{ label: 'Dispatcher', value: 'dispatcher', description: 'Scheduling and operations contact' },
|
|
124
124
|
];
|
|
125
125
|
|
|
126
|
+
export const entityTypes = [
|
|
127
|
+
{ label: 'Parcel', value: 'parcel', description: 'Standard parcel or package used by parcel-based pricing.' },
|
|
128
|
+
{ label: 'Package', value: 'package', description: 'Generic package or boxed item.' },
|
|
129
|
+
{ label: 'Product', value: 'product', description: 'Sellable product or catalog item.' },
|
|
130
|
+
{ label: 'Item', value: 'item', description: 'Generic inventory item when no stronger type applies.' },
|
|
131
|
+
{ label: 'SKU / Inventory Unit', value: 'sku', description: 'Stock keeping unit or tracked inventory piece.' },
|
|
132
|
+
{ label: 'Document', value: 'document', description: 'Envelopes, paperwork, and lightweight documents.' },
|
|
133
|
+
{ label: 'Envelope', value: 'envelope', description: 'Flat document mailer or envelope.' },
|
|
134
|
+
{ label: 'Box', value: 'box', description: 'Single boxed shipment unit.' },
|
|
135
|
+
{ label: 'Carton', value: 'carton', description: 'Single carton, box, or case.' },
|
|
136
|
+
{ label: 'Case', value: 'case', description: 'Case-packed goods or packaged case.' },
|
|
137
|
+
{ label: 'Bundle', value: 'bundle', description: 'Bundled or strapped items shipped together.' },
|
|
138
|
+
{ label: 'Bag', value: 'bag', description: 'Bagged goods, sacks, or pouches.' },
|
|
139
|
+
{ label: 'Tote', value: 'tote', description: 'Reusable tote, bin, or container.' },
|
|
140
|
+
{ label: 'Crate', value: 'crate', description: 'Rigid crate or caged shipment unit.' },
|
|
141
|
+
{ label: 'Pallet', value: 'pallet', description: 'Palletized freight or grouped cargo.' },
|
|
142
|
+
{ label: 'Container', value: 'container', description: 'Containerized shipment or storage container.' },
|
|
143
|
+
{ label: 'Freight', value: 'freight', description: 'Loose freight or larger shipment unit.' },
|
|
144
|
+
{ label: 'Cargo', value: 'cargo', description: 'General cargo not classified more specifically.' },
|
|
145
|
+
{ label: 'Equipment', value: 'equipment', description: 'Tools, machines, or operational equipment.' },
|
|
146
|
+
{ label: 'Machinery', value: 'machinery', description: 'Industrial machinery or large mechanical unit.' },
|
|
147
|
+
{ label: 'Asset', value: 'asset', description: 'Tracked business asset or returnable item.' },
|
|
148
|
+
{ label: 'Return', value: 'return', description: 'Returned goods or reverse-logistics item.' },
|
|
149
|
+
{ label: 'Sample', value: 'sample', description: 'Sample item for trial, demo, or inspection.' },
|
|
150
|
+
{ label: 'Spare Part', value: 'spare_part', description: 'Replacement component or service part.' },
|
|
151
|
+
{ label: 'Medical Supply', value: 'medical_supply', description: 'Healthcare, lab, or medical delivery item.' },
|
|
152
|
+
{ label: 'Food', value: 'food', description: 'Prepared food, meal, or grocery item.' },
|
|
153
|
+
{ label: 'Beverage', value: 'beverage', description: 'Drink, liquid refreshment, or bottled shipment.' },
|
|
154
|
+
{ label: 'Retail Goods', value: 'retail_goods', description: 'General consumer or retail merchandise.' },
|
|
155
|
+
{ label: 'Passenger', value: 'passenger', description: 'Person transported as part of the order payload.' },
|
|
156
|
+
];
|
|
157
|
+
|
|
126
158
|
export const contactStatuses = [
|
|
127
159
|
{ label: 'Active', value: 'active', description: 'Contact is valid and in use' },
|
|
128
160
|
{ label: 'Inactive', value: 'inactive', description: 'Contact is no longer valid' },
|
|
@@ -821,6 +853,7 @@ export default function fleetOpsOptions(key) {
|
|
|
821
853
|
fleetTypes,
|
|
822
854
|
fleetStatuses,
|
|
823
855
|
contactTypes,
|
|
856
|
+
entityTypes,
|
|
824
857
|
contactStatuses,
|
|
825
858
|
fuelReportTypes,
|
|
826
859
|
fuelReportStatuses,
|
|
@@ -5,7 +5,7 @@ export default function serializePayload(payload) {
|
|
|
5
5
|
const serialized = {
|
|
6
6
|
pickup: serializeModel(payload.pickup),
|
|
7
7
|
dropoff: serializeModel(payload.dropoff),
|
|
8
|
-
|
|
8
|
+
entities: serializeArray(payload.entities),
|
|
9
9
|
waypoints: serializeArray(payload.waypoints),
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/controllers/operations/orders/index/details/virtual';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@fleetbase/fleetops-engine/helpers/f-to-int';
|
package/composer.json
CHANGED
package/extension.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/fleetops-engine",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.45",
|
|
4
4
|
"description": "Fleet & Transport Management Extension for Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "fleet-ops"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@babel/core": "^7.23.2",
|
|
45
45
|
"@fleetbase/ember-core": "^0.3.18",
|
|
46
46
|
"@fleetbase/ember-ui": "^0.3.26",
|
|
47
|
-
"@fleetbase/fleetops-data": "^0.1.
|
|
47
|
+
"@fleetbase/fleetops-data": "^0.1.30",
|
|
48
48
|
"@fleetbase/leaflet-routing-machine": "^3.2.17",
|
|
49
49
|
"@fortawesome/ember-fontawesome": "^2.0.0",
|
|
50
50
|
"@fortawesome/fontawesome-svg-core": "6.4.0",
|