@fleetbase/fleetops-engine 0.6.12 → 0.6.14

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 (52) hide show
  1. package/addon/components/customer/orders.js +6 -8
  2. package/addon/components/layout/fleet-ops-sidebar.js +8 -0
  3. package/addon/components/live-map.js +6 -8
  4. package/addon/components/order-config/fields-editor.js +1 -1
  5. package/addon/components/route-optimization-engine-select-button.hbs +25 -0
  6. package/addon/components/route-optimization-engine-select-button.js +13 -0
  7. package/addon/controllers/operations/orders/index/new.js +52 -150
  8. package/addon/controllers/operations/orders/index/view.js +8 -9
  9. package/addon/controllers/settings/routing.js +47 -0
  10. package/addon/engine.js +27 -0
  11. package/addon/routes/application.js +8 -0
  12. package/addon/routes/operations/orders/index/view.js +12 -3
  13. package/addon/routes/settings/routing.js +3 -0
  14. package/addon/routes.js +1 -0
  15. package/addon/services/leaflet-router-control.js +64 -0
  16. package/addon/services/osrm.js +46 -0
  17. package/addon/services/route-optimization-interface.js +9 -0
  18. package/addon/services/route-optimization.js +67 -0
  19. package/addon/templates/operations/orders/index/new.hbs +21 -16
  20. package/addon/templates/settings/routing.hbs +32 -0
  21. package/app/components/route-optimization-engine-select-button.js +1 -0
  22. package/app/controllers/settings/routing.js +1 -0
  23. package/app/routes/settings/routing.js +1 -0
  24. package/app/services/leaflet-router-control.js +1 -0
  25. package/app/services/osrm.js +1 -0
  26. package/app/services/route-optimization-interface.js +1 -0
  27. package/app/services/route-optimization.js +1 -0
  28. package/app/templates/settings/routing.js +1 -0
  29. package/composer.json +1 -1
  30. package/extension.json +1 -1
  31. package/package.json +2 -2
  32. package/server/config/api.php +10 -1
  33. package/server/src/Auth/Schemas/FleetOps.php +5 -0
  34. package/server/src/Events/EntityActivityChanged.php +118 -0
  35. package/server/src/Events/EntityCompleted.php +118 -0
  36. package/server/src/Events/OrderCanceled.php +2 -2
  37. package/server/src/Events/OrderCompleted.php +2 -2
  38. package/server/src/Events/OrderDispatched.php +2 -2
  39. package/server/src/Events/OrderDriverAssigned.php +1 -1
  40. package/server/src/Events/OrderFailed.php +2 -2
  41. package/server/src/Events/OrderReady.php +1 -1
  42. package/server/src/Events/OrderStarted.php +1 -1
  43. package/server/src/Events/WaypointActivityChanged.php +2 -2
  44. package/server/src/Events/WaypointCompleted.php +2 -2
  45. package/server/src/Http/Controllers/Internal/v1/OrderController.php +1 -1
  46. package/server/src/Http/Controllers/Internal/v1/SettingController.php +30 -0
  47. package/server/src/Listeners/NotifyOrderEvent.php +1 -0
  48. package/server/src/Models/Order.php +7 -1
  49. package/server/src/Models/Payload.php +19 -8
  50. package/server/src/Models/Place.php +49 -13
  51. package/server/src/routes.php +2 -0
  52. package/translations/en-us.yaml +5 -0
@@ -18,10 +18,10 @@ class OrderDispatched extends ResourceLifecycleEvent
18
18
  /**
19
19
  * Assosciated activity which triggered the event.
20
20
  */
21
- public ?Activity $activity;
21
+ public ?Activity $activity = null;
22
22
 
23
23
  /**
24
24
  * Assosciated order waypoint which event is for.
25
25
  */
26
- public ?Waypoint $waypoint;
26
+ public ?Waypoint $waypoint = null;
27
27
  }
@@ -17,5 +17,5 @@ class OrderDriverAssigned extends ResourceLifecycleEvent
17
17
  /**
18
18
  * Assosciated activity which triggered the event.
19
19
  */
20
- public ?Activity $activity;
20
+ public ?Activity $activity = null;
21
21
  }
