An invoice's finalisation-time PDF render/store (fds__setInvoiceFile) can
fail silently; the row stays finalised with [file] = NULL and inv/rdoc
(the invoice list's preview icon) 404'd forever with no trace in any log.
- inv/rdoc and rem/idoc now regenerate and persist a missing document on
read, mirroring the legacy VB "idoc" fallback (Fuchs_Database ADR 0025),
gated to finalised + non-external invoices only — a draft render must
never be persisted, since fds__setInvoiceFile/fds__setReminderFile only
write WHERE [file] IS NULL and finalisation would then silently no-op.
- StoreInvoiceDocumentFileAsync/StoreReminderDocumentFileAsync now log
every distinct failure (render threw, render produced 0 bytes, DB write
threw, DB write matched 0 rows) instead of collapsing them into a single
silent empty-array return. The 0-rows case needed switching from
setSQLValue_async (reports success once the command completes, whatever
the row count) to setSQLValue_async_result, whose AffectedRows makes a
no-op UPDATE distinguishable from an actual write for the first time.
- HandleRequestIdoc/HandleReminderIdoc no longer treat
StoreInvoiceDocumentFileAsync's Array.Empty<byte>() failure signal as
success (a bare `ct == null` check never caught it), which would have
served a 0-byte "PDF".
- rdoc/HandleRequestIdoc reject a missing 'id' form field instead of doing
wasted work with an empty id.
- _BackupAndClearInvoiceFile.sql is annotated as a manual-only maintenance
utility, never called from app code, so it's clear why running it leaves
no application log trail.
- Client-side: $inv.pdfPreviewTimeout was 120 (ms) instead of 120000 — the
preview dialog was timing out client-side after 120ms on every call.
- Fixed a genuine process-wide test flake: PdfPipelineTests and
ERechnungMapperTests both install PdfSharp's global font resolver, and
whichever ran first would win, making the other's tests fail
deterministically once eRechnungLib's SystemFontResolver got there
first. FuchsPdf.EnsureFontResolver is now also race-safe under
concurrent first use (a check-then-set without a lock). Also removed
the InvoiceService.PublishAsync failure logging added mid-session after
code review found it duplicated every call site's own, more detailed
logging against ADR 0024's single-write-path rule.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Implemented a new method `ShapeTableNames` to gather entity and navigation table names, ensuring no blanks or duplicates are included.
- Updated `FdsMfrClient` to handle cases where schema queries return no tables, logging appropriate warnings and preventing misleading errors.
- Introduced `PdfPreviewAsync` in `IntranetController` to manage PDF preview rendering, capturing detailed logs for failures and notifying users with readable error messages.
- Added comprehensive unit tests for error logging and table name shaping to ensure robustness and correctness.
- Documented decision to persist user action failures to the `fds__admin_logdebug` table, enhancing support diagnostics.
Refactored refreshRow in fis.usr.de.js to look up the row node by id inside the AJAX response handler, ensuring correct replacement even with overlapping calls (e.g., rapid checkbox toggles). This prevents errors when a previous call detaches the node before a later response arrives. Enhanced fallback to full reload for robustness. Updated fis.usr.de.min.js accordingly. Reformatted tools.js with no content changes.
- Introduced a new module for managing user groups (`usrgrp`) in the administration interface.
- Implemented CRUD operations for user groups: list, create, rename, and delete.
- Added authorization checks to ensure only users with appropriate permissions can manage groups.
- Created corresponding frontend JavaScript and SCSS files for the new module.
- Updated documentation to reflect the new group management functionality and its authorization model.
- Added SQL stored procedures for group management operations.
- Established a new login script for database access, ensuring proper security practices.
The Admin status pill previously turned green whenever the mailer was
enabled in config, without proving the account/token actually work.
Add IComService.VerifyCredentialsAsync (GET /mailer/fc?fn=verify, also
passing serverid when configured so the API confirms this server
belongs to the account) and wire it into a new "email" system-status
probe, so the pill only goes green once the Mailer API itself confirms
the credentials.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removes the per-row indentation (span.ind) in favor of a small "belongs to
set" tag shown above each row's title, so membership is visible without
tracing indentation depth even while sets are expanded. Description text
is now clamped to 2 lines with ellipsis by default and expands to a
scrollable ~12-line view on click.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wire up two previously-unwired stored procedures for the usr module:
- fis_admin_createUserAccount gained the missing @authaccount +
intranet_admin auth gate, a duplicate-email check, and now returns
the (possibly auto-generated) password so it can be emailed to the
new user, mirroring fis_admin_resetUserPassword's pattern.
- fis_admin_delUserAccount gained the same missing auth gate plus a
guard against an admin deleting their own account.
New /do/usr/create and /do/usr/delete endpoints in
IntranetController.AdminUsers.cs, a "Neuer Benutzer" button and a
"Benutzer löschen" ribbon action in fis.usr.js, and unit tests for the
new welcome-email content (BuildWelcomeEmail).
Also restyle the user list (fis.usr.scss) from a plain bordered table
to a card-style row grid with avatar-initial badges and status pills,
matching the Admin dashboard's visual language.
Documented as ADR 0020 and in the admin-user-management concept doc.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
<img> is a nominally inline HTML element, but TinyMCE content places images
between paragraphs of text; without a forced line break before/after, an
image could render packed against adjacent text in the PDF. Insert a line
break ahead of the image (unless one is already there) and always after it.
Also fixes the img handler's early-return path to still apply the inline
size cap (MaxInlineImageWidth/Height) when only one of width/height was
resized, matching the aspect-ratio-locked branch.
Fuchs/wwwroot/web/tools.js: cosmetic gulp rebuild diff (leading blank line),
no behavior change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Set mit Preis" / "Nur Set mit Preis" collapsed every block into an inserted
"Gesamtumfang pauschal" row, nulled/removed all lines and cleared the user-defined
set structure. Both modes now convert every still-unconverted top-level set:
the set line takes the sum of its subtree, its members render without a price
(setprice) or are removed from itm + items (setonly); lines outside a set stay
individually priced and several sets stay separate lines. Nested sets are priced
once via their top-level set. One conversion primitive (ConvertSetHeader) now
backs all three triggers; item.setprice is a no-op on an already-converted set
(a repeated delta used to reset the set price to 0). The header's price_net is
mirrored too (flat PDF path unit price), and Build treats a null total_net as
"no price" like the flat path. The editor checks for a set before offering the
modes and the menu entry stays available for sets added later.
ADR 0009 is superseded by 0019; INVOICE_SET_PRICING.md rewritten accordingly.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Fuchs/gulpfile.js: pass encoding:false to gulp.src() in the copy task —
vinyl-fs otherwise UTF-8-decodes binary files by default, which was
silently corrupting the glyphicons webfont (and any other binary asset
copyconfig.json matches) on every `gulp copy`/`gulp all` run.
- Invoice/reminder editor: replace the row's flat action icons (edit/
delete/insert/set-price) with a single context button opening a
dropdown menu, reusing the existing $ocms.menu toolbar mechanism.
- Fix "-1" leaking into the Pos. column for Text/Title rows: $inv.rrw's
`dta.position || dta.SortOrder` fallback treated the numbering's
intentional empty string as unset and fell back to SortOrder (-1 for
such rows); now only a genuinely uncomputed position falls back.
- Zusatzempfänger (CC) is now edited together with the main invoice
email in one combined dialog ($inv.emailFrm/$inv.eEmail), wired into
all three entry points (create, continue, storno) instead of a
separate hover-revealed row with its own edit target.
- Add tooltips to every row/header edit control, including several that
had none before (header field pencils, the request/section title
edit button), phrased as object+verb ($rct.frmTT).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lets a back-office user correct/define which invoice line belongs to
which "set" and how deep it is nested, instead of relying only on the
implicit "everything after a Set header until the next Set" grouping
fds__prepInvoice derives. Sets may contain sets; every change applies
immediately to the cached draft session and is reflected in the editor
and PDF (ADR 0006/0008).
Backend:
- InvoiceSetStructure: pure parent-link forest (assign, change level,
create, place/move-sibling, depth-first linearise + level recompute).
- InvoiceDraftSetStructureDeltas: new inv/dpatch targets set.assign,
set.level, set.create, set.move.
- InvoiceSetPricing.Build is now order-preserving and nesting-aware
(governed by the nearest converted ancestor); the set-item switch
sums a header's transitive descendants so nested conversions never
double-count.
- FuchsPdf indents nested set members per level, mirroring the editor.
Frontend:
- New fis.inv_sets.js: "Set-Struktur bearbeiten" dialog — per-block
table of all lines with drag & drop reorder (before/after/into),
▲▼ order and ◀▶ level buttons, fold/unfold per set (view-only), and
a create bar for new Set/Text lines. Posts one delta per action; the
dialog is a pure view of the server state (ADR 0006).
- _Layout.cshtml / fis_main.js: cache-bust the dynamically imported
per-module bundles (web/fis.<mdl>.<locale>.js/.css) with an asset
version derived from the newest bundle write time, so a gulp rebuild
is picked up without a stale cached module.
Docs: ADR 0016, live-draft-editing concept, INVOICE_SET_PRICING.md,
USER_GUIDE.md, ARCHITECTURE.md updated. Tests: InvoiceSetStructureTests
(new) + additions to InvoiceDraftServiceTests/InvoiceSetPricingTests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Splits fn=admin into two independent modules: the original status
dashboard stays at fn=admin, user management moves to a new fn=usr
(IntranetController.AdminUsers.cs, fis.usr.js). Both are now reached
through a single "Administration" sidemenu dropdown (Systemüberwachung /
Benutzerverwaltung) gated by one shared fds_sys > 4 check, per ADR 0015
(supersedes 0014's "fold into one module" choice).
Also fixes the password-reset validation to check only a plain 6-char
minimum instead of silently also requiring ValidatePassword's default
2-uppercase/2-lowercase/2-digit composition, which contradicted the
"mind. 6 Zeichen" message shown to the admin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Routine dependency bumps (OpenTelemetry, Identity/JWT, ImageSharp,
HtmlAgilityPack, BouncyCastle.Cryptography, Microsoft.Extensions.*,
xunit.v3/Test SDK) across Fuchs, Fuchs.Tests, Fuchs_DataService, and
MFR_RESTClient. Also rebuilds the tools.js bundle and adds the VB.NET
Companion language-bridge setting used for the Intranet_Legacy VB code.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wires the previously-unused fis_admin_* user/group stored procs into the Admin
module (fds_sys-gated), adds a users grid with a per-row ribbon (reset
password, change email, manage group membership), and always emails a
password reset result to the user rather than showing it to the admin.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes the eRechnung output path: finalized invoices are emitted as a
ZUGFeRD/Factur-X EN 16931 hybrid (DATEV) or, when a Leitweg-ID is present, as
XRechnung 3.0 for B2G. Both are externally validated as ACCEPTED (0 errors) and
PDF/A-3B COMPLIANT against the ProcessWeb eInvoice service.
- ERechnungMapper: FdsInvoiceData -> EN 16931 model. Seller master data now from
Fuchs:ERechnung:Seller config (VAT id BT-31, Steuernummer BT-32, optional
Handelsregister BT-30, IBAN/BIC, contact). Adds payment terms BT-20/BT-9
(BR-CO-25), buyer VAT id, §13b reverse charge, and the service date/period
(BT-72 / BG-14) parsed from ProvisionPeriod (BR-DE-TMP-32). B2G -> XRechnung
profile with the required electronic addresses/contact.
- ERechnungValidator: client for POST /validatepdf (EN 16931 XML + PDF/A-3 in
one call). A pure "scenario not matched" with zero errors is not treated as a
hard failure; real errors optionally withhold the hybrid (FailOnError).
- ERechnungSettings: seller + validation config; wired in Program.cs; ServiceUrl
in appsettings.
- Online editor: structured German-only dialogs for the recipient address
(incl. Leitweg-ID) and the service date/period (single date or range).
- Bumps the eRechnungLib submodule to the CII rsm-namespace / PDF-A subtype fix.
Fuchs.Tests 514/514, eRechnungLib 97/97.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Created a new function `fds__getCompanyAddressJson` to return a company's postal address as a structured JSON object.
- Modified stored procedures `fds__createInvoice`, `fds__setInvoice`, and `fds__prepInvoice` to include a new parameter `@SendToAddressJson` for handling the address data.
- Updated the invoice table and user-defined types to accommodate the new `SendToAddressJson` field.
- Ensured that the address data is properly retrieved and stored in the invoice records.
- Ensure all email/SMS send attempts are audit-logged, including API errors, exceptions, invalid addresses, and disabled service cases; logs now include raw API responses or exception details for better diagnostics.
- Fix audit log parameter builder to never pass NULL for NOT NULL columns (`config`, `DateSent`).
- Add unit tests covering all send paths and audit log correctness.
- Percent-encode non-ASCII/control chars in `DocumentMetadataBuilder` for safe HTTP header metadata; add tests.
- Simplify admin test mail dialog JS and improve success handler robustness.
- Set `charset=utf-8` in admin controller JSON responses for OCORE consistency.
- Update minified JS to match new admin dialog logic.
- Introduced new JavaScript file `fis.admin_txt_de.js` for German translations of administration-related terms and messages.
- Created `fis.admin.css` for styling the administration interface, including layout, cards, and buttons.
- Added `fis.admin.de.js` for the main functionality of the administration module, implementing features such as system status checks and email testing.
- Minified version of the German JavaScript file created as `fis.admin.de.min.js`.
- Minified CSS file created as `fis.admin.min.css` for optimized loading.
- Introduced comprehensive unit tests for the Fuchs_DataService library, covering DATEV header formatting, CSV/XML generation, and FdsMfrClient construction.
- Implemented tests for FdsMfr.UpdateNeed parsing and FdsShared utility helpers, ensuring correct functionality and stability.
- Added tests for FdsConfig and FdsMfrClient to validate configuration resolution and client construction.
Document decisions on backend-authoritative invoice and reminder handling
- Created ADR 0008 to clarify that all invoice types and reminder stages are backend-authoritative during drafting and previewing.
- Established that all calculations and settings must be processed server-side, ensuring consistency between online editor and PDF outputs.
Define irreversible mutations for set-price modes in invoices
- Documented ADR 0009 to specify that the "Set mit Preis" and "Nur Set mit Preis" operations are irreversible mutations affecting service request blocks.
- Clarified that these operations are not display toggles but actual data changes, ensuring clear expectations for invoice handling.
Transition MFR ERP sync to in-process execution within the web app
- Created ADR 0010 to outline the migration of Fuchs_DataService from a standalone service to an in-process library within the Fuchs web application.
- Updated configuration and logging management to be handled by the host application, streamlining the sync process.
Add publish profile and periodic hosted service for job scheduling
- Introduced a publish profile for deployment to a specified folder.
- Implemented PeriodicHostedService to manage multiple independent jobs, including the MFR ERP sync, with configurable execution intervals.
Add dotnet-tools.json for EF Core CLI tools
- Included dotnet-tools.json to manage the version of dotnet-ef for Entity Framework Core migrations and commands.
Move the invoice draft editor to a backend single source of truth: an
in-memory InvoiceDraftSession (per-token, cached) holds the editable payload,
server-computed sums/VAT and validation, plus an automatic change history. The
browser posts single edits; the server recomputes and signals the editing
session over a dedicated SignalR hub (DraftPreviewHub) to re-fetch.
This reverses the previously-documented stateless editor (EVAL_live_invoice_editing,
INVOICE_LIFECYCLE §10), by explicit product decision — captured in ADR 0006 and 0007
plus the live-draft-editing concept doc.
Backend (this milestone):
- InvoiceDraftSession + ChangeHistoryEntry data holders
- InvoiceDraftCalculator: pure port of quantChange/invSumUpdate (§13b, VAT-by-rate)
and consistency checks — fully unit-tested
- IInvoiceDraftCache/InvoiceDraftCache: in-memory store with idle sliding TTL
- IInvoiceDraftService/InvoiceDraftEditService: open (payload or DB reload), patch,
build state, flush via existing RegisterInvoiceAsync (no new persistence), preview
from cache, discard (DB reload), history
- InvoiceDraftExpiryService: pre-expiry warning + eviction-with-reason
- DraftPreviewHub + IDraftNotifier/DraftNotifier: targeted draftReady/draftExpiring/
draftClosed signals per draft token
- inv/dopen|dstate|dpatch|dpreview|dsave|dhistory|ddiscard|dclose endpoints; save
reports success/failure via the existing EventService
- DI + hub mapping in Program.cs
Frontend (additive foundation): $fis.draft SignalR client for /draftpreview.
The editor DOM inversion (routing deltas, rendering from server state) is the
next, separately-verified step; existing endpoints are unaffected.
Tests: 30 new (calculator, cache, expiry, patch/history, flush); 306 total passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Modified the stored procedure `fds__admin_getReportCatalog.sql` to use the correct schema for `all_objects`.
- Added new folders and projects for `eRechnungLib` in the solution file `Fuchs_Intranet.slnx`, including validation and test projects.
- Updated submodule reference for `OCORE`.
- Added new submodule `eRechnungLib` with initial commit.
- Increased the length of the NameOfPayer field from NVARCHAR(60) to NVARCHAR(140) in the banking transactions function, table, temporary table, and user-defined type to accommodate longer names.
- Expanded the SepaRemittanceInformation field from VARCHAR(150) to VARCHAR(200) in the banking transactions function for more detailed remittance information.
- Modified the stored procedure fds__setBankingtransaction_done to return additional transaction data (ValueDate and Amount) along with the success flag for improved notification messaging.
- Added validation in MFRClientConfig constructor to ensure the provided URL is not null or empty, enhancing error handling for configuration settings.
- Updated FdsSqlOptions to accept an optional ILogger parameter for improved error logging.
- Modified FdsMfr and FdsMfrClient classes to pass the logger instance to FdsSqlOptions.
- Added detailed error logging in various methods to capture SQL execution issues and file handling errors.
- Improved documentation for FdsSqlOptions to clarify logging behavior.
- Updated Archive class to log compression errors, enhancing traceability of failures.
- Adjusted project configuration to suppress specific warnings related to transitive dependencies.
- Added NuGet.config to define package sources for dependency management.
- Updated submodule references for OCORE and related projects.
- Add AzureBlobStorageService, DocumentArchiveSyncService, and related config for secondary PDF archiving of invoices/reminders
- Add SQL procs and schema changes for archive backfill
- Update invoice/reminder services to upload PDFs to blob storage
- Add telemetry counters and unit tests for blob storage/archive logic
- Add Fuchs:Email:OverrideRecipient config and enforce dev/test email redirect in ProcessWebComService, with tests
- Improve JS date parsing (German formats), stricter JSON date detection
- Increase widget SQL timeouts, update dependencies, docs, and project files
- FdsConfig.Initialize now accepts IConfiguration for DI support; Program.cs updated to pass builder.Configuration.
- Gulp "copy" task logs copied files for better feedback.
- Bank statement file input now accepts all file types.
- Updated glyphicon font binary assets.
Front-end (fis.inv_shared.js / fis.inv_txt_de.js, rebuilt bundles):
- 3-way set display switch (setprice/itemprices/setonly) via admin.setmode,
emitted into InvoiceOptions by FdsInvoiceData.BuildInvoiceOptions.
- Each request block now posts items[] in the backend contract shape
(title/desc/qty/price_net/total_net + set type/setId tags) via itemToContract.
- invcPayload normalises the editor model to the field names BuildInvoiceParams
reads (sms totals -> new.total_net/total_gross, invoicetitle->title,
loc->provisionlocation, admin.paymentterms->new.paymentterm, CustomerId->customerid).
Back-end:
- BuildInvoiceOptions adds the setmode token alongside §13b.
- VAT rate+amount now taken from sms.vat (HighestVat) instead of the broken
items 'is List<object>' detection that pinned the rate to 19.
- InvoiceSetPricing blanks price/total cells for text/title heading lines.
Tests: set-pricing text-line blanking, HighestVat selection/parsing, updated
the VAT param test to the sms.vat contract. 180 passing.
Note: the second VAT slot (InvoiceVAT_2) stays unused by design; mixed-rate
invoices store only the highest rate (pre-existing, accepted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New CAMTParser project: namespace-agnostic parser for camt.052/053/054
producing a statement/entry model aligned with the banking columns
(account, amount, debit/credit, dates, counterparty, references, remittance).
- BankingService now auto-detects the upload format (XML→CAMT, else MT940)
and maps either into the same fds__tt__bankingtransactions DataTable, so the
bam/up handler transparently accepts both.
- Frontend (fis.bam.de.js) upload field now advertises accept for both
MT940 (.sta/.mt940/.txt) and CAMT (.xml/.camt).
- Tests (+14, 151 total): CamtParserTests cover parsing (credit/debit,
namespace-version agnostic, reversals), detection, and failure/edge inputs
(empty, invalid XML, non-CAMT); BankingDualFormatTests verify CAMT and MT940
both land in the same DataTable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extensive structured logging was added throughout IntranetController and all invoice/account handlers to improve traceability and debugging. Logging now covers action entry/exit, error conditions, and key parameters (user IDs, invoice IDs, etc.). Handlers log warnings for missing/invalid input and info/debug for significant events. Minor refactoring extracts form values for better logging. The jQuery `rwText` plugin was hardened against null input. Updated minified JS, font assets, and OCORE submodule. No functional changes to `tools.js`.