Repository navigation
This release adds Registry grouping and generated PO line numbers, unifies
numeric input and line totals, and improves maintenance, CSV round trips and
procurement evidence handling. It is the first release under the Semantic
Versioning policy described in versioning policy.
Five migrations run automatically on startup. Before upgrading, keep a full
backup of the database, documents and configuration. Review the
upgrade guide for historical PO
line reconciliation, maintenance price corrections, custom upload extension
lists and integration changes.
Added
-
The sidebar identifies unreleased builds as previews with their branch and
commit. Release-tag builds, and builds without Git information such as Docker
images, show the version number. -
Documents can be
.liclicense files and.msgOutlook messages. Existing
native installations using the previous default extension list gain them
automatically on upgrade. Installations with a customized
ALLOWED_UPLOAD_EXTENSIONSvalue need to add.lic,.msgto it. -
My Settings → Appearance has a per-user option to show or hide the Portfolio
overview in the sidebar (shown by default). -
Help and the user guide explain how to track SSL/TLS certificates, domain
names and other renewable items with the Other type, Type Description and
Renewable?. -
Hovering or focusing the Expiring or Expired stage on the Licenses page shows
a breakdown, for example "12 expiring · 5 renewal in progress · 1 retiring ·
6 not started". A license with a renewal in progress that is also scheduled
for retirement counts as renewal in progress. The stats API gained
expiring_breakdownandexpired_breakdown. -
A small ⓘ next to License Type, in every form that has one, explains the
difference between Maintenance and Service. Tooltips open on hover and on
keyboard focus. -
PO line numbers: every license and pending-order line with a PO number gets
a generated, read-only PO line number, unique per PO (PO numbers are
compared ignoring case and spacing) and never reused. The number appears in
the API (poLineNumber), as a new last PO Line column in the license CSV
export, and in the pending-order export. In the app it is shown as
PO # · LINE nin License Details, as a Line column on pending orders and in
the conversion screens, and as an optional PO Line column in the Registry
(hidden by default, sortable and filterable). CSV imports of new records keep the file's line
numbers when they are free; the preview shows any number that has to
change. Existing records are numbered once during the upgrade, in creation
order per PO; these historical numbers may not match POs issued before
LicenseTrack, so reconcile them before relying on PO number + line for
matching with other systems. -
Before a new version changes the database schema, LicenseTrack saves a copy of
the database inpre-upgrade/next to the database file (newest three kept).
Restarts after a failed upgrade reuse the first copy instead of replacing it. -
Group the Registry by a column, with an optional second level (for example
PO # then Publisher). Drag a column header onto the grouping strip or pick
one from the list. Groups start collapsed and show their line count, line
total per currency and earliest end date; purchase-order groups also show the
Total PO Value and flag a manual PO total that differs from its lines. Saved
views keep the grouping, and CSV export stays flat in grouped order.
Changed
-
A separately tracked maintenance line added next to a license is now
pre-filled with "product maintenance" instead of "product
maintenance/support". Existing records keep their description. -
Forms show fewer permanent hint lines: background explanations (for example
Supplier Contact, the manual PO total and several settings) moved into ⓘ
tooltips that open on hover or keyboard focus. Hints that prevent a mistake
stay visible. -
The app uses Maintenance consistently where it said "Support" or
"Maintenance / Support" (screens, alerts, emails, error messages, Help). For
example, "Support due" is now "Maintenance due" and "Start support renewal"
is now "Start maintenance renewal". Standalone support contracts, such as
premium or third-party support, are recorded as a Service. API fields and
routes, alert type keys and CSV headers are unchanged; older CSV headers are
still accepted on import. -
Help no longer lists the session timeout under My Settings: the session
timeout and minimum password length are server-wide settings. -
Opening a document preview in the Sourcing and Pending Orders document dialogs
collapses the document list and shows a much larger preview; the Add License
document list also collapses while a preview is open. The contract dialog
opens with Linked licenses collapsed (with a count) and its document folders
expanded. -
Internal: updated
brace-expansion(a dependency of the lint tooling) to
5.0.12 to clear a published advisory. It is not part of the app bundle. -
Added end-to-end tests that run the real frontend against a real backend for
the core write paths. -
The pending-order export's
PO Line #column now contains the generated PO
line number instead of the row's position. -
Pull requests now include a single-owner audit: every rule a change touches
names its one owner in code, and literal duplicates are caught by guard tests. -
Added a maintainer invariants document listing the rules LicenseTrack
guarantees on every entry point, and a pull request template. The architecture
map identifies the shared calculation, linking, grouping and upload owners.
Shared test cases guard currency-separated totals in reports, Registry groups
and overview totals, including manual PO totals. -
Internal: CSV import and export read their field list from one registry (no
behaviour change). License updates, field patches and import updates share
the notice-date reminder reset rule. Request schemas share the canonical money
validation helper while preserving their field lists, validation timing and
error messages. CSV import also uses the shared included-maintenance type set.
Renewal actions refresh license statistics once through the shared
invalidation group. Conversion defaults reuse the shared custom-field value
map. Removed an unused session-cookie helper and the unused APScheduler
runtime dependency. -
The demo mirrors canonical-number validation and notice handling, including
clearing handling state when the notice date changes. -
Budget owners now receive Maintenance Ending and Maintenance Expired alerts
for the included maintenance on their perpetual, OEM and freeware licenses,
alongside license expiry alerts. These rows are marked "Maintenance" and show
the maintenance dates. -
Updated the frontend test tooling (Vitest 5, jsdom 30.1) and removed automatic
test retries. Form workflow tests use atomic input changes and wait for
asynchronous submission; report workflow tests load their sections before
testing interactions. -
API requests with fields an endpoint doesn't define are logged as a warning,
naming the endpoint and the fields. SetSTRICT_REQUEST_FIELDS=trueto reject
them with a 422 instead; this may become the default in a later release, so
integrations should send only documented fields. -
Line Total is always quantity × unit price. The separate Line Total input is
gone from license, invoice and conversion forms, which show the calculated
value instead, and the Registry has one Line Total column (formerly
Calc. Total). For an invoice line that doesn't divide evenly, enter a precise
or net unit price; for a negotiated PO total, use the manual PO total. In CSV
imports a line total fills a missing unit price or is checked against it; a
Total PO Price column is no longer imported. -
The Registry's Total PO Value shows a lock icon when it comes from a manual PO
total.
Deprecated
- The
totalPoPricelicense API field. It is still accepted and returned but no
longer used; it will be removed in 1.3.0.
Fixed
-
CSV import warns when a nonblank external reference matches an existing
active license or an earlier row in the file, including records without PO,
contract or dates. Confirm the warning to deliberately reuse a reference. -
Converting merged co-term renewal lines requires an explicit cost-centre
choice when their predecessor licenses had different cost centres, matching
the budget-owner rule. The API enforces the same choice. -
CSV imports require warning acknowledgement when a new row's currency is
blank or missing. The preview identifies the affected rows and the currency
used. Update rows without a currency keep the record's existing currency. -
The Registry's column titles stay on top while the table scrolls. Only the
select-all box stayed; the titles scrolled away with the rows. -
A price cleared on a renewal line stayed cleared only until conversion: Convert
to License refilled the previous term's unit price, maintenance unit price or
maintenance cost. Prices now come from the renewal line only; starting a
renewal still copies the previous price there as a reference. -
Document file pickers offer exactly the file types the server accepts. They
used their own lists: license and procurement pickers left out Excel, Word
and CSV files, and the contract picker offered.doc, which the server
refused. -
Linking terms within one request follows one rule in both Set predecessors
and Set next term: a line can't roll into an earlier-starting term, a link
that would close a loop isn't offered, and lines that can't be linked are
shown with the reason instead of being hidden or failing on save. -
The Documents section opens expanded in every add, edit and conversion
dialog. It started collapsed in Add License, Convert All and the single
purchase-order conversion. -
Convert All kept the PO line's estimated total as the license Line Total even
when real unit prices were entered at conversion. -
Subscriptions and SaaS with included maintenance could take their maintenance
cost from an outdated stored total instead of quantity × unit price. -
The Add Maintenance dialog stored the coverage cost as the unit price, so
totals and reports multiplied it by the covered quantity. New records are
correct, and existing records created this way are corrected on upgrade. -
Unit prices are shown with every stored decimal (at least two), so 0.1234 no
longer displays as 0.12; totals keep two decimals. The localized CSV export
keeps unit price decimals too. -
Reading or scrolling inside a focused document preview now counts as activity,
so a long read no longer ends the session. -
Database restores run in the background of the server process, so the app
and/api/healthkeep responding (with statusmaintenance) during a long
restore. Pre-restore safety copies are now kept in apre-restorefolder
next to the database (newest three), and copies left next to the database by
older versions are moved there on the next restore. -
Application log messages (such as warnings about unknown request fields) are
no longer silenced after the database upgrade that runs at startup, and keep
following theLOG_LEVELsetting. -
Maintenance linking:
- A license can be changed into Maintenance from the edit form by choosing
the license it covers in the same step. - Choosing "Separately tracked" coverage, in the edit form or with Edit
coverage, offers a quick link to an existing maintenance record. - Maintenance records that already cover another license stay visible in
link searches, marked "Currently covers", and ask before covering one more. - The Renewal Workbench's "Record existing support" now lists existing
records to link. - All maintenance link dialogs search the same fields (including PO number,
contract, dates and the covered license), and show how many retired
records are hidden with an option to show and select them in Link existing,
Edit coverage and the full edit form.
- A license can be changed into Maintenance from the edit form by choosing
-
CSV export → import now preserves manual PO totals,
maintenance pricing (per-unit and free), and which licenses a maintenance
record covers (several allowed, separated by ";"). An exported lifecycle
status is honoured: Legacy stays Legacy, and an active record that expired
recently is no longer turned into Legacy. Update imports can correct an
included maintenance period. The API export gained request and purchase
dates, portal URL, maintenance fields and manual PO total, and its Line Total
column is quantity × unit price. -
One "same PO" rule: shared documents now match licenses whose PO numbers
differ only in case or spacing (listing, downloads, counts and completeness).
Email Supplier's "all matching licenses" includes only lines of the same
purchase and supplier. Document scope labels and delete warnings describe
the actual scope. -
Maintenance coverage has one owner: a license's active maintenance record is
always worked out from its linked records by date, on every path (linking,
CSV import, renewals, the daily hand-over). Linking an older or future record
no longer replaces coverage that is still running; an overlapping new term
takes over when the current one ends. Undoing a link to a renewal that hasn't
started yet now works. Disabling maintenance also removes planned terms.
Changing between perpetual, OEM and freeware keeps included maintenance, and
changing to another type keeps the old included period in history. -
"Renewal in progress" means the same for licenses and maintenance: maintenance
alerts mention it, licenses scheduled for retirement no longer appear in the
maintenance renewal list, and a license with a renewal in progress can't be
retired until the renewal is cancelled. Legacy licenses can't start a
renewal, and a renewed status can't be cleared through a normal edit.
Co-term merges require one license type and keep every covered license
linked to the new maintenance term. -
Numbers with three decimals are no longer multiplied by 1,000 under
comma-decimal number formats (e.g. nl-BE, de-DE). This affected Registry
inline edits, new pending-order lines, and conversion defaults. Stored
values are never re-interpreted, and price inputs keep every decimal
instead of rounding to two. Merging sourcing lines under these formats no
longer turns a combined quantity such as 1500 into 1.5. -
Typed amounts with thousands separators are read correctly, for example
2,443.00under the1,234.50number format (#82). Input that doesn't match
your number format is refused with a message next to the field instead of
being changed or cleared: under1.234,50,2,443.00is refused rather than
saved as 2.443. Every screen that takes a price, quantity, total or cost now
uses one number input with one set of rules, shared with the server, and the
API rejects numbers that aren't plain decimals (such as1,5). -
Under comma-decimal number formats, a number with a single dot and exactly
three digits after it (e.g.1.234) is refused as ambiguous, in number
filters and CSV import too; type1234for a whole number or1,234for a
decimal. -
Export Current View (localized) writes numbers with the same separators
the importer reads, keeping every quantity decimal, so the file imports back
with the same number format. Before, a quantity of 1000 under1.234,50was
written as1.000, and Swiss formats used a typographic apostrophe. -
CSV import reads an
Itemcolumn as the PO line number. Purchasing exports
(Flexera among them) useItemfor the line andDescriptionfor the
product. Earlier versions readItemas the software description; a file
that usesItemfor the product text needs that column mapped to Software
Description (or renamed toDescription) before import. -
The database models and migrations now describe the same schema, including
the unique OIDC identity index. A test fails the build if they drift apart
again. Document categories fit their column on every database. -
An active user is no longer logged out when their computer's clock runs
behind the server's: session responses now include the seconds remaining,
and the browser measures them on its own clock. -
Procurement conversion:
- Sourcing lines can no longer be added to a pending order that was
converted or cancelled in the meantime. - Conversion can no longer change a line's currency while the order has a
manual PO total; clear the total first. - A SaaS portal URL saved on a sourcing or PO line is kept at conversion.
- An invoice uploaded with a conversion is stored together with the new
licenses: if it can't be stored, nothing is converted and you can retry. - Older conversions whose invoice was lost now say to upload the invoice on
the pending order and retry, instead of retrying automatically. - Evidence transfers can no longer run twice at the same time.
- Sourcing lines can no longer be added to a pending order that was
Security
- Hardened request validation for signed-in browser sessions, including
sign-out, outbound URL checks for webhooks and SSO discovery, and API tokens
of accounts that must
change their password. Scripts that write through a browser session cookie
(rather than an API token) must now send theX-LicenseTrack-Request: 1
header, including when signing out; API-token and bearer requests are
unaffected. - SSO sign-in rate limiting now counts only failed sign-ins and bounds
in-memory attempt tracking. Successful sign-ins release their tracking
entries. The deployment guide documentsFORWARDED_ALLOW_IPSfor
reverse-proxy setups, and Docker
Compose passes it through.