@fleetbase/fleetops-engine 0.6.46 → 0.6.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/addon/components/admin/routing-settings.hbs +71 -0
  2. package/addon/components/admin/routing-settings.js +124 -0
  3. package/addon/components/customer/orders.js +1 -1
  4. package/addon/components/display-place.hbs +1 -1
  5. package/addon/components/map/order-list-overlay/order.hbs +18 -3
  6. package/addon/components/map/toolbar.hbs +2 -2
  7. package/addon/components/map/toolbar.js +10 -2
  8. package/addon/components/orchestrator/phase-builder.hbs +28 -5
  9. package/addon/components/orchestrator/phase-builder.js +21 -1
  10. package/addon/components/orchestrator/resource-panel.hbs +19 -1
  11. package/addon/components/orchestrator/resource-panel.js +4 -0
  12. package/addon/components/orchestrator-workbench.js +3 -1
  13. package/addon/components/order/details/purchase-rate.hbs +19 -22
  14. package/addon/components/order/details/route.hbs +26 -1
  15. package/addon/components/order/details/route.js +50 -0
  16. package/addon/components/order/details/tracking.hbs +171 -27
  17. package/addon/components/order/details/tracking.js +457 -0
  18. package/addon/components/order/form/details.hbs +1 -2
  19. package/addon/components/order/form/details.js +6 -0
  20. package/addon/components/order/form/route.js +9 -1
  21. package/addon/components/order/kanban-card.hbs +18 -3
  22. package/addon/components/order-list-overlay/order.hbs +18 -3
  23. package/addon/components/order-progress-card.hbs +21 -6
  24. package/addon/components/order-tracking-lookup.hbs +7 -7
  25. package/addon/components/order-tracking-lookup.js +1 -1
  26. package/addon/components/route-list.hbs +78 -8
  27. package/addon/components/route-list.js +48 -6
  28. package/addon/components/tracking-stop-progress.hbs +45 -0
  29. package/addon/components/tracking-stop-progress.js +79 -0
  30. package/addon/controllers/settings/payments/index.js +1 -1
  31. package/addon/controllers/settings/routing.js +98 -1
  32. package/addon/helpers/format-duration.js +38 -0
  33. package/addon/routes/application.js +6 -0
  34. package/addon/services/order-list-overlay.js +2 -2
  35. package/addon/styles/fleetops-engine.css +733 -0
  36. package/addon/templates/settings/routing.hbs +60 -2
  37. package/app/components/tracking-stop-progress.js +1 -0
  38. package/app/helpers/format-duration.js +1 -0
  39. package/composer.json +1 -1
  40. package/extension.json +1 -1
  41. package/package.json +3 -3
  42. package/pnpm-workspace.yaml +7 -0
  43. package/server/config/fleetops.php +32 -11
  44. package/server/migrations/2026_05_13_000001_repair_customer_contact_user_types.php +86 -0
  45. package/server/seeders/VroomOrchestrationSeeder.php +402 -0
  46. package/server/src/Http/Controllers/Api/v1/OrchestrationController.php +64 -0
  47. package/server/src/Http/Controllers/Api/v1/OrderController.php +26 -26
  48. package/server/src/Http/Controllers/Api/v1/PayloadController.php +10 -10
  49. package/server/src/Http/Controllers/Api/v1/ServiceQuoteController.php +1 -1
  50. package/server/src/Http/Controllers/Internal/v1/ContactController.php +41 -0
  51. package/server/src/Http/Controllers/Internal/v1/LiveController.php +6 -42
  52. package/server/src/Http/Controllers/Internal/v1/OrchestrationController.php +1 -1
  53. package/server/src/Http/Controllers/Internal/v1/OrderController.php +69 -15
  54. package/server/src/Http/Controllers/Internal/v1/ServiceQuoteController.php +1 -1
  55. package/server/src/Http/Controllers/Internal/v1/SettingController.php +119 -12
  56. package/server/src/Http/Resources/v1/Index/Payload.php +2 -2
  57. package/server/src/Models/Contact.php +122 -13
  58. package/server/src/Models/Order.php +1 -1
  59. package/server/src/Models/Payload.php +8 -8
  60. package/server/src/Models/Place.php +0 -3
  61. package/server/src/Models/ServiceRate.php +1 -1
  62. package/server/src/Observers/ContactObserver.php +8 -0
  63. package/server/src/Orchestration/Engines/CapacityAllocationEngine.php +220 -0
  64. package/server/src/Orchestration/Engines/VroomOrchestrationEngine.php +330 -38
  65. package/server/src/Orchestration/Support/OrchestrationPayloadBuilder.php +259 -51
  66. package/server/src/Providers/FleetOpsServiceProvider.php +27 -0
  67. package/server/src/Support/LiveOrderQuery.php +75 -0
  68. package/server/src/Support/Metrics.php +15 -0
  69. package/server/src/Support/OSRM.php +11 -6
  70. package/server/src/Support/OrderTracker.php +7 -582
  71. package/server/src/Support/Utils.php +1 -1
  72. package/server/src/Tracking/Contracts/TrackingProviderInterface.php +19 -0
  73. package/server/src/Tracking/Providers/CalculatedTrackingProvider.php +65 -0
  74. package/server/src/Tracking/Providers/GoogleRoutesTrackingProvider.php +118 -0
  75. package/server/src/Tracking/Providers/OsrmTrackingProvider.php +71 -0
  76. package/server/src/Tracking/README.md +25 -0
  77. package/server/src/Tracking/Support/FakeTrackingProvider.php +42 -0
  78. package/server/src/Tracking/TrackingContext.php +63 -0
  79. package/server/src/Tracking/TrackingContextBuilder.php +204 -0
  80. package/server/src/Tracking/TrackingIntelligenceService.php +180 -0
  81. package/server/src/Tracking/TrackingOptions.php +49 -0
  82. package/server/src/Tracking/TrackingProviderCapabilities.php +30 -0
  83. package/server/src/Tracking/TrackingProviderManager.php +89 -0
  84. package/server/src/Tracking/TrackingProviderRegistry.php +35 -0
  85. package/server/src/Tracking/TrackingProviderResult.php +20 -0
  86. package/server/src/Tracking/TrackingStop.php +59 -0
  87. package/server/src/routes.php +11 -0
  88. package/server/tests/CapacityAllocationEngineTest.php +108 -0
  89. package/server/tests/LiveOrderQueryTest.php +34 -0
  90. package/server/tests/OrchestrationPayloadBuilderTest.php +132 -0
  91. package/server/tests/OrchestratorConsumableApiTest.php +74 -0
  92. package/server/tests/PingDriverEndpointTest.php +19 -0
  93. package/server/tests/TrackingIntelligenceTest.php +176 -0
  94. package/server/tests/VroomOrchestrationEngineTest.php +159 -0
  95. package/translations/en-us.yaml +4 -0
