@fleetbase/fleetops-engine 0.6.49 → 0.6.51
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/README.md +185 -67
- package/addon/components/contact/panel-header.hbs +11 -48
- package/addon/components/customer/create-order-form.hbs +2 -2
- package/addon/components/customer/details.hbs +1 -2
- package/addon/components/customer/form.hbs +62 -50
- package/addon/components/customer/form.js +27 -1
- package/addon/components/customer/order-form.hbs +2 -2
- package/addon/components/customer/orders.hbs +50 -2
- package/addon/components/customer/orders.js +67 -5
- package/addon/components/display-place.hbs +2 -2
- package/addon/components/driver/form.hbs +1 -1
- package/addon/components/home/getting-started-guidance.hbs +113 -0
- package/addon/components/home/getting-started-guidance.js +107 -0
- package/addon/components/issue/details/comments.hbs +3 -0
- package/addon/components/issue/details/comments.js +3 -0
- package/addon/components/issue/details/correspondence.hbs +9 -0
- package/addon/components/issue/details/correspondence.js +83 -0
- package/addon/components/issue/details/documents.hbs +69 -0
- package/addon/components/issue/details/documents.js +85 -0
- package/addon/components/issue/details.hbs +220 -59
- package/addon/components/issue/details.js +245 -0
- package/addon/components/issue/form.hbs +19 -1
- package/addon/components/issue/panel-header.hbs +43 -0
- package/addon/components/issue/panel-header.js +22 -0
- package/addon/components/issue/timeline.hbs +40 -0
- package/addon/components/issue/timeline.js +45 -0
- package/addon/components/leaflet-tracking-marker.js +3 -0
- package/addon/components/map/drawer/position-listing.js +3 -0
- package/addon/components/map/google-live-map.js +6 -2
- package/addon/components/map/leaflet-live-map.hbs +48 -14
- package/addon/components/map/leaflet-live-map.js +201 -20
- package/addon/components/map/order-list-overlay.hbs +4 -4
- package/addon/components/modals/convert-customer-to-vendor.hbs +30 -0
- package/addon/components/modals/convert-customer-to-vendor.js +55 -0
- package/addon/components/modals/issue-assign.hbs +20 -0
- package/addon/components/modals/issue-assign.js +49 -0
- package/addon/components/modals/issue-close.hbs +30 -0
- package/addon/components/modals/issue-close.js +73 -0
- package/addon/components/modals/issue-status.hbs +20 -0
- package/addon/components/modals/issue-status.js +43 -0
- package/addon/components/modals/order-change-destination.hbs +46 -0
- package/addon/components/modals/order-change-destination.js +106 -0
- package/addon/components/modals/reset-customer-credentials.js +12 -6
- package/addon/components/modals/stop-activity.hbs +28 -0
- package/addon/components/modals/stop-activity.js +52 -0
- package/addon/components/modals/update-order-activity.hbs +78 -2
- package/addon/components/modals/update-order-activity.js +197 -5
- package/addon/components/orchestrator/resource-panel.hbs +0 -10
- package/addon/components/orchestrator/resource-panel.js +2 -4
- package/addon/components/order/activity-list.hbs +2 -2
- package/addon/components/order/activity-timeline.hbs +31 -29
- package/addon/components/order/details/activity.hbs +3 -3
- package/addon/components/order/details/activity.js +13 -2
- package/addon/components/order/details/documents.js +2 -0
- package/addon/components/order/details/proof.hbs +40 -38
- package/addon/components/order/details/proof.js +31 -4
- package/addon/components/order/details/purchase-rate.hbs +2 -2
- package/addon/components/order/details/route.hbs +11 -3
- package/addon/components/order/details/route.js +91 -14
- package/addon/components/order/details/tracking.hbs +101 -72
- package/addon/components/order/details/tracking.js +149 -2
- package/addon/components/order/details.hbs +5 -5
- package/addon/components/order/details.js +19 -1
- package/addon/components/order/form/details.hbs +3 -3
- package/addon/components/order/form/documents.js +11 -4
- package/addon/components/order/form/route.hbs +108 -104
- package/addon/components/order/form/route.js +37 -1
- package/addon/components/order/form/service-rate.hbs +2 -2
- package/addon/components/order/header.hbs +1 -1
- package/addon/components/order-config-manager/activity-flow.hbs +4 -1
- package/addon/components/order-config-manager/activity-flow.js +178 -12
- package/addon/components/place/address.js +1 -1
- package/addon/components/route-list.hbs +17 -29
- package/addon/components/route-list.js +42 -10
- package/addon/components/service-rate/details.hbs +42 -1
- package/addon/components/service-rate/form.hbs +119 -1
- package/addon/components/service-rate/form.js +59 -0
- package/addon/components/vehicle/form.js +1 -1
- package/addon/components/vehicle/pill.hbs +1 -1
- package/addon/components/vendor/personnel-manager.hbs +166 -0
- package/addon/components/vendor/personnel-manager.js +143 -0
- package/addon/components/widget/live-fleet.hbs +1 -0
- package/addon/components/widget/live-fleet.js +2 -7
- package/addon/components/widget/revenue-trend.hbs +1 -1
- package/addon/components/widget/revenue-trend.js +75 -2
- package/addon/controllers/management/contacts/customers/details.js +31 -13
- package/addon/controllers/management/contacts/customers.js +16 -5
- package/addon/controllers/management/contacts/index/details.js +31 -13
- package/addon/controllers/management/contacts/index.js +7 -0
- package/addon/controllers/management/issues/index/details.js +52 -6
- package/addon/controllers/management/vehicles/index/new.js +1 -1
- package/addon/controllers/management/vendors/index/details.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +15 -7
- package/addon/controllers/operations/orders/index.js +15 -1
- package/addon/extension.js +19 -12
- package/addon/helpers/format-duration.js +1 -38
- package/addon/helpers/place-address-html.js +2 -2
- package/addon/initializers/load-leaflet-assets.js +5 -6
- package/addon/initializers/patch-ember-leaflet-tooltip-layer.js +51 -0
- package/addon/routes/management/issues/index/details.js +1 -1
- package/addon/routes/operations/service-rates/index.js +1 -1
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +231 -1
- package/addon/services/geofence.js +163 -19
- package/addon/services/getting-started.js +44 -0
- package/addon/services/issue-actions.js +71 -0
- package/addon/services/leaflet-contextmenu-manager.js +27 -0
- package/addon/services/leaflet-map-manager.js +2 -0
- package/addon/services/map-adapter/leaflet.js +19 -0
- package/addon/services/service-areas.js +3 -0
- package/addon/services/vehicle-actions.js +1 -1
- package/addon/styles/fleetops-engine.css +1127 -1
- package/addon/templates/management/contacts/customers/details.hbs +1 -1
- package/addon/templates/management/contacts/index/details.hbs +1 -1
- package/addon/templates/management/contacts.hbs +2 -2
- package/addon/templates/management/issues/index/details/index.hbs +3 -1
- package/addon/templates/management/issues/index/details.hbs +3 -2
- package/addon/templates/management/vendors/index/details/personnel.hbs +1 -2
- package/addon/templates/operations/orders/index/details/index.hbs +2 -2
- package/addon/templates/operations/orders/index.hbs +1 -1
- package/addon/templates/operations/service-rates/index/edit.hbs +1 -0
- package/addon/templates/operations/service-rates/index/new.hbs +1 -0
- package/addon/templates/settings/payments/onboard.hbs +8 -8
- package/addon/utils/fleet-ops-options.js +2 -1
- package/addon/utils/leaflet-draw-namespace-guard.js +22 -0
- package/addon/utils/leaflet-plugin-loader.js +219 -0
- package/addon/utils/normalize-order-config-flow.js +41 -0
- package/addon/utils/place-address-html.js +3 -2
- package/addon/utils/route-visualization.js +54 -7
- package/addon/utils/setup-customer-portal.js +4 -64
- package/app/components/home/getting-started-guidance.js +1 -0
- package/app/components/issue/details/comments.js +1 -0
- package/app/components/issue/details/correspondence.js +1 -0
- package/app/components/issue/details/documents.js +1 -0
- package/app/components/issue/panel-header.js +1 -0
- package/app/components/modals/convert-customer-to-vendor.js +1 -0
- package/app/components/modals/issue-assign.js +1 -0
- package/app/components/modals/issue-close.js +1 -0
- package/app/components/modals/issue-status.js +1 -0
- package/app/components/modals/order-change-destination.js +1 -0
- package/app/components/modals/stop-activity.js +1 -0
- package/app/components/vendor/personnel-manager.js +1 -0
- package/app/helpers/format-duration.js +1 -1
- package/app/services/getting-started.js +1 -0
- package/app/utils/leaflet-draw-namespace-guard.js +1 -0
- package/app/utils/leaflet-plugin-loader.js +1 -0
- package/composer.json +1 -1
- package/extension.json +1 -1
- package/package.json +5 -5
- package/server/migrations/2026_05_21_000001_add_geographic_rules_to_service_rate_fees_table.php +37 -0
- package/server/migrations/2026_05_25_000001_add_endpoint_tracking_refs_to_payloads_table.php +50 -0
- package/server/migrations/2026_05_27_000001_default_driver_vehicle_statuses_to_available.php +48 -0
- package/server/migrations/2026_05_31_000001_add_portal_fields_to_vendor_personnels_table.php +30 -0
- package/server/migrations/2026_06_03_000000_ensure_tags_column_on_issues_table.php +31 -0
- package/server/migrations/2026_06_03_000001_add_order_uuid_to_issues_table.php +25 -0
- package/server/migrations/2026_06_04_000001_backfill_order_transaction_subjects.php +39 -0
- package/server/migrations/2026_06_04_000002_backfill_order_transaction_directions.php +24 -0
- package/server/resources/views/labels/default.php +57 -49
- package/server/resources/views/labels/waypoint-label.php +27 -20
- package/server/resources/views/mail/customer-credentials.blade.php +16 -13
- package/server/seeders/Testing/ConnectivitySeeder.php +1 -0
- package/server/seeders/Testing/FleetSeeder.php +22 -22
- package/server/seeders/Testing/OrdersSeeder.php +6 -22
- package/server/seeders/Testing/VroomOrchestrationSeeder.php +21 -21
- package/server/src/Console/Commands/AuditCustomerUserConflicts.php +75 -0
- package/server/src/Exceptions/CustomerUserConflictException.php +13 -0
- package/server/src/Expansions/UserFilterExpansion.php +28 -0
- package/server/src/Http/Controllers/Api/v1/ContactController.php +17 -2
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +876 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +1 -1
- package/server/src/Http/Controllers/Api/v1/IssueController.php +2 -0
- package/server/src/Http/Controllers/Api/v1/OrderConfigController.php +54 -0
- package/server/src/Http/Controllers/Api/v1/OrderController.php +109 -80
- package/server/src/Http/Controllers/Api/v1/PlaceController.php +10 -58
- package/server/src/Http/Controllers/Api/v1/ServiceAreaController.php +2 -2
- package/server/src/Http/Controllers/Api/v1/VehicleController.php +20 -3
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +154 -0
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +116 -6
- package/server/src/Http/Controllers/Internal/v1/GettingStartedController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/IssueController.php +251 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +101 -66
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +530 -52
- package/server/src/Http/Controllers/Internal/v1/PlaceController.php +10 -68
- package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +2 -2
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +54 -1
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +89 -0
- package/server/src/Http/Middleware/AuthenticateCustomerToken.php +34 -0
- package/server/src/Http/Requests/CreateCustomerOrderRequest.php +63 -0
- package/server/src/Http/Requests/CreateCustomerRequest.php +48 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/CreateIssueRequest.php +4 -0
- package/server/src/Http/Requests/CreateVehicleRequest.php +25 -1
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +1 -1
- package/server/src/Http/Requests/UpdateIssueRequest.php +4 -0
- package/server/src/Http/Requests/VerifyCreateCustomerRequest.php +29 -0
- package/server/src/Http/Resources/Internal/v1/OrderConfig.php +41 -0
- package/server/src/Http/Resources/v1/Customer.php +79 -0
- package/server/src/Http/Resources/v1/Index/Order.php +12 -9
- package/server/src/Http/Resources/v1/Issue.php +25 -0
- package/server/src/Http/Resources/v1/Order.php +20 -18
- package/server/src/Http/Resources/v1/OrderConfig.php +74 -0
- package/server/src/Http/Resources/v1/Payload.php +23 -20
- package/server/src/Http/Resources/v1/ServiceArea.php +33 -19
- package/server/src/Http/Resources/v1/ServiceRateFee.php +7 -0
- package/server/src/Listeners/HandleOrderDispatched.php +2 -2
- package/server/src/Mail/CustomerCredentialsMail.php +5 -6
- package/server/src/Models/Contact.php +122 -66
- package/server/src/Models/Customer.php +64 -0
- package/server/src/Models/Driver.php +12 -3
- package/server/src/Models/Entity.php +2 -2
- package/server/src/Models/Issue.php +47 -6
- package/server/src/Models/Order.php +62 -10
- package/server/src/Models/Payload.php +17 -1
- package/server/src/Models/ServiceArea.php +1 -1
- package/server/src/Models/ServiceRate.php +273 -1
- package/server/src/Models/ServiceRateFee.php +39 -9
- package/server/src/Models/TrackingNumber.php +8 -2
- package/server/src/Models/Vehicle.php +18 -2
- package/server/src/Models/VendorPersonnel.php +10 -1
- package/server/src/Models/Waypoint.php +2 -2
- package/server/src/Models/Zone.php +1 -1
- package/server/src/Notifications/LateDeparture.php +2 -2
- package/server/src/Notifications/ProlongedStoppage.php +2 -2
- package/server/src/Notifications/RouteDeviation.php +2 -2
- package/server/src/Observers/ContactObserver.php +2 -0
- package/server/src/Observers/PurchaseRateObserver.php +20 -0
- package/server/src/Observers/ServiceRateObserver.php +6 -6
- package/server/src/Observers/ZoneObserver.php +56 -0
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +1 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +2 -0
- package/server/src/Support/Analytics/AbstractAnalytics.php +6 -6
- package/server/src/Support/Analytics/FuelEfficiency.php +2 -2
- package/server/src/Support/Analytics/OrdersByStatus.php +1 -1
- package/server/src/Support/Analytics/RevenueTrend.php +0 -1
- package/server/src/Support/Analytics/TopDrivers.php +10 -2
- package/server/src/Support/CustomerAuth.php +98 -0
- package/server/src/Support/GettingStarted.php +144 -0
- package/server/src/Support/LabelPdf.php +52 -0
- package/server/src/Support/Metrics/AbstractMetric.php +4 -4
- package/server/src/Support/PlaceSearch.php +282 -0
- package/server/src/Support/ResolvesOrderServiceStops.php +522 -0
- package/server/src/Tracking/TrackingContextBuilder.php +41 -38
- package/server/src/Tracking/TrackingIntelligenceService.php +109 -9
- package/server/src/Tracking/TrackingStop.php +13 -11
- package/server/src/routes.php +76 -10
- package/server/tests/AnalyticsRoutesTest.php +8 -0
- package/server/tests/CapacityAllocationEngineTest.php +10 -10
- package/server/tests/CustomerContactIdentitySafetyTest.php +95 -0
- package/server/tests/CustomerEndpointTest.php +199 -0
- package/server/tests/DriverVehicleStatusDefaultTest.php +39 -0
- package/server/tests/GettingStartedStatusTest.php +21 -0
- package/server/tests/LabelPdfRenderingTest.php +23 -0
- package/server/tests/LiveControllerViewportTest.php +60 -0
- package/server/tests/MetricsRegistryTest.php +4 -8
- package/server/tests/OrchestrationPayloadBuilderTest.php +2 -2
- package/server/tests/OrderActivityFlowRegressionTest.php +116 -0
- package/server/tests/OrderConfigEndpointTest.php +86 -0
- package/server/tests/OrderFileAttachmentTest.php +20 -0
- package/server/tests/ServiceAreaZoneApiShapeTest.php +78 -0
- package/server/tests/VehicleDriverAssignmentTest.php +18 -0
- package/translations/ar-ae.yml +9 -0
- package/translations/bg-bg.yaml +9 -0
- package/translations/en-us.yaml +12 -0
- package/translations/es-pa.yaml +9 -0
- package/translations/fr-fr.yaml +9 -0
- package/translations/mn-mn.yaml +9 -0
- package/translations/pt-br.yaml +9 -0
- package/translations/ru-ru.yaml +9 -0
- package/translations/vi-vn.yaml +9 -0
- package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +0 -86
|
@@ -50,7 +50,7 @@ class DriverController extends Controller
|
|
|
50
50
|
$input = $request->except(['name', 'password', 'email', 'phone', 'location', 'altitude', 'heading', 'speed', 'meta']);
|
|
51
51
|
|
|
52
52
|
// Add default status
|
|
53
|
-
$input['status'] = '
|
|
53
|
+
$input['status'] = $request->input('status', 'available');
|
|
54
54
|
|
|
55
55
|
// get user details for driver
|
|
56
56
|
$userDetails = $request->only(['name', 'password', 'email', 'phone', 'timezone']);
|
|
@@ -30,6 +30,7 @@ class IssueController extends Controller
|
|
|
30
30
|
'type',
|
|
31
31
|
'report',
|
|
32
32
|
'priority',
|
|
33
|
+
'tags',
|
|
33
34
|
'status',
|
|
34
35
|
]);
|
|
35
36
|
|
|
@@ -85,6 +86,7 @@ class IssueController extends Controller
|
|
|
85
86
|
'type',
|
|
86
87
|
'report',
|
|
87
88
|
'priority',
|
|
89
|
+
'tags',
|
|
88
90
|
'status',
|
|
89
91
|
]);
|
|
90
92
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Http\Resources\v1\OrderConfig as OrderConfigResource;
|
|
6
|
+
use Fleetbase\FleetOps\Models\OrderConfig;
|
|
7
|
+
use Fleetbase\Http\Controllers\Controller;
|
|
8
|
+
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
9
|
+
use Illuminate\Http\Request;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Public, read-only API surface for OrderConfigs.
|
|
13
|
+
*
|
|
14
|
+
* The internal admin console manages the full OrderConfig lifecycle through
|
|
15
|
+
* `/int/v1/order-configs`. This public endpoint exposes the projected,
|
|
16
|
+
* non-sensitive subset (flow activities + metadata) that consumers like a
|
|
17
|
+
* customer portal need to render status filter chips, activity labels, and
|
|
18
|
+
* other UI driven by the active flow.
|
|
19
|
+
*/
|
|
20
|
+
class OrderConfigController extends Controller
|
|
21
|
+
{
|
|
22
|
+
/**
|
|
23
|
+
* List the OrderConfigs for the company resolved from the API credential.
|
|
24
|
+
*
|
|
25
|
+
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection
|
|
26
|
+
*/
|
|
27
|
+
public function query(Request $request)
|
|
28
|
+
{
|
|
29
|
+
$results = OrderConfig::queryWithRequest($request);
|
|
30
|
+
|
|
31
|
+
return OrderConfigResource::collection($results);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Find a single OrderConfig. The `{id}` segment accepts any identifier
|
|
36
|
+
* supported by {@see OrderConfig::resolveFromIdentifier} — `uuid`,
|
|
37
|
+
* `public_id`, `namespace`, or short `key` (e.g. `transport`).
|
|
38
|
+
*
|
|
39
|
+
* @return OrderConfigResource|\Illuminate\Http\JsonResponse
|
|
40
|
+
*/
|
|
41
|
+
public function find(string $id)
|
|
42
|
+
{
|
|
43
|
+
$orderConfig = OrderConfig::resolveFromIdentifier($id);
|
|
44
|
+
if (!$orderConfig) {
|
|
45
|
+
try {
|
|
46
|
+
$orderConfig = OrderConfig::findRecordOrFail($id);
|
|
47
|
+
} catch (ModelNotFoundException $e) {
|
|
48
|
+
return response()->apiError('Order config not found.', 404);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new OrderConfigResource($orderConfig);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -5,6 +5,7 @@ namespace Fleetbase\FleetOps\Http\Controllers\Api\v1;
|
|
|
5
5
|
use Fleetbase\FleetOps\Events\OrderDispatchFailed;
|
|
6
6
|
use Fleetbase\FleetOps\Events\OrderReady;
|
|
7
7
|
use Fleetbase\FleetOps\Events\OrderStarted;
|
|
8
|
+
use Fleetbase\FleetOps\Exceptions\CustomerUserConflictException;
|
|
8
9
|
use Fleetbase\FleetOps\Exceptions\UserAlreadyExistsException;
|
|
9
10
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
10
11
|
use Fleetbase\FleetOps\Http\Requests\CreateOrderRequest;
|
|
@@ -23,6 +24,7 @@ use Fleetbase\FleetOps\Models\Place;
|
|
|
23
24
|
use Fleetbase\FleetOps\Models\Proof;
|
|
24
25
|
use Fleetbase\FleetOps\Models\ServiceQuote;
|
|
25
26
|
use Fleetbase\FleetOps\Models\Waypoint;
|
|
27
|
+
use Fleetbase\FleetOps\Support\ResolvesOrderServiceStops;
|
|
26
28
|
use Fleetbase\FleetOps\Support\Utils;
|
|
27
29
|
use Fleetbase\Http\Controllers\Controller;
|
|
28
30
|
use Fleetbase\Http\Resources\Comment as CommentResource;
|
|
@@ -43,6 +45,8 @@ use Illuminate\Validation\ValidationException;
|
|
|
43
45
|
|
|
44
46
|
class OrderController extends Controller
|
|
45
47
|
{
|
|
48
|
+
use ResolvesOrderServiceStops;
|
|
49
|
+
|
|
46
50
|
/**
|
|
47
51
|
* Creates a new Fleetbase Order resource.
|
|
48
52
|
*
|
|
@@ -251,6 +255,13 @@ class OrderController extends Controller
|
|
|
251
255
|
$customer = Arr::only($customer, ['internal_id', 'name', 'title', 'email', 'phone', 'meta']);
|
|
252
256
|
|
|
253
257
|
try {
|
|
258
|
+
$customerCandidate = new Contact([
|
|
259
|
+
...$customer,
|
|
260
|
+
'company_uuid' => session('company'),
|
|
261
|
+
'type' => 'customer',
|
|
262
|
+
]);
|
|
263
|
+
$customerCandidate->assertCustomerIdentityIsAvailable();
|
|
264
|
+
|
|
254
265
|
$customer = Contact::firstOrCreate(
|
|
255
266
|
[
|
|
256
267
|
'company_uuid' => session('company'),
|
|
@@ -263,19 +274,12 @@ class OrderController extends Controller
|
|
|
263
274
|
'type' => 'customer',
|
|
264
275
|
]
|
|
265
276
|
);
|
|
277
|
+
} catch (CustomerUserConflictException $e) {
|
|
278
|
+
return response()->apiError($e->getMessage());
|
|
279
|
+
} catch (UserAlreadyExistsException $e) {
|
|
280
|
+
return response()->apiError($e->getMessage());
|
|
266
281
|
} catch (\Exception $e) {
|
|
267
282
|
return response()->apiError('Failed to find or create customer for order.');
|
|
268
|
-
} catch (UserAlreadyExistsException $e) {
|
|
269
|
-
try {
|
|
270
|
-
// If user already exist then assign user to this customer and the company
|
|
271
|
-
$existingUser = $e->getUser();
|
|
272
|
-
// Assign user to customer
|
|
273
|
-
if ($existingUser && $customer) {
|
|
274
|
-
$customer->assignUser($existingUser);
|
|
275
|
-
}
|
|
276
|
-
} catch (\Exception $e) {
|
|
277
|
-
return response()->apiError('Failed to find or create customer for order.');
|
|
278
|
-
}
|
|
279
283
|
}
|
|
280
284
|
|
|
281
285
|
if (Str::isUuid($customer)) {
|
|
@@ -1009,11 +1013,10 @@ class OrderController extends Controller
|
|
|
1009
1013
|
$driver->current_job_uuid = $order->uuid;
|
|
1010
1014
|
$driver->save();
|
|
1011
1015
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
$payload->setFirstWaypoint($activity, $location);
|
|
1016
|
+
// set first service stop destination for multi-stop routes
|
|
1017
|
+
if ($this->payloadUsesServiceStopActivity($payload)) {
|
|
1018
|
+
$this->ensurePayloadCurrentServiceStop($payload);
|
|
1019
|
+
}
|
|
1017
1020
|
$order->setRelation('payload', $payload);
|
|
1018
1021
|
|
|
1019
1022
|
// update order activity
|
|
@@ -1043,7 +1046,17 @@ class OrderController extends Controller
|
|
|
1043
1046
|
// if string $id
|
|
1044
1047
|
if (!$order) {
|
|
1045
1048
|
try {
|
|
1046
|
-
$order = Order::findRecordOrFail($id, [
|
|
1049
|
+
$order = Order::findRecordOrFail($id, [
|
|
1050
|
+
'driverAssigned',
|
|
1051
|
+
'payload.entities',
|
|
1052
|
+
'payload.pickup',
|
|
1053
|
+
'payload.dropoff',
|
|
1054
|
+
'payload.return',
|
|
1055
|
+
'payload.currentWaypoint',
|
|
1056
|
+
'payload.waypoints',
|
|
1057
|
+
'payload.waypointMarkers.place',
|
|
1058
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
1059
|
+
]);
|
|
1047
1060
|
} catch (ModelNotFoundException $exception) {
|
|
1048
1061
|
return response()->apiError('Order resource not found.', 404);
|
|
1049
1062
|
}
|
|
@@ -1094,65 +1107,59 @@ class OrderController extends Controller
|
|
|
1094
1107
|
/** @var \Fleetbase\LaravelMysqlSpatial\Types\Point */
|
|
1095
1108
|
$location = $order->getLastLocation();
|
|
1096
1109
|
|
|
1097
|
-
|
|
1098
|
-
$
|
|
1099
|
-
|
|
1100
|
-
// if is multi drop order and no current destination set it
|
|
1101
|
-
if ($isMultipleWaypointOrder && !$order->payload->current_waypoint_uuid) {
|
|
1102
|
-
$order->payload->setFirstWaypoint($activity, $location);
|
|
1103
|
-
}
|
|
1110
|
+
$usesServiceStopActivity = $this->payloadUsesServiceStopActivity($order->payload);
|
|
1111
|
+
$isLifecycleActivity = Utils::isActivity($activity) && in_array($activity->code, ['created', 'dispatched', 'started'], true);
|
|
1104
1112
|
|
|
1105
|
-
//
|
|
1106
|
-
if (
|
|
1107
|
-
//
|
|
1108
|
-
$
|
|
1109
|
-
return $waypoint->complete;
|
|
1110
|
-
});
|
|
1113
|
+
// Classic pickup/dropoff orders and static lifecycle statuses update the order.
|
|
1114
|
+
if (!$usesServiceStopActivity || $isLifecycleActivity) {
|
|
1115
|
+
// Update order activity
|
|
1116
|
+
$order->updateActivity($activity, $proof);
|
|
1111
1117
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1118
|
+
if (!$usesServiceStopActivity) {
|
|
1119
|
+
foreach ($order->payload->entities as $entity) {
|
|
1120
|
+
$entity->insertActivity($activity, $location, $proof);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1117
1123
|
|
|
1118
|
-
|
|
1119
|
-
$
|
|
1120
|
-
|
|
1121
|
-
});
|
|
1124
|
+
if ($usesServiceStopActivity && $activity->is('started')) {
|
|
1125
|
+
$this->ensurePayloadCurrentServiceStop($order->payload);
|
|
1126
|
+
}
|
|
1122
1127
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1128
|
+
// Handle order completion
|
|
1129
|
+
if (Utils::isActivity($activity) && $activity->completesOrder()) {
|
|
1130
|
+
$order->driverAssigned?->unassignCurrentJob();
|
|
1131
|
+
$order->complete($this->resolveProof($proof));
|
|
1126
1132
|
}
|
|
1133
|
+
|
|
1134
|
+
return new OrderResource($order->refresh());
|
|
1127
1135
|
}
|
|
1128
1136
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
// Update order activity
|
|
1133
|
-
$order->updateActivity($activity, $proof);
|
|
1137
|
+
if (!$order->started && in_array($order->status, ['created', 'dispatched'], true)) {
|
|
1138
|
+
return response()->apiError('Order must be started before waypoint activity can be updated.', 422);
|
|
1139
|
+
}
|
|
1134
1140
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
$entity->insertActivity($activity, $location, $proof);
|
|
1138
|
-
}
|
|
1139
|
-
} else {
|
|
1140
|
-
// Update parent order when status is `dispatched` or `started`
|
|
1141
|
-
if (in_array($activity->code, ['started', 'dispatched'])) {
|
|
1142
|
-
$order->updateActivity($activity, $proof);
|
|
1143
|
-
}
|
|
1141
|
+
$currentStop = $this->ensurePayloadCurrentServiceStop($order->payload);
|
|
1142
|
+
$isCompletingCurrentStop = Utils::isActivity($activity) && $activity->complete();
|
|
1144
1143
|
|
|
1145
|
-
|
|
1144
|
+
$this->updateCurrentServiceStopActivity($order, $activity, $location, $proof);
|
|
1145
|
+
|
|
1146
|
+
if (Utils::isActivity($activity) && $activity->is('canceled')) {
|
|
1147
|
+
$order->driverAssigned?->unassignCurrentJob();
|
|
1148
|
+
$order->cancel();
|
|
1149
|
+
|
|
1150
|
+
return new OrderResource($order->refresh());
|
|
1146
1151
|
}
|
|
1147
1152
|
|
|
1148
|
-
// Handle order completion
|
|
1149
1153
|
if (Utils::isActivity($activity) && $activity->completesOrder()) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1154
|
+
$nextStop = $isCompletingCurrentStop ? $this->advanceCurrentServiceStopDestination($order, $order->payload) : null;
|
|
1155
|
+
|
|
1156
|
+
if (!$nextStop || !$currentStop) {
|
|
1157
|
+
$order->driverAssigned?->unassignCurrentJob();
|
|
1158
|
+
$order->complete($this->resolveProof($proof));
|
|
1159
|
+
}
|
|
1153
1160
|
}
|
|
1154
1161
|
|
|
1155
|
-
return new OrderResource($order);
|
|
1162
|
+
return new OrderResource($order->refresh());
|
|
1156
1163
|
}
|
|
1157
1164
|
|
|
1158
1165
|
/**
|
|
@@ -1165,7 +1172,14 @@ class OrderController extends Controller
|
|
|
1165
1172
|
$waypointId = $request->input('waypoint');
|
|
1166
1173
|
|
|
1167
1174
|
try {
|
|
1168
|
-
$order = Order::findRecordOrFail($id, [
|
|
1175
|
+
$order = Order::findRecordOrFail($id, [
|
|
1176
|
+
'payload.pickup',
|
|
1177
|
+
'payload.dropoff',
|
|
1178
|
+
'payload.return',
|
|
1179
|
+
'payload.waypoints',
|
|
1180
|
+
'payload.waypointMarkers.place',
|
|
1181
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
1182
|
+
]);
|
|
1169
1183
|
} catch (ModelNotFoundException $exception) {
|
|
1170
1184
|
return response()->json(
|
|
1171
1185
|
[
|
|
@@ -1175,19 +1189,20 @@ class OrderController extends Controller
|
|
|
1175
1189
|
);
|
|
1176
1190
|
}
|
|
1177
1191
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1192
|
+
$canUpdateServiceStopActivity = $this->payloadUsesServiceStopActivity($order->payload)
|
|
1193
|
+
&& ($order->started || $order->started_at || !in_array($order->status, ['created', 'dispatched'], true));
|
|
1194
|
+
$stop = null;
|
|
1195
|
+
if ($canUpdateServiceStopActivity) {
|
|
1196
|
+
$stop = $waypointId
|
|
1197
|
+
? $this->resolveServiceStopFromKey($order->payload, $waypointId)
|
|
1198
|
+
: $this->payloadCurrentServiceStop($order->payload);
|
|
1184
1199
|
}
|
|
1185
1200
|
|
|
1186
1201
|
$orderConfig = $order->ensureOrderConfig();
|
|
1187
1202
|
if (!$orderConfig) {
|
|
1188
1203
|
return response()->apiError('No order config found for order.');
|
|
1189
1204
|
}
|
|
1190
|
-
$activities = $orderConfig->nextActivity(
|
|
1205
|
+
$activities = $stop ? $this->nextActivitiesForServiceStop($order, $order->payload, $stop) : $orderConfig->nextActivity();
|
|
1191
1206
|
|
|
1192
1207
|
// If activity is to complete order add proof of delivery properties if required
|
|
1193
1208
|
// This is a temporary fix until activity is updated to handle POD on it's own
|
|
@@ -1282,7 +1297,18 @@ class OrderController extends Controller
|
|
|
1282
1297
|
public function setDestination(string $id, string $placeId)
|
|
1283
1298
|
{
|
|
1284
1299
|
try {
|
|
1285
|
-
$order = Order::findRecordOrFail($id, [
|
|
1300
|
+
$order = Order::findRecordOrFail($id, [
|
|
1301
|
+
'payload.pickup',
|
|
1302
|
+
'payload.dropoff',
|
|
1303
|
+
'payload.return',
|
|
1304
|
+
'payload.waypoints',
|
|
1305
|
+
'payload.waypointMarkers.place',
|
|
1306
|
+
'payload.waypointMarkers.trackingNumber.status',
|
|
1307
|
+
'driverAssigned',
|
|
1308
|
+
'vehicleAssigned',
|
|
1309
|
+
'customer',
|
|
1310
|
+
'facilitator',
|
|
1311
|
+
]);
|
|
1286
1312
|
} catch (ModelNotFoundException $exception) {
|
|
1287
1313
|
return response()->apiError('Order resource not found.', 404);
|
|
1288
1314
|
}
|
|
@@ -1290,17 +1316,13 @@ class OrderController extends Controller
|
|
|
1290
1316
|
// Get the order payload
|
|
1291
1317
|
$payload = $order->payload;
|
|
1292
1318
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
?? (($payload->pickup && $payload->pickup->public_id === $placeId) ? $payload->pickup : null)
|
|
1296
|
-
?? (($payload->dropoff && $payload->dropoff->public_id === $placeId) ? $payload->dropoff : null);
|
|
1297
|
-
|
|
1298
|
-
if (!$place) {
|
|
1319
|
+
$stop = $this->resolveServiceStopFromKey($payload, $placeId);
|
|
1320
|
+
if (!$stop) {
|
|
1299
1321
|
return response()->apiError('Place resource is not a valid destination.', 422);
|
|
1300
1322
|
}
|
|
1301
1323
|
|
|
1302
1324
|
// Persist destination choice
|
|
1303
|
-
$
|
|
1325
|
+
$this->setPayloadCurrentServiceStop($payload, $stop);
|
|
1304
1326
|
|
|
1305
1327
|
return new OrderResource($order->refresh());
|
|
1306
1328
|
}
|
|
@@ -1385,7 +1407,7 @@ class OrderController extends Controller
|
|
|
1385
1407
|
|
|
1386
1408
|
// Load Order
|
|
1387
1409
|
try {
|
|
1388
|
-
$order = Order::findRecordOrFail($id);
|
|
1410
|
+
$order = Order::findRecordOrFail($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
|
|
1389
1411
|
} catch (ModelNotFoundException $e) {
|
|
1390
1412
|
return response()->apiError('Order resource not found.', 404);
|
|
1391
1413
|
}
|
|
@@ -1580,7 +1602,7 @@ class OrderController extends Controller
|
|
|
1580
1602
|
|
|
1581
1603
|
// Load Order
|
|
1582
1604
|
try {
|
|
1583
|
-
$order = Order::findRecordOrFail($id);
|
|
1605
|
+
$order = Order::findRecordOrFail($id, ['payload.pickup', 'payload.dropoff', 'payload.return', 'payload.waypoints', 'payload.waypointMarkers.place']);
|
|
1584
1606
|
} catch (ModelNotFoundException $e) {
|
|
1585
1607
|
return response()->apiError('Order resource not found.', 404);
|
|
1586
1608
|
}
|
|
@@ -1678,6 +1700,13 @@ class OrderController extends Controller
|
|
|
1678
1700
|
return $order;
|
|
1679
1701
|
}
|
|
1680
1702
|
|
|
1703
|
+
if (in_array($type, ['place', 'waypoint'], true)) {
|
|
1704
|
+
$stop = $this->resolveServiceStopFromKey($order->payload, $subjectId);
|
|
1705
|
+
if ($stop) {
|
|
1706
|
+
return ($stop['waypoint'] ?? null) instanceof Waypoint ? $stop['waypoint'] : ($stop['place'] ?? $order);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1681
1710
|
return match ($type) {
|
|
1682
1711
|
'place', 'waypoint' => Waypoint::withoutGlobalScopes()
|
|
1683
1712
|
->where('payload_uuid', $order->payload_uuid)
|
|
@@ -7,15 +7,11 @@ use Fleetbase\FleetOps\Http\Requests\UpdatePlaceRequest;
|
|
|
7
7
|
use Fleetbase\FleetOps\Http\Resources\v1\DeletedResource;
|
|
8
8
|
use Fleetbase\FleetOps\Http\Resources\v1\Place as PlaceResource;
|
|
9
9
|
use Fleetbase\FleetOps\Models\Place;
|
|
10
|
+
use Fleetbase\FleetOps\Support\PlaceSearch;
|
|
10
11
|
use Fleetbase\FleetOps\Support\Utils;
|
|
11
12
|
use Fleetbase\Http\Controllers\Controller;
|
|
12
13
|
use Fleetbase\LaravelMysqlSpatial\Types\Point;
|
|
13
|
-
use Geocoder\Laravel\Facades\Geocoder;
|
|
14
|
-
use Geocoder\Provider\GoogleMapsPlaces\GoogleMapsPlaces;
|
|
15
|
-
use Geocoder\Query\GeocodeQuery;
|
|
16
|
-
use Http\Adapter\Guzzle7\Client;
|
|
17
14
|
use Illuminate\Http\Request;
|
|
18
|
-
use Illuminate\Support\Facades\DB;
|
|
19
15
|
use Illuminate\Support\Str;
|
|
20
16
|
|
|
21
17
|
class PlaceController extends Controller
|
|
@@ -283,63 +279,19 @@ class PlaceController extends Controller
|
|
|
283
279
|
{
|
|
284
280
|
$searchQuery = strtolower($request->input('query'));
|
|
285
281
|
$limit = $request->input('limit', 10);
|
|
286
|
-
$geo = $request->
|
|
282
|
+
$geo = $request->boolean('geo');
|
|
287
283
|
$latitude = $request->input('latitude', false);
|
|
288
284
|
$longitude = $request->input('longitude', false);
|
|
289
285
|
|
|
290
|
-
$
|
|
291
|
-
->where('company_uuid', session('company'))
|
|
292
|
-
->whereNull('deleted_at')
|
|
293
|
-
->where(function ($q) use ($searchQuery) {
|
|
294
|
-
if (Utils::notEmpty($searchQuery)) {
|
|
295
|
-
$q->orWhere(DB::raw('lower(name)'), 'like', '%' . $searchQuery . '%');
|
|
296
|
-
$q->orWhere(DB::raw('lower(street1)'), 'like', '%' . $searchQuery . '%');
|
|
297
|
-
$q->orWhere(DB::raw('lower(postal_code)'), 'like', '%' . $searchQuery . '%');
|
|
298
|
-
}
|
|
299
|
-
})
|
|
300
|
-
->limit($limit)
|
|
301
|
-
->orderBy('name', 'desc')
|
|
302
|
-
->get()
|
|
303
|
-
->map(function ($place) {
|
|
304
|
-
$place = (array) $place;
|
|
305
|
-
$place['location'] = Utils::mysqlPointAsGeometry($place['location']);
|
|
306
|
-
$place = new Place($place);
|
|
307
|
-
$place->address = $place->toAddressString();
|
|
308
|
-
|
|
309
|
-
return $place;
|
|
310
|
-
})
|
|
311
|
-
->values();
|
|
312
|
-
|
|
313
|
-
if ($geo && Utils::notEmpty($searchQuery)) {
|
|
314
|
-
$httpClient = new Client();
|
|
315
|
-
$provider = new \Geocoder\Provider\GoogleMaps\GoogleMaps($httpClient, null, env('GOOGLE_MAPS_API_KEY'));
|
|
316
|
-
$geocoder = new \Geocoder\StatefulGeocoder($provider, 'en');
|
|
317
|
-
|
|
318
|
-
if ($latitude && $longitude) {
|
|
319
|
-
$geoResults = $geocoder->geocodeQuery(
|
|
320
|
-
GeocodeQuery::create($searchQuery)
|
|
321
|
-
->withData('mode', GoogleMapsPlaces::GEOCODE_MODE_SEARCH)
|
|
322
|
-
->withData('location', "$latitude, $longitude")
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
$geoResults = collect($geoResults->all());
|
|
326
|
-
} else {
|
|
327
|
-
$geoResults = Geocoder::geocode($searchQuery)->get();
|
|
328
|
-
}
|
|
286
|
+
$query = Place::where('company_uuid', session('company'))->whereNull('deleted_at');
|
|
329
287
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
$results = $results
|
|
340
|
-
->sortBy('name')
|
|
341
|
-
->values()
|
|
342
|
-
->toArray();
|
|
288
|
+
$results = PlaceSearch::search($query, $searchQuery, [
|
|
289
|
+
'limit' => $limit,
|
|
290
|
+
'geo' => $geo,
|
|
291
|
+
'latitude' => $latitude,
|
|
292
|
+
'longitude' => $longitude,
|
|
293
|
+
'no_query_order' => 'name_desc',
|
|
294
|
+
]);
|
|
343
295
|
|
|
344
296
|
return PlaceResource::collection($results);
|
|
345
297
|
}
|
|
@@ -24,7 +24,7 @@ class ServiceAreaController extends Controller
|
|
|
24
24
|
public function create(CreateServiceAreaRequest $request)
|
|
25
25
|
{
|
|
26
26
|
// get request input
|
|
27
|
-
$input = $request->only(['name', 'type', 'status', 'country', 'color', 'stroke_color', 'trigger_on_entry', 'trigger_on_exit', 'dwell_threshold_minutes', 'speed_limit_kmh']);
|
|
27
|
+
$input = $request->only(['name', 'type', 'status', 'country', 'border', 'color', 'stroke_color', 'trigger_on_entry', 'trigger_on_exit', 'dwell_threshold_minutes', 'speed_limit_kmh']);
|
|
28
28
|
|
|
29
29
|
// get radius for creating service area border - default to 500 meters
|
|
30
30
|
$radius = (int) $request->input('radius', 500);
|
|
@@ -99,7 +99,7 @@ class ServiceAreaController extends Controller
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// get request input
|
|
102
|
-
$input = $request->only(['name', 'type', 'status', 'country', 'color', 'stroke_color', 'trigger_on_entry', 'trigger_on_exit', 'dwell_threshold_minutes', 'speed_limit_kmh']);
|
|
102
|
+
$input = $request->only(['name', 'type', 'status', 'country', 'border', 'color', 'stroke_color', 'trigger_on_entry', 'trigger_on_exit', 'dwell_threshold_minutes', 'speed_limit_kmh']);
|
|
103
103
|
|
|
104
104
|
// get radius for creating service area border - default to 500 meters
|
|
105
105
|
$radius = $request->input('radius', 500);
|
|
@@ -67,7 +67,7 @@ class VehicleController extends Controller
|
|
|
67
67
|
$vehicle = Vehicle::create($input);
|
|
68
68
|
|
|
69
69
|
// driver assignment
|
|
70
|
-
if ($request->
|
|
70
|
+
if ($request->exists('driver') && !empty($request->input('driver'))) {
|
|
71
71
|
// set this vehicle to the driver
|
|
72
72
|
try {
|
|
73
73
|
$driver = Driver::findRecordOrFail($request->input('driver'));
|
|
@@ -80,8 +80,7 @@ class VehicleController extends Controller
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
$
|
|
84
|
-
$driver->save();
|
|
83
|
+
$vehicle->assignDriver($driver);
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
// response the driver resource
|
|
@@ -150,6 +149,24 @@ class VehicleController extends Controller
|
|
|
150
149
|
// save the update
|
|
151
150
|
$vehicle->save();
|
|
152
151
|
|
|
152
|
+
if ($request->exists('driver')) {
|
|
153
|
+
if (empty($request->input('driver'))) {
|
|
154
|
+
$vehicle->unassignDriver();
|
|
155
|
+
} else {
|
|
156
|
+
try {
|
|
157
|
+
$driver = Driver::findRecordOrFail($request->input('driver'));
|
|
158
|
+
$vehicle->assignDriver($driver);
|
|
159
|
+
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $exception) {
|
|
160
|
+
return response()->json(
|
|
161
|
+
[
|
|
162
|
+
'error' => 'The driver attempted to assign this vehicle was not found.',
|
|
163
|
+
],
|
|
164
|
+
404
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
153
170
|
// get udpated vehicle
|
|
154
171
|
$vehicle = $vehicle->refresh();
|
|
155
172
|
|