@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
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Models;
|
|
4
4
|
|
|
5
|
-
use Barryvdh\DomPDF\Facade\Pdf;
|
|
6
5
|
use Fleetbase\Casts\Json;
|
|
7
6
|
use Fleetbase\Casts\PolymorphicType;
|
|
8
7
|
use Fleetbase\FleetOps\Events\OrderCanceled;
|
|
@@ -10,6 +9,7 @@ use Fleetbase\FleetOps\Events\OrderCompleted;
|
|
|
10
9
|
use Fleetbase\FleetOps\Events\OrderDispatched;
|
|
11
10
|
use Fleetbase\FleetOps\Events\OrderDriverAssigned;
|
|
12
11
|
use Fleetbase\FleetOps\Flow\Activity;
|
|
12
|
+
use Fleetbase\FleetOps\Support\LabelPdf;
|
|
13
13
|
use Fleetbase\FleetOps\Support\OrderTracker;
|
|
14
14
|
use Fleetbase\FleetOps\Support\Utils;
|
|
15
15
|
use Fleetbase\FleetOps\Traits\HasTrackingNumber;
|
|
@@ -169,6 +169,7 @@ class Order extends Model
|
|
|
169
169
|
'tracking',
|
|
170
170
|
'total_entities',
|
|
171
171
|
'transaction_amount',
|
|
172
|
+
'transaction_currency',
|
|
172
173
|
'customer_name',
|
|
173
174
|
'customer_phone',
|
|
174
175
|
'facilitator_name',
|
|
@@ -275,7 +276,32 @@ class Order extends Model
|
|
|
275
276
|
*/
|
|
276
277
|
public function pdfLabel()
|
|
277
278
|
{
|
|
278
|
-
return
|
|
279
|
+
return LabelPdf::fromHtml($this->label());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
public function attachFiles($uploads): self
|
|
283
|
+
{
|
|
284
|
+
$ids = collect($uploads)
|
|
285
|
+
->map(function ($upload) {
|
|
286
|
+
if (is_string($upload)) {
|
|
287
|
+
return $upload;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return data_get($upload, 'uuid') ?? data_get($upload, 'id');
|
|
291
|
+
})
|
|
292
|
+
->filter()
|
|
293
|
+
->unique()
|
|
294
|
+
->values();
|
|
295
|
+
|
|
296
|
+
if ($ids->isEmpty()) {
|
|
297
|
+
return $this;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
\Fleetbase\Models\File::whereIn('uuid', $ids)->get()->each(function ($file) {
|
|
301
|
+
$file->setKey($this);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
return $this;
|
|
279
305
|
}
|
|
280
306
|
|
|
281
307
|
/**
|
|
@@ -483,6 +509,16 @@ class Order extends Model
|
|
|
483
509
|
return data_get($this, 'transaction.amount');
|
|
484
510
|
}
|
|
485
511
|
|
|
512
|
+
/**
|
|
513
|
+
* The transaction amount for the order.
|
|
514
|
+
*
|
|
515
|
+
* @return string
|
|
516
|
+
*/
|
|
517
|
+
public function getTransactionCurrencyAttribute()
|
|
518
|
+
{
|
|
519
|
+
return data_get($this, 'transaction.currency');
|
|
520
|
+
}
|
|
521
|
+
|
|
486
522
|
/**
|
|
487
523
|
* The customer name for the order.
|
|
488
524
|
*
|
|
@@ -1118,7 +1154,17 @@ class Order extends Model
|
|
|
1118
1154
|
$this->setRelation('purchaseRate', $purchaseRate);
|
|
1119
1155
|
|
|
1120
1156
|
if ($purchaseRate->transaction_uuid) {
|
|
1121
|
-
$
|
|
1157
|
+
$transaction = Transaction::where('uuid', $purchaseRate->transaction_uuid)->first();
|
|
1158
|
+
if ($transaction instanceof Transaction) {
|
|
1159
|
+
$transaction->update([
|
|
1160
|
+
'subject_uuid' => $this->uuid,
|
|
1161
|
+
'subject_type' => static::class,
|
|
1162
|
+
'context_uuid' => $purchaseRate->uuid,
|
|
1163
|
+
'context_type' => PurchaseRate::class,
|
|
1164
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
1165
|
+
]);
|
|
1166
|
+
$this->setRelation('transaction', $transaction);
|
|
1167
|
+
}
|
|
1122
1168
|
}
|
|
1123
1169
|
|
|
1124
1170
|
if (
|
|
@@ -1151,12 +1197,17 @@ class Order extends Model
|
|
|
1151
1197
|
'company_uuid' => session('company', $this->company_uuid),
|
|
1152
1198
|
'customer_uuid' => $this->customer_uuid,
|
|
1153
1199
|
'customer_type' => $this->customer_type,
|
|
1200
|
+
'subject_uuid' => $this->uuid,
|
|
1201
|
+
'subject_type' => static::class,
|
|
1202
|
+
'context_uuid' => $this->uuid,
|
|
1203
|
+
'context_type' => static::class,
|
|
1154
1204
|
'gateway_transaction_id' => Transaction::generateNumber(),
|
|
1155
1205
|
'gateway' => 'internal',
|
|
1156
1206
|
'amount' => 0,
|
|
1157
1207
|
'currency' => Utils::getCompanyTransactionCurrency($this->company ?? $this->company_uuid),
|
|
1158
1208
|
'description' => 'Dispatch order',
|
|
1159
1209
|
'type' => 'dispatch',
|
|
1210
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
1160
1211
|
'status' => 'success',
|
|
1161
1212
|
]);
|
|
1162
1213
|
|
|
@@ -1354,8 +1405,9 @@ class Order extends Model
|
|
|
1354
1405
|
*
|
|
1355
1406
|
* The process involves the following steps:
|
|
1356
1407
|
* 1. Creating a new Activity instance with the 'completed' code and relevant details.
|
|
1357
|
-
* 2.
|
|
1358
|
-
*
|
|
1408
|
+
* 2. Updating the order's activity with the new 'completed' activity through `updateActivity`,
|
|
1409
|
+
* which applies and persists the 'completed' status.
|
|
1410
|
+
* 3. Notifying that the order has been completed via `notifyCompleted`.
|
|
1359
1411
|
*
|
|
1360
1412
|
* @param Proof|null $proof Optional. Additional proof or details for the activity update.
|
|
1361
1413
|
*
|
|
@@ -1363,15 +1415,15 @@ class Order extends Model
|
|
|
1363
1415
|
*/
|
|
1364
1416
|
public function complete(?Proof $proof = null): self
|
|
1365
1417
|
{
|
|
1366
|
-
$this->notifyCompleted();
|
|
1367
|
-
|
|
1368
1418
|
$doesntHaveCompletedActivity = TrackingStatus::where(['tracking_number_uuid' => $this->tracking_number_uuid, 'code' => 'COMPLETED'])->doesntExist();
|
|
1369
1419
|
if ($doesntHaveCompletedActivity) {
|
|
1370
1420
|
$activity = $this->config()->getCompletedActivity();
|
|
1371
1421
|
|
|
1372
|
-
|
|
1422
|
+
$this->updateActivity($activity, $proof);
|
|
1373
1423
|
}
|
|
1374
1424
|
|
|
1425
|
+
$this->notifyCompleted();
|
|
1426
|
+
|
|
1375
1427
|
return $this;
|
|
1376
1428
|
}
|
|
1377
1429
|
|
|
@@ -1991,11 +2043,11 @@ class Order extends Model
|
|
|
1991
2043
|
return collect();
|
|
1992
2044
|
}
|
|
1993
2045
|
|
|
1994
|
-
$drivers = Driver::where(['status' => '
|
|
2046
|
+
$drivers = Driver::where(['status' => 'available', 'online' => 1])
|
|
1995
2047
|
->whereHas('company', function ($q) {
|
|
1996
2048
|
$q->whereHas('users', function ($q) {
|
|
1997
2049
|
$q->whereHas('driver', function ($q) {
|
|
1998
|
-
$q->where(['status' => '
|
|
2050
|
+
$q->where(['status' => 'available', 'online' => 1]);
|
|
1999
2051
|
$q->whereNull('deleted_at');
|
|
2000
2052
|
});
|
|
2001
2053
|
});
|
|
@@ -61,7 +61,23 @@ class Payload extends Model
|
|
|
61
61
|
*
|
|
62
62
|
* @var array
|
|
63
63
|
*/
|
|
64
|
-
protected $fillable = [
|
|
64
|
+
protected $fillable = [
|
|
65
|
+
'_key',
|
|
66
|
+
'company_uuid',
|
|
67
|
+
'pickup_uuid',
|
|
68
|
+
'pickup_tracking_number_uuid',
|
|
69
|
+
'dropoff_uuid',
|
|
70
|
+
'dropoff_tracking_number_uuid',
|
|
71
|
+
'return_uuid',
|
|
72
|
+
'return_tracking_number_uuid',
|
|
73
|
+
'current_waypoint_uuid',
|
|
74
|
+
'meta',
|
|
75
|
+
'payment_method',
|
|
76
|
+
'cod_amount',
|
|
77
|
+
'cod_currency',
|
|
78
|
+
'cod_payment_method',
|
|
79
|
+
'type',
|
|
80
|
+
];
|
|
65
81
|
|
|
66
82
|
/**
|
|
67
83
|
* The attributes that should be cast to native types.
|
|
@@ -216,6 +216,14 @@ class ServiceRate extends Model
|
|
|
216
216
|
return $this->rate_calculation_method === 'per_meter';
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Check if the rate calculation method is "multi_zone_distance".
|
|
221
|
+
*/
|
|
222
|
+
public function isMultiZoneDistance(): bool
|
|
223
|
+
{
|
|
224
|
+
return $this->rate_calculation_method === 'multi_zone_distance';
|
|
225
|
+
}
|
|
226
|
+
|
|
219
227
|
/**
|
|
220
228
|
* Check if the rate calculation method is "per_drop".
|
|
221
229
|
*/
|
|
@@ -329,6 +337,16 @@ class ServiceRate extends Model
|
|
|
329
337
|
return $this;
|
|
330
338
|
}
|
|
331
339
|
|
|
340
|
+
$serviceRateFees = collect($serviceRateFees)
|
|
341
|
+
->map(fn ($fee) => $this->normalizeServiceRateFeePayload($fee))
|
|
342
|
+
->filter()
|
|
343
|
+
->values()
|
|
344
|
+
->toArray();
|
|
345
|
+
|
|
346
|
+
if (!$serviceRateFees) {
|
|
347
|
+
return $this;
|
|
348
|
+
}
|
|
349
|
+
|
|
332
350
|
$iterate = count($serviceRateFees);
|
|
333
351
|
|
|
334
352
|
for ($i = 0; $i < $iterate; $i++) {
|
|
@@ -356,6 +374,43 @@ class ServiceRate extends Model
|
|
|
356
374
|
return $this;
|
|
357
375
|
}
|
|
358
376
|
|
|
377
|
+
/**
|
|
378
|
+
* Normalize embedded service-rate fee payloads before persistence.
|
|
379
|
+
*/
|
|
380
|
+
public function normalizeServiceRateFeePayload($fee): ?array
|
|
381
|
+
{
|
|
382
|
+
if (!is_array($fee)) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
$fee['service_area_uuid'] ??= data_get($fee, 'service_area.uuid');
|
|
387
|
+
$fee['zone_uuid'] ??= data_get($fee, 'zone.uuid');
|
|
388
|
+
|
|
389
|
+
if (Utils::castBoolean($fee['is_fallback'] ?? false)) {
|
|
390
|
+
$fee['service_area_uuid'] = null;
|
|
391
|
+
$fee['zone_uuid'] = null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return collect($fee)
|
|
395
|
+
->only([
|
|
396
|
+
'uuid',
|
|
397
|
+
'service_rate_uuid',
|
|
398
|
+
'service_area_uuid',
|
|
399
|
+
'zone_uuid',
|
|
400
|
+
'label',
|
|
401
|
+
'priority',
|
|
402
|
+
'is_fallback',
|
|
403
|
+
'distance',
|
|
404
|
+
'distance_unit',
|
|
405
|
+
'min',
|
|
406
|
+
'max',
|
|
407
|
+
'unit',
|
|
408
|
+
'fee',
|
|
409
|
+
'currency',
|
|
410
|
+
])
|
|
411
|
+
->toArray();
|
|
412
|
+
}
|
|
413
|
+
|
|
359
414
|
/**
|
|
360
415
|
* Set the service rate parcel fees for the current object.
|
|
361
416
|
*
|
|
@@ -506,7 +561,7 @@ class ServiceRate extends Model
|
|
|
506
561
|
public static function getServicableForPlaces($places = [], $service = null, $currency = null, ?\Closure $queryCallback = null): array
|
|
507
562
|
{
|
|
508
563
|
$reader = new GeoJSONReader();
|
|
509
|
-
$serviceRatesQuery = static::with(['zone', 'serviceArea', 'rateFees', 'parcelFees']);
|
|
564
|
+
$serviceRatesQuery = static::with(['zone', 'serviceArea', 'rateFees.zone', 'rateFees.serviceArea', 'parcelFees']);
|
|
510
565
|
|
|
511
566
|
if ($currency) {
|
|
512
567
|
$serviceRatesQuery->whereRaw('lower(currency) = ?', [strtolower($currency)]);
|
|
@@ -738,6 +793,12 @@ class ServiceRate extends Model
|
|
|
738
793
|
]);
|
|
739
794
|
}
|
|
740
795
|
|
|
796
|
+
if ($this->isMultiZoneDistance()) {
|
|
797
|
+
[$multiZoneFee, $multiZoneLines] = $this->quoteMultiZoneDistance($waypoints, $totalDistance);
|
|
798
|
+
$subTotal += $multiZoneFee;
|
|
799
|
+
$lines = $lines->merge($multiZoneLines);
|
|
800
|
+
}
|
|
801
|
+
|
|
741
802
|
if ($this->isAlgorithm()) {
|
|
742
803
|
$resolvedEndpointCount = $endpointCount ?? $this->inferEndpointCountFromStops($waypoints);
|
|
743
804
|
$rateFee = $this->normalizeCalculatedMoney(Algo::exec(
|
|
@@ -927,6 +988,12 @@ class ServiceRate extends Model
|
|
|
927
988
|
]);
|
|
928
989
|
}
|
|
929
990
|
|
|
991
|
+
if ($this->isMultiZoneDistance()) {
|
|
992
|
+
[$multiZoneFee, $multiZoneLines] = $this->quoteMultiZoneDistance($waypoints, $totalDistance);
|
|
993
|
+
$subTotal += $multiZoneFee;
|
|
994
|
+
$lines = $lines->merge($multiZoneLines);
|
|
995
|
+
}
|
|
996
|
+
|
|
930
997
|
if ($this->isAlgorithm()) {
|
|
931
998
|
$rateFee = $this->normalizeCalculatedMoney(Algo::exec(
|
|
932
999
|
$this->algorithm,
|
|
@@ -1086,6 +1153,211 @@ class ServiceRate extends Model
|
|
|
1086
1153
|
return 2;
|
|
1087
1154
|
}
|
|
1088
1155
|
|
|
1156
|
+
protected function quoteMultiZoneDistance($waypoints = [], ?int $totalDistance = 0): array
|
|
1157
|
+
{
|
|
1158
|
+
$this->loadMissing(['rateFees.zone', 'rateFees.serviceArea']);
|
|
1159
|
+
|
|
1160
|
+
$rules = $this->rateFees
|
|
1161
|
+
->filter(fn ($rule) => !$rule->is_fallback && ($rule->zone || $rule->serviceArea))
|
|
1162
|
+
->sortByDesc(fn ($rule) => (int) ($rule->priority ?? 0))
|
|
1163
|
+
->values();
|
|
1164
|
+
|
|
1165
|
+
$fallbackRule = $this->rateFees
|
|
1166
|
+
->filter(fn ($rule) => (bool) $rule->is_fallback)
|
|
1167
|
+
->sortByDesc(fn ($rule) => (int) ($rule->priority ?? 0))
|
|
1168
|
+
->first();
|
|
1169
|
+
|
|
1170
|
+
if ($rules->isEmpty() && !$fallbackRule) {
|
|
1171
|
+
return [0, collect()];
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
$pricedDistances = $this->calculateMultiZoneDistances($waypoints, $rules, $fallbackRule, $totalDistance);
|
|
1175
|
+
$lines = collect();
|
|
1176
|
+
$subTotal = 0;
|
|
1177
|
+
|
|
1178
|
+
foreach ($pricedDistances as $entry) {
|
|
1179
|
+
$rule = $entry['rule'];
|
|
1180
|
+
if (!$rule instanceof ServiceRateFee) {
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
$distanceInMeters = (float) ($entry['distance_m'] ?? 0);
|
|
1185
|
+
if ($distanceInMeters <= 0) {
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
$unit = strtolower($rule->distance_unit ?: 'km');
|
|
1190
|
+
$distance = $this->normalizeDistanceForUnit($distanceInMeters, $unit);
|
|
1191
|
+
$fee = $this->normalizeCalculatedMoney($distance * Utils::numbersOnly($rule->fee ?? 0));
|
|
1192
|
+
$subTotal += $fee;
|
|
1193
|
+
|
|
1194
|
+
if ($rule->is_fallback) {
|
|
1195
|
+
$label = 'Out-of-zone distance charge';
|
|
1196
|
+
} else {
|
|
1197
|
+
$label = data_get($rule, 'zone.name') ?: data_get($rule, 'serviceArea.name') ?: $rule->label ?: 'Geographic';
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (!$rule->is_fallback && !str_ends_with(strtolower($label), 'distance charge')) {
|
|
1201
|
+
$label .= ' distance charge';
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
$lines->push([
|
|
1205
|
+
'details' => sprintf('%s (%s %s x %s)', $label, round($distance, 2), $unit, Utils::moneyFormat($rule->fee, $this->currency)),
|
|
1206
|
+
'amount' => $fee,
|
|
1207
|
+
'formatted_amount' => Utils::moneyFormat($fee, $this->currency),
|
|
1208
|
+
'currency' => $this->currency,
|
|
1209
|
+
'code' => 'MULTI_ZONE_DISTANCE_FEE',
|
|
1210
|
+
]);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
return [$subTotal, $lines];
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
protected function calculateMultiZoneDistances($waypoints, $rules, ?ServiceRateFee $fallbackRule = null, ?int $totalDistance = 0): array
|
|
1217
|
+
{
|
|
1218
|
+
$places = collect($waypoints)->map(fn ($place) => Place::createFromMixed($place))->filter()->values();
|
|
1219
|
+
|
|
1220
|
+
if ($places->count() < 2 || (int) $totalDistance <= 0) {
|
|
1221
|
+
return $fallbackRule ? [['rule' => $fallbackRule, 'distance_m' => (float) ($totalDistance ?? 0)]] : [];
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
$reader = new GeoJSONReader();
|
|
1225
|
+
$ruleGeometries = $rules->map(function ($rule) use ($reader) {
|
|
1226
|
+
return [
|
|
1227
|
+
'rule' => $rule,
|
|
1228
|
+
'geometry' => $this->readRateRuleGeometry($rule, $reader),
|
|
1229
|
+
];
|
|
1230
|
+
})->filter(fn ($entry) => $entry['geometry'])->values();
|
|
1231
|
+
|
|
1232
|
+
if ($ruleGeometries->isEmpty()) {
|
|
1233
|
+
return $fallbackRule ? [['rule' => $fallbackRule, 'distance_m' => (float) $totalDistance]] : [];
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
$distances = [];
|
|
1237
|
+
$sampledTotal = 0;
|
|
1238
|
+
|
|
1239
|
+
for ($i = 0; $i < $places->count() - 1; $i++) {
|
|
1240
|
+
$start = $this->getLngLatFromPlace($places[$i]);
|
|
1241
|
+
$end = $this->getLngLatFromPlace($places[$i + 1]);
|
|
1242
|
+
|
|
1243
|
+
if (!$start || !$end) {
|
|
1244
|
+
continue;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
$legDistance = $this->haversineDistanceInMeters($start['lat'], $start['lng'], $end['lat'], $end['lng']);
|
|
1248
|
+
$steps = max(1, min(500, (int) ceil($legDistance / 1000)));
|
|
1249
|
+
|
|
1250
|
+
for ($step = 0; $step < $steps; $step++) {
|
|
1251
|
+
$fromRatio = $step / $steps;
|
|
1252
|
+
$toRatio = ($step + 1) / $steps;
|
|
1253
|
+
$midRatio = ($fromRatio + $toRatio) / 2;
|
|
1254
|
+
|
|
1255
|
+
$from = $this->interpolateLngLat($start, $end, $fromRatio);
|
|
1256
|
+
$to = $this->interpolateLngLat($start, $end, $toRatio);
|
|
1257
|
+
$mid = $this->interpolateLngLat($start, $end, $midRatio);
|
|
1258
|
+
|
|
1259
|
+
$sampleDistance = $this->haversineDistanceInMeters($from['lat'], $from['lng'], $to['lat'], $to['lng']);
|
|
1260
|
+
$sampledTotal += $sampleDistance;
|
|
1261
|
+
|
|
1262
|
+
$matchedRule = $this->matchMultiZoneRule($mid, $ruleGeometries) ?: $fallbackRule;
|
|
1263
|
+
if (!$matchedRule instanceof ServiceRateFee) {
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
$key = $matchedRule->uuid ?? spl_object_hash($matchedRule);
|
|
1268
|
+
if (!isset($distances[$key])) {
|
|
1269
|
+
$distances[$key] = ['rule' => $matchedRule, 'distance_m' => 0];
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
$distances[$key]['distance_m'] += $sampleDistance;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
if ($sampledTotal > 0 && $totalDistance > 0) {
|
|
1277
|
+
$scale = $totalDistance / $sampledTotal;
|
|
1278
|
+
foreach ($distances as &$entry) {
|
|
1279
|
+
$entry['distance_m'] *= $scale;
|
|
1280
|
+
}
|
|
1281
|
+
unset($entry);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return array_values($distances);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
protected function readRateRuleGeometry(ServiceRateFee $rule, GeoJSONReader $reader)
|
|
1288
|
+
{
|
|
1289
|
+
$border = data_get($rule, 'zone.border') ?: data_get($rule, 'serviceArea.border');
|
|
1290
|
+
|
|
1291
|
+
if (!$border) {
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
try {
|
|
1296
|
+
if (is_object($border) && method_exists($border, 'toJson')) {
|
|
1297
|
+
return $reader->read($border->toJson());
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
if (is_array($border) || is_object($border)) {
|
|
1301
|
+
return $reader->read(json_encode($border, JSON_UNESCAPED_UNICODE));
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
if (is_string($border)) {
|
|
1305
|
+
return $reader->read($border);
|
|
1306
|
+
}
|
|
1307
|
+
} catch (\Throwable $e) {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
protected function matchMultiZoneRule(array $point, $ruleGeometries): ?ServiceRateFee
|
|
1315
|
+
{
|
|
1316
|
+
$brickPoint = \Brick\Geo\Point::fromText(sprintf('POINT (%F %F)', $point['lng'], $point['lat']), 4326);
|
|
1317
|
+
|
|
1318
|
+
foreach ($ruleGeometries as $entry) {
|
|
1319
|
+
if ($entry['geometry']->contains($brickPoint)) {
|
|
1320
|
+
return $entry['rule'];
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return null;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
protected function getLngLatFromPlace(?Place $place): ?array
|
|
1328
|
+
{
|
|
1329
|
+
if (!$place instanceof Place) {
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
$point = $place->getLocationAsPoint();
|
|
1334
|
+
|
|
1335
|
+
if (!$point || !method_exists($point, 'getLat') || !method_exists($point, 'getLng')) {
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
return ['lat' => (float) $point->getLat(), 'lng' => (float) $point->getLng()];
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
protected function interpolateLngLat(array $start, array $end, float $ratio): array
|
|
1343
|
+
{
|
|
1344
|
+
return [
|
|
1345
|
+
'lat' => $start['lat'] + (($end['lat'] - $start['lat']) * $ratio),
|
|
1346
|
+
'lng' => $start['lng'] + (($end['lng'] - $start['lng']) * $ratio),
|
|
1347
|
+
];
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
protected function haversineDistanceInMeters(float $lat1, float $lng1, float $lat2, float $lng2): float
|
|
1351
|
+
{
|
|
1352
|
+
$earthRadius = 6371000;
|
|
1353
|
+
$latDelta = deg2rad($lat2 - $lat1);
|
|
1354
|
+
$lngDelta = deg2rad($lng2 - $lng1);
|
|
1355
|
+
|
|
1356
|
+
$a = sin($latDelta / 2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($lngDelta / 2) ** 2;
|
|
1357
|
+
|
|
1358
|
+
return $earthRadius * 2 * atan2(sqrt($a), sqrt(1 - $a));
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1089
1361
|
/**
|
|
1090
1362
|
* Find the ServiceRateFee based on the total distance.
|
|
1091
1363
|
*
|
|
@@ -7,6 +7,7 @@ use Fleetbase\FleetOps\Support\Utils;
|
|
|
7
7
|
use Fleetbase\Models\Model;
|
|
8
8
|
use Fleetbase\Traits\HasUuid;
|
|
9
9
|
use Fleetbase\Traits\TracksApiCredential;
|
|
10
|
+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
10
11
|
|
|
11
12
|
class ServiceRateFee extends Model
|
|
12
13
|
{
|
|
@@ -32,7 +33,22 @@ class ServiceRateFee extends Model
|
|
|
32
33
|
*
|
|
33
34
|
* @var array
|
|
34
35
|
*/
|
|
35
|
-
protected $fillable = [
|
|
36
|
+
protected $fillable = [
|
|
37
|
+
'_key',
|
|
38
|
+
'service_rate_uuid',
|
|
39
|
+
'service_area_uuid',
|
|
40
|
+
'zone_uuid',
|
|
41
|
+
'label',
|
|
42
|
+
'priority',
|
|
43
|
+
'is_fallback',
|
|
44
|
+
'distance',
|
|
45
|
+
'distance_unit',
|
|
46
|
+
'min',
|
|
47
|
+
'max',
|
|
48
|
+
'unit',
|
|
49
|
+
'fee',
|
|
50
|
+
'currency',
|
|
51
|
+
];
|
|
36
52
|
|
|
37
53
|
/**
|
|
38
54
|
* The attributes that should be cast to native types.
|
|
@@ -40,10 +56,12 @@ class ServiceRateFee extends Model
|
|
|
40
56
|
* @var array
|
|
41
57
|
*/
|
|
42
58
|
protected $casts = [
|
|
43
|
-
'min'
|
|
44
|
-
'max'
|
|
45
|
-
'fee'
|
|
46
|
-
'distance'
|
|
59
|
+
'min' => 'integer',
|
|
60
|
+
'max' => 'integer',
|
|
61
|
+
'fee' => Money::class,
|
|
62
|
+
'distance' => 'integer',
|
|
63
|
+
'priority' => 'integer',
|
|
64
|
+
'is_fallback' => 'boolean',
|
|
47
65
|
];
|
|
48
66
|
|
|
49
67
|
/**
|
|
@@ -62,14 +80,26 @@ class ServiceRateFee extends Model
|
|
|
62
80
|
|
|
63
81
|
public static function onRowInsert($row)
|
|
64
82
|
{
|
|
65
|
-
$row['fee']
|
|
66
|
-
$row['distance']
|
|
67
|
-
$row['min']
|
|
68
|
-
$row['max']
|
|
83
|
+
$row['fee'] = Money::apply($row['fee'] ?? 0);
|
|
84
|
+
$row['distance'] = Utils::numbersOnly($row['distance'] ?? null);
|
|
85
|
+
$row['min'] = Utils::numbersOnly($row['min'] ?? null);
|
|
86
|
+
$row['max'] = Utils::numbersOnly($row['max'] ?? null);
|
|
87
|
+
$row['priority'] = Utils::numbersOnly($row['priority'] ?? 0);
|
|
88
|
+
$row['is_fallback'] = Utils::castBoolean($row['is_fallback'] ?? false);
|
|
69
89
|
|
|
70
90
|
return $row;
|
|
71
91
|
}
|
|
72
92
|
|
|
93
|
+
public function serviceArea(): BelongsTo
|
|
94
|
+
{
|
|
95
|
+
return $this->belongsTo(ServiceArea::class, 'service_area_uuid', 'uuid');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public function zone(): BelongsTo
|
|
99
|
+
{
|
|
100
|
+
return $this->belongsTo(Zone::class, 'zone_uuid', 'uuid');
|
|
101
|
+
}
|
|
102
|
+
|
|
73
103
|
public function setDistanceAttribute($value)
|
|
74
104
|
{
|
|
75
105
|
$this->attributes['distance'] = Utils::numbersOnly($value);
|
|
@@ -14,6 +14,7 @@ use Fleetbase\Traits\SendsWebhooks;
|
|
|
14
14
|
use Fleetbase\Traits\TracksApiCredential;
|
|
15
15
|
use Illuminate\Support\Carbon;
|
|
16
16
|
use Illuminate\Support\Facades\DB;
|
|
17
|
+
use Illuminate\Support\Facades\Schema;
|
|
17
18
|
use Illuminate\Support\Str;
|
|
18
19
|
use Milon\Barcode\Facades\DNS2DFacade as DNS2D;
|
|
19
20
|
|
|
@@ -242,7 +243,7 @@ class TrackingNumber extends Model
|
|
|
242
243
|
$status = strtolower($trackingStatus->code);
|
|
243
244
|
$owner = $this->load(['owner'])->getRelationValue('owner');
|
|
244
245
|
|
|
245
|
-
if ($owner && $owner->isFillable('status') && $owner->status !== $status) {
|
|
246
|
+
if ($owner && static::ownerHasStatusColumn($owner) && $owner->isFillable('status') && $owner->status !== $status) {
|
|
246
247
|
$this->owner->status = $status;
|
|
247
248
|
|
|
248
249
|
$this->owner->save();
|
|
@@ -303,7 +304,7 @@ class TrackingNumber extends Model
|
|
|
303
304
|
TrackingNumber::where('uuid', $uuid)->update(['status_uuid' => $trackingStatusId]);
|
|
304
305
|
|
|
305
306
|
// update owner status
|
|
306
|
-
if ($owner && $owner instanceof Model && $owner->isFillable('status')) {
|
|
307
|
+
if ($owner && $owner instanceof Model && static::ownerHasStatusColumn($owner) && $owner->isFillable('status')) {
|
|
307
308
|
// runs event cycles
|
|
308
309
|
// $model->update([ 'status' => 'created' ]);
|
|
309
310
|
|
|
@@ -313,4 +314,9 @@ class TrackingNumber extends Model
|
|
|
313
314
|
|
|
314
315
|
return $uuid;
|
|
315
316
|
}
|
|
317
|
+
|
|
318
|
+
protected static function ownerHasStatusColumn(Model $owner): bool
|
|
319
|
+
{
|
|
320
|
+
return Schema::hasColumn($owner->getTable(), 'status');
|
|
321
|
+
}
|
|
316
322
|
}
|
|
@@ -223,6 +223,7 @@ class Vehicle extends Model
|
|
|
223
223
|
*/
|
|
224
224
|
protected $attributes = [
|
|
225
225
|
'avatar_url' => 'https://flb-assets.s3-ap-southeast-1.amazonaws.com/static/vehicle-icons/mini_bus.svg',
|
|
226
|
+
'status' => 'available',
|
|
226
227
|
];
|
|
227
228
|
|
|
228
229
|
/**
|
|
@@ -324,6 +325,14 @@ class Vehicle extends Model
|
|
|
324
325
|
return $this->hasOne(Driver::class, 'vehicle_uuid', 'uuid')->without(['vehicle']);
|
|
325
326
|
}
|
|
326
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Set the vehicle status attribute.
|
|
330
|
+
*/
|
|
331
|
+
public function setStatusAttribute(?string $status = 'available'): void
|
|
332
|
+
{
|
|
333
|
+
$this->attributes['status'] = $status === null || $status === 'active' ? 'available' : $status;
|
|
334
|
+
}
|
|
335
|
+
|
|
327
336
|
public function category(): BelongsTo
|
|
328
337
|
{
|
|
329
338
|
return $this->belongsTo(Category::class, 'category_uuid', 'uuid');
|
|
@@ -597,6 +606,14 @@ class Vehicle extends Model
|
|
|
597
606
|
return $this;
|
|
598
607
|
}
|
|
599
608
|
|
|
609
|
+
public function unassignDriver(): self
|
|
610
|
+
{
|
|
611
|
+
Driver::where('vehicle_uuid', $this->uuid)->update(['vehicle_uuid' => null]);
|
|
612
|
+
$this->unsetRelation('driver');
|
|
613
|
+
|
|
614
|
+
return $this;
|
|
615
|
+
}
|
|
616
|
+
|
|
600
617
|
/**
|
|
601
618
|
* Retrieves the last known position of the driver within the current company context.
|
|
602
619
|
*
|
|
@@ -727,9 +744,8 @@ class Vehicle extends Model
|
|
|
727
744
|
'plate_number' => $plateNumber,
|
|
728
745
|
'vin' => $vin,
|
|
729
746
|
'type' => $type,
|
|
730
|
-
'status' => '
|
|
747
|
+
'status' => 'available',
|
|
731
748
|
'online' => 0,
|
|
732
|
-
'status' => 'active',
|
|
733
749
|
]);
|
|
734
750
|
|
|
735
751
|
// If driver was resolved assign driver to vehicle
|