Files
Fuchs_Intranet/Fuchs_Database/dbo/Stored Procedures/_BackupAndClearInvoiceFile.sql
T
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

72 lines
2.6 KiB
Transact-SQL

-- NOTE: Manual-only maintenance utility. Never called from application code (IntranetController /
-- InvoiceService) — run by hand against the database when needed. Ignore this proc when
-- investigating missing invoice PDFs via app logs: it clears [fds__invoices].[file] without going
-- through the app, so it leaves no application log entry.
-- Example usage: EXEC dbo.usp_BackupAndClearInvoiceFile @InvoiceID = 'R2025-0057';
CREATE PROCEDURE [dbo].[_BackupAndClearInvoiceFile]
@InvoiceID nvarchar(25)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @invID varchar(10);
DECLARE @today datetime;
DECLARE @td nvarchar(max);
DECLARE @sql nvarchar(max) = '';
DECLARE @ErrorMessage nvarchar(500);
-- Get the invoice ID
SELECT TOP(1) @invID = [id]
FROM dbo.fds__invoices
WHERE InvoiceId = @InvoiceID;
-- Check if invoice exists
IF ISNULL(@invID, '') = ''
BEGIN
SET @ErrorMessage = 'Invoice not found: ' + @InvoiceID;
PRINT @ErrorMessage;
RETURN -1;
END
-- Generate backup table prefix with timestamp
SET @today = GETDATE();
SET @td = FORMAT(@today, 'yyyy-MM-dd_HHmm');
PRINT 'Backup table prefix: ' + @td;
BEGIN TRY
-- Build dynamic SQL for backup and update
SET @sql = @sql + N'SELECT * INTO [' + @td + ' fds__invoices] FROM dbo.fds__invoices WHERE Id = ''' + CAST(@invID as nvarchar(max)) + ''';';
SET @sql = @sql + N'SELECT * INTO [' + @td + ' fds__invoice_items] FROM dbo.fds__invoice_items WHERE InvId = ''' + CAST(@invID as nvarchar(max)) + ''';';
SET @sql = @sql + N'SELECT * INTO [' + @td + ' fds__invoice_details] FROM dbo.fds__invoice_details WHERE InvId = ''' + CAST(@invID as nvarchar(max)) + ''';';
-- Clear file column after successful backup
SET @sql = @sql + N'IF EXISTS (SELECT 1 FROM [dbo].[' + @td + ' fds__invoices])
BEGIN
UPDATE dbo.fds__invoices SET [file] = NULL WHERE [Id] = ''' + CAST(@invID as nvarchar(max)) + ''';
END;';
-- Execute the dynamic SQL
EXEC sp_executesql @sql;
PRINT 'SUCCESS: Backup created and file column cleared for Invoice: ' + @InvoiceID;
Print Formatmessage('https://sanitaerfuchs.de/intranet/do/req/idoc?id=%s&create=1', @invID);
Print Formatmessage('https://sanitaerfuchs.de/intranet/do/inv/rdoc?id=%s&create=1', @invID);
RETURN 0;
END TRY
BEGIN CATCH
SET @ErrorMessage = 'ERROR: ' + ERROR_MESSAGE();
PRINT @ErrorMessage;
RETURN -1;
END CATCH
END