@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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Models;
|
|
4
4
|
|
|
5
5
|
use Fleetbase\Models\Model;
|
|
6
|
+
use Fleetbase\Models\User;
|
|
6
7
|
use Fleetbase\Traits\HasUuid;
|
|
7
8
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
8
9
|
|
|
@@ -22,7 +23,7 @@ class VendorPersonnel extends Model
|
|
|
22
23
|
*
|
|
23
24
|
* @var array
|
|
24
25
|
*/
|
|
25
|
-
protected $fillable = ['vendor_uuid', 'contact_uuid'];
|
|
26
|
+
protected $fillable = ['vendor_uuid', 'contact_uuid', 'role', 'status', 'invited_by_uuid'];
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* These attributes that can be queried.
|
|
@@ -60,4 +61,12 @@ class VendorPersonnel extends Model
|
|
|
60
61
|
{
|
|
61
62
|
return $this->belongsTo(Contact::class);
|
|
62
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The Fleetbase user who invited this personnel.
|
|
67
|
+
*/
|
|
68
|
+
public function invitedBy(): BelongsTo
|
|
69
|
+
{
|
|
70
|
+
return $this->belongsTo(User::class, 'invited_by_uuid');
|
|
71
|
+
}
|
|
63
72
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Models;
|
|
4
4
|
|
|
5
|
-
use Barryvdh\DomPDF\Facade\Pdf;
|
|
6
5
|
use Fleetbase\Casts\PolymorphicType;
|
|
6
|
+
use Fleetbase\FleetOps\Support\LabelPdf;
|
|
7
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
8
8
|
use Fleetbase\FleetOps\Traits\HasTrackingNumber;
|
|
9
9
|
use Fleetbase\FleetOps\Traits\PayloadAccessors;
|
|
@@ -94,7 +94,7 @@ class Waypoint extends Model
|
|
|
94
94
|
*/
|
|
95
95
|
public function pdfLabel()
|
|
96
96
|
{
|
|
97
|
-
return
|
|
97
|
+
return LabelPdf::fromHtml($this->label());
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
@@ -12,9 +12,9 @@ class LateDeparture extends Notification implements ShouldQueue
|
|
|
12
12
|
{
|
|
13
13
|
use Queueable;
|
|
14
14
|
|
|
15
|
-
public static string $name
|
|
15
|
+
public static string $name = 'Late Departure';
|
|
16
16
|
public static string $description = 'Notify when an order has not departed after the configured grace period.';
|
|
17
|
-
public static string $package
|
|
17
|
+
public static string $package = 'fleet-ops';
|
|
18
18
|
|
|
19
19
|
public function __construct(public Order $order, public array $context = [])
|
|
20
20
|
{
|
|
@@ -12,9 +12,9 @@ class ProlongedStoppage extends Notification implements ShouldQueue
|
|
|
12
12
|
{
|
|
13
13
|
use Queueable;
|
|
14
14
|
|
|
15
|
-
public static string $name
|
|
15
|
+
public static string $name = 'Prolonged Stoppage';
|
|
16
16
|
public static string $description = 'Notify when a vehicle remains stopped during an active trip beyond the configured threshold.';
|
|
17
|
-
public static string $package
|
|
17
|
+
public static string $package = 'fleet-ops';
|
|
18
18
|
|
|
19
19
|
public function __construct(public Order $order, public array $context = [])
|
|
20
20
|
{
|
|
@@ -12,9 +12,9 @@ class RouteDeviation extends Notification implements ShouldQueue
|
|
|
12
12
|
{
|
|
13
13
|
use Queueable;
|
|
14
14
|
|
|
15
|
-
public static string $name
|
|
15
|
+
public static string $name = 'Route Deviation';
|
|
16
16
|
public static string $description = 'Notify when tracked vehicle location deviates from the active route beyond the configured threshold.';
|
|
17
|
-
public static string $package
|
|
17
|
+
public static string $package = 'fleet-ops';
|
|
18
18
|
|
|
19
19
|
public function __construct(public Order $order, public array $context = [])
|
|
20
20
|
{
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Fleetbase\FleetOps\Observers;
|
|
4
4
|
|
|
5
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
5
6
|
use Fleetbase\FleetOps\Models\PurchaseRate;
|
|
6
7
|
use Fleetbase\FleetOps\Support\Utils;
|
|
7
8
|
use Fleetbase\Models\Company;
|
|
@@ -18,7 +19,12 @@ class PurchaseRateObserver
|
|
|
18
19
|
*/
|
|
19
20
|
public function creating(PurchaseRate $purchaseRate)
|
|
20
21
|
{
|
|
22
|
+
if (!$purchaseRate->uuid) {
|
|
23
|
+
$purchaseRate->uuid = PurchaseRate::generateUuid();
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
$purchaseRate->load(['serviceQuote.items', 'serviceQuote.serviceRate']);
|
|
27
|
+
$order = $this->resolveOrder($purchaseRate);
|
|
22
28
|
|
|
23
29
|
// get company
|
|
24
30
|
$company = Company::where('uuid', session('company', $purchaseRate->company_uuid))->first();
|
|
@@ -32,12 +38,17 @@ class PurchaseRateObserver
|
|
|
32
38
|
'company_uuid' => session('company', $purchaseRate->company_uuid),
|
|
33
39
|
'customer_uuid' => $purchaseRate->customer_uuid,
|
|
34
40
|
'customer_type' => $purchaseRate->customer_type,
|
|
41
|
+
'subject_uuid' => $order?->uuid,
|
|
42
|
+
'subject_type' => $order ? Order::class : null,
|
|
43
|
+
'context_uuid' => $purchaseRate->uuid,
|
|
44
|
+
'context_type' => PurchaseRate::class,
|
|
35
45
|
'gateway_transaction_id' => $purchaseRate->getMeta('transaction_id', Transaction::generateNumber()),
|
|
36
46
|
'gateway' => 'internal',
|
|
37
47
|
'amount' => data_get($purchaseRate, 'serviceQuote.amount', 0),
|
|
38
48
|
'currency' => $currency,
|
|
39
49
|
'description' => 'Dispatch order',
|
|
40
50
|
'type' => 'dispatch',
|
|
51
|
+
'direction' => Transaction::DIRECTION_CREDIT,
|
|
41
52
|
'status' => 'success',
|
|
42
53
|
]);
|
|
43
54
|
|
|
@@ -56,4 +67,13 @@ class PurchaseRateObserver
|
|
|
56
67
|
$purchaseRate->transaction_uuid = $transaction->uuid;
|
|
57
68
|
$purchaseRate->status = $purchaseRate->status ?: Transaction::STATUS_SUCCESS;
|
|
58
69
|
}
|
|
70
|
+
|
|
71
|
+
protected function resolveOrder(PurchaseRate $purchaseRate): ?Order
|
|
72
|
+
{
|
|
73
|
+
if (!$purchaseRate->payload_uuid) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Order::where('payload_uuid', $purchaseRate->payload_uuid)->first();
|
|
78
|
+
}
|
|
59
79
|
}
|
|
@@ -14,10 +14,10 @@ class ServiceRateObserver
|
|
|
14
14
|
*/
|
|
15
15
|
public function created(ServiceRate $serviceRate)
|
|
16
16
|
{
|
|
17
|
-
$serviceRateFees = request()->input('serviceRate.rate_fees');
|
|
18
|
-
$serviceRateParcelFees = request()->input('serviceRate.parcel_fees');
|
|
17
|
+
$serviceRateFees = request()->input('serviceRate.rate_fees', request()->input('service_rate.rate_fees'));
|
|
18
|
+
$serviceRateParcelFees = request()->input('serviceRate.parcel_fees', request()->input('service_rate.parcel_fees'));
|
|
19
19
|
|
|
20
|
-
if ($serviceRate->isFixedMeter() || $serviceRate->isPerDrop()) {
|
|
20
|
+
if ($serviceRate->isFixedMeter() || $serviceRate->isPerDrop() || $serviceRate->isMultiZoneDistance()) {
|
|
21
21
|
$serviceRate->setServiceRateFees($serviceRateFees);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -33,10 +33,10 @@ class ServiceRateObserver
|
|
|
33
33
|
*/
|
|
34
34
|
public function updated(ServiceRate $serviceRate)
|
|
35
35
|
{
|
|
36
|
-
$serviceRateFees = request()->input('serviceRate.rate_fees');
|
|
37
|
-
$serviceRateParcelFees = request()->input('serviceRate.parcel_fees');
|
|
36
|
+
$serviceRateFees = request()->input('serviceRate.rate_fees', request()->input('service_rate.rate_fees'));
|
|
37
|
+
$serviceRateParcelFees = request()->input('serviceRate.parcel_fees', request()->input('service_rate.parcel_fees'));
|
|
38
38
|
|
|
39
|
-
if ($serviceRate->isFixedMeter() || $serviceRate->isPerDrop()) {
|
|
39
|
+
if ($serviceRate->isFixedMeter() || $serviceRate->isPerDrop() || $serviceRate->isMultiZoneDistance()) {
|
|
40
40
|
$serviceRate->setServiceRateFees($serviceRateFees);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Observers;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\ServiceArea;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Zone;
|
|
7
|
+
|
|
8
|
+
class ZoneObserver
|
|
9
|
+
{
|
|
10
|
+
/**
|
|
11
|
+
* Handle the Zone "created" event.
|
|
12
|
+
*/
|
|
13
|
+
public function created(Zone $zone): void
|
|
14
|
+
{
|
|
15
|
+
$this->invalidateServiceAreaCache($zone);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handle the Zone "updated" event.
|
|
20
|
+
*/
|
|
21
|
+
public function updated(Zone $zone): void
|
|
22
|
+
{
|
|
23
|
+
$this->invalidateServiceAreaCache($zone, $zone->getOriginal('service_area_uuid'));
|
|
24
|
+
$this->invalidateServiceAreaCache($zone);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Handle the Zone "deleted" event.
|
|
29
|
+
*/
|
|
30
|
+
public function deleted(Zone $zone): void
|
|
31
|
+
{
|
|
32
|
+
$this->invalidateServiceAreaCache($zone);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Handle the Zone "restored" event.
|
|
37
|
+
*/
|
|
38
|
+
public function restored(Zone $zone): void
|
|
39
|
+
{
|
|
40
|
+
$this->invalidateServiceAreaCache($zone);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Invalidate service area API cache because service area responses embed zones.
|
|
45
|
+
*/
|
|
46
|
+
protected function invalidateServiceAreaCache(Zone $zone, ?string $serviceAreaUuid = null): void
|
|
47
|
+
{
|
|
48
|
+
$serviceAreaUuid ??= $zone->service_area_uuid;
|
|
49
|
+
if (!$serviceAreaUuid) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ServiceArea::invalidateApiCacheManually($zone->company_uuid);
|
|
54
|
+
ServiceArea::invalidateApiCacheManually();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -85,7 +85,7 @@ class VroomOrchestrationEngine implements OrchestrationEngineInterface
|
|
|
85
85
|
$vroomPayload = [
|
|
86
86
|
'jobs' => [],
|
|
87
87
|
'shipments' => [],
|
|
88
|
-
'vehicles'
|
|
88
|
+
'vehicles' => array_map(function (array $v) use ($profile) {
|
|
89
89
|
$vehicle = [
|
|
90
90
|
'id' => crc32($v['id']), // VROOM requires integer IDs
|
|
91
91
|
'description' => json_encode(['vehicle_id' => $v['id'], 'driver_id' => $v['driver_id']]),
|
|
@@ -29,6 +29,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
29
29
|
\Fleetbase\FleetOps\Models\ServiceRate::class => \Fleetbase\FleetOps\Observers\ServiceRateObserver::class,
|
|
30
30
|
\Fleetbase\FleetOps\Models\PurchaseRate::class => \Fleetbase\FleetOps\Observers\PurchaseRateObserver::class,
|
|
31
31
|
\Fleetbase\FleetOps\Models\ServiceArea::class => \Fleetbase\FleetOps\Observers\ServiceAreaObserver::class,
|
|
32
|
+
\Fleetbase\FleetOps\Models\Zone::class => \Fleetbase\FleetOps\Observers\ZoneObserver::class,
|
|
32
33
|
\Fleetbase\FleetOps\Models\TrackingNumber::class => \Fleetbase\FleetOps\Observers\TrackingNumberObserver::class,
|
|
33
34
|
\Fleetbase\FleetOps\Models\Driver::class => \Fleetbase\FleetOps\Observers\DriverObserver::class,
|
|
34
35
|
\Fleetbase\FleetOps\Models\Vehicle::class => \Fleetbase\FleetOps\Observers\VehicleObserver::class,
|
|
@@ -56,6 +57,7 @@ class FleetOpsServiceProvider extends CoreServiceProvider
|
|
|
56
57
|
\Fleetbase\FleetOps\Console\Commands\FixInvalidPolymorphicRelationTypeNamespaces::class,
|
|
57
58
|
\Fleetbase\FleetOps\Console\Commands\AssignDriverRoles::class,
|
|
58
59
|
\Fleetbase\FleetOps\Console\Commands\AssignCustomerRoles::class,
|
|
60
|
+
\Fleetbase\FleetOps\Console\Commands\AuditCustomerUserConflicts::class,
|
|
59
61
|
\Fleetbase\FleetOps\Console\Commands\SimulateOrderRouteNavigation::class,
|
|
60
62
|
\Fleetbase\FleetOps\Console\Commands\DebugOrderTracker::class,
|
|
61
63
|
\Fleetbase\FleetOps\Console\Commands\PurgeUnpurchasedServiceQuotes::class,
|
|
@@ -40,12 +40,12 @@ abstract class AbstractAnalytics
|
|
|
40
40
|
{
|
|
41
41
|
if ($period !== null && $period !== '') {
|
|
42
42
|
$days = match ($period) {
|
|
43
|
-
'7d'
|
|
44
|
-
'14d'
|
|
45
|
-
'30d'
|
|
46
|
-
'90d'
|
|
47
|
-
'180d'
|
|
48
|
-
'365d'
|
|
43
|
+
'7d' => 7,
|
|
44
|
+
'14d' => 14,
|
|
45
|
+
'30d' => 30,
|
|
46
|
+
'90d' => 90,
|
|
47
|
+
'180d' => 180,
|
|
48
|
+
'365d' => 365,
|
|
49
49
|
default => null,
|
|
50
50
|
};
|
|
51
51
|
|
|
@@ -22,7 +22,7 @@ class FuelEfficiency extends AbstractAnalytics
|
|
|
22
22
|
$fuelByWeek = FuelReport::where('company_uuid', $this->company->uuid)
|
|
23
23
|
->where('currency', $currency)
|
|
24
24
|
->whereBetween('created_at', [$start, $end])
|
|
25
|
-
->selectRaw(
|
|
25
|
+
->selectRaw('YEARWEEK(created_at, 1) as wk, MIN(DATE(created_at)) as wk_start, SUM(amount) as total_cost')
|
|
26
26
|
->groupBy('wk')
|
|
27
27
|
->orderBy('wk')
|
|
28
28
|
->get();
|
|
@@ -30,7 +30,7 @@ class FuelEfficiency extends AbstractAnalytics
|
|
|
30
30
|
$distanceByWeek = Order::where('company_uuid', $this->company->uuid)
|
|
31
31
|
->where('status', 'completed')
|
|
32
32
|
->whereBetween('updated_at', [$start, $end])
|
|
33
|
-
->selectRaw(
|
|
33
|
+
->selectRaw('YEARWEEK(updated_at, 1) as wk, SUM(distance) as total_distance')
|
|
34
34
|
->groupBy('wk')
|
|
35
35
|
->orderBy('wk')
|
|
36
36
|
->pluck('total_distance', 'wk');
|
|
@@ -41,7 +41,7 @@ class OrdersByStatus extends AbstractAnalytics
|
|
|
41
41
|
while ($cursor <= $endDay) {
|
|
42
42
|
$labels[] = $cursor->format('M j');
|
|
43
43
|
foreach (array_keys(self::STATUS_PALETTE) as $status) {
|
|
44
|
-
$match
|
|
44
|
+
$match = $rows->first(fn ($r) => $r->bucket === $cursor->format('Y-m-d') && $r->status === $status);
|
|
45
45
|
$byStatus[$status][] = $match ? (int) $match->total : 0;
|
|
46
46
|
}
|
|
47
47
|
$cursor->addDay();
|
|
@@ -12,7 +12,7 @@ use Illuminate\Support\Carbon;
|
|
|
12
12
|
*/
|
|
13
13
|
class TopDrivers extends AbstractAnalytics
|
|
14
14
|
{
|
|
15
|
-
protected int $limit
|
|
15
|
+
protected int $limit = 10;
|
|
16
16
|
protected string $sortBy = 'orders_completed';
|
|
17
17
|
|
|
18
18
|
public function limit(int $limit): self
|
|
@@ -38,7 +38,15 @@ class TopDrivers extends AbstractAnalytics
|
|
|
38
38
|
|
|
39
39
|
$sortColumn = match ($this->sortBy) {
|
|
40
40
|
'distance' => 'distance_m',
|
|
41
|
-
'on_time' => '
|
|
41
|
+
'on_time' => 'CASE
|
|
42
|
+
WHEN SUM(CASE WHEN orders.scheduled_at IS NOT NULL THEN 1 ELSE 0 END) > 0
|
|
43
|
+
THEN SUM(CASE
|
|
44
|
+
WHEN orders.scheduled_at IS NOT NULL
|
|
45
|
+
AND TIMESTAMPDIFF(SECOND, orders.scheduled_at, orders.updated_at) <= 1800
|
|
46
|
+
THEN 1 ELSE 0 END)
|
|
47
|
+
/ SUM(CASE WHEN orders.scheduled_at IS NOT NULL THEN 1 ELSE 0 END)
|
|
48
|
+
ELSE -1
|
|
49
|
+
END',
|
|
42
50
|
default => 'orders_completed',
|
|
43
51
|
};
|
|
44
52
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Contact;
|
|
6
|
+
use Illuminate\Http\Request;
|
|
7
|
+
use Illuminate\Support\Str;
|
|
8
|
+
use Laravel\Sanctum\PersonalAccessToken;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper for resolving the authenticated customer from a request.
|
|
12
|
+
*
|
|
13
|
+
* The customer token is issued by `CustomerController` as a Sanctum
|
|
14
|
+
* `PersonalAccessToken` whose `name` column carries the owning Contact's UUID.
|
|
15
|
+
* Mirrors {@see \Fleetbase\Storefront\Support\Storefront::getCustomerFromToken}
|
|
16
|
+
* but with no Storefront-specific session.
|
|
17
|
+
*/
|
|
18
|
+
class CustomerAuth
|
|
19
|
+
{
|
|
20
|
+
public const HEADER = 'Customer-Token';
|
|
21
|
+
|
|
22
|
+
public const APP_BINDING = 'currentCustomer';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the Contact represented by the `Customer-Token` header.
|
|
26
|
+
*
|
|
27
|
+
* Preference order when a User has more than one customer-type Contact:
|
|
28
|
+
* 1. Contact whose UUID matches the token's `name` column (Storefront convention).
|
|
29
|
+
* 2. Contact in the same company as the resolved API credential (`session('company')`).
|
|
30
|
+
* 3. The first remaining customer-type Contact for the token's User.
|
|
31
|
+
*/
|
|
32
|
+
public static function resolveFromHeader(?Request $request = null): ?Contact
|
|
33
|
+
{
|
|
34
|
+
$request = $request ?? request();
|
|
35
|
+
$token = $request->header(self::HEADER);
|
|
36
|
+
if (!$token) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
$accessToken = PersonalAccessToken::findToken($token);
|
|
41
|
+
if (!$accessToken) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Storefront convention: token name = Contact UUID.
|
|
46
|
+
if (is_string($accessToken->name) && Str::isUuid($accessToken->name)) {
|
|
47
|
+
$contact = Contact::where('uuid', $accessToken->name)
|
|
48
|
+
->where('type', 'customer')
|
|
49
|
+
->first();
|
|
50
|
+
if ($contact) {
|
|
51
|
+
return $contact;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fallback: pick the customer Contact bound to the token's user, preferring
|
|
56
|
+
// the same company as the API credential when available.
|
|
57
|
+
$userId = $accessToken->tokenable_id ?? null;
|
|
58
|
+
$userIdentifier = $accessToken->tokenable->uuid ?? $userId;
|
|
59
|
+
if (!$userIdentifier) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
$contactQuery = Contact::where('user_uuid', $userIdentifier)
|
|
64
|
+
->where('type', 'customer');
|
|
65
|
+
|
|
66
|
+
$sessionCompany = session('company');
|
|
67
|
+
if ($sessionCompany) {
|
|
68
|
+
$companyPreferred = (clone $contactQuery)
|
|
69
|
+
->where('company_uuid', $sessionCompany)
|
|
70
|
+
->first();
|
|
71
|
+
if ($companyPreferred) {
|
|
72
|
+
return $companyPreferred;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return $contactQuery->first();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Return the currently-bound customer (set by AuthenticateCustomerToken middleware).
|
|
81
|
+
*/
|
|
82
|
+
public static function current(): ?Contact
|
|
83
|
+
{
|
|
84
|
+
return app()->bound(self::APP_BINDING) ? app(self::APP_BINDING) : null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Bind the resolved customer for the current request lifecycle.
|
|
89
|
+
*/
|
|
90
|
+
public static function setCurrent(Contact $contact): void
|
|
91
|
+
{
|
|
92
|
+
app()->instance(self::APP_BINDING, $contact);
|
|
93
|
+
session([
|
|
94
|
+
'customer_id' => $contact->uuid,
|
|
95
|
+
'contact_id' => $contact->uuid,
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\FleetOps\Models\Driver;
|
|
6
|
+
use Fleetbase\FleetOps\Models\Order;
|
|
7
|
+
use Fleetbase\FleetOps\Models\TrackingStatus;
|
|
8
|
+
use Fleetbase\Models\Company;
|
|
9
|
+
|
|
10
|
+
class GettingStarted
|
|
11
|
+
{
|
|
12
|
+
protected Company $company;
|
|
13
|
+
|
|
14
|
+
public static function forCompany(Company $company): static
|
|
15
|
+
{
|
|
16
|
+
return new static($company);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public function __construct(Company $company)
|
|
20
|
+
{
|
|
21
|
+
$this->company = $company;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public function get(): array
|
|
25
|
+
{
|
|
26
|
+
$steps = $this->steps();
|
|
27
|
+
$completed = collect($steps)->filter(fn ($step) => $step['completed'])->count();
|
|
28
|
+
$total = count($steps);
|
|
29
|
+
$nextStep = collect($steps)->first(fn ($step) => !$step['completed']);
|
|
30
|
+
$isCompleted = $completed === $total;
|
|
31
|
+
|
|
32
|
+
return [
|
|
33
|
+
'profile_source' => 'generic',
|
|
34
|
+
'profile' => null,
|
|
35
|
+
'is_completed' => $isCompleted,
|
|
36
|
+
'progress' => [
|
|
37
|
+
'completed' => $completed,
|
|
38
|
+
'total' => $total,
|
|
39
|
+
'percent' => $total > 0 ? (int) round(($completed / $total) * 100) : 0,
|
|
40
|
+
],
|
|
41
|
+
'next_step' => $nextStep['key'] ?? null,
|
|
42
|
+
'steps' => $steps,
|
|
43
|
+
'recommendations' => $this->recommendations(),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected function steps(): array
|
|
48
|
+
{
|
|
49
|
+
$companyUuid = $this->company->uuid;
|
|
50
|
+
|
|
51
|
+
$hasDriver = Driver::where('company_uuid', $companyUuid)->exists();
|
|
52
|
+
$hasOrder = Order::where('company_uuid', $companyUuid)->exists();
|
|
53
|
+
|
|
54
|
+
$hasAssignedOrder = Order::where('company_uuid', $companyUuid)
|
|
55
|
+
->whereNotNull('driver_assigned_uuid')
|
|
56
|
+
->exists();
|
|
57
|
+
|
|
58
|
+
$hasOrderActivity = TrackingStatus::where('tracking_statuses.company_uuid', $companyUuid)
|
|
59
|
+
->whereNotNull('tracking_statuses.tracking_number_uuid')
|
|
60
|
+
->whereNotIn('tracking_statuses.code', ['CREATED', 'ORDER_CREATED'])
|
|
61
|
+
->exists();
|
|
62
|
+
|
|
63
|
+
return [
|
|
64
|
+
[
|
|
65
|
+
'key' => 'add_driver',
|
|
66
|
+
'title' => 'Add a driver',
|
|
67
|
+
'description' => 'Create your first driver profile so work can be assigned.',
|
|
68
|
+
'estimate' => '2 min',
|
|
69
|
+
'completed' => $hasDriver,
|
|
70
|
+
'icon' => 'id-card',
|
|
71
|
+
'route' => 'console.fleet-ops.management.drivers.index.new',
|
|
72
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/drivers',
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
'key' => 'create_order',
|
|
76
|
+
'title' => 'Create an order',
|
|
77
|
+
'description' => 'Add a delivery or service order to start dispatching.',
|
|
78
|
+
'estimate' => '3 min',
|
|
79
|
+
'completed' => $hasOrder,
|
|
80
|
+
'icon' => 'box',
|
|
81
|
+
'route' => 'console.fleet-ops.operations.orders.index.new',
|
|
82
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/orders',
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
'key' => 'assign_driver',
|
|
86
|
+
'title' => 'Assign driver to order',
|
|
87
|
+
'description' => 'Connect the order to a driver for execution.',
|
|
88
|
+
'estimate' => '1 min',
|
|
89
|
+
'completed' => $hasAssignedOrder,
|
|
90
|
+
'icon' => 'user-check',
|
|
91
|
+
'route' => 'console.fleet-ops.operations.orders.index',
|
|
92
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/dispatch',
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
'key' => 'update_activity',
|
|
96
|
+
'title' => 'Update order activity',
|
|
97
|
+
'description' => 'Move the order through its workflow and capture progress.',
|
|
98
|
+
'estimate' => '2 min',
|
|
99
|
+
'completed' => $hasOrderActivity,
|
|
100
|
+
'icon' => 'route',
|
|
101
|
+
'route' => 'console.fleet-ops.operations.orders.index',
|
|
102
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/tracking',
|
|
103
|
+
],
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected function recommendations(): array
|
|
108
|
+
{
|
|
109
|
+
return [
|
|
110
|
+
[
|
|
111
|
+
'key' => 'route_optimization',
|
|
112
|
+
'title' => 'Route Optimization',
|
|
113
|
+
'description' => 'Plan efficient multi-stop routes and reduce time on the road.',
|
|
114
|
+
'icon' => 'route',
|
|
115
|
+
'accent' => 'blue',
|
|
116
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/orchestrator',
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
'key' => 'live_fleet',
|
|
120
|
+
'title' => 'Live Fleet Map',
|
|
121
|
+
'description' => 'Watch active drivers, vehicles, and orders in real time.',
|
|
122
|
+
'icon' => 'map-location-dot',
|
|
123
|
+
'accent' => 'green',
|
|
124
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/live-map',
|
|
125
|
+
],
|
|
126
|
+
[
|
|
127
|
+
'key' => 'service_rates',
|
|
128
|
+
'title' => 'Service Rates',
|
|
129
|
+
'description' => 'Create pricing rules for deliveries, zones, and service areas.',
|
|
130
|
+
'icon' => 'receipt',
|
|
131
|
+
'accent' => 'amber',
|
|
132
|
+
'docs_url' => 'https://www.fleetbase.io/docs/fleet-ops/service-rates',
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
'key' => 'customer_portal',
|
|
136
|
+
'title' => 'Customer Portal',
|
|
137
|
+
'description' => 'Let customers submit orders and track deliveries from a branded portal.',
|
|
138
|
+
'icon' => 'store',
|
|
139
|
+
'accent' => 'purple',
|
|
140
|
+
'docs_url' => 'https://www.fleetbase.io/docs/customer-portal',
|
|
141
|
+
],
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support;
|
|
4
|
+
|
|
5
|
+
use Barryvdh\DomPDF\Facade\Pdf;
|
|
6
|
+
|
|
7
|
+
class LabelPdf
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Create a PDF instance for FleetOps labels with UTF-8 and embedded-font
|
|
11
|
+
* options applied consistently across order, waypoint, and entity labels.
|
|
12
|
+
*/
|
|
13
|
+
public static function fromHtml(string $html)
|
|
14
|
+
{
|
|
15
|
+
$pdf = Pdf::loadHTML($html, 'UTF-8');
|
|
16
|
+
|
|
17
|
+
if (method_exists($pdf, 'setOptions')) {
|
|
18
|
+
$pdf->setOptions(static::options());
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (method_exists($pdf, 'getDomPDF')) {
|
|
22
|
+
$dompdf = $pdf->getDomPDF();
|
|
23
|
+
foreach (static::dompdfOptions() as $key => $value) {
|
|
24
|
+
if (method_exists($dompdf, 'set_option')) {
|
|
25
|
+
$dompdf->set_option($key, $value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return $pdf;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public static function options(): array
|
|
34
|
+
{
|
|
35
|
+
return [
|
|
36
|
+
'defaultFont' => 'DejaVu Sans',
|
|
37
|
+
'isHtml5ParserEnabled' => true,
|
|
38
|
+
'isFontSubsettingEnabled' => true,
|
|
39
|
+
'isRemoteEnabled' => false,
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static function dompdfOptions(): array
|
|
44
|
+
{
|
|
45
|
+
return [
|
|
46
|
+
'defaultFont' => 'DejaVu Sans',
|
|
47
|
+
'isHtml5ParserEnabled' => true,
|
|
48
|
+
'isFontSubsettingEnabled' => true,
|
|
49
|
+
'isRemoteEnabled' => false,
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
}
|