Release Notes
Every user-visible change to NomaUBL — UI, REST API, CLI, behaviour — is consigned here. The most recent release sits at the top. This page mirrors the About this release card and the dedicated Release Notes screen surfaced inside the application.
2026.05.4 — 2026-05-07
Dashboard rebuilt as a 12-column widget grid + Integration Errors page upgraded into a proper failure-analysis tool, with rule descriptions extracted from the bundled Schematron files so users no longer have to decode a rule code by hand. Light mode is now the default. Two Oracle-dialect bugs that silently emptied dashboard panels on customer installs are fixed.
Dashboard
- Twelve-column widget grid replaces the previous stacked layout. Hero row (Total / In flight / Rejected — IT / Rejected — Business) drives off the existing per-status counts; below it, a Pipeline funnel, a Volume chart, paired Recent activity / Stale + Top failing rules, Per-company / E-Reporting coverage, and Round-trip / Scheduler health rows.
- Recent activity / Top failing rules row split 6/6 down the middle to match the rows below — the previous 8/4 split made the Recent activity card visibly wider than its right-hand neighbour.
- Top failing rules widget gets an
ALL / UBL / INTEGcategory toggle in its header and replaces the proportional bars with equal-width ranked rows (rank badge + rule code + secondary description line + count). The old proportional bars made counts of 160 vs 10 visually almost identical. - Hero stat cards click through with a multi-status filter (
/api/invoices?status=A,B,C) so the In flight / Rejected — IT / Rejected — Business cards land on a properly filtered list instead of dropping the status filter.
Integration Errors page
- View toggle: by-event (existing flat table) and by-rule (ranked cards grouped by rule + source, each card showing invoice count and per-severity chips). Cards stay equal-width via
auto-fillso a short last row no longer stretches a lone card across the full row. - New
Unmatched onlycheckbox — keeps the previous behaviour (errors with no joined invoice) one click away while letting the default view show all errors. - Category filter (
UBL validation/Integration / lifecycle) on both views, mapped to aUVSRCL IN (...)list on the backend so Schematron / XSD rules can be analysed separately from runtime / dispatcher errors (PDF, PA, DB, …). - Row click on the by-event view opens the invoice detail modal on its History tab (mirrors Notifications).
- Deep-links from the dashboard's Top failing rules widget — the View all link opens the by-rule tab and clicking a specific rule lands on the by-event view with that rule pre-applied as a filter chip.
Validation rule catalogue
- New
ValidationRuleCatalog(Java) parses the bundled.schfiles at first call and extracts a{rule id → human description}map by matching[<id>]<sep><description>lines inside each<assert>block. The separator is permissive (-or:, optional surrounding whitespace) so it covers the three formats in the four bundled schematrons:- EN 16931:
[BR-CL-23]-Description(no spaces) - FREXT-IC:
[BR-FREXT-IC-08] - Description(spaces) - CIUS-FR:
[BR-FR-23/BT-49] : Description(colon, plus the FNFE-MPE convention where the assert id uses_and the bracket label uses/— the lookup retries with_→/automatically).
- EN 16931:
- The opening-tag matcher captures attributes as a single blob so it finds
id="…"regardless of attribute order — the earlier draft requiredidto be the first attribute and silently skipped every CIUS-FR rule. - Twelve lifecycle / integration rules from
ErrorCatalog(UBL_CREATION,DB_INSERT,PA_SEND, …) are seeded with FR descriptions in the same map. - New endpoint
GET /api/integration-errors/catalogreturns the merged catalogue. The frontend caches it once per page load via a smalluseRuleCataloghook. - Where rule codes appear (Top failing rules widget, by-rule cards, by-event Rule column) the description is shown as a tooltip and as a secondary line under the code.
- Known gap: the
BR-FR-CPROschematron's 34 asserts have noidattribute, so the validator records empty rule codes — those rows remain unlabelled.
Oracle dialect fixes
loadByCompanyandloadRoundTripStatsboth usedcolumn <> ''to guard against empty rows. On Oracle, empty strings are stored asNULL, andNULL <> ''evaluates toNULL(treated as false), so the WHERE collapsed and both queries returned zero rows on customer Oracle installs while working fine on the local Postgres. Replaced byLENGTH(TRIM(column)) > 0(loadByCompany) and removed entirely fromloadRoundTripStatswhere the IN-list already filters NULLs on both dialects.
Theme
- Light mode is now the default for first-time visitors. The toggle still persists the user's choice in
localStorage, so anyone who previously selected dark mode keeps it.
2026.05.3 — 2026-05-06
Notification system — invoice status changes now reach users through a portal inbox, email (with the rendered invoice PDF attached by default), and external API calls, all governed by user-defined rules.
Storage + dispatcher
- New
F564253table (Oracle + Postgres DDL, plusAuthManager.initTablesso a fresh install creates it automatically). One row per delivered notification, keyed byNTUKIDalone, with(NTDOC, NTDCT, NTKCO)referencing the invoice. Composite indexes on(NTUSER, NTEV01, NTUPMJ DESC)for the bell badge / inbox queries and(NTDOC, NTDCT, NTKCO)for invoice-level history. - New
custom.ubl.notifypackage:NotificationDispatcher(singleton with a 2-thread asynchronous worker pool),NotificationDatabaseHandler(CRUD onF564253with insert-time width truncation matching the column sizes),EmailDispatcherand theNotificationRulePOJO. - Rules persist as
notification-ruleresources in a dedicatedconfig-notifications.jsonfile alongside the main config;ConfigJsondispatches to the right file based on the resource type. - Daily retention sweep in
BackgroundSchedulerdriven byglobal.notificationsRetentionDays(default 90,0disables). - The dispatcher registers a JVM shutdown hook on first
initialize()so CLI runs that exit immediately after a status update still drain the worker pool (2-second grace) before the process dies.
Status-change integration points
- Hook in
InvoiceStatusCatalog.StatusTransition.apply()after the DB writes — rules are matched and dispatched on every status change. All exceptions are caught: a notification failure never aborts the underlying status update. - The just-applied status / reason / action data is passed straight to
NotificationDispatcher.fire(...)so the dispatcher does not re-readF564231from a separate JDBC connection — that read returned a stale snapshot when the calling transaction (e.g.ImportStatusHandler) had not committed yet, surfacing the previous status's label in the notification body. - The UI-driven
SetStatusModal(DB target) now also fires notifications — that path bypassedStatusTransition.applyand was previously silent. - CLI modes (
-process,-fetch-import,-fetch-status,-fetch-single,-fetch-all, legacy-run) now call a sharedinitRuntimeCatalogshelper that initialises bothInvoiceStatusCatalogand theNotificationDispatcher. Without this, batch flows updated statuses but skipped notifications because the dispatcher singleton was never initialised.
Email channel
- Single SMTP transaction per dispatch — every address (the portal user's resolved email plus every entry in
emailRecipients) goes on the message'sTo:header in oneTransport.sendMessagecall. The previous per-address loop hung whenever an SMTP server mishandled the connection close mid-loop, silently swallowing every send after the first. - Explicit
Transport.connect/sendMessage/closeinsidetry/finallywith hard socket timeouts (connectiontimeout,timeout,writetimeout— all 20 s). Close failures are swallowed so a stuck cleanup cannot poison the next dispatch. - Auto-rendered invoice PDF attached by default — controlled by the rule's
attachPdfflag (defaultY). Rendered once per dispatch and reused across every recipient. PDF render failures are logged but never fail the email itself. - Default subject and body when the rule's
emailSubject/emailBodyare blank —Invoice {doc} {dct} {kco} — {statusLabel}for the email subject,Status / Reason / Actionlines for the body. The portalNTMSGPkeeps just the status label since the inbox UI already showsNTDOC · NTDCT · NTKCOalongside it.
Recipient model
- Portal target (user / role) and
emailRecipientsare independent fields — set either or both. The portal target also receives the email channel automatically when their account has aUSEMAILon file. emailRecipientsaccepts both,and;separators.- Backward compat: legacy rules with
recipientType=emailare migrated on load — the address moves intoemailRecipients, the type clears. - Recipient resolution is now tolerant: when the
F564250lookup fails (nodb-nomaublconnector, missing table, transient outage), a portal-only recipient is still emitted using the literal username instead of dropping the rule with a No recipients resolved message.
Auth-disabled installations
- When
global.authEnabled != "Y", the dispatcher writes portal rows under a single broadcast sentinel (NTUSER='*') and the inbox / bell endpoints query the same sentinel when no user is logged in. Notifications work without anyF564250row. - The bell stays visible in the top utility bar regardless of auth state (previously hidden inside the auth-only branch).
- The recipient editor adapts its labels: when auth is disabled, the empty option reads Broadcast — all portal users instead of None — emails only.
Frontend
- New Notifications page (promoted from Management to Application in 2026.05.4) — inbox with All / Unread tabs, mark-all-read, per-row dismiss, status badge with catalog-driven colours, accent stripe for unread rows, and a meta line showing
doc · dct · kco · reason · action · rule. Clicking a row opens the linked invoice inInvoiceDetailModal. See Notifications. - New Notification Rules editor — sidebar list + form with sections for trigger, channels, recipient, email content, action call, and a synchronous Test panel that actually fires the rule. Trigger codes use chip multi-selects fed from the
statusesandrejection-reason-codesresources, so the rule trigger and the codes assignable to an invoice cannot drift apart. The action section mirrors Process API: connector dropdown, endpoint dropdown populated fromapi.connectors.listEndpoints(...), and parameter rows pre-populated from the endpoint's defined params. See Notification Rules. - New NotificationBell in the top utility bar — polls
/api/notifications/unread-countevery 30 s, shows a red badge with the unread count, and drops down the last 6 notifications. Clicking a notification marks it read and opens the invoice modal directly via anomaubl:open-notificationwindow event (so it works even when the user is already on the inbox page and the component does not remount). - Role grants for the two new pages plus a new
bellicon in the central icon set.
2026.05.2 — 2026-05-06
French B2B PA submission — qualified PDF attachments (LISIBLE + co-attached documents) and several round-trip-integrity fixes that surfaced while shipping it.
LISIBLE attachment + qualified additional attachments
- New
lisibleY/N flag on doc-templates. WhenY, a PDF is rendered from the freshly-built UBL via the resolvedpdf-templateand embedded back as the human-readable invoice attachment (cbc:ID = "LISIBLE"). Independent of the existingattachmentdropdown — both can fire on the same invoice. - New
additionalAttachmentsJSON property — list of{code, path}entries embedded ascac:AdditionalDocumentReferenceblocks. UI editor in the Document tab with a code dropdown (RIB,BON_LIVRAISON,BON_COMMANDE,PJA,BORDEREAU_SUIVI,BORDEREAU_SUIVI_VALIDATION,DOCUMENT_ANNEXE,ETAT_ACOMPTE,FACTURE_PAIEMENT_DIRECT,RECAPITULATIF_COTRAITANCE,FEUILLE_DE_STYLE) and a path field. Paths support%APP_HOME%,%ENV%,%DOCNAME%,%KCO%,%DOC%,%DCT%placeholders. Missing files are logged and skipped — they do not fail the surrounding processing. Tranform.embedPdfInUBLgains a new String-code overload. The qualifier becomescbc:IDof the insertedcac:AdditionalDocumentReference.cbc:DocumentTypeCodeis not emitted (UBL-SR-43 reserves it for invoiced objects 130 / 50) andcbc:DocumentDescriptionis not emitted either (the PA returned HTTP 400 when it was present alongside the embedded attachment). The 3-arg signature is unchanged so callers using the legacyPDF_Invoiceshape are unaffected.PdfTemplateEngine.renderandInvoicePdfGenerator.generategain amergeAttachmentboolean. The LISIBLE flow passesfalseso the rendered PDF does not inherit any already-embedded attachment from the legacycreate/attachflow — otherwise LISIBLE would visibly duplicate the original PDF inside itself.
XML format fixes for PA acceptance
The Plateforme Agréée parser turned out to be byte-sensitive in ways the legacy code did not honour:
embedPdfInUBLused to round-trip the UBL through JDK's Xalan transformer which emits<?xml version = '1.0' encoding = 'UTF-8'?>(spaces around=, single quotes). Switched to Saxon (the same engineconvertToUBLuses) so the output matches<?xml version="1.0" encoding="UTF-8" standalone="no"?>byte-for-byte. ExplicitSTANDALONE = "no"output property;filenameattribute emitted beforemimeCodeto match the legacy ordering.convertToUBLalso forcesstandalone="no"so flows that bypassembedPdfInUBL(no-attachment runs) get the same XML declaration the PA accepts.- New
AdditionalDocumentReferenceis inserted on its own line — the indent text is copied from the existing whitespace text-node, with extra leading-indent injection when the previous sibling is an Element (consecutive attachments + the trailing<cac:AccountingSupplierParty>each keep their\nseparator).
ConfigJson re-indent guard
tryReIndentJson was unwrapping every string that started with { or [ as nested JSON — including template placeholders like "{content}", "{statusAt}", "{reportName}" used by other templates. The unwrap "succeeded" because the bracket structure balanced, producing object-of-bare-words garbage that left config.json unparseable on the next reload. New looksLikeJson() heuristic requires either an empty container or a real JSON value-starter (quoted key after {, etc.) before recursing. Existing templates with {placeholder} strings now round-trip cleanly.
Misc
parseUblXmlreadscbc:LineExtensionAmount(BT-131) directly andInvoiceDetailModaldisplays it as the line amount instead of recomputingqty × price ± allowances— the recompute double-counted when the unit price was already net (a 45 × 12,75 line with a 489,15 line allowance was showing 84,60 instead of 573,75 in the modal while the PDF was correct).ubl-template.xsl: newTAG_CUSTOMER_SIRETslot (BT-46) alongside the existing buyer SIREN.
2026.05.1 — 2026-05-05
PDF template engine — Phase 2: PDF templates become first-class shareable resources, gain a generic XPath-driven block section, and a full visual canvas editor.
PDF templates as first-class resources (Phase 2a)
- New resource type
pdf-template, persisted inconfig-pdf.json(renamed from the originalconfig-pdf-templates.jsonfor brevity). - New top-level PDF Templates page (sidebar entry under Management) for creating, copying, importing, exporting and editing layouts independently of any document template. See PDF Templates for the full reference.
- Documents reference a layout via the
pdfTemplateproperty on the doc-template resource. Many documents can share one PDF template — edit once, propagate everywhere. - Resolution chain: doc-template's
pdfTemplate→defaultPdfTemplateonglobal→ bundled built-in. The reserved namebuilt-inis read-only and always available as a safety fallback. - Pretty-printed JSON on disk: nested
template/configobjects are emitted as real JSON objects (with proper indentation), not escaped strings, so they remain readable in any editor. Round-tripped via the sharedConfigJson.readPropertyValueso loaders stay agnostic of the on-disk shape.
Generic block section (Phase 2b)
- New section type
block— XPath-driven layout primitives that compose into arbitrary content without needing a bespoke section class:text(literal),field(XPath value, with optional inline label anddate/currency/number/percentformatting);image,spacer,hr;row/columncontainers (alignment + gap);repeat(XPath → list, renders itschildper match);if(XPath → boolean, renderschildwhen true);table—rows × colsgrid with optional cell borders and a styled header row. Settingxpathmakes it iterate (one row per match), with children acting as the per-row template.
- Table cell XPaths inside an iterating table are auto-relativized: an absolute path that starts with the iterator's xpath is stripped at render time so each row evaluates against its own iterated context node.
- Row
align: end | centershrinks the row to ~50% width and aligns the whole table to that side, instead of stretching equal cells across the page (so "label + value" stays grouped on the right).
Visual canvas editor (Phase 2c)
- New
BlockCanvasEditormounted inside anyblocksection's drawer: three stacked panes (tree, toolbar, inspector) plus a JSON escape hatch. - Inspector: per-kind attribute forms with a style sub-panel; the Kind select at the top morphs the selected node in place (column → repeat without a delete-and-re-add) via a
transmuteKindhelper that carries over compatible fields. - Sample XML loader lifted to the template-level header — load once, every block reuses the entries for XPath autocomplete.
- The XPath picker preserves
cbc:/cac:namespace prefixes (required by the namespace-aware backend) and emits/*/<full-path>so picks are unambiguous and root-agnostic (Invoice and CreditNote alike). Inside an iterating ancestor, the picker further strips the iterator prefix so cells start with relative paths (cbc:TaxAmountinstead of/*/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount). - Live preview moved to the top of the form and opens in a 960 × 85vh modal — no more scroll-down / scroll-up loop while iterating on a layout.
- Section picker moved to the top and converted to chips: one click adds a section at the top of the list, auto-expanded so the inspector is immediately visible.
blockcan be added several times per template; each block has a user-friendlynameshown next to the section row.
Preset section drawer rewrite
- Toggles use the shared
Checkboxcomponent (consistent rounded-blue style instead of the native dark-mode checkboxes). - Toggles are parsed by their
Category · Nameprefix and grouped into side-by-side columns mirroring the actual PDF layout — Header reads as METAS | SUPPLIERS, Line Table as Group headers | Columns | Sub-details instead of one long flat list. - Drawer border softened: thin solid border with a 2 px blue accent on the left.
Critical correctness fixes
- JSON top-level scanner: the bespoke parser used naive
indexOfto locate"<key>", which silently matched the first nested occurrence. A table withchildrenlisted beforexpathended up with the table'sxpathresolved to the first child's xpath, the iterator returned 0 nodes, and the entire table vanished from the rendered PDF. Replaced byfindTopLevelValueStartthat respects bracket / string state and only matches keys at depth 1. AffectsreadString,readFloat,readBool,readStringArray, and thetree/children/child/stylelookups inparseNode. - Editor echo loop:
parseConfigwas dropping the block'snamefield andPdfTemplateForm'suseEffect [value]re-parsed on every echo — combined, every keystroke triggeredsetSelectedPath([])and remounted the inspector, stealing focus and (for repeated emits) freezing the page. Both functions now preservename, and both seed effects skip when the incoming value matches the last value emitted (lastEmittedRef). - Iterating tables: an empty NodeList now returns
nullcleanly so OpenPDF doesn't choke on a 0-row table, per-cell renders are wrapped in their own try/catch so a single bad cell becomes an inline[ERROR]placeholder instead of breaking the whole row layout.
Backend / shared helpers
PdfContextnow carries the parsed namespace-awareDocumentso block sections can run XPath without re-parsing the UBL XML.ConfigJsonexposesreadPropertyValue,findStringEnd,findMatchingBracket,jsonUnescape,compactJsonandtryReIndentJsonso other resource types can opt into pretty-printed nested JSON storage.
2026.05.0 — 2026-05-05
A large refactor across the document-processing pipeline, the PDF generator, and the invoice REST surface.
Document-template-driven processing (XML or UBL)
- New
sourceproperty (XML|UBL) on every document template. The Document tab in Management → Documents picks it. XMLkeeps today's behaviour (XSL transform → UBL → DB → PA send).UBLis for files that are already UBL 2.1 invoices. The(doc, dct, kco)primary key is parsed from the invoice'scbc:IDvia a regex with named groups (idPattern+ per-keydocDefault/dctDefault/kcoDefaultfallbacks). Examples:F202600025→^(?<dct>[A-Z]+)(?<doc>\d+)$(kcoDefault = 00001)38706889RI00001→^(?<doc>\d+)(?<dct>[A-Z]+)(?<kco>\d+)$
- The Document tab includes a Sample cbc:ID + Suggest + Test helper so the regex doesn't have to be written by hand: paste a real ID, Suggest fills the regex by splitting on letter / digit transitions, Test runs the current pattern + defaults and shows the extracted
(doc, dct, kco)live. - The
DOC_DCT_KCO_ubl.xmlfilename convention is no longer required — UBL processing always derives the keys fromcbc:ID. Filenames can be anything.
Single processing entry point
- The Process XML and Process UBL pages are gone. One Process Document page replaces them; the form switches XML vs UBL controls based on the picked template's
source. - One backend route:
POST /api/process(replaces/api/runand/api/process-ubl). - One CLI flag:
-process <config> <template> <fileOrDir> [type] [flags](replaces-xmland-ubl).nomaubl.sh process …. -fetch-singleand-fetch-alldrop theirprocessTypeargument — inferred from the template. Fetch Input and Settings → Scheduling both lose the Process type toggle.
New Documents page
- Sidebar → Management → Documents opens a dedicated page for document templates (the rows in
config-documents.jsonreferenced byF564231.UHTMPL). Carries its own Add / Import / Copy / Remove buttons plus a Description input. Settings keeps Add Connector / Copy / Remove for system templates and connectors.
F564231.UHTMPL — invoice → template link
- New column
UHTMPL VARCHAR2(40)on the invoice header table. Persisted at processing time (XML and UBL paths) so the PDF generator can resolve the per-documentpdfTemplateJSON when rendering. - DDL added to
sql/oracle/ddl.sqlandsql/postgres/ddl.sql.
Invoice REST surface — clean primary key
- Every
/api/invoices/{…}route now uses(doc, dct, kco)directly (/api/invoices/{doc}/{dct}/{kco}/lines,/lifecycle,/errors,/vat,/notes,/xml,/resend,/validate,/status, plusPUT/DELETE). The composite-string id, thecastToVarchar(UHDOC)helper, and the||UHDCT||UHKCOconcat in WHERE clauses are gone for every key lookup. Faster (index-friendly) and Postgres-clean. - New
bindDoc()helper binds the numericUHDOCparameter viasetInt; Postgres no longer rejectssetStringagainst anINTEGERcolumn.
/invoice/view (PDF) — dual-form lookup
GET /invoice/view?id=<UBL invoice number>(matched againstUHK74FLEN) or?doc=&dct=&kco=. The page can be linked from anywhere — including pasted-into-browser URLs — without needing a composite identifier.
PDF generator refactor
InvoicePdfGeneratorcollapsed from a 1027-line monolith into acustom.ubl.pdfpackage:PdfTheme,UblXmlHelpers,InvoiceData,UblParser,PdfDrawing,PdfContext,PdfTemplate/PdfTemplateEngine/PdfTemplateLoader/DefaultPdfTemplate,PdfSectionregistry, and one class per section undercustom.ubl.pdf.sections/(Header, Parties, Agent, LineTable, DocAllowances, VatBreakdown, TotalsBox, Payment, NoteBlock).- New JSON template engine — sections are listed and reordered in the template, each with its own per-section
config(column visibility, sub-detail toggles, group-header behaviour, page-break per delivery, …). Edited in Documents → 🖼 PDF Template with drag-and-drop reorder, expandable per-section config drawer, and a live preview iframe (POST /api/pdf-templates/preview). - Per-document override is stored as the
pdfTemplateJSON property on the doc-template resource (linked byF564231.UHTMPL). When unset, the bundled default layout is used.
Frontend polish
- Sidebar — new Documents entry under Management; Process Document entry replaces Process XML / Process UBL; thin themed scrollbar so overflowing groups are reachable in both light and dark modes.
- Path placeholders (
%APP_HOME%,%ENV%,%PROCESS_HOME%) in the Process Document page are expanded server-side before the absolute-path / basename branching, so files picked via Browse work even whendirInputis a placeholder template. - XML — JDE spool renamed to XML spool everywhere — the XSL pipeline is generic, only BIP is JDE-specific.
- Various small fixes: Documents row description binding, Settings modal cleanup, status modal / send-status / email path migrated to
(doc, dct, kco), AiAssistantaugmentPromptWithSitemapremoved.
Roles / Permissions
- New
documentspage key in the role editor (under Management) so installations with restricted roles can grant or deny the new page.
2026.04.10 — 2026-05-04
E-Invoicing settings — hybrid FTP send / API status configuration
- The PA Connection tab used to hide the API config (Base URL, Auth, Status Retrieval, Background Scheduling) when Send Mode = FTP, making it impossible to configure a hybrid setup where invoices ship via SFTP but import polling, status retrieval and seller actions still go through the API. All sections are now always visible; the mode select is renamed Send Mode with a hint clarifying it only governs outbound transport. Each non-active section's title gains a small grey hint (used for status retrieval / import / seller actions or used when Send Mode = FTP) so the relationship between fields and operations is obvious at a glance.
XSL — BT-46 (Buyer SIRET)
- Fixed
XTSE0680: Parameter siren is not declared in the called templateinubl-common.xsl: the call toubl:party-siretwas passing<xsl:with-param name="siren">instead ofname="siret"(copy-paste from the adjacentubl:company-sirenblock). - Fixed
cvc-complex-type.2.4.aXSD validation when emitting BT-46:<cac:PartyIdentification>is a child of<cac:Party>, not<cac:PartyLegalEntity>. The SIRET emit moved out ofubl:party-legal-entity(the embedded helper call was structurally invalid) and now lives directly in the doc XSL'scac:Partyblock, beforecac:PartyTaxScheme/cac:PostalAddress. Doc XSLs that need BT-46 should follow the same pattern. - Added
TAG_CUSTOMER_SIRET(BT-46) to the XSL editor catalogue and promoted it to the buyer section's main list so it appears next toTAG_CUSTOMER_SIRENin Variables.
2026.04.9 — 2026-04-30
Settings — fix stale editor state when switching between reference lists
- Switching from one template to another sometimes opened the right editor with rows from the previously-viewed list. Two issues: editors that seed internal state from props on mount (the 14 list editors, StatusesEditor, DocumentTypesEditor, …) kept showing the previous template's rows because their
useStateinitialiser ran only once; andselectTemplate()flippedselectedimmediately butpropsonly updated once the fetch returned, so rapid clicks could land a stale fetch on the wrong template. - Fix: editor render is now wrapped in a keyed
<div key={selected}>withdisplay: contentsso React unmounts the previous editor and mounts a fresh one whenever the user picks a different template.selectTemplate()now clearseditData / props / rawPropssynchronously and tracks a fetch-sequence counter, dropping any response that's been superseded by a later click.
Invoice detail — Download UBL button
- New Download UBL button on the History tab of the invoice detail modal, next to Validate UBL. Saves the raw UBL XML stored in
F564231.UHTXFTto a local file named{doc}-{dct}-{kco}.xml. Reuses theGET /api/invoices/{id}/xmlendpoint and theublRawXmlalready loaded into modal state, so there's no extra round-trip. Disabled with a tooltip until the XML has finished loading.
AI Assistant — auto-greeting on first open
- Opening the chat panel with no history now auto-sends a localised greeting (Bonjour / Hello depending on the UI language), so the assistant introduces itself and lists its main capabilities without the user typing a first prompt. One-shot per page load: closing / reopening the panel mid-session doesn't repeat the greeting, and existing conversations stay intact.
2026.04.8 — 2026-04-29
AI Assistant — lifecycle history tool + REST delegation
- New
lifecycle_historylocal tool: returns every status transition for an invoice from F564235 (sequence, status code + label, message, date / time, PA rejection reason + label, expected action + label, status note) — same payload the History tab of the invoice detail modal renders. Lets the AI answer "why was invoice X rejected / what did the PA say" without falling back to "I don't have access to this history". validation_errorsandlifecycle_historynow delegate to the existingWebApiHandler.handleInvoiceErrors/handleInvoiceLifecycleREST handlers (the React UI's History tab uses the same calls). Removed the duplicate hardcoded SQL in the AI tools so the AI automatically benefits from the configurableUBLTableConfigtable names, dialect-aware queries (Oracle vs Postgres), and status-label resolution maintained on the REST side. One source of truth.- The full invoice + e-reporting status catalogues are now appended to the system prompt at chat time (read from
InvoiceStatusCatalogandEReportingStatusCatalog, so user customisations apply). The model used to guess codes from words like litige (picking 49 instead of the real 207) — now it has the table in context and uses the exact code withlist_invoices/list_ereports. AIChatPanel: the textarea is re-focused as soon as the assistant finishes streaming, so follow-up questions don't require clicking back into the input each time.
2026.04.7 — 2026-04-29
AI Assistant — fix url_not_allowed from web_fetch
- Anthropic's
web_fetchonly fetches URLs that have previously appeared in user messages or prior tool results. The 2026.04.6 sitemap-injection put the URLs in the system prompt where they don't count, so every fetch returnedurl_not_allowed. A first attempt to expose the catalogue through a customlist_docs_pagestool returning JSON also failed — Anthropic's URL extractor scans tool_result content as plain text and doesn't recognise URLs wrapped in JSON quotes. - Final fix:
list_docs_pagesnow returns the sitemap as plain text, one bare URL per line. Anthropic's extractor picks them up, and the subsequentweb_fetchcall succeeds. Claude follows a clean two-step flow:list_docs_pages→web_fetch→ answer. - Server-tool calls + their results are now surfaced in the chat as inline pills (📖
web_fetch · <url>, 📥web_fetch_result · ✓ <url>or ❌<error_code>), so failure modes are visible instead of swallowed — this is what made the JSON-vs-text bug debuggable.
2026.04.6 — 2026-04-29
AI Assistant — sitemap-driven docs lookup
- The model used to know it could fetch
docs.nomana-it.frbut had no idea which URLs existed; it would either guess and miss, or give up and answer from prior knowledge. The backend now fetchessitemap.xmlonce on startup, filters entries to the documentation prefix (defaulthttps://docs.nomana-it.fr/nomaubl), and injects the resulting page list into the system prompt — so the model picks a real URL instead of guessing. - Sitemap is cached for 6 hours and capped at 200 pages. Failures are silent (the previous snapshot keeps serving) and never block the chat call.
- Two new optional
globalproperties:aiDocsSitemapUrl(defaulthttps://docs.nomana-it.fr/sitemap.xml, empty disables sitemap injection) andaiDocsPathPrefix(defaulthttps://docs.nomana-it.fr/nomaubl).
2026.04.5 — 2026-04-29
AI Assistant — web_fetch backward-compat fix
- Existing configs that predate 2026.04.4 don't have the new
aiDocsDomainsproperty in theirglobaltemplate, so the assistant launched with noweb_fetchtool and would (correctly) reply that it didn't have access to docs lookup. - New semantics for
aiDocsDomainsinAiAssistant: missing property → default todocs.nomana-it.fr(backward-compat — no manual edit required); empty string → explicitly disabled;"a,b,c"→ use that list. No DB or API changes; only a server-side default.
2026.04.4 — 2026-04-29
AI Assistant — tool use (docs lookup + read-only data tools)
- The chat panel now lets the model call tools mid-conversation instead of replying from prior knowledge alone. Two layers: documentation lookup via Anthropic's server-managed
web_fetch_20250910tool, withallowed_domainslocked to the list inglobal.aiDocsDomains(defaultdocs.nomana-it.fr) — and read-only operational tools executed locally against the same DB the web UI uses (list_invoices,explain_status_code,validation_errors,list_ereports). Toggled viaglobal.aiToolsEnabled(default Y). - New
global.anthropicSystemPromptoverrides the built-in NomaUBL assistant prompt. Empty = use the bundled default which describes the product, points the model at the docs URL, and lists status-code ranges (1373 / 99xx / 9950 – 9957). - Backend rewritten as
AiAssistant.java: agentic loop with up to 5 tool turns, streams text deltas as{type:"token"}and surfaces tool invocations as{type:"tool_call",name,summary}so the UI can render an inline pill (📖 web_fetch, 🔍 local tool) above the assistant bubble while the call is in flight. - Settings → System → Global → AI tab gains three new fields: System Prompt (textarea), Allowed Doc Domains (comma list), Custom Tools (Y/N).
- Assistant replies are now rendered as Markdown via
react-markdown+remark-gfm: headings, bold, lists, GFM tables, fenced code blocks, inline code and links all render correctly. External links open in a new tab.
See AI Capabilities for the full user-facing reference.
2026.04.3 — 2026-04-29
E-Reporting XML — Flux 10 specification compliance
EReportingXmlBuilderrewritten to match the official FNFE-MPE Flux 10 element names (TT-1..TT-99, TG-8..TG-39). Old custom names (<Identifier>,<DocumentType>,<Flux>,<Period>,<Customer>,<Totals>,<TaxBreakdown>) replaced with the spec's<Id>,<IssueDateTime><DateTimeString>,<TypeCode>,<Sender><Id schemeId>+<Name>+<RoleCode>,<Issuer><Id schemeId>+<Name>+<RoleCode>,<TransactionsReport><ReportPeriod><StartDate>+<EndDate>. Dates emitted asyyyymmdd(period) andyyyymmddhhmmss(issue datetime), no separators.- B2C / B2BINT routing rule G6.28 enforced: B2C transactions never emit individual
<Invoice>blocks — only aggregated<Transactions>(one per CategoryCode + currency, with nested per-rate<TaxSubtotal>carrying TaxPercent / TaxableAmount / TaxTotal). B2BINT keeps emitting one<Invoice>per invoice with ID, IssueDate, TypeCode, CurrencyCode, Seller (declarant), Buyer (counterparty), MonetaryTotal, and per-rate<TaxSubTotal>. - Per G6.23, every
TaxAmount/TaxTotalis now expressed in EUR (thecurrencyIdattribute is locked toEUR) regardless of the source invoice's currency. Taxable amounts retain the source currency. <TransactionsReport><Transactions><CategoryCode>(TT-81) restricted to the spec subsetTLB1(goods VAT-able),TPS1(services VAT-able),TNT1(non-VAT-able),TMA1(margin scheme); falls back toTLB1/TNT1based on the rate when the source row carries an out-of-list value.- New optional configuration on the
e-reportingtemplate:senderName,senderRoleCode(defaultWK),issuerName,issuerSchemeId(default0002, with0223/0227/0228/0229selectable for international cases),issuerRoleCode(SE/BY),businessProcessIdandbusinessProcessTypeId(TT-28 / TT-29 emitted on per-invoice<BusinessProcess>for B2BINT only), andflowName(TT-2). The Settings → System → E-Reporting editor exposes them as three grouped sections: Sender (PA), Issuer (Declarant), Business Process. <ReportDocument><Id>defaults to{siren}-{flux}-{start}-{end}when no transmission ID is supplied — a stable per-period value the PA can deduplicate against.- Dedicated e-reporting status catalogue (codes 9950 – 9957), independent from the invoice 99xx range. New
EReportingStatusCatalogloads from the newereporting-statusessystem template (FR / EN labels editable in Settings → System → ereporting-statuses, alongside the existingstatusestemplate). Codes:9950 EREPORT_CREATED,9951 EREPORT_SUBMIT_SKIPPED,9952 EREPORT_SENT_TO_PA,9953 EREPORT_PENDING,9954 EREPORT_ERROR_SENT,9955 EREPORT_DEPOSITED,9956 EREPORT_FAILED_IMPORT,9957 EREPORT_REJECTED. The catalogue seeds itself with built-in defaults if the template is missing, so existing installs keep working without manual migration.EReportingHandlerrewired to use these codes (was previously reusing the invoice ones); the PA-submission-skipped case now lands on its own code instead of reusingSTATUS_CREATED. EReportingFetchernow reads VAT subtotals primarily from the UBL XML stored inF564231.UHTXFT(parses the document-levelcac:TaxTotal/cac:TaxSubtotalnodes — line-level subtotals are ignored to avoid double-counting). The previous behaviour (queryingF564234) is kept as a fallback so deployments that don't materialise the per-tax summary table still produce reports. Fixes the "empty<Transactions>block" symptom in B2C reports whereF564234was not populated even though the invoices and their UBL XML were correctly stored.
Settings → list editors (focus loss when typing)
- Fixed across 15 list editors (Statuses, Countries, ActionCodes, CurrencyCodes, CustomizationIds, InvoiceTypes, PaymentMeans, NoteTypes, DocumentReferenceCodes, RejectionReasonCodes, SchemeIds, UnitCodes, ProfileIds, VatexCodes, VatCategories): typing into any row would lose focus after every character — and freshly-added rows could never be filled in.
- Root cause: each editor had a
useEffect(() => setRowsState(...), [props])that re-derived the local rows from the parent props on every render. Because the editor itself echoes rows back to the parent on every keystroke (and the parent re-renders), this created a round-trip that recreated the rows array → React unmounted / remounted the inputs → caret was kicked out of the field. Worse,*RowsToPropsfilters out rows whose code / key is empty, so any new row vanished entirely from the parent's props on the first keystroke in a non-key column. - Fix: removed the props → rows resync. Each editor seeds its local rows from props once on mount and is the sole writer afterwards. React keys stay tied to the array index so existing rows keep their input identity across re-renders.
- For
StatusesEditorspecifically, thetypeanddescriptiontemplate fields are now preserved when echoing back to the parent (they were silently dropped before, which would corrupt thestatusestemplate on Save).
E-Reporting schema rework
- F564240, F564241 and F564242 column names overhauled to match the JDE-style structure:
- F564240:
RGDOC → RGUKID(PK = RGUKID alone, no flux / kco component; declared asNUMBER(15)on Oracle /BIGINTon Postgres in both the static DDL andAuthManager.initTables),RGFLUX → RGY56BAR,RGTYPCD → RGDCT,RGPSTART → RGEFTJ,RGPEND → RGEFDJ,RGISSUID → RGY56EPID,RGINVCNT → RGNINV. DroppedRGSENDID(sender stays in config + XML, not persisted) andRGUKIDSZ(PA UUID no longer stored — PA outcome tracked through status + lifecycle only). - F564241: FK column to F564240 keeps the parent's prefix (
RGUKID);RHFLUX → RHY56BAR;RHKCOdropped (reach KCO via the parent). PK =(RGUKID, RHSEQN). - F564242: FK column is
RGUKID;RIFLUX → RIY56BAR; report-sideRIKCOdropped. The invoice triplet drops theINVinfix:RIINVDOC / RIINVDCT / RIINVKCO → RIDOC / RIDCT / RIKCO. PK =(RGUKID, RIDOC, RIDCT, RIKCO).
- F564240:
- Mirrored across Oracle DDL, Postgres DDL and
AuthManager.initTables(so the Initialize Database action creates the new structure). EReportingDatabaseHandler: field renamergdoc → rgukid;kcono longer carried at the handler level (only F564240 has it, set at insertReport time).nextSequence()now doesMAX(RGUKID) + 1(globally unique).insertReport(typeCode, kco, …)signature:senderIdparameter removed.updatePATransactionId()removed (column gone). All child-table inserts updated to the new column names.EReportingFetcherNOT EXISTS check rewritten to the newRIDOC / RIDCT / RIKCO / RIY56BARcolumns.- REST API simplified accordingly:
/api/ereporting/{flux}/{kco}/{rgdoc}→/api/ereporting/{rgukid}./api/ereporting/{flux}/{kco}/{rgdoc}/resend→/api/ereporting/{rgukid}/resend. JSON output:rgdoc→rgukid;senderandpaUuidfields removed. - Frontend types, API client, list page, detail modal, columns and i18n updated to match (resend echoes the PA UUID once in its response so it can still be displayed in the success banner).
- Detail modal → Invoices tab: dropped the Number column. The DOC / DCT / KCO triplet is the canonical identifier; the UBL invoice number was just a display copy of the same data, removed alongside its i18n key, the
invoiceNumberfield onEReportInvoiceLink, and the matchingUHK74FLENjoin in the backend SQL.
Dashboard / About card
- The EXTENDED-CTC-FR schematron is now listed in the About this release card (key
frExtendedCtc, label EXTENDED-CTC-FR) — it was shipped with the JAR but missing from/api/build-info. BuildInfonow picks the FNFE-MPE version stamp (Schematron yyyymmdd_NAME_VX.Y.Z …) before the genericSchematron version X.Y.Zpattern; both Flux 2 and Extended-CTC-FR embed the underlying EN 16931 source version (1.3.15) in their comments which previously misled the parser.- Tightened the EN 16931 date capture to ISO
yyyy-mm-ddso the rendered last-update no longer carries the trailing--from the source's-->close tag.
2026.04.2 — 2026-04-29
Validation
- Fixed: re-validating an existing invoice from InvoiceDetailModal → History (and the standalone
validateUblDirectpath) failed withcvc-elt.1.a: Cannot find the declaration of element 'Invoice'. TheDocumentBuilderFactoryused to parse the UBL was not namespace-aware by default, so the XSD validator couldn't bind<Invoice>/<CreditNote>to the UBL 2.1 schema.setNamespaceAware(true)is now set on both parser instances.
2026.04.1 — 2026-04-29
Processing Log
- UBL processing now writes a
START/ENDpair toF564237so the Processing Log covers ProcessUBL (/api/process-ubl), fetch-invoices in UBL mode and the-ublCLI — same as-xmlalready did.FEMODE = PROCESSfor these rows;FETMPLis empty (no document template applies to UBL processing).
UBL Validation page
- Fixed: uploading a UBL file no longer lands in
<input>/_ubl/(a literal substitution of the_ublsentinel template). Uploads now use the conventional<input>/ubl/folder, matching the fetch / list-files convention. - Fixed: validating an uploaded UBL file no longer fails with
No such file or directory. Basenames in the form's file field are resolved against<dirInput>/ubl/before parsing; absolute paths from the file browser keep working as before.
Validation
- New EXTENDED-CTC-FR schematron (FNFE-MPE
EXTENDED-CTC-FR-UBL-V1.3.0) bundled and wired intoUBLValidator. - Schematron flavour is now driven by
cbc:CustomizationID(BT-24). When the URN containsEXTENDED/extension, the EXTENDED-CTC-FR ruleset is run instead of EN 16931 + CIUS-FR (it's a derived superset that intentionally relaxes some EN 16931 rules — e.g.UBL-CR-550is commented out soInvoiceLine/Deliveryis permitted). All other values keep the previous behaviour: EN 16931 base + CIUS-FR (BR-FR Flux 2) overlay. CPRO-B2G still self-gates oncbc:Note#ADN#B2Gregardless of profile.
Configuration / UBL Defaults
- New
customization-idssystem list (BT-24) seeded with the standard French URNs (EN 16931 base, FNFE-MPE Basic / Extended CTC, Factur-X Minimum / Basic / Basic WL / Extended, Peppol BIS Billing 3) — fully editable in Settings → Customization IDs. - UBL Defaults → Header: BT-24 is now a dropdown populated from the
customization-idslist (free-text remains as fallback when the list is empty or the value is not registered).
Replace mode
- Replace-mode reprocessing now purges
F564235(lifecycle) andF564236(validation errors) in addition toF564231/F564233/F564234. Previously these two append-only tables kept growing across re-runs, leaving stale lifecycle history and validation errors mixed with the latest run's data. - New
UBLDatabaseHandler.purgeForReplace()does a one-shot purge of all five UBL tables for a given(doc, dct, kco). Called byUBLInvoiceProcessor.process(UBL path) andCustomUBL(XML path) wheneverreplaceMode=true, so both paths now have identical replace-mode semantics regardless of whether the F564230 row already exists.
2026.04.0 — 2026-04-29
E-Reporting (Flux 10.1 / 10.3)
- New top-level E-Reporting page: list, detail modal, generate dialog.
- New tables
F564240,F564241,F564242(configurable indb-nomaublsettings; created by Initialize Database). - New
e-reportingsystem template + per-companye-reporting-{kco}overrides; submission reuses thee-invoicing[-{kco}]PA token via a newreport-importendpoint. - CLI:
-ereporting <config> [start=YYYYMMDD] [end=YYYYMMDD] [kco=...] [flux=10.1,10.3] [type=IN]. - Background scheduler: new
ereportingIntervaljob inglobal. - Detail modal: invoices tab uses
DataTablewith CSV / Excel export. - Detail modal: download generated XML button (replaces the inline XML tab).
Processing Log
- New Processing Log entry under the Management menu, backed by
F564237. - Grouped view (default) collapses every job into a single
START → ENDrow, with status badge, duration and an expandable list of intermediate steps; flat view kept for power users. - Toolbar: dropdowns for Mode and Template, period picker (default: last 7 days), file-name search.
Dashboard
- New About this release card pinned at the bottom of the dashboard with the release number, build date, AFNOR profile version and schematron versions per module (EN 16931, BR-FR Flux 2, BR-FR CPRO).
Documentation
- New Release Notes page (Documentation menu) rendering this file.
- Maintained in two languages —
RELEASE.md(English) andRELEASE.fr.md(French) bundled in the JAR; the page picks the right one from the active UI language. - Top-of-page table of contents with one chip per release linking to its section.
- In-house Markdown renderer with lazy list-continuation handling so hard-wrapped bullets render as a single item.
Settings
db-nomaubleditor exposes the three new e-reporting table names (tableEReporting,tableEReportingHist,tableEReportingMap), defaulted toF564240/F564241/F564242.- Initialize Database now creates the three e-reporting tables in addition to the existing UBL / auth tables.
- Roles page-permission picker exposes the new
processinglogandreleasenotespages so existing roles can be granted access.
Backend
DatabaseDialect.writeText/readTextdefaults — XML stored asCLOB(Oracle) /TEXT(Postgres) using portablesetString/getString(avoids the pgjdbcgetClob → OIDpitfall).nodeToBytesinUBLDatabaseHandlernow setsOutputKeys.INDENT="no"so document XML written toF564230.FETXFTdoes not pick up Saxon's pretty-print under fat-jar runs (same fix already applied to UBL)./api/build-info(public) returns release metadata + bundledRELEASE.md/RELEASE.fr.md.
1.0.0 — Initial release
NomaUBL is a Java 17 + React e-invoicing platform that turns ERP output (JD Edwards, SAP, NetSuite, custom) into standards-compliant UBL 2.1 documents, validates them, submits them to a French Plateforme Agréée (PA), and tracks the full invoice lifecycle.
Core pipeline (Source ERP → UBL → PA)
- JDE XML extraction from the BIP Print Queue (
F95630/F95631/F9563110), JDE Archive, SFTP and the local filesystem; routed by document-type templates (invoices,credit_notes, …). - XSLT 2.0 transformation via Saxon-HE — generates UBL 2.1 invoices and credit notes, with a configurable XSL framework (
ubl-common.xsl+ubl-template.xsl). - Validation: XSD (UBL 2.1) + Schematron — EN 16931, BR-FR Flux 2 (CIUS-FR / FNFE-MPE) and BR-FR CPRO (Chorus Pro for B2G), with severities (
fatal,error,warning,info). - PA submission over HTTP (Java 11
HttpClient), with OAuth2 bearer-token caching and auto-refresh on 401, plus an SFTP fallback channel. - Per-company PA overrides via
e-invoicing-{kco}system templates — independent credentials, endpoints and tokens per issuing company. - PPF directory pre-flight (non-blocking) via the
e-directorytemplate — looks the customer up before sending and surfaces a warning when the recipient is unreachable. - PDF generation via Oracle BI Publisher (
oracle.xdo) with optional Ghostscript post-processing and an iText-based embed of the PDF ascac:AdditionalDocumentReferencein the UBL. - Mock PA (
paUseMock=Y) with success / failure / token-expiry behaviours for end-to-end tests without a live platform.
Document, status and lifecycle storage
Oracle / PostgreSQL schema (configurable in db-nomaubl):
| Table | Purpose |
|---|---|
F564230 | Source archive — original JDE XML, processing flags |
F564231 | UBL header — EN 16931 BT-* fields, generated UBL XML, current status |
F564233 | UBL invoice lines |
F564234 | UBL VAT summary per category / rate |
F564235 | Lifecycle events (history) |
F564236 | XSD / Schematron validation errors |
F564237 | Runtime processing log (one row per START / END / error event) |
F564250 / F564251 / F564252 | Users / Roles / Sessions |
- Dialect-aware DDL via
DatabaseDialect— Oracle (BLOB,NUMBER,VARCHAR2) and PostgreSQL (BYTEA,INTEGER,VARCHAR). - Initialize Database action in Settings creates the full schema and bootstraps default
admin/viewerroles. - JDE Julian dates stored as integers (
CYYDDD - 1900000) and converted on the fly for the UI.
Invoice status catalog
- 30+ status codes covering the full AFNOR XP Z12-014 V1.3 lifecycle:
STATUS_CREATED → STATUS_VALIDATED → STATUS_SENT_TO_PA → STATUS_PENDING → STATUS_DEPOSITED → …plus dispute, factoring and routing-error states. - Internal workflow codes (
9900–9907) and PA-mapped UNTDID 1373 codes (1,8,10,37,43,45–51). - All codes / labels / PA mappings are data-driven from the
statusessystem template — editable in Settings. StatusTransition.apply()updatesF564231and inserts anF564235lifecycle event in one call.
CLI
Long-running and one-shot modes — all driven from a single config.json:
| Mode | Purpose |
|---|---|
-config | Open the Swing GUI (FlatLaf dark) |
-xml | Process JDE XML files: SINGLE / BURST / UBL / AUTO |
-ubl | Validate + load existing UBL files into the DB |
-fetch-single, -fetch-all | Pull from BIP / archive / directory + process |
-fetch-import | Poll PA for status of pending invoices (9906) |
-fetch-status | Retrieve PA lifecycle events and update DB |
-extract | Extract input/output files from a JDE BIP job |
-serve | Embedded HTTP server + background scheduler |
-install | Bootstrap an environment directory tree |
-password | Encode a password for storage |
-updUser | Update JDE user on submitted jobs |
Web UI (React 19 + Vite)
- Dashboard with status counters, integration-error tile, quick actions and license / build info.
- Invoices — paged + filterable list, detail modal (Summary, Parties, Lines, VAT, Notes, History, PDF tabs), in-place create / edit / copy / resend, set-status (PA or DB-only), email with PDF attachment.
- Integration Errors — every validation row in
F564236that has no matching invoice (broken submissions). - Extract & Process — single and batch fetchers from BIP, FTP, archive or local files.
- Process UBL — load and validate existing UBL XML.
- Validate — XSD + Schematron tester for ad-hoc UBL files.
- XSL Editor — Monaco-based editor with XML browser, template-aware variable picker and per-template framework installer.
- XML Viewer — Monaco-based viewer / formatter with local + server load and save.
- UBL Defaults — per-company defaults (currency, payment means, tax categories, etc.).
- Status Reference — full AFNOR XP Z12-014 V1.3 reference.
- Reason Codes — full AFNOR XP Z12-012 Annexe A reference.
- UBL Reference — BT-* glossary.
- File Versions — SQLite-backed version history for editable XSL / XSD / Schematron / RTF / config files, with upload / restore / download.
Settings (configuration manager)
- Live-edit
config.jsonfrom the browser. System templates:global,e-invoicing,e-directory,statuses,db-nomaubl,db-jde,ftp-jde,fetch-invoices. - Code lists:
invoice-types,vat-categories,vatex-codes,payment-means,scheme-ids,unit-codes,countries,note-types,currency-codes,rejection-reason-codes,action-codes,document-reference-codes,profile-ids. - Document-type templates: per-document RTF / XSL / burst-key / routing / processing-type bindings.
- API connector templates with placeholder substitution (
{{username}},{{token}},{{content}}, …) and pluggable auth (NONE,BASIC,BEARER,API_KEY,OAUTH2). - Per-company
e-invoicing-{kco}overrides.
Authentication & RBAC
- Built-in user / role / session tables (
F564250–F564252). - PBKDF2-HMAC-SHA256 password hashes, force-password-change on first login, per-role page allow-list and per-role company filter.
- Toggleable via
authEnabledinglobal(off → no login). - Default
admin(full) andviewer(read-only subset) roles bootstrapped on Initialize Database.
Background scheduler
Driven from global.fetch*Interval (minutes — 0 disables):
fetchImportInterval— periodic PA import-status polling.fetchStatusInterval— periodic PA lifecycle retrieval.fetchAll.N.{interval,label,params}— multiple batch document-processing jobs.
Embedded HTTP API
A minimal REST + static file server (com.sun.net.httpserver) hosts the React bundle at / and exposes /api/* for invoices, templates, fetch / extract, validation, file system, license, packaging, authentication, and OpenAPI documentation at /api/docs.
Email & i18n
- SMTP send (TLS / SSL) with per-invoice PDF attachment.
- Full English / French translations across the UI.
Licensing
- RS256-signed JWT licenses verified at runtime against a bundled PEM public key —
full(all features) orrestricted(read-only views) modes.