@fleetbase/fleetops-engine 0.6.68 → 0.6.70
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/RELEASE.md +44 -9
- package/addon/components/admin/telematics-settings.hbs +143 -0
- package/addon/components/admin/telematics-settings.js +145 -0
- package/addon/components/contact/form.hbs +13 -2
- package/addon/components/customer/details.hbs +17 -17
- package/addon/components/customer/form.hbs +13 -2
- package/addon/components/customer/form.js +0 -56
- package/addon/components/driver/details.hbs +13 -19
- package/addon/components/driver/form.hbs +20 -35
- package/addon/components/driver/form.js +0 -58
- package/addon/components/fuel-integration/hub.hbs +1 -1
- package/addon/components/layout/fleet-ops-sidebar.js +37 -17
- package/addon/components/map/leaflet-live-map.hbs +4 -242
- package/addon/components/map/leaflet-live-map.js +2 -1
- package/addon/components/map/marker-card/driver.hbs +57 -0
- package/addon/components/map/marker-card/driver.js +3 -0
- package/addon/components/map/marker-card/vehicle.hbs +78 -0
- package/addon/components/map/marker-card/vehicle.js +3 -0
- package/addon/components/modals/driver-form.hbs +2 -2
- package/addon/components/modals/reset-profile-credentials.hbs +23 -0
- package/addon/components/modals/reset-profile-credentials.js +70 -0
- package/addon/components/profile-identity-hint.hbs +13 -0
- package/addon/components/profile-identity-hint.js +49 -0
- package/addon/components/telematic/hub.hbs +1 -1
- package/addon/components/widget/live-fleet.hbs +8 -40
- package/addon/controllers/analytics/reports/index.js +5 -0
- package/addon/controllers/connectivity/devices/index.js +4 -0
- package/addon/controllers/connectivity/fuel-providers/index.js +6 -4
- package/addon/controllers/connectivity/sensors/index.js +4 -0
- package/addon/controllers/connectivity/telematics/index.js +4 -0
- package/addon/controllers/maintenance/equipment/index.js +11 -4
- package/addon/controllers/maintenance/inspection-forms/index.js +2 -2
- package/addon/controllers/maintenance/inspection-submissions/index.js +8 -2
- package/addon/controllers/maintenance/maintenances/index.js +4 -0
- package/addon/controllers/maintenance/parts/index.js +11 -4
- package/addon/controllers/maintenance/schedules/index.js +17 -4
- package/addon/controllers/maintenance/work-orders/index.js +11 -4
- package/addon/controllers/management/contacts/index.js +4 -0
- package/addon/controllers/management/drivers/index.js +8 -0
- package/addon/controllers/management/fleets/index.js +4 -0
- package/addon/controllers/management/fuel-reports/index.js +4 -0
- package/addon/controllers/management/fuel-transactions/index.js +9 -7
- package/addon/controllers/management/issues/index.js +4 -0
- package/addon/controllers/management/places/index.js +4 -0
- package/addon/controllers/management/vehicles/index.js +4 -0
- package/addon/controllers/management/vendors/index.js +4 -0
- package/addon/controllers/operations/orders/index/details.js +30 -1
- package/addon/controllers/operations/orders/index.js +7 -0
- package/addon/controllers/operations/service-rates/index.js +3 -0
- package/addon/controllers/settings/index.js +13 -0
- package/addon/controllers/settings/telematics.js +113 -0
- package/addon/extension.js +42 -8
- package/addon/routes/analytics/index.js +14 -1
- package/addon/routes/analytics/reports/index/details.js +1 -1
- package/addon/routes/analytics/reports/index/edit.js +1 -1
- package/addon/routes/analytics/reports/index/new.js +14 -1
- package/addon/routes/analytics/reports.js +14 -1
- package/addon/routes/connectivity/devices/index/new.js +14 -1
- package/addon/routes/connectivity/devices.js +14 -1
- package/addon/routes/connectivity/events.js +14 -1
- package/addon/routes/connectivity/fuel-providers/details/transactions.js +12 -0
- package/addon/routes/connectivity/fuel-providers/details.js +9 -0
- package/addon/routes/connectivity/fuel-providers/edit.js +9 -0
- package/addon/routes/connectivity/fuel-providers/new.js +14 -1
- package/addon/routes/connectivity/fuel-providers.js +14 -1
- package/addon/routes/connectivity/sensors/index/new.js +14 -1
- package/addon/routes/connectivity/sensors.js +14 -1
- package/addon/routes/connectivity/telematics/new.js +14 -1
- package/addon/routes/connectivity/telematics.js +14 -1
- package/addon/routes/connectivity/tracking.js +14 -1
- package/addon/routes/maintenance/equipment/index/details.js +2 -2
- package/addon/routes/maintenance/equipment/index/edit.js +2 -2
- package/addon/routes/maintenance/equipment/index/new.js +14 -1
- package/addon/routes/maintenance/equipment.js +14 -1
- package/addon/routes/maintenance/index.js +14 -1
- package/addon/routes/maintenance/inspection-forms/index/details.js +10 -1
- package/addon/routes/maintenance/inspection-forms/index/edit.js +10 -1
- package/addon/routes/maintenance/inspection-forms/index/new.js +14 -1
- package/addon/routes/maintenance/inspection-forms.js +14 -1
- package/addon/routes/maintenance/inspection-submissions/index/details.js +10 -1
- package/addon/routes/maintenance/inspection-submissions/index/edit.js +10 -1
- package/addon/routes/maintenance/inspection-submissions/index/new.js +14 -1
- package/addon/routes/maintenance/inspection-submissions.js +14 -1
- package/addon/routes/maintenance/maintenances/index/details.js +2 -2
- package/addon/routes/maintenance/maintenances/index/edit.js +2 -2
- package/addon/routes/maintenance/maintenances/index/new.js +14 -1
- package/addon/routes/maintenance/maintenances.js +14 -1
- package/addon/routes/maintenance/parts/index/details.js +2 -2
- package/addon/routes/maintenance/parts/index/edit.js +2 -2
- package/addon/routes/maintenance/parts/index/new.js +14 -1
- package/addon/routes/maintenance/parts.js +14 -1
- package/addon/routes/maintenance/schedules/index/details.js +2 -2
- package/addon/routes/maintenance/schedules/index/edit.js +2 -2
- package/addon/routes/maintenance/schedules/index/new.js +15 -1
- package/addon/routes/maintenance/schedules.js +15 -1
- package/addon/routes/maintenance/work-orders/index/details.js +2 -2
- package/addon/routes/maintenance/work-orders/index/edit.js +2 -2
- package/addon/routes/maintenance/work-orders/index/new.js +14 -1
- package/addon/routes/maintenance/work-orders.js +14 -1
- package/addon/routes/management/fuel-transactions/index/details.js +9 -0
- package/addon/routes/management/fuel-transactions.js +14 -1
- package/addon/routes/management/index.js +13 -0
- package/addon/routes/management/trailers.js +16 -0
- package/addon/routes/management/vendors/integrated/details.js +14 -1
- package/addon/routes/management/vendors/integrated/edit.js +14 -1
- package/addon/routes/management/vendors/integrated/new.js +14 -1
- package/addon/routes/management/vendors/integrated.js +14 -1
- package/addon/routes/operations/orders.js +40 -1
- package/addon/routes/operations/routes/index/details.js +14 -1
- package/addon/routes/operations/routes/index/new.js +13 -0
- package/addon/routes/operations/routes.js +14 -1
- package/addon/routes/operations/service-rates/index/new.js +11 -0
- package/addon/routes/operations/service-rates.js +14 -1
- package/addon/routes/settings/avatars.js +14 -1
- package/addon/routes/settings/custom-fields.js +14 -1
- package/addon/routes/settings/map.js +14 -1
- package/addon/routes/settings/notifications.js +11 -0
- package/addon/routes/settings/orchestrator.js +1 -1
- package/addon/routes/settings/payments/index.js +1 -1
- package/addon/routes/settings/routing.js +14 -1
- package/addon/routes/settings/scheduling.js +14 -1
- package/addon/routes/settings/telematics.js +21 -0
- package/addon/routes/virtual.js +11 -0
- package/addon/routes.js +1 -0
- package/addon/services/contact-actions.js +50 -89
- package/addon/services/driver-actions.js +98 -0
- package/addon/templates/settings/telematics.hbs +70 -0
- package/addon/utils/profile-login-actions.js +172 -0
- package/app/components/admin/telematics-settings.js +1 -0
- package/app/components/{modals/reset-customer-credentials.js → map/marker-card/driver.js} +1 -1
- package/app/components/map/marker-card/vehicle.js +1 -0
- package/app/components/modals/reset-profile-credentials.js +1 -0
- package/app/components/profile-identity-hint.js +1 -0
- package/app/controllers/settings/telematics.js +1 -0
- package/app/routes/settings/telematics.js +1 -0
- package/app/templates/settings/telematics.js +1 -0
- package/composer.json +2 -2
- package/docs/AFAQY.md +1 -1
- package/docs/TELEMATICS_QUEUES.md +1 -1
- package/docs/TELEMETRY_ARCHITECTURE.md +72 -1
- package/extension.json +1 -1
- package/package.json +3 -3
- package/scripts/pest-bootstrap.php +12 -0
- package/server/config/telemetry.php +12 -0
- package/server/migrations/2026_09_21_000001_convert_profile_only_users_to_managed_accounts.php +132 -0
- package/server/migrations/2026_09_23_000001_add_retention_index_to_device_events.php +40 -0
- package/server/migrations/2026_09_23_000002_add_retention_index_to_positions.php +40 -0
- package/server/migrations/2026_09_23_000003_add_retention_index_to_telematic_sync_runs.php +40 -0
- package/server/resources/views/mail/driver-credentials.blade.php +21 -0
- package/server/src/Auth/Schemas/FleetOps.php +104 -5
- package/server/src/Console/Commands/DrainTelematicInbox.php +3 -13
- package/server/src/Console/Commands/FixCustomerCompanies.php +1 -1
- package/server/src/Console/Commands/FixDriverCompanies.php +1 -1
- package/server/src/Console/Commands/ProcessOperationalAlerts.php +3 -3
- package/server/src/Console/Commands/PruneTelematicsData.php +318 -0
- package/server/src/Exceptions/ProfileIdentityConflictException.php +34 -0
- package/server/src/Http/Controllers/Api/v1/CustomerController.php +14 -0
- package/server/src/Http/Controllers/Api/v1/DriverController.php +56 -39
- package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +9 -0
- package/server/src/Http/Controllers/FleetOpsController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/AnalyticsController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/ContactController.php +16 -17
- package/server/src/Http/Controllers/Internal/v1/CustomerController.php +54 -4
- package/server/src/Http/Controllers/Internal/v1/DeviceController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/DeviceEventController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/DriverController.php +204 -162
- package/server/src/Http/Controllers/Internal/v1/FleetOpsLookupController.php +47 -0
- package/server/src/Http/Controllers/Internal/v1/FuelProviderConnectionController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/FuelProviderTransactionController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/FuelReportController.php +9 -3
- package/server/src/Http/Controllers/Internal/v1/InspectionFormController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +16 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/MaintenanceScheduleController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/ManifestController.php +15 -0
- package/server/src/Http/Controllers/Internal/v1/MetricsController.php +11 -0
- package/server/src/Http/Controllers/Internal/v1/NavigatorController.php +42 -1
- package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +12 -0
- package/server/src/Http/Controllers/Internal/v1/OrderController.php +178 -27
- package/server/src/Http/Controllers/Internal/v1/PaymentController.php +13 -0
- package/server/src/Http/Controllers/Internal/v1/RadarController.php +19 -0
- package/server/src/Http/Controllers/Internal/v1/ServiceAreaController.php +9 -3
- package/server/src/Http/Controllers/Internal/v1/SettingController.php +465 -14
- package/server/src/Http/Controllers/Internal/v1/TelematicController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/TrailerController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/VehicleController.php +21 -0
- package/server/src/Http/Controllers/Internal/v1/VendorController.php +17 -0
- package/server/src/Http/Controllers/Internal/v1/WorkOrderController.php +11 -0
- package/server/src/Http/Requests/CreateDriverRequest.php +6 -2
- package/server/src/Http/Requests/Internal/CreateDriverRequest.php +9 -18
- package/server/src/Http/Resources/v1/Contact.php +3 -0
- package/server/src/Http/Resources/v1/Driver.php +3 -0
- package/server/src/Jobs/DispatchTelematicsRetentionJobs.php +70 -0
- package/server/src/Jobs/PruneTelematicsDataJob.php +74 -0
- package/server/src/Listeners/NotifyDriverOnShiftChange.php +3 -3
- package/server/src/Mail/CustomerCredentialsMail.php +1 -1
- package/server/src/Mail/DriverCredentialsMail.php +55 -0
- package/server/src/Models/Contact.php +55 -90
- package/server/src/Models/Sensor.php +3 -5
- package/server/src/Observers/ContactObserver.php +2 -22
- package/server/src/Observers/DriverObserver.php +5 -6
- package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +9 -1
- package/server/src/Providers/FleetOpsServiceProvider.php +18 -0
- package/server/src/Support/Ai/Capabilities/AbstractFleetOpsAICapability.php +62 -5
- package/server/src/Support/Ai/Capabilities/AssetStatusCapability.php +24 -5
- package/server/src/Support/Ai/Capabilities/CreateOrderPreviewCapability.php +6 -2
- package/server/src/Support/Ai/Capabilities/OrderInsightsCapability.php +40 -7
- package/server/src/Support/Ai/Capabilities/SearchResourcesCapability.php +86 -24
- package/server/src/Support/Ai/FleetOpsAiConsoleCommands.php +122 -0
- package/server/src/Support/Ai/Tools/CreateOrderTool.php +86 -0
- package/server/src/Support/Ai/Tools/ImportOrdersTool.php +53 -0
- package/server/src/Support/Ai/Tools/OptimizeOrderRouteTool.php +70 -0
- package/server/src/Support/Ai/Tools/SearchResourcesTool.php +73 -0
- package/server/src/Support/Analytics/LiveFleet.php +18 -2
- package/server/src/Support/Authorization.php +103 -0
- package/server/src/Support/Database/TableIndexes.php +66 -0
- package/server/src/Support/OrderTracker.php +2 -2
- package/server/src/Support/ProfileAccountManager.php +405 -0
- package/server/src/Support/Reporting/FleetOpsReportSchema.php +476 -116
- package/server/src/Support/Telematics/Retention/RetentionPolicy.php +300 -0
- package/server/src/Support/Telematics/Retention/TelemetryActivity.php +40 -0
- package/server/src/Support/Telematics/TelematicService.php +12 -2
- package/server/src/Support/Telematics/Telemetry/Ingestor.php +4 -2
- package/server/src/Tracking/TrackingIntelligenceService.php +1 -1
- package/server/src/Tracking/TrackingOptions.php +2 -2
- package/server/src/Traits/AuthorizesMethods.php +37 -0
- package/server/src/routes.php +14 -1
- package/server/tests/AiOperationalQueryCapabilityTest.php +1 -0
- package/server/tests/AnalyticsRoutesTest.php +29 -2
- package/server/tests/ApiCustomerControllerContractsTest.php +21 -0
- package/server/tests/ApiDriverControllerContractsTest.php +102 -20
- package/server/tests/CommandContractsTest.php +4 -4
- package/server/tests/ControllerHelperContractsTest.php +3 -11
- package/server/tests/Feature/Http/AfaqyRealtimeIngestionTest.php +21 -1
- package/server/tests/Feature/Http/Api/DriverControllerAuthFlowsTest.php +36 -0
- package/server/tests/Feature/Http/Api/DriverControllerSessionHelpersTest.php +19 -6
- package/server/tests/Feature/Http/Api/ProfileOnlyUserConversionMigrationTest.php +201 -0
- package/server/tests/Feature/Http/Api/TelematicsRetentionIndexMigrationTest.php +115 -0
- package/server/tests/Feature/Http/Internal/ContactControllerEndpointsTest.php +6 -4
- package/server/tests/Feature/Http/Internal/CustomerControllerCredentialsTest.php +6 -2
- package/server/tests/Feature/Http/Internal/DriverControllerContractsTest.php +23 -0
- package/server/tests/Feature/Http/Internal/DriverControllerCreateRecordTest.php +42 -17
- package/server/tests/Feature/Http/Internal/DriverControllerExistingUserAdoptionTest.php +168 -277
- package/server/tests/Feature/Http/Internal/DriverControllerLoginManagementTest.php +716 -0
- package/server/tests/Feature/Http/Internal/OrderControllerContractsTest.php +10 -3
- package/server/tests/Feature/Http/Internal/OrderControllerImportFromFilesTest.php +18 -0
- package/server/tests/Feature/Http/Internal/OrderControllerTenantScopingTest.php +411 -0
- package/server/tests/Feature/Http/Internal/OrderControllerUpstreamNotFoundTest.php +8 -20
- package/server/tests/Feature/Http/Internal/SettingControllerHelpersTest.php +71 -0
- package/server/tests/Feature/Http/Internal/TelematicsStorageDeadlinesTest.php +119 -0
- package/server/tests/InternalCustomerControllerContractsTest.php +32 -1
- package/server/tests/InternalImportExportControllerContractsTest.php +5 -8
- package/server/tests/ModelAccessorContractsTest.php +82 -20
- package/server/tests/NavigatorControllerContractsTest.php +36 -4
- package/server/tests/NotificationAndMailContractsTest.php +31 -0
- package/server/tests/ObserverContractsTest.php +28 -3
- package/server/tests/OrchestratorConsumableApiTest.php +42 -0
- package/server/tests/ProviderContractsTest.php +10 -0
- package/server/tests/ReportSchemaContractsTest.php +120 -6
- package/server/tests/RequestContractsTest.php +19 -11
- package/server/tests/SettingControllerContractsTest.php +429 -18
- package/server/tests/SmallControllerContractsTest.php +8 -5
- package/server/tests/Support/PruneTelematicsDataProbe.php +55 -0
- package/server/tests/SupportJobAndAiCoverageTest.php +3 -3
- package/server/tests/TelematicControllerContractsTest.php +22 -4
- package/server/tests/Unit/Console/Commands/ProcessOperationalAlertsCommandTest.php +2 -2
- package/server/tests/Unit/Console/PruneTelematicsDataCommandTest.php +353 -0
- package/server/tests/Unit/Jobs/DispatchTelematicsRetentionJobsTest.php +114 -0
- package/server/tests/Unit/Jobs/PruneTelematicsDataJobTest.php +60 -0
- package/server/tests/Unit/Models/ContactCustomerUserTest.php +90 -14
- package/server/tests/Unit/Models/ContactTest.php +48 -5
- package/server/tests/Unit/Models/SensorTest.php +2 -2
- package/server/tests/Unit/Models/SmallModelSurfacesTest.php +31 -32
- package/server/tests/Unit/Observers/ObserverListenerSeamsTest.php +7 -2
- package/server/tests/Unit/Orchestration/VroomEngineTransportAndSettingsTest.php +20 -0
- package/server/tests/Unit/Providers/FleetOpsAiRegistrationTest.php +15 -2
- package/server/tests/Unit/Support/Ai/AssetStatusCapabilityQueriesTest.php +42 -0
- package/server/tests/Unit/Support/Ai/Capabilities/OrderInsightsCapabilityTest.php +19 -1
- package/server/tests/Unit/Support/Ai/Capabilities/SearchResourcesCapabilityTest.php +85 -1
- package/server/tests/Unit/Support/Ai/FleetOpsAiConsoleCommandsTest.php +31 -0
- package/server/tests/Unit/Support/Ai/Tools/FleetOpsAiToolsTest.php +246 -0
- package/server/tests/Unit/Support/AiCapabilityPermissionsTest.php +3 -3
- package/server/tests/Unit/Support/Analytics/LiveFleetTest.php +6 -0
- package/server/tests/Unit/Support/AuthorizationTest.php +142 -0
- package/server/tests/Unit/Support/MaintainableAndOperationalAlertsTest.php +12 -1
- package/server/tests/Unit/Support/MoreQuerySeamsTest.php +11 -2
- package/server/tests/Unit/Support/ProfileAccountManagerTest.php +643 -0
- package/server/tests/Unit/Support/Telematics/Retention/RetentionPolicyTest.php +190 -0
- package/server/tests/Unit/Support/Telematics/Retention/TelemetryActivityTest.php +68 -0
- package/server/tests/Unit/Tracking/TrackingOptionsCompanySettingsTest.php +109 -0
- package/translations/en-us.yaml +85 -2
- package/translations/uk-ua.yaml +3475 -0
- package/addon/components/modals/reset-customer-credentials.hbs +0 -18
- package/addon/components/modals/reset-customer-credentials.js +0 -55
|
@@ -34,17 +34,20 @@ class OrderInsightsCapability extends AbstractFleetOpsAICapability
|
|
|
34
34
|
return ['authorized' => false, 'message' => 'Current user cannot access Fleet-Ops orders.'];
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
$prompt
|
|
38
|
-
$window
|
|
39
|
-
$amount
|
|
40
|
-
$
|
|
37
|
+
$prompt = $this->prompt($task);
|
|
38
|
+
$window = $this->dateWindow($prompt);
|
|
39
|
+
$amount = $this->amountThreshold($prompt);
|
|
40
|
+
$currency = $this->currency();
|
|
41
|
+
$minor = $amount !== null ? $this->toMinorUnits($amount, $currency) : null;
|
|
42
|
+
$query = $this->orderQuery(session('company'));
|
|
41
43
|
|
|
42
44
|
if ($window) {
|
|
43
45
|
$query->whereBetween('created_at', [$window['start'], $window['end']]);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
if ($
|
|
47
|
-
|
|
48
|
+
if ($minor !== null) {
|
|
49
|
+
// Transaction amounts are stored in the currency's minor unit (e.g. cents).
|
|
50
|
+
$query->whereHas('transaction', fn ($transaction) => $transaction->where('amount', '>', $minor));
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
$total = (clone $query)->count();
|
|
@@ -59,6 +62,7 @@ class OrderInsightsCapability extends AbstractFleetOpsAICapability
|
|
|
59
62
|
'end' => $window['end']->toIso8601String(),
|
|
60
63
|
] : null,
|
|
61
64
|
'amount_threshold' => $amount,
|
|
65
|
+
'amount_currency' => $amount !== null ? $currency : null,
|
|
62
66
|
'count' => $total,
|
|
63
67
|
'counts_by_status' => (clone $query)
|
|
64
68
|
->selectRaw('status, count(*) as aggregate')
|
|
@@ -83,6 +87,35 @@ class OrderInsightsCapability extends AbstractFleetOpsAICapability
|
|
|
83
87
|
return null;
|
|
84
88
|
}
|
|
85
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Converts a major-unit amount (e.g. 500.00) to the minor units transactions are stored in,
|
|
92
|
+
* honoring the currency's ISO-4217 exponent (JPY has none, BHD has three).
|
|
93
|
+
*/
|
|
94
|
+
protected function toMinorUnits(float $amount, string $currency): int
|
|
95
|
+
{
|
|
96
|
+
$exponent = 2;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
if (class_exists(\Money\Currencies\ISOCurrencies::class)) {
|
|
100
|
+
$exponent = (new \Money\Currencies\ISOCurrencies())->subunitFor(new \Money\Currency(strtoupper($currency)));
|
|
101
|
+
}
|
|
102
|
+
} catch (\Throwable) {
|
|
103
|
+
$exponent = 2;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return (int) round($amount * (10 ** $exponent));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @codeCoverageIgnore
|
|
111
|
+
*/
|
|
112
|
+
protected function currency(): string
|
|
113
|
+
{
|
|
114
|
+
$company = function_exists('session') ? \Fleetbase\Models\Company::where('uuid', session('company'))->first(['uuid', 'currency']) : null;
|
|
115
|
+
|
|
116
|
+
return strtoupper((string) ($company?->currency ?: 'USD'));
|
|
117
|
+
}
|
|
118
|
+
|
|
86
119
|
protected function dateWindow(string $prompt): ?array
|
|
87
120
|
{
|
|
88
121
|
return $this->relativeDateResolver()->resolveWindow($prompt);
|
|
@@ -90,7 +123,7 @@ class OrderInsightsCapability extends AbstractFleetOpsAICapability
|
|
|
90
123
|
|
|
91
124
|
protected function orderQuery(?string $companyUuid): mixed
|
|
92
125
|
{
|
|
93
|
-
return Order::where('company_uuid', $companyUuid);
|
|
126
|
+
return $this->scopeToPermission(Order::where('company_uuid', $companyUuid), 'fleet-ops list order');
|
|
94
127
|
}
|
|
95
128
|
|
|
96
129
|
protected function relativeDateResolver(): AiRelativeDateResolver
|
|
@@ -48,19 +48,69 @@ class SearchResourcesCapability extends AbstractFleetOpsAICapability
|
|
|
48
48
|
$prompt = (string) $task->prompt;
|
|
49
49
|
$terms = $this->searchTerms($prompt);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
if (empty($terms)) {
|
|
52
|
+
return [
|
|
53
|
+
'query_terms' => [],
|
|
54
|
+
'results' => [],
|
|
55
|
+
'message' => 'The prompt did not reference a specific Fleet-Ops record, so no records were searched.',
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return $this->searchAll($terms);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Search the given resource types (all when null) for the terms. A failing search is reported
|
|
64
|
+
* without discarding the others.
|
|
65
|
+
*/
|
|
66
|
+
protected function searchAll(array $terms, ?array $types = null): array
|
|
67
|
+
{
|
|
68
|
+
$results = [];
|
|
69
|
+
$failed = [];
|
|
70
|
+
$searches = [
|
|
71
|
+
'orders' => fn () => $this->orders($terms),
|
|
72
|
+
'vehicles' => fn () => $this->vehicles($terms),
|
|
73
|
+
'drivers' => fn () => $this->drivers($terms),
|
|
74
|
+
'work_orders' => fn () => $this->workOrders($terms),
|
|
75
|
+
'maintenances' => fn () => $this->maintenances($terms),
|
|
76
|
+
'devices' => fn () => $this->devices($terms),
|
|
77
|
+
'sensors' => fn () => $this->sensors($terms),
|
|
78
|
+
'telematics' => fn () => $this->telematics($terms),
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
if ($types !== null) {
|
|
82
|
+
$searches = array_intersect_key($searches, array_flip($types));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
foreach ($searches as $resource => $search) {
|
|
86
|
+
try {
|
|
87
|
+
$results[$resource] = $search();
|
|
88
|
+
} catch (\Throwable $e) {
|
|
89
|
+
$failed[] = $resource;
|
|
90
|
+
$this->reportSearchFailure($resource, $e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
$payload = [
|
|
52
95
|
'query_terms' => $terms,
|
|
53
|
-
'results' => array_filter(
|
|
54
|
-
'orders' => $this->orders($terms),
|
|
55
|
-
'vehicles' => $this->vehicles($terms),
|
|
56
|
-
'drivers' => $this->drivers($terms),
|
|
57
|
-
'work_orders' => $this->workOrders($terms),
|
|
58
|
-
'maintenances' => $this->maintenances($terms),
|
|
59
|
-
'devices' => $this->devices($terms),
|
|
60
|
-
'sensors' => $this->sensors($terms),
|
|
61
|
-
'telematics' => $this->telematics($terms),
|
|
62
|
-
]),
|
|
96
|
+
'results' => array_filter($results),
|
|
63
97
|
];
|
|
98
|
+
|
|
99
|
+
if (!empty($failed)) {
|
|
100
|
+
$payload['unavailable_search'] = $failed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return $payload;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @codeCoverageIgnore
|
|
108
|
+
*/
|
|
109
|
+
protected function reportSearchFailure(string $resource, \Throwable $e): void
|
|
110
|
+
{
|
|
111
|
+
if (function_exists('report')) {
|
|
112
|
+
report($e);
|
|
113
|
+
}
|
|
64
114
|
}
|
|
65
115
|
|
|
66
116
|
protected function matchesPrompt(string $prompt): bool
|
|
@@ -76,7 +126,7 @@ class SearchResourcesCapability extends AbstractFleetOpsAICapability
|
|
|
76
126
|
|
|
77
127
|
return $this->orderSearchQuery()
|
|
78
128
|
->where(function ($query) use ($terms) {
|
|
79
|
-
$this->whereLikeAny($query, ['public_id', 'internal_id'
|
|
129
|
+
$this->whereLikeAny($query, ['public_id', 'internal_id'], $terms);
|
|
80
130
|
$query->orWhereHas('trackingNumber', fn ($tracking) => $this->whereLikeAny($tracking, ['tracking_number', 'barcode'], $terms));
|
|
81
131
|
})
|
|
82
132
|
->limit(5)
|
|
@@ -127,7 +177,7 @@ class SearchResourcesCapability extends AbstractFleetOpsAICapability
|
|
|
127
177
|
|
|
128
178
|
return $this->driverSearchQuery()
|
|
129
179
|
->where(function ($query) use ($terms) {
|
|
130
|
-
$this->whereLikeAny($query, ['public_id', '
|
|
180
|
+
$this->whereLikeAny($query, ['public_id', 'internal_id', 'drivers_license_number'], $terms);
|
|
131
181
|
$query->orWhereHas('user', fn ($user) => $this->whereLikeAny($user, ['name', 'email', 'phone'], $terms));
|
|
132
182
|
})
|
|
133
183
|
->limit(5)
|
|
@@ -146,27 +196,27 @@ class SearchResourcesCapability extends AbstractFleetOpsAICapability
|
|
|
146
196
|
|
|
147
197
|
protected function workOrders(array $terms): array
|
|
148
198
|
{
|
|
149
|
-
return $this->generic(WorkOrder::class, 'fleet-ops see work-order', ['public_id', '
|
|
199
|
+
return $this->generic(WorkOrder::class, 'fleet-ops see work-order', ['public_id', 'code', 'subject'], 'console.fleet-ops.maintenance.work-orders.index.details', $terms);
|
|
150
200
|
}
|
|
151
201
|
|
|
152
202
|
protected function maintenances(array $terms): array
|
|
153
203
|
{
|
|
154
|
-
return $this->generic(Maintenance::class, 'fleet-ops see maintenance', ['public_id', '
|
|
204
|
+
return $this->generic(Maintenance::class, 'fleet-ops see maintenance', ['public_id', 'summary', 'notes'], 'console.fleet-ops.maintenance.maintenances.index.details', $terms);
|
|
155
205
|
}
|
|
156
206
|
|
|
157
207
|
protected function devices(array $terms): array
|
|
158
208
|
{
|
|
159
|
-
return $this->generic(Device::class, 'fleet-ops see device', ['public_id', '
|
|
209
|
+
return $this->generic(Device::class, 'fleet-ops see device', ['public_id', 'name', 'device_id', 'imei', 'serial_number'], 'console.fleet-ops.connectivity.devices.index.details', $terms);
|
|
160
210
|
}
|
|
161
211
|
|
|
162
212
|
protected function sensors(array $terms): array
|
|
163
213
|
{
|
|
164
|
-
return $this->generic(Sensor::class, 'fleet-ops see sensor', ['public_id', '
|
|
214
|
+
return $this->generic(Sensor::class, 'fleet-ops see sensor', ['public_id', 'name', 'internal_id', 'serial_number', 'imei'], 'console.fleet-ops.connectivity.sensors.index.details', $terms);
|
|
165
215
|
}
|
|
166
216
|
|
|
167
217
|
protected function telematics(array $terms): array
|
|
168
218
|
{
|
|
169
|
-
return $this->generic(Telematic::class, 'fleet-ops see telematic', ['public_id', '
|
|
219
|
+
return $this->generic(Telematic::class, 'fleet-ops see telematic', ['public_id', 'name'], 'console.fleet-ops.connectivity.telematics.details', $terms);
|
|
170
220
|
}
|
|
171
221
|
|
|
172
222
|
protected function generic(string $modelClass, string $permission, array $columns, string $route, array $terms): array
|
|
@@ -193,23 +243,35 @@ class SearchResourcesCapability extends AbstractFleetOpsAICapability
|
|
|
193
243
|
|
|
194
244
|
protected function orderSearchQuery()
|
|
195
245
|
{
|
|
196
|
-
return
|
|
197
|
-
->where('company_uuid', session('company'))
|
|
246
|
+
return $this->scopeToPermission(
|
|
247
|
+
Order::with(['transaction', 'trackingNumber'])->where('company_uuid', session('company')),
|
|
248
|
+
'fleet-ops list order'
|
|
249
|
+
);
|
|
198
250
|
}
|
|
199
251
|
|
|
200
252
|
protected function vehicleSearchQuery()
|
|
201
253
|
{
|
|
202
|
-
return Vehicle::where('company_uuid', session('company'));
|
|
254
|
+
return $this->scopeToPermission(Vehicle::where('company_uuid', session('company')), 'fleet-ops list vehicle');
|
|
203
255
|
}
|
|
204
256
|
|
|
205
257
|
protected function driverSearchQuery()
|
|
206
258
|
{
|
|
207
|
-
return Driver::with('user')
|
|
208
|
-
->where('company_uuid', session('company'));
|
|
259
|
+
return $this->scopeToPermission(Driver::with('user')->where('company_uuid', session('company')), 'fleet-ops list driver');
|
|
209
260
|
}
|
|
210
261
|
|
|
211
262
|
protected function genericSearchQuery(string $modelClass)
|
|
212
263
|
{
|
|
213
|
-
|
|
264
|
+
$permission = match ($modelClass) {
|
|
265
|
+
WorkOrder::class => 'fleet-ops list work-order',
|
|
266
|
+
Maintenance::class => 'fleet-ops list maintenance',
|
|
267
|
+
Device::class => 'fleet-ops list device',
|
|
268
|
+
Sensor::class => 'fleet-ops list sensor',
|
|
269
|
+
Telematic::class => 'fleet-ops list telematic',
|
|
270
|
+
default => null,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
$query = $modelClass::where('company_uuid', session('company'));
|
|
274
|
+
|
|
275
|
+
return $permission ? $this->scopeToPermission($query, $permission) : $query;
|
|
214
276
|
}
|
|
215
277
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support\Ai;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fleet-Ops pages and dialogs Fleetbase AI may offer to open. Definitions are plain arrays so Fleet-Ops
|
|
7
|
+
* does not depend on the AI extension's classes; nothing runs until the user confirms it in the console.
|
|
8
|
+
*/
|
|
9
|
+
class FleetOpsAiConsoleCommands
|
|
10
|
+
{
|
|
11
|
+
public const ENGINE = '@fleetbase/fleetops-engine';
|
|
12
|
+
|
|
13
|
+
protected const DOCS = 'https://fleetbase.io/docs/fleet-ops';
|
|
14
|
+
|
|
15
|
+
public static function all(): array
|
|
16
|
+
{
|
|
17
|
+
return array_merge(static::resources(), static::settings());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Open, create, import, and view commands for Fleet-Ops records.
|
|
22
|
+
*/
|
|
23
|
+
public static function resources(): array
|
|
24
|
+
{
|
|
25
|
+
$resources = [
|
|
26
|
+
// key => [singular, plural, list route, new route, detail route, permission resource, actions service, supports import, docs path, keywords]
|
|
27
|
+
'orders' => ['order', 'Orders', 'operations.orders.index', 'operations.orders.index.new', 'operations.orders.index.details', 'order', 'order-actions', 'importOrders', 'operations/orders', ['dispatch', 'delivery', 'shipment', 'job']],
|
|
28
|
+
'drivers' => ['driver', 'Drivers', 'management.drivers.index', 'management.drivers.index.new', 'management.drivers.index.details', 'driver', 'driver-actions', 'import', 'resources/drivers', ['courier', 'navigator']],
|
|
29
|
+
'vehicles' => ['vehicle', 'Vehicles', 'management.vehicles.index', 'management.vehicles.index.new', 'management.vehicles.index.details', 'vehicle', 'vehicle-actions', 'import', 'resources/vehicles', ['truck', 'van', 'car', 'fleet']],
|
|
30
|
+
'contacts' => ['contact', 'Contacts', 'management.contacts.index', 'management.contacts.index.new', 'management.contacts.index.details', 'contact', 'contact-actions', 'import', 'resources/contacts', ['person']],
|
|
31
|
+
'customers' => ['customer', 'Customers', 'management.contacts.customers', 'management.contacts.customers.new', 'management.contacts.customers.details', 'contact', 'customer-actions', 'import', 'resources/contacts', ['client', 'contacts']],
|
|
32
|
+
'places' => ['place', 'Places', 'management.places.index', 'management.places.index.new', 'management.places.index.details', 'place', 'place-actions', 'import', 'resources/places', ['address', 'location', 'depot', 'warehouse']],
|
|
33
|
+
'fleets' => ['fleet', 'Fleets', 'management.fleets.index', 'management.fleets.index.new', 'management.fleets.index.details', 'fleet', 'fleet-actions', null, 'resources/fleets', ['team']],
|
|
34
|
+
'issues' => ['issue', 'Issues', 'management.issues.index', 'management.issues.index.new', 'management.issues.index.details', 'issue', 'issue-actions', null, 'resources/issues', ['problem', 'incident']],
|
|
35
|
+
'work_orders' => ['work order', 'Work Orders', 'maintenance.work-orders.index', 'maintenance.work-orders.index.new', 'maintenance.work-orders.index.details', 'work-order', 'work-order-actions', null, 'maintenance/work-orders', ['maintenance', 'repair']],
|
|
36
|
+
'maintenances' => ['maintenance', 'Maintenances', 'maintenance.maintenances.index', 'maintenance.maintenances.index.new', 'maintenance.maintenances.index.details', 'maintenance', 'maintenance-actions', null, 'maintenance', ['service', 'repair']],
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
$commands = [];
|
|
40
|
+
|
|
41
|
+
foreach ($resources as $key => [$singular, $plural, $listRoute, $newRoute, $detailRoute, $permission, $service, $import, $docs, $keywords]) {
|
|
42
|
+
$base = [
|
|
43
|
+
'breadcrumb' => "Fleet-Ops › {$plural}",
|
|
44
|
+
'keywords' => array_merge(['fleet-ops', $singular], $keywords),
|
|
45
|
+
'docs_url' => static::DOCS . "/{$docs}",
|
|
46
|
+
'module' => 'fleet-ops',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
$commands[] = $base + [
|
|
50
|
+
'id' => "fleet-ops.{$key}.open",
|
|
51
|
+
'label' => "Open {$plural}",
|
|
52
|
+
'description' => "Go to the Fleet-Ops {$plural} list.",
|
|
53
|
+
'steps' => [['type' => 'navigate', 'route' => "console.fleet-ops.{$listRoute}"]],
|
|
54
|
+
'permissions' => ["fleet-ops list {$permission}"],
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
$commands[] = $base + [
|
|
58
|
+
'id' => "fleet-ops.{$key}.create",
|
|
59
|
+
'label' => "Create {$singular}",
|
|
60
|
+
'description' => "Open the new {$singular} form in Fleet-Ops.",
|
|
61
|
+
'steps' => [['type' => 'navigate', 'route' => "console.fleet-ops.{$newRoute}"]],
|
|
62
|
+
'permissions' => ["fleet-ops create {$permission}"],
|
|
63
|
+
'keywords' => array_merge($base['keywords'], ['new', 'add', 'create']),
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
$commands[] = $base + [
|
|
67
|
+
'id' => "fleet-ops.{$key}.view",
|
|
68
|
+
'label' => "Open {$singular}",
|
|
69
|
+
'description' => "Open a specific {$singular} by its public id.",
|
|
70
|
+
'steps' => [['type' => 'navigate', 'route' => "console.fleet-ops.{$detailRoute}", 'models' => ['public_id']]],
|
|
71
|
+
'permissions' => ["fleet-ops see {$permission}"],
|
|
72
|
+
'params' => ['public_id' => ['type' => 'string', 'description' => "The {$singular} public id."]],
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
if ($import) {
|
|
76
|
+
$commands[] = $base + [
|
|
77
|
+
'id' => "fleet-ops.{$key}.import",
|
|
78
|
+
'label' => "Import {$plural}",
|
|
79
|
+
'description' => "Go to {$plural} in Fleet-Ops and open the spreadsheet import, including the template download.",
|
|
80
|
+
'steps' => [
|
|
81
|
+
['type' => 'navigate', 'route' => "console.fleet-ops.{$listRoute}"],
|
|
82
|
+
['type' => 'service', 'engine' => static::ENGINE, 'service' => $service, 'method' => $import],
|
|
83
|
+
],
|
|
84
|
+
'permissions' => ["fleet-ops import {$permission}"],
|
|
85
|
+
'keywords' => array_merge($base['keywords'], ['import', 'spreadsheet', 'csv', 'excel', 'xlsx', 'template', 'bulk', 'upload']),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return $commands;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public static function settings(): array
|
|
94
|
+
{
|
|
95
|
+
$settings = [
|
|
96
|
+
'map' => ['Map', 'map-settings', ['map', 'provider', 'google maps', 'leaflet', 'openstreetmap', 'tiles', 'view maps']],
|
|
97
|
+
'navigator-app' => ['Navigator App', 'navigator-settings', ['navigator', 'driver app', 'mobile app', 'onboard']],
|
|
98
|
+
'routing' => ['Routing', 'routing-settings', ['routing', 'route', 'optimization', 'osrm', 'distance']],
|
|
99
|
+
'notifications' => ['Notifications', 'notification-settings', ['notification', 'alert', 'email', 'sms']],
|
|
100
|
+
'custom-fields' => ['Custom Fields', 'custom-field', ['custom field', 'field', 'attribute']],
|
|
101
|
+
'scheduling' => ['Scheduling', null, ['schedule', 'shift', 'calendar']],
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
$commands = [];
|
|
105
|
+
|
|
106
|
+
foreach ($settings as $route => [$label, $permission, $keywords]) {
|
|
107
|
+
$commands[] = [
|
|
108
|
+
'id' => 'fleet-ops.settings.' . str_replace('-', '_', $route) . '.open',
|
|
109
|
+
'label' => "Open {$label} settings",
|
|
110
|
+
'breadcrumb' => "Fleet-Ops › Settings › {$label}",
|
|
111
|
+
'description' => "Go to the Fleet-Ops {$label} settings.",
|
|
112
|
+
'steps' => [['type' => 'navigate', 'route' => "console.fleet-ops.settings.{$route}"]],
|
|
113
|
+
'permissions' => $permission ? ["fleet-ops view {$permission}"] : [],
|
|
114
|
+
'keywords' => array_merge(['fleet-ops', 'settings', 'configure'], $keywords),
|
|
115
|
+
'docs_url' => static::DOCS . "/settings/{$route}",
|
|
116
|
+
'module' => 'fleet-ops',
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return $commands;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support\Ai\Tools;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Ai\Contracts\AIToolCapabilityInterface;
|
|
6
|
+
use Fleetbase\Ai\Models\AiTask;
|
|
7
|
+
use Fleetbase\Ai\Support\AiToolContext;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Ai\Capabilities\CreateOrderPreviewCapability;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Lets the model prepare a Fleet-Ops order draft from structured fields. The draft is shown to the user
|
|
12
|
+
* as a preview card; the order is only created when the user applies it.
|
|
13
|
+
*/
|
|
14
|
+
class CreateOrderTool extends CreateOrderPreviewCapability implements AIToolCapabilityInterface
|
|
15
|
+
{
|
|
16
|
+
public function toolName(): string
|
|
17
|
+
{
|
|
18
|
+
return 'propose_create_order';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public function toolDescription(): string
|
|
22
|
+
{
|
|
23
|
+
return 'Prepare a Fleet-Ops order draft for the user to review. Call once per order (several calls prepare several drafts). '
|
|
24
|
+
. 'Each draft appears as a card the user must confirm; the order is NOT created by this tool. '
|
|
25
|
+
. 'Pass addresses or saved place names exactly as the user gave them. Use ISO 8601 for scheduled_at, resolved with the temporal context. '
|
|
26
|
+
. 'Only fill fields the user provided or explicitly asked you to make up (for example test data). After calling, tell the user to review the draft card; never claim the order exists.';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public function toolParameters(): array
|
|
30
|
+
{
|
|
31
|
+
return [
|
|
32
|
+
'type' => 'object',
|
|
33
|
+
'properties' => [
|
|
34
|
+
'pickup' => ['type' => 'string', 'description' => 'Pickup address or saved place name.'],
|
|
35
|
+
'dropoff' => ['type' => 'string', 'description' => 'Dropoff address or saved place name.'],
|
|
36
|
+
'scheduled_at' => ['type' => 'string', 'description' => 'Optional ISO 8601 date and time.'],
|
|
37
|
+
'driver' => ['type' => 'string', 'description' => 'Optional driver name or public id.'],
|
|
38
|
+
'vehicle' => ['type' => 'string', 'description' => 'Optional vehicle name, plate, or public id.'],
|
|
39
|
+
'order_config' => ['type' => 'string', 'description' => 'Optional order configuration key, e.g. transport.'],
|
|
40
|
+
'notes' => ['type' => 'string', 'description' => 'Optional notes for the order.'],
|
|
41
|
+
'dispatch' => ['type' => 'boolean', 'description' => 'Dispatch immediately. Only true when the user asked for it.'],
|
|
42
|
+
],
|
|
43
|
+
'required' => ['pickup', 'dropoff'],
|
|
44
|
+
'additionalProperties' => false,
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public function availableFor(AiToolContext $context): bool
|
|
49
|
+
{
|
|
50
|
+
return $context->audience->canAll($this->permissions());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public function invoke(AiTask $task, array $arguments, AiToolContext $context): array
|
|
54
|
+
{
|
|
55
|
+
$draft = array_filter([
|
|
56
|
+
'payload' => array_filter([
|
|
57
|
+
'pickup_query' => $this->argument($arguments, 'pickup'),
|
|
58
|
+
'dropoff_query' => $this->argument($arguments, 'dropoff'),
|
|
59
|
+
]),
|
|
60
|
+
'scheduled_at' => $this->argument($arguments, 'scheduled_at'),
|
|
61
|
+
'driver_query' => $this->argument($arguments, 'driver'),
|
|
62
|
+
'vehicle_query' => $this->argument($arguments, 'vehicle'),
|
|
63
|
+
'type' => $this->argument($arguments, 'order_config'),
|
|
64
|
+
'notes' => $this->argument($arguments, 'notes'),
|
|
65
|
+
'dispatched' => (bool) ($arguments['dispatch'] ?? false),
|
|
66
|
+
], fn ($value) => $value !== null && $value !== []);
|
|
67
|
+
|
|
68
|
+
$preview = $context->addActionPreview($this, $this->preview($task, ['source' => 'tool', 'draft' => $draft]));
|
|
69
|
+
|
|
70
|
+
return [
|
|
71
|
+
'preview_id' => $preview['preview_id'],
|
|
72
|
+
'ready' => $preview['ready'] ?? false,
|
|
73
|
+
'missing_fields' => $preview['missing_fields'] ?? [],
|
|
74
|
+
'fields' => $preview['fields'] ?? [],
|
|
75
|
+
'status' => 'draft_shown_to_user',
|
|
76
|
+
'message' => 'A draft order card is shown to the user. The order does not exist until the user reviews the card and clicks ' . ($preview['apply_label'] ?? 'Create order') . '.',
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected function argument(array $arguments, string $key): ?string
|
|
81
|
+
{
|
|
82
|
+
$value = trim((string) ($arguments[$key] ?? ''));
|
|
83
|
+
|
|
84
|
+
return $value === '' ? null : $value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support\Ai\Tools;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Ai\Contracts\AIToolCapabilityInterface;
|
|
6
|
+
use Fleetbase\Ai\Models\AiTask;
|
|
7
|
+
use Fleetbase\Ai\Support\AiToolContext;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Ai\Capabilities\ImportOrdersPreviewCapability;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Explains what a Fleet-Ops order import needs. Fleetbase AI cannot process an uploaded spreadsheet,
|
|
12
|
+
* so this only reports the accepted formats and required columns for the user to act on themselves.
|
|
13
|
+
*/
|
|
14
|
+
class ImportOrdersTool extends ImportOrdersPreviewCapability implements AIToolCapabilityInterface
|
|
15
|
+
{
|
|
16
|
+
public function toolName(): string
|
|
17
|
+
{
|
|
18
|
+
return 'fleetops_import_requirements';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public function toolDescription(): string
|
|
22
|
+
{
|
|
23
|
+
return 'Report what a Fleet-Ops order or resource import needs: accepted file formats and the columns a spreadsheet must contain. '
|
|
24
|
+
. 'Use it when the user asks about importing or bulk uploading orders, vehicles, or drivers from a spreadsheet. '
|
|
25
|
+
. 'This does NOT import anything and cannot read an uploaded file; never tell the user rows were imported.';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public function toolParameters(): array
|
|
29
|
+
{
|
|
30
|
+
return [
|
|
31
|
+
'type' => 'object',
|
|
32
|
+
'properties' => new \stdClass(),
|
|
33
|
+
'additionalProperties' => false,
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public function availableFor(AiToolContext $context): bool
|
|
38
|
+
{
|
|
39
|
+
return $context->audience->canAll($this->permissions());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public function invoke(AiTask $task, array $arguments, AiToolContext $context): array
|
|
43
|
+
{
|
|
44
|
+
$requirements = $this->resolve($task);
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
'accepted_sources' => $requirements['accepted_sources'] ?? [],
|
|
48
|
+
'minimum_columns' => $requirements['minimum_columns'] ?? [],
|
|
49
|
+
'can_import_here' => false,
|
|
50
|
+
'message' => $requirements['message'] ?? 'Fleetbase AI cannot process uploaded spreadsheets.',
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Fleetbase\FleetOps\Support\Ai\Tools;
|
|
4
|
+
|
|
5
|
+
use Fleetbase\Ai\Contracts\AIToolCapabilityInterface;
|
|
6
|
+
use Fleetbase\Ai\Models\AiTask;
|
|
7
|
+
use Fleetbase\Ai\Support\AiToolContext;
|
|
8
|
+
use Fleetbase\FleetOps\Support\Ai\Capabilities\OptimizeOrderRouteCapability;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Lets the model propose a resequenced waypoint order for a Fleet-Ops order. The proposal is shown as
|
|
12
|
+
* a preview card; the route is only changed when the user applies it.
|
|
13
|
+
*/
|
|
14
|
+
class OptimizeOrderRouteTool extends OptimizeOrderRouteCapability implements AIToolCapabilityInterface
|
|
15
|
+
{
|
|
16
|
+
public function toolName(): string
|
|
17
|
+
{
|
|
18
|
+
return 'propose_optimize_order_route';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public function toolDescription(): string
|
|
22
|
+
{
|
|
23
|
+
return 'Prepare an optimized waypoint sequence for one Fleet-Ops order, for the user to review. '
|
|
24
|
+
. 'Use it when the user asks to optimize, reorder, or resequence the stops on an order. '
|
|
25
|
+
. 'Each proposal appears as a card the user must confirm; the route is NOT changed by this tool. '
|
|
26
|
+
. 'After calling, tell the user to review the card; never claim the route was changed.';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public function toolParameters(): array
|
|
30
|
+
{
|
|
31
|
+
return [
|
|
32
|
+
'type' => 'object',
|
|
33
|
+
'properties' => [
|
|
34
|
+
'order' => ['type' => 'string', 'description' => 'The order public id, internal id, or UUID, exactly as the user gave it.'],
|
|
35
|
+
],
|
|
36
|
+
'required' => ['order'],
|
|
37
|
+
'additionalProperties' => false,
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public function availableFor(AiToolContext $context): bool
|
|
42
|
+
{
|
|
43
|
+
return $context->audience->canAll($this->permissions());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public function invoke(AiTask $task, array $arguments, AiToolContext $context): array
|
|
47
|
+
{
|
|
48
|
+
$order = trim((string) ($arguments['order'] ?? ''));
|
|
49
|
+
|
|
50
|
+
if ($order === '') {
|
|
51
|
+
return ['error' => 'invalid_arguments', 'message' => 'An order id is required to optimize a route.'];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The capability resolves the order from the prompt text, so hand it a copy carrying just the
|
|
55
|
+
// identifier the model supplied. The copy is never saved.
|
|
56
|
+
$probe = clone $task;
|
|
57
|
+
$probe->prompt = $order;
|
|
58
|
+
|
|
59
|
+
$preview = $context->addActionPreview($this, $this->preview($probe));
|
|
60
|
+
|
|
61
|
+
return [
|
|
62
|
+
'preview_id' => $preview['preview_id'],
|
|
63
|
+
'ready' => $preview['ready'] ?? false,
|
|
64
|
+
'missing_fields' => $preview['missing_fields'] ?? [],
|
|
65
|
+
'fields' => $preview['fields'] ?? [],
|
|
66
|
+
'status' => 'proposal_shown_to_user',
|
|
67
|
+
'message' => $preview['message'] ?? 'A route proposal card is shown to the user.',
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
}
|