@@ -18,10 +18,10 @@ class OrderFailed extends ResourceLifecycleEvent
18
18
  /**
19
19
  * Assosciated activity which triggered the event.
20
20
  */
21
- public ?Activity $activity;
21
+ public ?Activity $activity = null;
22
22
 
23
23
  /**
24
24
  * Assosciated order waypoint which event is for.
25
25
  */
26
- public ?Waypoint $waypoint;
26
+ public ?Waypoint $waypoint = null;
27
27
  }
@@ -17,5 +17,5 @@ class OrderReady extends ResourceLifecycleEvent
17
17
  /**
18
18
  * Assosciated activity which triggered the event.
19
19
  */
20
- public ?Activity $activity;
20
+ public ?Activity $activity = null;
21
21
  }
@@ -17,5 +17,5 @@ class OrderStarted extends ResourceLifecycleEvent
17
17
  /**
18
18
  * Assosciated activity which triggered the event.
19
19
  */
20
- public ?Activity $activity;
20
+ public ?Activity $activity = null;
21
21
  }
@@ -21,12 +21,12 @@ class WaypointActivityChanged implements ShouldBroadcast
21
21
  /**
22
22
  * The waypoint which is completed.
23
23
  */
24
- public Waypoint $waypoint;
24
+ public ?Waypoint $waypoint = null;
25
25
 
26
26
  /**
27
27
  * The activity which triggered the waypoint completed.
28
28
  */
29
- public Activity $activity;
29
+ public ?Activity $activity = null;
30
30
 
31
31
  /**
32
32
  * The event id.
@@ -21,12 +21,12 @@ class WaypointCompleted implements ShouldBroadcast
21
21
  /**
22
22
  * The waypoint which is completed.
23
23
  */
24
- public Waypoint $waypoint;
24
+ public ?Waypoint $waypoint = null;
25
25
 
26
26
  /**
27
27
  * The activity which triggered the waypoint completed.
28
28
  */
29
- public Activity $activity;
29
+ public ?Activity $activity = null;
30
30
 
31
31
  /**
32
32
  * The event id.
@@ -354,7 +354,7 @@ class OrderController extends FleetOpsController
354
354
  */
355
355
  public function bulkCancel(BulkActionRequest $request)
356
356
  {
357
- /** @var Order */
357
+ /** @var \Illuminate\Database\Eloquent\Collection $orders */
358
358
  $orders = Order::whereIn('uuid', $request->input('ids'))->get();
359
359
 
360
360
  $count = $orders->count();
@@ -174,4 +174,34 @@ class SettingController extends Controller
174
174
  'notificationSettings' => $notificationSettings,
175
175
  ]);
176
176
  }
177
+
178
+ /**
179
+ * Save routing settings.
180
+ *
181
+ * @param Request $request the HTTP request object containing the routing settings data
182
+ *
183
+ * @return \Illuminate\Http\JsonResponse a JSON response
184
+ */
185
+ public function saveRoutingSettings(Request $request)
186
+ {
187
+ $router = $request->input('router');
188
+ Setting::configureCompany('routing', ['router' => $router]);
189
+
190
+ return response()->json([
191
+ 'status' => 'ok',
192
+ 'message' => 'Routing settings succesfully saved.',
193
+ ]);
194
+ }
195
+
196
+ /**
197
+ * Retrieve and return the routing settings.
198
+ *
199
+ * @return \Illuminate\Http\JsonResponse
200
+ */
201
+ public function getRoutingSettings()
202
+ {
203
+ $routingSettings = Setting::lookupCompany('routing', ['router' => 'osrm']);
204
+
205
+ return response()->json($routingSettings);
206
+ }
177
207
  }
@@ -34,6 +34,7 @@ class NotifyOrderEvent implements ShouldQueue
34
34
  $reason = $event->activity ? $event->activity->get('details') : '';
35
35
  NotificationRegistry::notify(OrderCanceled::class, $order, $reason, $event->waypoint);
36
36
  }