@@ -1,4 +1,4 @@
1
- <Layout::Section::Header @title={{t "settings.routing.fleet-ops-routing-settings"}}>
1
+ <Layout::Section::Header @title="FleetOps Routing & Tracking">
2
2
  <Button
3
3
  @type="primary"
4
4
  @size="sm"
@@ -48,10 +48,68 @@
48
48
  />
49
49
  </InputGroup>
50
50
  </ContentPanel>
51
+ <ContentPanel @title="Tracking Intelligence" @open={{true}} @wrapperClass="bordered-classic">
52
+ <InputGroup @name="Tracking Provider" @helpText="Choose the provider used for live tracking intelligence and ETA calculations.">
53
+ <Select
54
+ @value={{this.trackingProvider}}
55
+ @options={{this.trackingProviderOptions}}
56
+ @optionLabel="label"
57
+ @optionValue="value"
58
+ @onSelect={{fn (mut this.trackingProvider)}}
59
+ @placeholder="Select tracking provider"
60
+ class="w-full"
61
+ />
62
+ </InputGroup>
63
+ <InputGroup @name="Fallback Providers" @helpText="Providers used when the selected tracking provider is unavailable.">
64
+ <div class="fleetbase-model-select fleetbase-power-select ember-model-select">
65
+ <PowerSelectMultiple
66
+ @searchEnabled={{true}}
67
+ @options={{this.trackingProviderOptions}}
68
+ @selected={{this.selectedTrackingFallbackOptions}}
69
+ @onChange={{this.setTrackingFallbacks}}
70
+ @placeholder="Select fallback providers"
71
+ @triggerClass="form-select form-input flex-1"
72
+ as |provider|
73
+ >
74
+ {{provider.label}}
75
+ </PowerSelectMultiple>
76
+ </div>
77
+ </InputGroup>
78
+ <div class="flex justify-end">
79
+ <Button
80
+ @type="default"
81
+ @size="xs"
82
+ @icon={{if this.showTrackingAdvancedSettings "chevron-up" "chevron-down"}}
83
+ @text="Advanced Settings"
84
+ @onClick={{this.toggleTrackingAdvancedSettings}}
85
+ />
86
+ </div>
87
+ {{#if this.showTrackingAdvancedSettings}}
88
+ <div class="mt-2 space-y-4 rounded-md border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800">
89
+ <InputGroup @name="Traffic-Aware ETA" @helpText="Use provider traffic data when supported.">
90
+ <Toggle @isToggled={{this.trackingTrafficEnabled}} @onToggle={{fn (mut this.trackingTrafficEnabled)}} />
91
+ </InputGroup>
92
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
93
+ <InputGroup @name="Summary Cache TTL" @helpText="Seconds to cache the final tracking summary.">
94
+ <Input @type="number" @value={{this.trackingCacheTtlSeconds}} min="15" max="300" class="form-input w-full" />
95
+ </InputGroup>
96
+ <InputGroup @name="Route Cache TTL" @helpText="Seconds to cache provider route responses.">
97
+ <Input @type="number" @value={{this.trackingRouteCacheTtlSeconds}} min="60" max="1800" class="form-input w-full" />
98
+ </InputGroup>
99
+ <InputGroup @name="Stale Location Threshold" @helpText="Seconds before driver location is marked stale.">
100
+ <Input @type="number" @value={{this.trackingStaleLocationThresholdSeconds}} min="30" max="3600" class="form-input w-full" />
101
+ </InputGroup>
102
+ <InputGroup @name="Fallback Speed" @helpText="Kilometers per hour used by the calculated provider.">
103
+ <Input @type="number" @value={{this.trackingDefaultVehicleSpeedKph}} min="1" max="160" class="form-input w-full" />
104
+ </InputGroup>
105
+ </div>
106
+ </div>
107
+ {{/if}}
108
+ </ContentPanel>
51
109
  <RegistryYield @registry="fleet-ops:template:settings:routing" as |RegistryComponent|>
52
110
  <RegistryComponent @controller={{this}} />
53
111
  </RegistryYield>
54
112
  </div>
55
113
  </div>
56
114
  <Spacer @height="600px" />
57
- </Layout::Section::Body>
115
+ </Layout::Section::Body>
@@ -0,0 +1 @@
1
+ export { default } from '@fleetbase/fleetops-engine/components/tracking-stop-progress';
@@ -0,0 +1 @@
1
+ export { default, formatDurationValue } from '@fleetbase/fleetops-engine/helpers/format-duration';
package/composer.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fleetbase/fleetops-api",
3
- "version": "0.6.46",
3
+ "version": "0.6.48",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "keywords": [
6
6
  "fleetbase-extension",
package/extension.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Fleet-Ops",
3
- "version": "0.6.46",
3
+ "version": "0.6.48",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "repository": "https://github.com/fleetbase/fleetops",
6
6
  "license": "AGPL-3.0-or-later",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fleetbase/fleetops-engine",
3
- "version": "0.6.46",
3
+ "version": "0.6.48",
4
4
  "description": "Fleet & Transport Management Extension for Fleetbase",
5
5
  "fleetbase": {
6
6
  "route": "fleet-ops"
@@ -42,9 +42,9 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@babel/core": "^7.23.2",
45
- "@fleetbase/ember-core": "^0.3.18",
45
+ "@fleetbase/ember-core": "^0.3.19",
46
46
  "@fleetbase/ember-ui": "^0.3.29",
47
- "@fleetbase/fleetops-data": "^0.1.31",
47
+ "@fleetbase/fleetops-data": "^0.1.32",
48
48
  "@fleetbase/leaflet-routing-machine": "^3.2.17",
49
49
  "@fortawesome/ember-fontawesome": "^2.0.0",
50
50
  "@fortawesome/fontawesome-svg-core": "6.4.0",
@@ -0,0 +1,7 @@
1
+ allowBuilds:
2
+ '@fortawesome/fontawesome-common-types': false
3
+ '@fortawesome/fontawesome-svg-core': false
4
+ '@fortawesome/free-brands-svg-icons': false
5
+ '@fortawesome/free-solid-svg-icons': false
6
+ core-js: false
7
+ fsevents: false
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * -------------------------------------------
5
5
  * FleetOps API Configuration
6
- * -------------------------------------------
6
+ * -------------------------------------------.
7
7
  */
8
8
  return [
9
9
  /*
@@ -14,17 +14,17 @@ return [
14
14
  'api' => [
15
15
  'version' => '0.0.1',
16
16
  'routing' => [
17
- 'prefix' => null,
18
- 'internal_prefix' => 'int'
19
- ]
17
+ 'prefix' => null,
18
+ 'internal_prefix' => 'int',
19
+ ],
20
20
  ],
21
21
  'connection' => [
22
- 'db' => env('DB_CONNECTION', 'mysql')
22
+ 'db' => env('DB_CONNECTION', 'mysql'),
23
23
  ],
24
24
 
25
25
  /*
26
26
  |--------------------------------------------------------------------------
27
- | Facilitator Fee - This is a percentage fee the system admin takes when
27
+ | Facilitator Fee - This is a percentage fee the system admin takes when
28
28
  | facilitating any payments in the system.
29
29
  | Example: if `10` then 10% fee will be taken on all payments.
30
30
  |--------------------------------------------------------------------------
@@ -37,7 +37,7 @@ return [
37
37
  |--------------------------------------------------------------------------
38
38
  */
39
39
  'osrm' => [
40
- 'host' => env('OSRM_HOST', 'https://router.project-osrm.org')
40
+ 'host' => env('OSRM_HOST', 'https://router.project-osrm.org'),
41
41
  ],
42
42
 
43
43
  /*
@@ -61,12 +61,33 @@ return [
61
61
 
62
62
  /*
63
63
  |--------------------------------------------------------------------------
64
- | Distance Matrix Calculator
64
+ | Distance Matrix Calculator
65
65
  | Options: "calculate", "google", "osrm"
66
66
  |--------------------------------------------------------------------------
67
67
  */
68
68
  'distance_matrix' => [
69
- 'provider' => env('DISTANCE_MATRIX_PROVIDER', 'calculate')
69
+ 'provider' => env('DISTANCE_MATRIX_PROVIDER', 'calculate'),
70
+ ],
71
+
72
+ /*
73
+ |--------------------------------------------------------------------------
74
+ | Order Tracking Intelligence
75
+ |--------------------------------------------------------------------------
76
+ |
77
+ | Provider-neutral tracking data for active orders. The provider registry
78
+ | can be extended by third-party packages without editing FleetOps core.
79
+ |
80
+ | Built-in providers: "google_routes", "osrm", "calculated"
81
+ |--------------------------------------------------------------------------
82
+ */
83
+ 'tracking' => [
84
+ 'provider' => env('TRACKING_PROVIDER', 'google_routes'),
85
+ 'fallbacks' => array_filter(explode(',', env('TRACKING_PROVIDER_FALLBACKS', 'osrm,calculated'))),
86
+ 'traffic_enabled' => env('TRACKING_TRAFFIC_ENABLED', true),
87
+ 'cache_ttl_seconds' => env('TRACKING_CACHE_TTL_SECONDS', 60),
88
+ 'route_cache_ttl_seconds' => env('TRACKING_ROUTE_CACHE_TTL_SECONDS', 600),
89
+ 'stale_location_threshold_seconds' => env('TRACKING_STALE_LOCATION_THRESHOLD_SECONDS', 300),
90
+ 'default_vehicle_speed_kph' => env('TRACKING_DEFAULT_VEHICLE_SPEED_KPH', 35),
70
91
  ],
71
92
 
72
93
  /*
@@ -76,7 +97,7 @@ return [
76
97
  */
77
98
  'navigator' => [
78
99
  'bypass_verification_code' => env('SMS_AUTH_BYPASS_CODE', env('NAVIGATOR_BYPASS_VERIFICATION_CODE')),
79
- 'app_identifier' => env('NAVIGATOR_APP_IDENTIFIER', 'io.fleetbase.navigator')
100
+ 'app_identifier' => env('NAVIGATOR_APP_IDENTIFIER', 'io.fleetbase.navigator'),
80
101
  ],
81
102
 
82
103
  /*
@@ -189,5 +210,5 @@ return [
189
210
  |--------------------------------------------------------------------------
190
211
  */
191
212
  'versions' => ['2020-09-30', '2024-03-12'],
192
- 'version' => '2024-03-12',
213
+ 'version' => '2024-03-12',
193
214
  ];
@@ -0,0 +1,86 @@
1
+ <?php
2
+
3
+ use Fleetbase\FleetOps\Models\Contact;
4
+ use Fleetbase\Models\CompanyUser;
5
+ use Illuminate\Database\Migrations\Migration;
6
+ use Illuminate\Support\Facades\DB;
7
+ use Illuminate\Support\Facades\Schema;
8
+
9
+ return new class extends Migration {
10
+ /**
11
+ * Run the migrations.
12
+ *
13
+ * @return void
14
+ */
15
+ public function up()
16
+ {
17
+ Contact::withoutGlobalScopes()
18
+ ->where('type', 'customer')
19
+ ->whereNull('deleted_at')
20
+ ->chunkById(100, function ($contacts) {
21
+ foreach ($contacts as $contact) {
22
+ try {
23
+ $contact->repairCustomerTypeInvariant(true);
24
+ } catch (Throwable $e) {
25
+ // Skip ambiguous or unrecoverable records; runtime invariants handle new writes.
26
+ }
27
+ }
28
+ });
29
+
30
+ if (!$this->canUseCustomerRoleHint()) {
31
+ return;
32
+ }
33
+
34
+ DB::table('contacts')
35
+ ->select('contacts.id')
36
+ ->join('company_users', function ($join) {
37
+ $join->on('company_users.user_uuid', '=', 'contacts.user_uuid');
38
+ $join->on('company_users.company_uuid', '=', 'contacts.company_uuid');
39
+ })
40
+ ->join('model_has_roles', function ($join) {
41
+ $join->on('model_has_roles.model_uuid', '=', 'company_users.uuid');
42
+ $join->where('model_has_roles.model_type', CompanyUser::class);
43
+ })
44
+ ->join('roles', 'roles.id', '=', 'model_has_roles.role_id')
45
+ ->where('roles.name', 'Fleet-Ops Customer')
46
+ ->whereNull('contacts.deleted_at')
47
+ ->where(function ($query) {
48
+ $query->whereNull('contacts.type');
49
+ $query->orWhere('contacts.type', '!=', 'customer');
50
+ })
51
+ ->distinct()
52
+ ->orderBy('contacts.id')
53
+ ->chunk(100, function ($rows) {
54
+ foreach ($rows as $row) {
55
+ $contact = Contact::withoutGlobalScopes()->find($row->id);
56
+ if (!$contact) {
57
+ continue;
58
+ }
59
+
60
+ try {
61
+ $contact->repairCustomerTypeInvariant(true);
62
+ } catch (Throwable $e) {
63
+ // Skip ambiguous or unrecoverable records; role is only a recovery hint.
64
+ }
65
+ }
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Reverse the migrations.
71
+ *
72
+ * @return void
73
+ */
74
+ public function down()
75
+ {
76
+ // Data repair is intentionally not reversible.
77
+ }
78
+
79
+ private function canUseCustomerRoleHint(): bool
80
+ {
81
+ return Schema::hasTable('contacts')
82
+ && Schema::hasTable('company_users')
83
+ && Schema::hasTable('model_has_roles')
84
+ && Schema::hasTable('roles');
85
+ }
86
+ };