Follow-up to 99bf533 from code review:
- The AffectedRows == 0 guard in Store{Invoice,Reminder}DocumentFileAsync
was dead: fds__setInvoiceFile / fds__setReminderFile run SET NOCOUNT ON,
so ExecuteNonQuery returns -1 whether or not the UPDATE ... WHERE [file]
IS NULL matched. Both procs already return the updated Id through their
OUTPUT clause, so read that result set (getSQLDatatable_async) instead:
empty rowset = no-op store, logged, and the blob upload / FileCreated
event are skipped rather than reporting unpersisted bytes as success.
- inv/rdoc's regenerate path built the download name from the raw
DocumentName; a user-typed title with '/' truncated FileInfo.Name.
SafeDocumentFileName strips editor markup and every invalid filename
char, and the path no longer goes through FileInfo at all.
- Reminder counterpart tests for the store-file failure branches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>