Added
- Fifth wheel and travel trailer cards show the tow vehicle and average propane use per month, in the slots where motorized cards show the odometer and fuel economy
- Financing tracking for lease and loan payments and upfront fees, with a Financing tab and cost analytics
- Admins can allow an SSO account to relink after its identity-provider account was re-created, from the member card; tools/oidc_allow_relink.py does it when the only admin is locked out
- MYGARAGE_TRUSTED_PROXIES and MYGARAGE_CLIENT_IP_HEADER: behind a reverse proxy, rate limits and audit logs see the real client instead of the proxy
- The Backup tab shows a full restore waiting for the restart, and can cancel it
- Pending reminders are listed by how soon they're due, and each shows whether it's overdue or due soon, with a progress bar and what's left (#192)
- Supplies: search, filters, sort, grouping, a list view, out of stock highlights, and log purchase and adjustment from the card (#191)
- Supplies: category suggestions in the supply form (#191)
- Supplies: pick a volume unit per supply (mL, fl oz, qt, gal, in US or UK flavour); changing it affects display only (#191)
- Toll tags: pick a country, then its toll system (United States, Malaysia and now Italy); Other saves the name you type
Changed
- Vehicle cards and the vehicle hero flag reminders due within 30 days, by date or by projected mileage and hours, instead of every pending reminder; the fleet strip's count is the sum of those badges
- The notification bell warns about mileage and hours reminders projected to come due within two weeks, not only dated ones
- PostgreSQL money columns widen to hold the new maximums; the migration is forward-only and rewrites no data
- A full backup restore finishes when MyGarage restarts: it checks and stages the backup, and the next start saves a safety backup of the data it replaces, then swaps the backup in before opening the database, so an older backup is migrated on that start
- A mileage or hours reminder with no recent driving rate to project from counts as due soon once it's 90% of the way to its target (#192)
- Note: volumes UK users logged as quarts were stored as US quarts and stay as stored; a picked unit applies from now on (#191)
- A new toll tag no longer starts on EZ TAG; the country is preselected from your currency or language when only one fits
Deprecated
- The spending-anomaly message field in the analytics API is deprecated; the app builds its own sentence from amount, baseline and deviation_percent
Fixed
- A supply's average unit cost is per quart for imperial users; it showed the price of a litre next to quarts, and the label now names the unit (#191)
- Overdue reminders always surface on the calendar as "Overdue" on today, whether tripped by date, mileage, or hours; before, a mileage-overdue reminder sat months out as on-track or vanished outside the fetched window (#195)
- Calendar reminders not yet due sit at the earlier of their hard date and usage projection, matching the reminders list; a snoozed reminder already due moves to the day the snooze ends instead of disappearing
- The notification bell names the mileage or hours target that tripped a reminder, with the current reading, instead of only a date; the unread badge no longer swallows clicks meant for the bell (#195)
- Theme and accent apply before first paint in production: the CSP now allows the shell's inline script by hash instead of refusing it
- The smallest font subset was inlined as a data: URL and refused by the CSP; fonts are always emitted as files
- The dashboard sort trigger read "Sort: Sort by Name"; the order is now "Name" in every language, and the French trigger is translated
- Recall "Resolved" and document "Uploaded" dates rendered as "Invalid Date"
- Garage analytics 3- and 6-month average lines skip months with only insurance or financing instead of counting them as zero
- CSV, JSON and third-party imports refuse the negative, out-of-range and NaN values the app itself refuses, instead of storing them
- Clearing a service visit's vendor, odometer, engine hours, notes, claim number or fees on edit now saves instead of keeping the old value
- Editing a toll transaction's date now saves; the new date was silently dropped
- Editing a fill-up with an HH:MM trip duration no longer fails; only new fill-ups accepted that format
- A warranty with no end date no longer gets today's date when opened and saved, and its end date and mileage limit can be cleared
- Setting a toll transaction back to "None (manual payment)" now unlinks its toll tag
- Clearing a field when editing a fill-up, DEF or propane record now saves instead of keeping the old value; a fill-up still needs a reading and a fuel amount
- Editing a reminder clears its notes when emptied and drops the date, mileage or hours target its new type doesn't use; a smart reminder switched from mileage to hours no longer fails
- Clearing a field on edit now saves for spot rentals, address book entries, DTC notes, user names and relationships, documents, notes, photo captions, LiveLink device labels and parameter display settings
- A null for a required field through the API is a validation error instead of a server error or a silent skip
- Opening and saving a spot rental keeps its total, and the nightly suggestion covers every night of the stay
- Opening and saving a spot rental billing entry keeps a hand-adjusted total
- A toll transaction's amount can no longer be emptied on edit
- The window sticker review saves only what you change, no longer crashes when the OCR missed a price, edits every field it shows and shows fuel economy in your units
- Clearing Exterior color in the vehicle details clears the colour the overview shows
- Saving vehicle settings no longer restamps the latest engine-hours reading with today's date or turns off hours tracking when stats fail to load
- Tax renewal dates, tax types and recall announce dates can be left empty or cleared
- Editing an address book entry keeps its custom category and where it came from
- Overwriting a reminder pack keeps its description and vehicle types
- The timezone can stay on the server default, and saving system settings no longer rewrites every setting
- Move Up and Move Down reorder the family dashboard
- A blank OIDC scope or claim setting uses the default instead of blocking sign-in, and the full-name claim setting takes effect
- A tire set can't be renamed to blank, a toll tag with an unlisted system can be re-saved, and a document needs a title
- Server validation errors show on the field they belong to instead of a generic banner
- Large amounts in forint, yen and other high-denomination currencies are accepted
- Currencies without cents, like yen, no longer show ".00" on amounts
- The toll CSV export, the insurance coverage CSV column and PDF charts no longer show "$" for other currencies
- A number too large to store gets a clear error instead of a server error
- Bulk archive keeps vehicles on the dashboard by default every time, not only the first time it opens
- An SSO sign-in can no longer take over an account linked to a different sign-in
- The member card shows which users sign in with SSO and drops the password reset that always failed for them; admins can still disable and delete them
- The last active admin can't be disabled, demoted or deleted
- Edit User names the SSO provider and locks an SSO user's name
- SSO account linking writes its audit row in the same transaction as the link, and an overlong User-Agent no longer fails audit writes on PostgreSQL
- A refused or failed SSO sign-in returns to the login page with a reason instead of a page of raw JSON
- The SSO test connection explains an issuer blocked as a private address and names MYGARAGE_TRUSTED_HOSTS, and asks for a full URL when the issuer isn't one, instead of failing with a server error
- A record holding a number outside today's input range (a LiveLink state of charge over 100%, a negative odometer, a model year before 1900) no longer breaks the page that lists it
- SSO users whose identity provider username has a dot or another character a local username can't have no longer get a server error after signing in
- An imported fill-up with an unknown price basis, charge level or charge location, or a webhook fill-up with an unknown price basis or fuel type, is refused instead of stored, and one already stored no longer breaks the fuel list
- A record holding text longer or shorter than today's input rules, or an email at a local domain, no longer breaks the page that lists it
- Renewing an insurance policy with an empty field label, or completing a reminder with an empty or over-long title, no longer fails with a server error
- A currency setting the app doesn't support falls back to US dollars instead of crashing the fuel, DEF, propane and analytics cost cards
- The window sticker review shows option prices in your currency and number format, and its MSRP inputs name the currency
- A window sticker whose OCR text is longer than a field holds now saves, with the text cut to fit, instead of failing or breaking the vehicle page
- The window sticker review always shows the fuel economy and environmental rating inputs, so you can add what the OCR missed
- A form's field error now clears as soon as you edit that field, in the reminder completion, archive, add-to-policy, pricing, window sticker review and service visit forms
- Family dashboard members with the same position appear in the same order on the dashboard and in the family management dialog
- Adding or editing a POI provider with a missing or wrong-typed value, or saving a setting with a key longer than 50 characters, is a validation error instead of a server error or a silently stored value
- Adding, changing or removing a POI search provider in Settings now saves
- An address book email over 100 characters is refused instead of failing with a server error on PostgreSQL
- A full backup restore writes the database file MyGarage actually uses; with a file not named mygarage.db it left the live database alone and deleted its write-ahead log
- A PostgreSQL database URL with "sqlite" in its password or database name is no longer mistaken for SQLite
- A full backup restore no longer rewrites the database while MyGarage has it open, which could corrupt the restored data; a backup whose database is damaged or missing is refused
- A full or safety backup of a database whose path contains "#" or "?" archived an empty database instead of the real one
- A full backup restore leaves out, and logs, a photo, document or attachment that was a link to a folder or to a file outside the backup, instead of failing
- A gas station added on the Address Book page can be picked on a fill-up, and isn't copied into the vendor list (#194)
- A station added from a fill-up shows under Gas Station in the address book (#194)
- A place saved from the POI Finder shows under its Address Book category, and places it saved before show under theirs
- A gas station moved to another category in the address book stops being offered on fill-ups and can be picked as a vendor (#194)
- Picking a saved place in the fill-up station or spot rental location field no longer reopens the list (#194)
- Following the tow vehicle link on a trailer's Overview no longer also opens the tow editor (#179)
- Text on the tire cards can be selected and copied; tapping the card still opens its history (#179)
- Text on the address book cards can be selected and copied (#179)
- Selecting text in a calendar event, a family member header, a service visit row or the LiveLink widget no longer opens, toggles or navigates; calendar events open from the keyboard (#179)
- The family member card's action buttons work from the keyboard; Enter on one toggled the card instead
- Clicking a reminder in the calendar's month grid opens it, as in the upcoming list
- Calendar bulk selection works from the keyboard
- On the window sticker test page, Remove clears the chosen file instead of opening the file picker
- A vehicle, tire, service line, LiveLink device, topic map or reminder anchor holding a value the app doesn't recognise (for example from a restored backup) loads (the value shows as unknown or unset) instead of failing the page
- Saving over a reminder pack that names a vehicle type the app doesn't recognise drops that type and saves, instead of failing
- The vehicle page's type chip shows the translated type name (Fifth Wheel) instead of the raw value (FifthWheel)
- A LiveLink device that reports a blank firmware version no longer gets an update notice for every release
- Clearing a LiveLink sensor's name falls back to its device id everywhere, including the delete prompt
- A recall campaign number longer than 20 characters is refused instead of failing to save on PostgreSQL
- A LiveLink sensor name longer than 100 characters is refused instead of failing to save on PostgreSQL
- An NHTSA API URL setting saved with spaces around it is used without them, instead of breaking recall and TSB checks
- Recall checks work again where the stored NHTSA recalls URL is the full endpoint (an install from before v2.19.0, or the Integrations tab saved with the field blank); it's read as its base
- The vehicle page's header counts, reminders, and odometer and hours readings now refresh after a new reading, fill-up, service visit, tire change, vehicle import or a reminder completed on the calendar, instead of waiting for a reload
- A toll system name of only spaces is refused, and editing a tag normalizes known spellings like creating one does
Security
- An SSO sign-in whose email matches an existing account asks for that account's password instead of linking it automatically
- With MYGARAGE_TRUSTED_PROXIES set, X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host only count when they come from a trusted proxy
- The SSO sign-in start and callback are rate limited per client like password login, and a limited attempt returns to the login page with a reason