Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
cb17e8e
fix(orders): measure order distance and time by road using the compan…
roncodes Oct 15, 2026
b7dffb4
fix(orders): allow partial payload edits and keep addresses sent with…
roncodes Oct 22, 2026
80cbed1
fix(orders,quotes): idempotent order creation, UTC scheduling, quote …
roncodes Oct 22, 2026
7db38fa
fix(tracking): accept single inner spaces in tracking number lookups
shivanathd Oct 22, 2026
4ee3a82
fix(tracking): keep only letters and digits in the generated number p…
shivanathd Oct 22, 2026
02a6a6a
fix(security): never link a profile to another organization's login a…
roncodes Oct 23, 2026
6845d8f
fix(orders): keep accepting Unix timestamps for scheduled_at, cover t…
roncodes Oct 23, 2026
4161375
fix(security): an account with a live profile elsewhere is not unowned
roncodes Oct 23, 2026
9ca9a78
chore(release): v0.6.75
roncodes Oct 23, 2026
fdaa94a
fix(tracking): generate tracking numbers from letters and digits only
roncodes Oct 23, 2026
bb54a0f
docs(release): v0.6.75 notes cover #416
roncodes Oct 23, 2026
1884225
Merge pull request #411 from fleetbase/fix/order-edit-partial-payload…
roncodes Oct 23, 2026
fe0a691
Merge pull request #414 from fleetbase/fix/profile-account-cross-tena…
roncodes Oct 23, 2026
fe565f6
Merge pull request #413 from shivanathd/mindcat/tracking-number-inner…
roncodes Oct 23, 2026
8832c16
Merge pull request #412 from fleetbase/fix/quote-preview-currency-utc…
roncodes Oct 23, 2026
a4cdc2d
Merge pull request #416 from fleetbase/fix/tracking-number-generation…
roncodes Oct 23, 2026
088e3b5
feat(routing): Valhalla for order distance and tracking; Google only …
roncodes Oct 24, 2026
62282b8
feat(routing): admin provider policy with system defaults and organiz…
roncodes Oct 24, 2026
4710825
Merge remote-tracking branch 'origin/release/v0.6.75' into fix/order-…
roncodes Oct 24, 2026
18fe224
refactor(routing): one routing engine for order distance, ETAs and th…
roncodes Oct 24, 2026
d272ae7
fix(routing): Valhalla is the default engine; OSRM is never a fallbac…
roncodes Oct 24, 2026
14df669
fix(routing): never call an organization routing host on a private ad…
roncodes Oct 24, 2026
e9abc9b
feat(navigator): App Review access, so app store reviewers can sign i…
roncodes Oct 24, 2026
e58cb8c
docs(release): link the Navigator App Review entry to its PR
roncodes Oct 24, 2026
e9535b6
test(navigator): the App Review card turns access on, shows the code …
roncodes Oct 24, 2026
8a134ff
Merge pull request #418 from fleetbase/feat/navigator-review-access
roncodes Oct 25, 2026
479c3ec
Merge pull request #406 from fleetbase/fix/order-road-distance
roncodes Oct 25, 2026
8976af7
Merge pull request #417 from fleetbase/fix/org-routing-host-guard
roncodes Oct 25, 2026
a0bd27a
docs(release): add the routing engine (#406) and private routing host…
roncodes Oct 25, 2026
89948e3
feat(routing): OSRM server in Admin -> Fleet-Ops -> Routing and per o…
roncodes Oct 27, 2026
c1ee6e3
test(navigator): cover the no-driver and blank-identity guards
roncodes Oct 27, 2026
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
29 changes: 24 additions & 5 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
> v0.6.74 ~ "Settings-backed geocoding everywhere"
> v0.6.75 ~ "Road distance and ETAs from one routing engine, tenant-isolated profile accounts and App Review access for Navigator"

---
## Highlights

- **Fix: the console geocoder endpoints return results.** `int/v1/geocoder/query` and `int/v1/geocoder/reverse` returned `[]` for valid addresses whenever the Google Maps API key was set in the console instead of in the environment. This broke address lookup in the coordinates and autocomplete inputs, for example when editing a place. Both endpoints now use the same geocoder as `places/lookup`. (#408, fleetbase/fleetbase#723)
- **Fix: every other geocoding path uses the settings-backed key.** Place creation from an address or coordinates, place search, address-only place creation on the public API, and driver tracking locality all go through the same geocoder. Failed lookups are now logged instead of hidden. (#409)
- **Fix: `config/geocoder.php` passes the region rather than the locale to Google Maps, and uses the default cache store.** (#409)
- **Feature: one routing engine for order distance, ETAs and the map.** An order's stored distance and time, its live ETAs and remaining distance, and the route drawn on the map now all come from the organization's routing engine. Before this, order distance defaulted to a straight-line estimate at a flat ~50 km/h; a reported ~30 km run was stored as 25.0 km / 30 min and is now 32.1 km / 32 min. The engine is chosen in order: the organization's routing engine, then the admin default, then the platform's Valhalla server, then the straight-line estimate. An engine only counts if it is allowed and configured. A failed road lookup falls back to the straight-line estimate and is logged, instead of storing 0. Road results are cached for 24 hours. (#406)
- **Feature: routing provider controls in Admin → Fleet-Ops → Routing.** Administrators choose which providers organizations may use (Google, Valhalla, OSRM, VROOM), with per-organization exceptions. They also set the default routing and optimization engines, and whether server-side routing uses roads or only the straight-line estimate. Saved admin settings take precedence over environment variables. **Google is off until an administrator enables it**, for map routing, optimization, order distance and ETAs. The public OSRM demo server is never called by the server. The admin menu entry "Fleet-Ops Config" is now "Fleet-Ops". (#406)
- **Feature: the OSRM server is a setting, for the platform and per organization.** Administrators set it in Admin → Fleet-Ops → Routing → OSRM Server; `OSRM_HOST` is now only the starting value. Organizations that route or optimize with OSRM can use their own server under Fleet-Ops Settings → Routing → OSRM Server, like the Valhalla and VROOM overrides. An organization host must be a public address (`OSRM_ALLOW_PRIVATE_HOSTS=true` lifts that on self-hosted installs). Order distance, ETAs, tracking and the console's OSRM routes and route control all use the resolved server. The public demo server still only draws map routes.
- **Security: an organization's routing host can't point at a private address.** Order distance, ETAs and VROOM allocation ignore an organization's Valhalla or VROOM override whose host is loopback, a private or link-local network, cloud metadata or another non-public range, and use the system server instead. This also covers values saved before the Valhalla and VROOM extensions started rejecting them. (#417)
- **Security: a profile never links to another organization's login account.** Creating a driver, customer or contact with an email or phone that belongs to another organization's account linked that account and exposed its name, email and phone. The new organization could then reset its password or deactivate it. Only accounts that already belong to the organization, or belong to no organization, are reused now. The refusal does not say whose account it is. `POST /v1/drivers` also ignores a `company` that is not the API credential's own. Logins already shared by the old behaviour can only be managed by their holder. (#414)
- **Fix: orders can be edited with a partial payload.** `PUT /v1/orders/{id}` accepts a `payload` with only the parts that change, such as only entities or only a new dropoff. (#411)
- **Fix: addresses sent with coordinates keep their street.** A place given as a street address plus `latitude`/`longitude` was reverse-geocoded and stored as a different nearby address. The street and the coordinates are now kept as sent, without a geocoding call. (#411)
- **Feature: idempotent order creation.** `POST /v1/orders` accepts an `Idempotency-Key` header. A retry with the same key within 24 hours returns the order already created, with `Idempotent-Replayed: true`, and a concurrent duplicate gets `409`. (#412)
- **Fix: `scheduled_at` keeps its UTC offset.** `2026-10-21T11:00:00-07:00` is now stored as 18:00 UTC instead of 11:00, and `PUT /v1/orders/{id}/schedule` stores the UTC instant of a date and time in the company timezone. Rows written before this keep their old values. (#412)
- **Fix: service quotes.** The `currency` filter works again, stored quotes expire after 48 hours by default (`fleetops.service_quotes.ttl_minutes`), and `preview=1` prices a request without storing quote rows. (#412)
- **Fix: tracking numbers with a space can be looked up again.** Since v0.6.72, a number such as `AL 7796486490AE`, generated for a company whose name has a space in its first three letters, got "not found". Single inner spaces are accepted now. New numbers only use letters and digits: the company prefix skips spaces and punctuation ("Al Noor" gives `ALN`), and a region such as "New York" becomes `NEWYORK`. (#413, #416)
- **Feature: App Review access for Navigator.** Apple and Google reviewers can't receive the texted sign-in code. An organization can turn on **Settings → Navigator App → App Review**: reviewers sign in to its Navigator with the test number +1 202 555 0144 (a range kept for fiction, never texted) and a six-digit code the owner creates and enters in App Store Connect and Google Play Console. The code is stored hashed, shown once, and only works for that organization. The first sign-in adds an **App Reviewer** driver, which ad-hoc offers, nearest-driver assignment and the orchestrator always leave out. Built on core-api's App Review access; the `NAVIGATOR_*` review env settings still work on self-hosted installs. See `docs/NAVIGATOR_APP_REVIEW.md`. (#418)

---
## Upgrading
- No migrations or commands.
- **Requires fleetbase/core-api v1.6.74** (`composer.json` now requires `^1.6.74`), which adds App Review access and its `review_accesses` table: run `php artisan migrate`.
- **Routing defaults changed (#406).** Review **Admin → Fleet-Ops → Routing** after upgrading:
- The default routing engine is now **Valhalla** (it was OSRM). Without a Valhalla server (`VALHALLA_HOST`, or the Valhalla extension's settings), order distance and ETAs use the straight-line estimate. Self-hosters running their own OSRM can make it the default in the panel or with `ROUTING_DISPLAY_ENGINE=osrm`.
- **Google is off.** Turn it on in the panel or set `ROUTING_GOOGLE_ENABLED=true`. `DISTANCE_MATRIX_PROVIDER=google` also enables it until the panel is saved.
- Tracking follows the routing engine. `TRACKING_PROVIDER` and `TRACKING_PROVIDER_FALLBACKS` are no longer read, and saved tracking provider and fallback choices are ignored.
- `DISTANCE_MATRIX_PROVIDER` only seeds the admin defaults now; it no longer overrides order distance.
- New starting values, read until the panel is first saved: `ROUTING_GOOGLE_ENABLED`, `ROUTING_DISPLAY_ENGINE`, `ROUTING_OPTIMIZATION_ENGINE`, `ROUTING_SERVER_ROUTING`, plus `VALHALLA_HOST`, `VALHALLA_API_KEY`, `VALHALLA_COSTING` and `VALHALLA_TIMEOUT`.
- Existing orders: started, unfinished orders from the last 2 days are re-measured by `fleetops:update-estimations`; unstarted orders pick up road distance at their next ready or edit; completed and cancelled orders keep their stored values. No migration.
- **OSRM server.** Nothing to do: `OSRM_HOST` keeps working until a host is saved in Admin → Fleet-Ops → Routing. Organization OSRM servers are new and empty.
- **Private routing hosts (#417).** On self-hosted installs, an organization override pointing at a private host (for example `http://valhalla:8002`) now falls back to the system server. Set `VALHALLA_ALLOW_PRIVATE_HOSTS=true` or `VROOM_ALLOW_PRIVATE_HOSTS=true` to allow it. System-level private hosts are unaffected.
- **Fleetbase Cloud.** After deploying, set the Valhalla host to `https://valhalla.routing.fleetbase.io` with Cloud's gateway key, Google and OSRM off, and Valhalla as the default engine with road routing. Enter the gateway key only after fleetbase/valhalla v0.0.5 and fleetbase/vroom v0.0.5 ship (valhalla#9, vroom#8), because the extensions' settings endpoints return the key to any signed-in user until then.

---
## Need help?
Expand Down
134 changes: 111 additions & 23 deletions addon/components/admin/routing-settings.hbs
Original file line number Diff line number Diff line change
@@ -1,36 +1,123 @@
<div class="space-y-6">
<InfoBlock @type="info" @icon="circle-info" class="mb-4">
Configure system-wide routing engine defaults here. Organizations can optionally override these values from their own routing settings.
Configure system-wide routing engine defaults here. Organizations can optionally override these values from their own routing settings, but only with providers that are switched on
below.
</InfoBlock>

<ContentPanel @title="Tracking Defaults" @open={{true}} @wrapperClass="bordered-classic">
<InputGroup @name="Default Tracking Provider" @helpText="Default provider for organization-level tracking intelligence unless an organization overrides it.">
<ContentPanel @title="Provider Access" @open={{true}} @wrapperClass="bordered-classic">
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
Choose which providers organizations may use for map routing, route optimization, order distance and tracking. A provider switched off here is never called, even if an
organization selected it; they fall back to the defaults below. Changes apply on every server within 30 seconds.
</p>
<div class="space-y-3">
{{#each this.providerAccessRows as |provider|}}
<div class="flex items-center justify-between rounded-md border border-gray-200 px-3 py-2 dark:border-gray-700" data-test-provider-access={{provider.key}}>
<div>
<div class="text-sm font-semibold">{{provider.label}}</div>
{{#if provider.billed}}
<div class="text-xs text-red-600 dark:text-red-400">Billed per request to the platform's Google Maps key.</div>
{{/if}}
</div>
<Toggle @isToggled={{provider.enabled}} @onToggle={{fn this.toggleProvider provider.key}} />
</div>
{{/each}}
</div>
</ContentPanel>

<ContentPanel @title="Routing Defaults" @open={{true}} @wrapperClass="bordered-classic">
<InputGroup
@name="Default Routing Engine"
@helpText="Used for everything route, distance and time for organizations that have not chosen their own: map routes, each order's distance and time, and live ETAs while it is in progress."
>
<Select
class="w-full"
@value={{this.defaultDisplayEngine}}
@options={{this.displayEngineOptions}}
@optionLabel="name"
@optionValue="key"
@placeholder="Select default routing engine"
@onSelect={{fn (mut this.defaultDisplayEngine)}}
/>
</InputGroup>
<InputGroup @name="Default Route Optimization Engine" @helpText="Decides the order stops are visited in, for organizations that have not chosen their own.">
<Select
class="w-full"
@value={{this.defaultOptimizationEngine}}
@options={{this.optimizationEngineOptions}}
@optionLabel="name"
@optionValue="key"
@placeholder="Select default route optimization engine"
@onSelect={{fn (mut this.defaultOptimizationEngine)}}
/>
</InputGroup>
<InputGroup
@name="Server-side Routing"
@helpText="Straight-line stops every routing call for order distance, time and live ETAs at once, for every organization. Map routes are not affected."
>
<Select
class="w-full"
@value={{this.trackingProvider}}
@options={{this.trackingProviderOptions}}
@value={{this.serverRouting}}
@options={{this.serverRoutingOptions}}
@optionLabel="label"
@optionValue="value"
@placeholder="Select default tracking provider"
@onSelect={{fn (mut this.trackingProvider)}}
@onSelect={{fn (mut this.serverRouting)}}
data-test-server-routing
/>
</InputGroup>
<InputGroup @name="Default Fallback Providers" @helpText="Providers used when the selected tracking provider is unavailable.">
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
<PowerSelectMultiple
@searchEnabled={{true}}
@searchField="label"
@options={{this.trackingProviderOptions}}
@selected={{this.selectedTrackingFallbackOptions}}
@onChange={{this.setTrackingFallbacks}}
@placeholder="Select default fallback providers"
@triggerClass="form-select form-input form-input-sm flex-1"
as |provider|
>
{{provider.label}}
</PowerSelectMultiple>
</div>
</ContentPanel>

<ContentPanel @title="OSRM Server" @open={{true}} @wrapperClass="bordered-classic">
<InputGroup @name="OSRM Host" @helpText={{this.osrmHelpText}}>
<Input @value={{this.osrmHost}} placeholder="https://osrm.example.com" class="w-full form-input" data-test-osrm-host />
</InputGroup>
</ContentPanel>

<ContentPanel @title="Organization Exceptions" @open={{true}} @wrapperClass="bordered-classic">
<p class="mb-4 text-sm text-gray-600 dark:text-gray-400">
Switch a provider on for specific organizations only, for example Google for a customer who pays for it.
</p>
<div class="space-y-3">
{{#each this.providerExceptionRows as |row|}}
<div class="flex items-center gap-3 rounded-md border border-gray-200 px-3 py-2 dark:border-gray-700" data-test-provider-exception={{row.exception.company_uuid}}>
<div class="w-1/3 truncate text-sm font-semibold">{{row.exception.company_name}}</div>
<div class="fleetbase-model-select fleetbase-power-select ember-model-select flex-1">
<PowerSelectMultiple
@options={{this.exceptionProviderOptions}}
@selected={{row.selected}}
@onChange={{fn this.setExceptionProviders row.exception}}
@placeholder="Select providers"
@triggerClass="form-select form-input form-input-sm flex-1"
as |provider|
>
{{provider.label}}
</PowerSelectMultiple>
</div>
<Button @type="danger" @size="xs" @icon="trash" @onClick={{fn this.removeProviderException row.exception}} />
</div>
{{else}}
<div class="text-sm text-gray-500">No exceptions. Every organization follows Provider Access.</div>
{{/each}}
</div>
<InputGroup @name="Add Organization" class="mt-4">
<ModelSelect
@modelName="company"
@query={{hash view="admin"}}
@placeholder="Search organizations"
@triggerClass="form-select form-input"
@infiniteScroll={{false}}
@renderInPlace={{true}}
@onChange={{this.addProviderException}}
as |company|
>
{{company.name}}
</ModelSelect>
</InputGroup>
</ContentPanel>

<ContentPanel @title="Tracking Behaviour" @open={{true}} @wrapperClass="bordered-classic">
<p class="mb-2 text-sm text-gray-600 dark:text-gray-400">
Live ETAs, remaining distance and route lines come from each organization's routing engine. These settings only tune their behaviour.
</p>
<div class="flex justify-end">
<Button
@type="default"
Expand Down Expand Up @@ -68,6 +155,7 @@
<RegistryComponent />
</div>
</RegistryYield>
<Spacer @height="200px" />
</div>

<EmberWormhole @to="next-view-section-subheader-actions">
Expand All @@ -80,4 +168,4 @@
@isLoading={{or this.saveSettings.isRunning this.loadSettings.isRunning}}
@disabled={{this.saveSettings.isRunning}}
/>
</EmberWormhole>
</EmberWormhole>
Loading
Loading