Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 11 additions & 47 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,22 @@
> v0.6.70 ~ "Telematics data retention"
> v0.6.71 ~ "Extensions can add columns, actions and buttons to Fleet-Ops tables and panels"

---
## What's New
- **Telematics Data settings.** A new Fleet-Ops → Settings → Telematics Data page lets each organization decide how long telemetry is kept: device events, raw event payloads, positions, processed and quarantined delivery envelopes, and sync run diagnostics. Administrators set system-wide defaults from the admin console (Fleet-Ops Config → Telematics Data). A value of 0 keeps rows forever.
- **Scheduled retention sweep.** `fleetops:prune-telematics-data` runs every fifteen minutes and applies each organization's policy in bounded batches, so a large backlog is drained gradually without stalling ingestion. Defaults: events 30 days, raw payloads stripped after 7 days, positions 90 days, processed deliveries 24 hours, quarantined deliveries 7 days, sync runs 7 days.
- **Storage usage and on-demand cleanup.** The settings page shows rows, oldest row and estimated size per table for your organization, and "Run cleanup now" queues an immediate retention run.
## Highlights

---
## Fixes
- Device events no longer store the raw provider unit twice. `payload` keeps the raw unit; `meta` holds only the normalized block (speed, heading, odometer, ignition, fuel level, timing). New event rows are roughly half the size.
- Telemetry-driven saves (device, event, position, vehicle, sensors) no longer write to the activity log on every poll. Organizations that want them can enable "Log telemetry activity" in Telematics Data settings. Manual edits are logged as before.
- Delivery inbox and sync run retention moved from the drain command into the retention sweep; `fleetops:drain-telematic-inbox` now only recovers deliveries and finishes interrupted runs.

---
## Upgrade notes
- New indexes on `device_events (company_uuid, created_at)`, `positions (company_uuid, created_at)` and `telematic_sync_runs (telematic_uuid, updated_at)`; on very large tables run `php artisan migrate` in a maintenance window.
- Retention is enabled by default. Existing rows older than the defaults are removed over successive runs after upgrading; set the values to 0 before upgrading if you need to keep history indefinitely.
- Deleting rows frees space inside the database files, not on disk. Run `OPTIMIZE TABLE` off-peak to return space to the operating system.
- Run `php artisan fleetbase:create-permissions` to register the `telematics-settings` permission and the Telematics Settings Manager policy and role.
- **Resource view registries.** Extensions can add the following through `fleet-ops:table:<resource>:<slot>` and `fleet-ops:details:<resource>:<slot>`:
- columns, row actions, bulk actions and toolbar buttons on every top-level Fleet-Ops table;
- header buttons and "…" menu items on its details panels and side panels.

---
## Testing
- Backend tests cover the retention policy layering and clamping, the prune command (per-company policies, compaction, inbox tables via connections, orphans, batch caps, dry runs, filters, locking), the settings and storage usage endpoints, the index migrations, the on-demand job, and the meta and activity-log changes to ingestion.
- Ember tests cover the settings route guard, the settings controller, and the admin defaults component.
> v0.6.70 ~ "Order reporting and a cleaner default dashboard"

---
## What's New
- **Report on orders, their items and tracking.** The report builder can answer questions like "Which products sold the most this month?" and "What did orders total this month?".
- Orders expose their ID and Internal ID, and relationships for tracking (tracking number, region, latest status), Order Config, Customer Vendor, Created By, Purchase Rate › Service Quote and Payload Return.
- `Payload` exposes its items with name, SKU, price, dimensions, metadata and destination. Select an item column for one row per item, or group by one to summarise per product.
- Summary columns count orders distinctly, so they stay correct when item rows are selected. Distance, duration and transaction amount have totals and averages.
- `meta` has no fixed shape, so no column assumes a key in it. Read keys with a computed column, for example `CAST(JSON_UNQUOTE(JSON_EXTRACT(payload.entities.meta, '$.quantity')) AS DECIMAL(15,2))`.
- Column labels drop the table name ("Type", not "Order Type"). Distance and duration are labelled in meters and seconds, and money columns are marked "(minor units)".
- **A cleaner default dashboard.** Fleet-Ops widgets declare their place on the default dashboard: Radar first, then Active Orders and Drivers Online, a full-width Live Fleet map, and Revenue Trend, Top Drivers and Maintenance side by side.
- **Live Fleet widget cards match the live map.** Clicking a driver or vehicle on the widget map shows the same card as **Operations › Live Map**.
Built-in items carry stable ids, so registered items can be placed before or after them.
- **Fix: registered details tabs.** Opening a driver, vehicle or trailer side panel removed the route from tabs other extensions had registered, which broke them in the full details view. Side panels also left out the tabs they could render.
- **Fix: the sensor details view showed the tabs registered for places.**