37
+
37
38
  if ($event instanceof \Fleetbase\FleetOps\Events\OrderCompleted) {
38
39
  NotificationRegistry::notify(OrderCompleted::class, $order, $event->waypoint);
39
40
  }
@@ -1173,6 +1173,12 @@ class Order extends Model
1173
1173
  {
1174
1174
  $this->status = 'canceled';
1175
1175
 
1176
+ // if saving update the status and add the cancel activity
1177
+ $this->loadMissing('orderConfig');
1178
+ $canceledActivity = $this->orderConfig->getCanceledActivity();
1179
+ $this->updateActivity($canceledActivity);
1180
+
1181
+ // trigger integrated vendor cancelation
1176
1182
  if ($this->isIntegratedVendorOrder()) {
1177
1183
  $api = $this->facilitator->api();
1178
1184
 
@@ -1181,7 +1187,7 @@ class Order extends Model
1181
1187
  }
1182
1188
  }
1183
1189
 
1184
- return event(new OrderCanceled($this));
1190
+ return dispatch(fn () => event(new OrderCanceled($this)))->afterCommit();
1185
1191
  }
1186
1192
 
1187
1193
  /**
@@ -3,6 +3,8 @@
3
3
  namespace Fleetbase\FleetOps\Models;
4
4
 
5
5
  use Fleetbase\Casts\Json;
6
+ use Fleetbase\FleetOps\Events\EntityActivityChanged;
7
+ use Fleetbase\FleetOps\Events\EntityCompleted;
6
8
  use Fleetbase\FleetOps\Events\WaypointActivityChanged;
7
9
  use Fleetbase\FleetOps\Events\WaypointCompleted;
8
10
  use Fleetbase\FleetOps\Flow\Activity;
@@ -337,11 +339,13 @@ class Payload extends Model
337
339
  }
338
340
 
339
341
  // Handle customer assosciation for waypoint
340
- if (is_array($attributes) && Utils::notEmpty($attributes['customer_uuid']) && Utils::notEmpty($attributes['customer_type'])) {
341
- $customerTypeNamespace = Utils::getMutationType($attributes['customer_type']);
342
- $customerExists = app($customerTypeNamespace)->where('uuid', $attributes['customer_uuid'])->exists();
342
+ $customerId = data_get($attributes, 'customer_uuid');
343
+ $customerType = data_get($attributes, 'customer_type', 'fleetops:contact');
344
+ if ($customerId && $customerType) {
345
+ $customerTypeNamespace = Utils::getMutationType($customerType);
346
+ $customerExists = app($customerTypeNamespace)->where('uuid', $customerId)->exists();
343
347
  if ($customerExists) {
344
- $waypoint['customer_uuid'] = $attributes['customer_uuid'];
348
+ $waypoint['customer_uuid'] = $customerId;
345
349
  $waypoint['customer_type'] = $customerTypeNamespace;
346
350
  }
347
351
  }
@@ -392,11 +396,13 @@ class Payload extends Model
392
396
  }
393
397
 
394
398
  // Handle customer assosciation for waypoint
395
- if (is_array($attributes) && Utils::notEmpty($attributes['customer_uuid']) && Utils::notEmpty($attributes['customer_type'])) {
396
- $customerTypeNamespace = Utils::getMutationType($attributes['customer_type']);
397
- $customerExists = app($customerTypeNamespace)->where('uuid', $attributes['customer_uuid'])->exists();
399
+ $customerId = data_get($attributes, 'customer_uuid');
400
+ $customerType = data_get($attributes, 'customer_type', 'fleetops:contact');
401
+ if ($customerId && $customerType) {
402
+ $customerTypeNamespace = Utils::getMutationType($customerType);
403
+ $customerExists = app($customerTypeNamespace)->where('uuid', $customerId)->exists();
398
404
  if ($customerExists) {
399
- $waypoint['customer_uuid'] = $attributes['customer_uuid'];
405
+ $waypoint['customer_uuid'] = $customerId;
400
406
  $waypoint['customer_type'] = $customerTypeNamespace;
401
407
  }
402
408
  }
@@ -792,6 +798,11 @@ class Payload extends Model
792
798
  $entities = $this->entities->where('destination_uuid', $this->current_waypoint_uuid);
793
799
  foreach ($entities as $entity) {
794
800
  $entity->insertActivity($activity, $location, $proof);
801
+ if ($activity && $activity->complete()) {
802
+ event(new EntityCompleted($entity, $activity));
803
+ } else {
804
+ event(new EntityActivityChanged($entity, $activity));
805
+ }
795
806
  }
796
807
 
797
808
  // if this activity completes the waypoint notify waypoint customer
@@ -366,6 +366,22 @@ class Place extends Model
366
366
  return static::createFromGoogleAddress($results->first(), $saveInstance);
367
367
  }
368
368
 
369
+ /**
370
+ * Create a new Place instance from a geocoding lookup.
371
+ *
372
+ * @return \Fleetbase\Models\Place|null
373
+ */
374
+ public static function getValuesFromGeocodingLookup(string $address): array
375
+ {
376
+ $results = \Geocoder\Laravel\Facades\Geocoder::geocode($address)->get();
377
+
378
+ if ($results->isEmpty() || !$results->first()) {
379
+ return ['street1' => $address];
380
+ }
381
+
382
+ return static::getGoogleAddressArray($results->first());
383
+ }
384
+
369
385
  /**
370
386
  * Create a new Place instance from a geocoding lookup.
371
387
  *
@@ -482,12 +498,12 @@ class Place extends Model
482
498
  if (is_string($place)) {
483
499
  // Check if $place is a valid public_id, return matching Place object if found
484
500
  if (Utils::isPublicId($place)) {
485
- return Place::where('public_id', $place)->first();
501
+ return static::where('public_id', $place)->first();
486
502
  }
487
503
 
488
504
  // Check if $place is a valid uuid, return matching Place object if found
489
505
  if (Str::isUuid($place)) {
490
- return Place::where('uuid', $place)->first();
506
+ return static::where('uuid', $place)->first();
491
507
  }
492
508
 
493
509
  // Attempt to find by address or name
@@ -521,12 +537,27 @@ class Place extends Model
521
537
  $uuid = data_get($place, 'uuid');
522
538
 
523
539
  // If $place has a valid uuid and a matching Place object exists, return the uuid
524
- if (Str::isUuid($uuid) && $existingPlace = Place::where('uuid', $uuid)->first()) {
540
+ if (Str::isUuid($uuid) && $existingPlace = static::where('uuid', $uuid)->first()) {
525
541
  return $existingPlace;
526
542
  }
527
543
 
528
- // Otherwise, create a new Place object with the given attributes
529
- return Place::create($place);
544
+ // If has $attributes['address']
545
+ if (!empty($place['address'])) {
546
+ return static::createFromGeocodingLookup($place['address'], $saveInstance);
547
+ }
548
+
549
+ // Perform google lookup to fill address
550
+ $street1 = $place['street1'];
551
+ if ($street1) {
552
+ return static::create(array_merge($place, static::getValuesFromGeocodingLookup($street1)));
553
+ }
554
+
555
+ // Otherwise, create a new Place owith the given attributes
556
+ if (empty($place['location'])) {
557
+ $place['location'] = new SpatialPoint(0, 0);
558
+ }
559
+
560
+ return static::create($place);
530
561
  }
531
562
  // If $place is a GoogleAddress object
532
563
  elseif ($place instanceof \Geocoder\Provider\GoogleMaps\Model\GoogleAddress) {
@@ -547,10 +578,10 @@ class Place extends Model
547
578
  {
548
579
  if (Utils::isCoordinatesStrict($place)) {
549
580
  // create a place from coordinates using reverse loopup
550
- return Place::insertFromCoordinates($place);
581
+ return static::insertFromCoordinates($place);
551
582
  } elseif (is_string($place)) {
552
583
  if (Utils::isPublicId($place)) {
553
- $resolvedPlace = Place::where('public_id', $place)->first();
584
+ $resolvedPlace = static::where('public_id', $place)->first();
554
585
 
555
586
  if ($resolvedPlace) {
556
587
  return $resolvedPlace->uuid;
@@ -558,14 +589,19 @@ class Place extends Model
558
589
  }
559
590
 
560
591
  if (Str::isUuid($place)) {
561
- $resolvedPlace = Place::where('uuid', $place)->first();
592
+ $resolvedPlace = static::where('uuid', $place)->first();
562
593
 
563
594
  if ($resolvedPlace) {
564
595
  return $resolvedPlace->uuid;
565
596
  }
566
597
  }
567
598
 
568
- return Place::insertFromGeocodingLookup($place);
599
+ // handle address if set
600
+ if (!empty($place['address'])) {
601
+ return static::insertFromGeocodingLookup($place['address']);
602
+ }
603
+
604
+ return static::insertFromGeocodingLookup($place);
569
605
  } elseif (is_array($place) || is_object($place)) {
570
606
  // if place already exists just return uuid
571
607
  if (static::isValidPlaceUuid(data_get($place, 'uuid'))) {
@@ -592,12 +628,12 @@ class Place extends Model
592
628
 
593
629
  public static function isValidPlaceUuid($uuid): bool
594
630
  {
595
- return is_string($uuid) && Str::isUuid($uuid) && Place::where('uuid', $uuid)->exists();
631
+ return is_string($uuid) && Str::isUuid($uuid) && static::where('uuid', $uuid)->exists();
596
632
  }
597
633
 
598
634
  public static function isValidPlacePublicId($publicId): bool
599
635
  {
600
- return is_string($publicId) && Utils::isPublicId($publicId) && Place::where('public_id', $publicId)->exists();
636
+ return is_string($publicId) && Utils::isPublicId($publicId) && static::where('public_id', $publicId)->exists();
601
637
  }
602
638
 
603
639
  /**
@@ -717,7 +753,7 @@ class Place extends Model
717
753
  return null;
718
754
  }
719
755
 
720
- $place = Place::createFromGeocodingLookup($address, false);
756
+ $place = static::createFromGeocodingLookup($address, false);
721
757
  foreach ($addressFields as $field => $options) {
722
758
  if ($place->isFillable($field) && empty($place->{$field})) {
723
759
  $value = Utils::or($row, array_merge([$field], $options['alias']));
@@ -783,7 +819,7 @@ class Place extends Model
783
819
  * $data = ['address' => '123 Main St, Anytown, USA'];
784
820
  * $place = Place::createFromImport($data, true);
785
821
  */
