Files
Fuchs_Intranet/Fuchs/Docs/Decisions
StefanandClaude Sonnet 5 99bf5337f8 fix: invoice/reminder PDF preview 404s and their unlogged root causes
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>
2026-09-16 21:07:24 +02:00
..

Decisions

This folder holds Architecture Decision Records (ADRs) — short, immutable records of a specific technical choice, why it was made, and what it implies going forward.

What belongs here

A decision, not a how-to. If it answers "why do we do X this way, and what else did we consider," it's a decision. If it explains "how subsystem X currently works," that belongs in ../Concepts instead (and a decision often triggers a concept doc to be created/updated).

File naming

NNNN-kebab-case-title.md, four-digit zero-padded, sequential across the whole folder (0001-..., 0002-...). Never reuse or renumber.

Required YAML frontmatter

Every decision file starts with:

---
status: Accepted        # Proposed | Accepted | Superseded
date: 2026-07-03         # date the decision was accepted
applyTo:                 # glob(s) — files/areas this decision governs
  - "Fuchs/Notifications/**"
supersededBy: ""         # filename of the decision that replaced this one, if any
---

Agents (Claude, Copilot, Codex) must scan the YAML frontmatter of every file in this folder first (cheap — no need to read the body) and only read the full body of decisions whose applyTo glob matches the files they're about to touch, or whose subject is otherwise clearly relevant to the task. This keeps decision-following cheap even as the folder grows.

Body template

# NNNN — Title

## Context
What problem/situation forced a choice.

## Decision
What was decided, stated plainly.

## Consequences
What this implies for future code — constraints, follow-ups, trade-offs
accepted knowingly.

## Alternatives considered
Options that were rejected and why (optional but preferred).

Rules

  • Decisions are immutable once Accepted. Do not edit the Decision/ Consequences of an existing file to reverse it. Instead, write a new decision, set its applyTo/subject accordingly, and set the old file's status: Superseded + supersededBy: NNNN-new-file.md.
  • Follow existing decisions. Before implementing anything in an area covered by an Accepted decision, read it and conform to it. If you believe a decision is wrong, raise it with the user rather than silently deviating.
  • Capture new decisions as they happen. Whenever the user (or the code you're writing) settles a non-obvious architectural or cross-cutting choice — not a routine implementation detail — add a decision here in the same change, and create/update the matching concept doc in ../Concepts.