---
## Fixes
- Custom field values on fuel reports and service areas are saved. They were dropped because the save hooks read the snake_case payload root, while the console sends `fuelReport` and `serviceArea`.
- The report schema no longer references 13 columns and join keys that don't exist: driver name, email and phone and the driver's vehicle, the vehicle's driver, and the fuel report cost, odometer and date. Transaction summaries sum `transaction.amount`.
- Every report table, and the item join, leaves soft-deleted rows out on core-api v1.6.64 and later.

---
## Dependencies
- Order items, JSON totals and soft deletes in reports need `fleetbase/core-api` v1.6.64. The schema still registers on older versions.
- The report builder and the default dashboard order ship in `@fleetbase/ember-ui` v0.4.4.

---
## Testing
- Every declared report column and join key was checked against the database schema, and order reports were run against real storefront orders on MySQL in strict mode.
- Tests cover the report schema at 100% line coverage, the Live Fleet map cards and the default dashboard order.
## Upgrading
Needs fleetbase/ember-core v0.3.25 and fleetbase/ember-ui v0.4.5.

---
## Need help?
- [GitHub Discussions](https://github.com/fleetbase/fleetbase/discussions)
- [Discord](https://discord.gg/HnTqQ6zAVn)
---
8 changes: 7 additions & 1 deletion addon/components/order/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ export default class OrderFormComponent extends Component {
});
this.args.resource.payload.set('type', orderConfig.key);

this.customFields = yield this.customFieldsRegistry.loadSubjectCustomFields.perform(orderConfig);
// loadSubjectCustomFields rejects when the custom fields cannot be loaded (ember-core 0.3.25).
try {
this.customFields = yield this.customFieldsRegistry.loadSubjectCustomFields.perform(orderConfig);
} catch (err) {
this.customFields = undefined;
debug('Unable to load order custom fields: ' + err.message);
}
}