786
- public static function createFromImport(array $row, bool $saveInstance = false): Place
822
+ public static function createFromImport(array $row, bool $saveInstance = false): ?Place
787
823
  {
788
824
  // Filter array for null key values
789
825
  $row = array_filter($row);
@@ -469,6 +469,8 @@ Route::prefix(config('fleetops.api.routing.prefix', null))->namespace('Fleetbase
469
469
  $router->get('notification-registry', 'SettingController@getNotificationRegistry');
470
470
  $router->get('notification-settings', 'SettingController@getNotificationSettings');
471
471
  $router->post('notification-settings', 'SettingController@saveNotificationSettings');
472
+ $router->get('routing-settings', 'SettingController@getRoutingSettings');
473
+ $router->post('routing-settings', 'SettingController@saveRoutingSettings');
472
474
  }
473
475
  );
474
476
  $router->group(
@@ -298,6 +298,7 @@ fleet-ops:
298
298
  loading-vehicle: Loading fleet vehicles...
299
299
  layout:
300
300
  fleet-ops-sidebar:
301
+ routing: Routing
301
302
  notifications: Notifications
302
303
  payments: Payments
303
304
  navigator-app: Navigator App
@@ -1635,6 +1636,10 @@ fleet-ops:
1635
1636
  fleet-ops-notification-settings: Fleet-Ops Notification Settings
1636
1637
  fleet-ops-notifications: Fleet-Ops Notifications
1637
1638
  configure-notifications: Configure Notifications
1639
+ routing:
1640
+ fleet-ops-routing-settings: Fleet-Ops Routing Settings
1641
+ fleet-ops-routing: Fleet-Ops Routing
1642
+ configure-routing: Configure Routing
1638
1643
  navigator-app:
1639
1644
  navigator-app-settings: Navigator App Settings
1640
1645
  payments: