fix(invoices): send invoice amounts as exact cent-rounded decimals
Playwright Tests / test (push) Canceled after 0s

Stornorechnung saves still failed: the VAT amount was formatted invariantly
("-615.3757999999999") and re-parsed under the server's de-DE culture as
-6153757999999999, overflowing numeric(10,3) in fds__setInvoice.

- FdsInvoiceData.BuildInvoiceParams: amount params are decimal(10,x) via
  AmountParam/ParseAmount (culture-independent, rounded to cents); amounts
  beyond +/-9,999,999.99 throw InvoiceAmountOutOfRangeException.
- InvoiceService.RegisterInvoiceAsync: refuses such an invoice with a logged
  error (EventId InvoiceAmountRejected) instead of storing a wrong amount.
- Invoice editor: vat_val is cent-rounded decimal, no float noise.
- Tests: InvoiceAmountParamTests replaces the float-based Storno test.
- Bump Azure.Storage.Blobs 12.30.0; OCORE/OCORE_web submodules (AngleSharp
  1.8.3, Azure.Storage.Files.Shares 12.28.0).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Stefan
2026-10-01 10:57:41 +02:00
co-authored by Claude Opus 5.5
parent 0a8384ab33
commit d0002ccdad
9 changed files with 267 additions and 56 deletions
+14 -1
View File
@@ -70,6 +70,8 @@ public class InvoiceService : IInvoiceService
private string Conn => _intranet.Intranet__SQLConnectionString;
private static readonly EventId InvoiceAmountRejected = new(0, "InvoiceAmountRejected");
public async Task<FdsInvoiceData> LoadInvoiceAsync(string id, string userAccountId, DatabaseSecurity dbSec)
{
_logger.LogDebug("LoadInvoiceAsync id={Id} user={User}", id, userAccountId);
@@ -179,7 +181,18 @@ public class InvoiceService : IInvoiceService
if (invoice.NewValues == null) { _logger.LogWarning("RegisterInvoiceAsync: no form values supplied (user={User})", userAccountId); return invoice; }
var pl = new List<SqlParameter> { SQL_VarChar("@authuser", userAccountId) };
pl.AddRange(invoice.BuildInvoiceParams(change, invId));
try
{
pl.AddRange(invoice.BuildInvoiceParams(change, invId));
}
catch (InvoiceAmountOutOfRangeException ex)
{
// Refuse rather than store a wrong amount; an empty registration makes the caller report the save as failed.
_logger.LogError(InvoiceAmountRejected, ex,
"RegisterInvoiceAsync: refused invoice {InvId} - amount {Parameter} = {Value} is out of range (user={User})",
invId, ex.Parameter, ex.Value, userAccountId);
return invoice;
}
if (change) pl.Add(SQL_VarChar("@InvId", invId));
bool hasProvisionLocation = invoice.RawProvisionLocation.Length > 0;