@task *selectDriver(driver) {
Expand Down
11 changes: 10 additions & 1 deletion addon/controllers/analytics/reports/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default class AnalyticsReportsIndexController extends Controller {
@service intl;

/** query params */
@tracked queryParams = ['page', 'limit', 'sort', 'query', 'public_id', 'name', 'created_at'];
@tracked queryParams = this.reportActions.queryParamsFor(['page', 'limit', 'sort', 'query', 'public_id', 'name', 'created_at']);
@tracked page = 1;
@tracked limit;
@tracked sort = '-created_at';
Expand All @@ -20,11 +20,13 @@ export default class AnalyticsReportsIndexController extends Controller {
get actionButtons() {
return [
{
id: 'refresh',
icon: 'refresh',
onClick: this.reportActions.refresh,
helpText: this.intl.t('common.refresh'),
},
{
id: 'create',
text: this.intl.t('common.new'),
type: 'primary',
icon: 'plus',
Expand All @@ -37,6 +39,7 @@ export default class AnalyticsReportsIndexController extends Controller {
get bulkActions() {
return [
{
id: 'bulk-delete',
label: 'Delete selected...',
class: 'text-red-500',
fn: this.reportActions.bulkDelete,
Expand All @@ -48,6 +51,7 @@ export default class AnalyticsReportsIndexController extends Controller {
get columns() {
return [
{
id: 'title',
sticky: true,
label: 'Title',
valuePath: 'title',
Expand All @@ -59,6 +63,7 @@ export default class AnalyticsReportsIndexController extends Controller {
filterComponent: 'filter/string',
},
{
id: 'public-id',
label: 'ID',
valuePath: 'public_id',
cellComponent: 'click-to-copy',
Expand All @@ -69,6 +74,7 @@ export default class AnalyticsReportsIndexController extends Controller {
filterComponent: 'filter/string',
},
{
id: 'row-actions',
label: '',
cellComponent: 'table/cell/dropdown',
ddButtonText: false,
Expand All @@ -81,11 +87,13 @@ export default class AnalyticsReportsIndexController extends Controller {
width: 60,
actions: [
{
id: 'view',
label: 'View report...',
fn: this.reportActions.transition.view,
permission: 'iam view report',
},
{
id: 'edit',
label: 'Edit report...',
fn: this.reportActions.transition.edit,
permission: 'iam update report',
Expand All @@ -94,6 +102,7 @@ export default class AnalyticsReportsIndexController extends Controller {
separator: true,
},
{
id: 'delete',
label: 'Delete report...',
fn: this.reportActions.delete,
permission: 'iam delete report',
Expand Down
10 changes: 8 additions & 2 deletions addon/controllers/connectivity/devices/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
@service trailerActions;

/** query params */
@tracked queryParams = [
@tracked queryParams = this.deviceActions.queryParamsFor([
'name',
'status',
'attachment_state',
Expand All @@ -36,7 +36,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
'public_id',
'created_at',
'updated_at',
];
]);
@tracked page = 1;
@tracked limit;
@tracked sort = '-created_at';
Expand All @@ -56,25 +56,29 @@ export default class ConnectivityDevicesIndexController extends Controller {
/** action buttons */
@tracked actionButtons = [
{
id: 'refresh',
icon: 'refresh',
onClick: this.deviceActions.refresh,
helpText: this.intl.t('common.refresh'),
},
{
id: 'create',
text: this.intl.t('common.new'),
type: 'primary',
icon: 'plus',
onClick: this.deviceActions.transition.create,
permission: 'fleet-ops create device',
},
{
id: 'import',
text: this.intl.t('common.import'),
type: 'magic',
icon: 'upload',
onClick: this.deviceActions.import,
permission: 'fleet-ops import device',
},
{
id: 'export',
text: this.intl.t('common.export'),
icon: 'long-arrow-up',
iconClass: 'rotate-icon-45',
Expand All @@ -87,6 +91,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
/** bulk action buttons */
@tracked bulkActions = [
{
id: 'bulk-delete',
label: 'Delete selected...',
class: 'text-red-500',
fn: this.deviceActions.bulkDelete,
Expand All @@ -111,6 +116,7 @@ export default class ConnectivityDevicesIndexController extends Controller {
separator: true,
},
{
id: 'delete',
label: this.intl.t('common.delete-resource', { resource: this.intl.t('resource.device') }),
fn: this.deviceActions.delete,
permission: 'fleet-ops delete device',
Expand Down
1 change: 1 addition & 0 deletions addon/controllers/connectivity/devices/index/details.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default class ConnectivityDevicesIndexDetailsController extends Controlle
permission: 'fleet-ops update device',
},
{
id: 'more',
icon: 'ellipsis-h',
iconPrefix: 'fas',
renderInPlace: true,
Expand Down
32 changes: 31 additions & 1 deletion addon/controllers/connectivity/events/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,20 @@ export default class ConnectivityEventsIndexController extends Controller {
@service telematicActions;

/** query params */
@tracked queryParams = ['page', 'limit', 'sort', 'query', 'telematic', 'device', 'event_type', 'severity', 'processed', 'occurred_at', 'created_at', 'updated_at'];
@tracked queryParams = this.deviceEventActions.queryParamsFor([
'page',
'limit',
'sort',
'query',
'telematic',
'device',
'event_type',
'severity',
'processed',
'occurred_at',
'created_at',
'updated_at',
]);
@tracked page = 1;
@tracked limit;
@tracked sort = '-created_at';
Expand All @@ -95,6 +108,7 @@ export default class ConnectivityEventsIndexController extends Controller {
/** action buttons */
@tracked actionButtons = [
{
id: 'refresh',
icon: 'refresh',
onClick: this.deviceEventActions.refresh,
helpText: this.intl.t('common.refresh'),
Expand All @@ -107,6 +121,7 @@ export default class ConnectivityEventsIndexController extends Controller {
/** columns */
@tracked columns = [
{
id: 'event-type',
sticky: true,
label: 'Event',
valuePath: 'event_type',
Expand All @@ -121,6 +136,7 @@ export default class ConnectivityEventsIndexController extends Controller {
filterComponent: 'filter/string',
},
{
id: 'device-display-name',
label: 'Device',
valuePath: 'device.displayName',
cellComponent: 'cell/device-identity',
Expand All @@ -137,6 +153,7 @@ export default class ConnectivityEventsIndexController extends Controller {
modelNamePath: 'displayName',
},
{
id: 'provider',
label: 'Provider',
valuePath: 'provider',
cellComponent: 'cell/telematic-identity',
Expand All @@ -152,6 +169,7 @@ export default class ConnectivityEventsIndexController extends Controller {
model: 'telematic',
},
{
id: 'severity',
label: 'Severity',
valuePath: 'severity',
cellComponent: 'table/cell/status',
Expand All @@ -165,12 +183,14 @@ export default class ConnectivityEventsIndexController extends Controller {
filterOptionValue: 'value',
},
{
id: 'message',
label: 'Message',
valuePath: 'message',
resizable: true,
sortable: false,
},
{
id: 'code',
label: 'Code',
valuePath: 'code',
resizable: true,
Expand All @@ -180,27 +200,31 @@ export default class ConnectivityEventsIndexController extends Controller {
filterComponent: 'filter/string',
},
{
id: 'ident',
label: 'IDENT',
valuePath: 'ident',
hidden: true,
resizable: true,
sortable: true,
},
{
id: 'protocol',
label: 'Protocol',
valuePath: 'protocol',
hidden: true,
resizable: true,
sortable: true,
},
{
id: 'state',
label: 'State',
valuePath: 'state',
hidden: true,
resizable: true,
sortable: true,
},
{
id: 'processed-at',
label: 'Processed',
valuePath: 'processedAt',
sortParam: 'processed_at',
Expand All @@ -214,6 +238,7 @@ export default class ConnectivityEventsIndexController extends Controller {
filterOptionValue: 'value',
},
{
id: 'occurred-at',
label: 'Occurred',
valuePath: 'occurredAt',
sortParam: 'occurred_at',
Expand All @@ -224,6 +249,7 @@ export default class ConnectivityEventsIndexController extends Controller {
filterComponent: 'filter/date',
},
{
id: 'created-at',
label: this.intl.t('column.created-at'),
valuePath: 'createdAt',
sortParam: 'created_at',
Expand All @@ -234,6 +260,7 @@ export default class ConnectivityEventsIndexController extends Controller {
filterComponent: 'filter/date',
},
{
id: 'updated-at',
label: this.intl.t('column.updated-at'),
valuePath: 'updatedAt',
sortParam: 'updated_at',
Expand All @@ -245,6 +272,7 @@ export default class ConnectivityEventsIndexController extends Controller {
hidden: true,
},
{
id: 'row-actions',
label: '',
cellComponent: 'table/cell/dropdown',
ddButtonText: false,
Expand All @@ -257,11 +285,13 @@ export default class ConnectivityEventsIndexController extends Controller {
width: 60,
actions: [
{
id: 'view',
label: this.intl.t('common.view-resource', { resource: this.intl.t('resource.device-event') }),
fn: this.deviceEventActions.transition.view,
permission: 'fleet-ops view device-event',
},
{
id: 'mark-processed',
label: 'Mark processed',
fn: this.markProcessed,
permission: 'fleet-ops update device-event',
Expand Down
Loading
Loading