- Updated the stored procedure `fds__admin_backupAndClearInvoiceFile.sql` to back up not only the invoice file but also JSON snapshots of related data (invoices, items, details, service requests) to ensure consistency and traceability.
- Modified the table structure of `fds__invoice_file_backups` to accommodate new JSON columns for storing these snapshots.
- Added an ADR (0030) document to outline the decision and rationale behind these changes, emphasizing the importance of maintaining a consistent set of data alongside the backed-up file.
An admin-only page that surfaces the otherwise hidden maintenance actions as
cards, each a small form. It exists so support does not need SSMS or a
hand-crafted POST for actions like resetting an invoice's stored PDF. It is
a regular lazily loaded frontend module (fn = specialties), gated like the
rest of the Administration menu (fds_sys > 4), and reachable both from the
menu (Administration → Spezialfunktionen) and by opening the URL
/specialties directly.
The menu entry is deliberately hidden: it only appears when the Administration
dropdown is opened with Shift held (Shift+click, or Shift+Enter/Space on
the focused toggle); a plain open hides it again
($fis.bindShiftOnlyMenuItems in fis_main_menu.js, driven by the item's
lclass: 'shift-only'). This keeps the maintenance actions out of casual
reach without adding another authorization level — the item is still part of
the menu array, so the /specialties deep link is unaffected.
How it works
Module load follows the standard contract (see
admin-system-status.md): the menu item init:specialties →
$ocms.init('specialties') → POST /do/specialties/auth → when
manage > 0, import web/fis.specialties.de.js + fis.specialties.css
and call init2. IntranetController.Specialties.cs answers only that
auth probe (same GetSystemAdminAuthAsync gate as the Admin module) and
401s everything else.
Deep link.IntranetController.Index renders the same SPA shell for
every /{fn} URL and ignores fn; there is no client router. fis_main_go.js
therefore calls $fis.openModuleFromPath() once the admin menu has settled:
it takes the first path segment and calls $ocms.init(fn)only if the
user's menu offers an init:<fn> item (admin dropdown included). A
non-admin opening /specialties sees the dashboard, as before; arbitrary
paths never trigger a module load.
Cards.fis.specialties.js keeps an array cards of builder
functions. Each card is title + description + <form> + result box;
$spc.submit(c, { button, confirm, run, report }) wires HTML5 validation,
a confirm(), the postXT call (run(values) returns the request) and
the inline result (report(response) → { ok, lines }). The card calls
the action's real endpoint — every such endpoint enforces the admin
gate itself; the page adds no authority.
Current cards
Rechnungsdatei zurücksetzen (Soft-Delete) → POST /do/admin/backupclearinvoicefile/<internal Id> (ADR 0027). Shows the
outcome and the blob backup URI, or the German text for the service's
reason (invoice_not_found, not_final, external, no_file,
db_error). An invoice without a stored file (hasFile from
fds__getInvoice) is answered no_file before any blob copy, so
repeated clicks leave no spare backup_* blobs.
Adding a card: append a builder to cards, add its texts to
fis.specialties_txt_de.js, run npx gulp min. No server change is
needed unless the action itself is new.
Build: bundle entries in Fuchs/bdlconfig.json
(intranet:specialties); generated files under wwwroot/web/.