Refactor code structure for improved readability and maintainability
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
2026-07-08 19:33:23 +02:00
parent 4abf81cd7d
commit 59a2b86c09
23 changed files with 676 additions and 60 deletions
@@ -240,11 +240,22 @@ public partial class IntranetController
}
// ── Form helpers ─────────────────────────────────────────────────────────
// Reads from the posted form when available, falling back to the query string. This
// supports endpoints in _allowedGet (e.g. req/idoc, rem/idoc) that are invoked via a
// plain GET (window.open with '?id=...'), where Request.Form has no Content-Type and
// would otherwise throw InvalidOperationException.
protected bool HasForm(params string[] keys) =>
keys.All(k => Request.Form.ContainsKey(k) && !string.IsNullOrWhiteSpace(Request.Form[k]));
keys.All(k => !string.IsNullOrWhiteSpace(FormValue(k)));
protected string Form(string key, string fallback = "") =>
Request.Form.TryGetValue(key, out var v) ? v.ToString() : fallback;
FormValue(key) ?? fallback;
private string? FormValue(string key) =>
RequestValueHelper.Resolve(
Request.HasFormContentType,
Request.HasFormContentType ? Request.Form : Microsoft.AspNetCore.Http.FormCollection.Empty,
Request.Query,
key);
private static (DateTime? From, DateTime? To) BankingDateRange(System.Data.DataTable tbl)
{
@@ -87,7 +87,13 @@ public partial class IntranetController
}
catch (Exception ex)
{
_logger.LogError(ex, "HandleInvoiceGet failed for id={InvoiceId} user={User}", Form("id"), UserAccountID);
string invoiceId = Form("id");
_logger.LogError(ex, "HandleInvoiceGet failed for id={InvoiceId} user={User}", invoiceId, UserAccountID);
// This handler has its own catch (returns 500) and so never reaches the Do safety net;
// notify the user here so a failed invoice load is not silently swallowed.
await _events.InvoiceIssueAsync(
"Die Rechnung konnte aufgrund eines Fehlers nicht geladen werden.",
UserAccountID, invoiceId);
return StatusCode(500);
}
}
@@ -388,8 +394,22 @@ public partial class IntranetController
sqldset.Tables("itm").Columns.Contains("order") ? "order" : ""))
{
var d = sitm.toObjectDictionary();
double net = Convert.ToDouble(d.no("value_total", 0));
double vat = Convert.ToDouble(d.no("vat", 0));
double net = Convert.ToDouble(d.no("value_total", 0));
double vat = Convert.ToDouble(d.no("vat", 0));
double value = Convert.ToDouble(d.no("value", 0));
string quantityStr = d.nz("Quantity");
// quantityhours/UnitString reconstruct the hour-based quantity editor's raw
// fields (e.g. "5 Std" -> quantityhours=5, UnitString="Std") from the persisted
// "Quantity" string, mirroring the legacy fds__invoice_data ndic mapping — without
// this, re-editing a reloaded hour-based item showed an empty quantity field.
object quantityHours = "";
if (value != 0 && !string.IsNullOrEmpty(quantityStr))
{
long qh = (long)(net / value);
if (quantityStr.StartsWith(qh.ToString(CultureInfo.InvariantCulture) + " ", StringComparison.Ordinal))
quantityHours = qh;
}
string unitString = !string.IsNullOrEmpty(quantityStr) ? quantityStr.RightFromFirst(" ") : "";
itm.Add(new Dictionary<string, object?>
{
["Id"] = d["Id"],
@@ -399,8 +419,11 @@ public partial class IntranetController
["svcnet_val"] = d.no("value_service", 0),
["net"] = d.no("value", 0),
["quantity"] = d["Quantity"],
["quantityhours"] = quantityHours,
["UnitString"] = unitString,
["Type"] = d["Type"],
["Note"] = null,
["NameOrNumber"] = "",
["htmltext"] = d["Text"],
["position"] = d["Position"],
["SortOrder"] = d["SortOrder"]
@@ -22,7 +22,9 @@ public partial class IntranetController
{
case "get":
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("Reminder get: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogDebug("Reminder get: preparing reminder for invoice {InvId} type={Type} level={Level} user={User}",
Form("id"), Form("type"), Form("level"), UserAccountID);
var pl = StdParamlist(
SQL_VarChar("@InvId", Form("id")),
SQL_VarChar("@type", Form("type")),
@@ -32,17 +34,21 @@ public partial class IntranetController
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "rem" },
Security: DbSec, options: SqlOpt(fn, id, code));
if (!string.IsNullOrEmpty(dset.Exception))
_logger.LogError("Reminder get: SQL error for invoice {InvId}: {SqlError}, user={User}", Form("id"), dset.Exception, UserAccountID);
return await JSONAsync(new { rm = dset.Table("rem").FirstRow.toObjectDictionary() });
}
case "prep":
{
if (!HasForm("remc")) return BadRequest400();
if (!HasForm("remc")) { _logger.LogWarning("Reminder prep: missing form field 'remc', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Reminder prep: creating draft reminder, user={User}", UserAccountID);
var ctd = JsonConvert.DeserializeObject(Form("remc"))!;
var fdRem = await _reminders.RegisterReminderAsync(
new FdsReminderData(ctd), change: false, remId: "", UserAccountID, DbSec);
if (!string.IsNullOrEmpty(fdRem.Id))
{
_logger.LogInformation("Reminder prep: draft reminder {RemId} created, user={User}", fdRem.Id, UserAccountID);
await _events.ReminderDraftCreatedAsync(fdRem, UserAccountID);
var imgcol = await _pdf.DocToImageCollectionAsync(_reminders.GenerateReminderPdf(fdRem, fdRem.IsDraft));
return await JSONAsync(new { id = fdRem.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
@@ -54,7 +60,8 @@ public partial class IntranetController
case "srs":
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("Reminder srs: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Reminder srs: marking reminder {RemId} as sent, user={User}", Form("id"), UserAccountID);
var pl = StdParamlist(SQL_VarChar("@Id", Form("id")), SQL_Bit("@auto", false));
var dt2 = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__setReminderSent] @Id, @auto, @authuser;",
@@ -63,17 +70,25 @@ public partial class IntranetController
if (string.IsNullOrEmpty(dt2.Exception))
await _events.ReminderMarkedSentAsync(Form("id"), Form("id"), UserAccountID);
else
{
_logger.LogError("Reminder srs: SQL error marking reminder {RemId} sent: {SqlError}, user={User}", Form("id"), dt2.Exception, UserAccountID);
await _events.ReminderIssueAsync(
$"Mahnung {Form("id")} konnte nicht als versandt markiert werden.",
UserAccountID, Form("id"));
}
return string.IsNullOrEmpty(dt2.Exception) ? await JSONAsync(new { ok = true }) : StatusCode(500);
}
case "rdoc":
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("Reminder rdoc: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogDebug("Reminder rdoc: fetching stored reminder document {RemId} typ={Typ} user={User}", Form("id"), Form("typ"), UserAccountID);
var (file, fc) = await _reminders.GetStoredFileAsync(Form("id"), UserAccountID, DbSec);
if (file == null || fc == null) return StatusCode(404, new { error = "Dokument wurde nicht gefunden" });
if (file == null || fc == null)
{
_logger.LogWarning("Reminder rdoc: document not found for reminder {RemId} user={User}", Form("id"), UserAccountID);
return StatusCode(404, new { error = "Dokument wurde nicht gefunden" });
}
return Form("typ") != "img"
? await FileContentResultAsync(fc, file.MimeType(), file.Name)
: await JSONAsync(new { id = Form("id"), img = await BuildPdfImageArray(fc) });
@@ -84,13 +99,16 @@ public partial class IntranetController
case "lrem":
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("Reminder lrem: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogDebug("Reminder lrem: listing reminders for invoice {InvId} user={User}", Form("id"), UserAccountID);
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__lookupReminders] @InvId, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@InvId", Form("id"))),
tablenames: new[] { "ov", "rem" },
Security: DbSec, options: SqlOpt(fn, id, code));
if (!string.IsNullOrEmpty(dset.Exception))
_logger.LogError("Reminder lrem: SQL error for invoice {InvId}: {SqlError}, user={User}", Form("id"), dset.Exception, UserAccountID);
return await JSONAsync(new
{
ov = dset.Table("ov").FirstRow.toStringDictionary(),
@@ -98,18 +116,23 @@ public partial class IntranetController
});
}
default: return await JSONAsync(new { ok = true });
default:
_logger.LogWarning("Do_Process_Reminder: unhandled action id={Id}, user={User}", id, UserAccountID);
return await JSONAsync(new { ok = true });
}
}
private async Task<IActionResult> HandleReminderConf(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("HandleReminderConf: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("HandleReminderConf: finalizing reminder {RemId} user={User}", Form("id"), UserAccountID);
var dt = await getSQLDatatable_async(
"EXECUTE [dbo].[fds__setReminderFinal] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code));
if (!string.IsNullOrEmpty(dt.Exception))
_logger.LogError("HandleReminderConf: SQL error finalizing reminder {RemId}: {SqlError}, user={User}", Form("id"), dt.Exception, UserAccountID);
var frdic = dt.FirstRow.toObjectDictionary();
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
{
@@ -167,9 +190,10 @@ public partial class IntranetController
private async Task<IActionResult> HandleReminderIdoc(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) { _logger.LogWarning("HandleReminderIdoc: missing/empty form field 'id', user={User}", UserAccountID); return StatusCode(404); }
_logger.LogDebug("HandleReminderIdoc: reminderId={RemId} typ={Typ} create={Create} user={User}", Form("id"), Form("typ"), Form("create", "0"), UserAccountID);
var fdRem = await _reminders.LoadReminderAsync(Form("id"), UserAccountID, DbSec);
if (string.IsNullOrEmpty(fdRem.Id)) return StatusCode(404, new { error = "Erinnerung wurde nicht gefunden" });
if (string.IsNullOrEmpty(fdRem.Id)) { _logger.LogWarning("HandleReminderIdoc: reminder not found id={RemId} user={User}", Form("id"), UserAccountID); return StatusCode(404, new { error = "Erinnerung wurde nicht gefunden" }); }
string filename = fdRem.ReminderRegistration!.nz("DocumentName").ne($"Zahlungserinnerung_{fdRem.Id}.pdf");
if (Form("typ") != "img")
{
@@ -184,7 +208,8 @@ public partial class IntranetController
private async Task<IActionResult> HandleReminderResend(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) { _logger.LogWarning("HandleReminderResend: missing/empty form field 'id', user={User}", UserAccountID); return StatusCode(404); }
_logger.LogInformation("HandleReminderResend: resending reminder {RemId} user={User}", Form("id"), UserAccountID);
var pl = StdParamlist(SQL_VarChar("@Id", Form("id")), new SqlParameter("@includefile", true));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getReminder] @Id, @includefile, @authuser;",
@@ -27,13 +27,16 @@ public partial class IntranetController
case "rthd":
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("Requests rthd: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Requests rthd: toggling hidden state for request {ReqId}, user={User}", Form("id"), UserAccountID);
var sqldt = await getSQLDatatable_async(
"EXECUTE [dbo].[fds__toggleRequestHidden] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_BigInt("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code));
if (sqldt.Count == 0) return StatusCode(404, new { error = "not found" });
if (!string.IsNullOrEmpty(sqldt.Exception))
_logger.LogError("Requests rthd: SQL error for request {ReqId}: {SqlError}, user={User}", Form("id"), sqldt.Exception, UserAccountID);
if (sqldt.Count == 0) { _logger.LogWarning("Requests rthd: request {ReqId} not found, user={User}", Form("id"), UserAccountID); return StatusCode(404, new { error = "not found" }); }
var dic = sqldt.FirstRow.toObjectDictionary();
return await JSONAsync(new { id = dic["EntityId"], visible = dic.no("hidden", false) is not true });
}
@@ -45,12 +48,17 @@ public partial class IntranetController
case "save":
{
if (!HasForm("invc")) return BadRequest400();
if (!HasForm("invc")) { _logger.LogWarning("Requests save: missing form field 'invc', user={User}", UserAccountID); return BadRequest400(); }
bool saveChange = !string.IsNullOrEmpty(Form("id"));
_logger.LogInformation("Requests save: saving invoice draft change={Change} invId={InvId} user={User}", saveChange, Form("id"), UserAccountID);
var fdInv = await _invoices.RegisterInvoiceAsync(
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
change: !string.IsNullOrEmpty(Form("id")), invId: Form("id"), UserAccountID, DbSec);
change: saveChange, invId: Form("id"), UserAccountID, DbSec);
if (!string.IsNullOrEmpty(fdInv.Id))
await _events.InvoiceDraftRegisteredAsync(fdInv, !string.IsNullOrEmpty(Form("id")), UserAccountID);
{
_logger.LogInformation("Requests save: invoice draft {InvId} saved, user={User}", fdInv.Id, UserAccountID);
await _events.InvoiceDraftRegisteredAsync(fdInv, saveChange, UserAccountID);
}
return !string.IsNullOrEmpty(fdInv.Id)
? await JSONAsync(new { id = fdInv.Id })
: await InvoiceIssueResult("Die Rechnung konnte aufgrund eines Fehlers nicht gespeichert werden.");
@@ -58,12 +66,14 @@ public partial class IntranetController
case "sprep":
{
if (!HasForm("invc")) return BadRequest400();
if (!HasForm("invc")) { _logger.LogWarning("Requests sprep: missing form field 'invc', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Requests sprep: preparing new invoice draft, user={User}", UserAccountID);
var fdInv = await _invoices.RegisterInvoiceAsync(
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
change: false, invId: "", UserAccountID, DbSec);
if (!string.IsNullOrEmpty(fdInv.Id))
{
_logger.LogInformation("Requests sprep: invoice draft {InvId} created, user={User}", fdInv.Id, UserAccountID);
await _events.InvoiceDraftRegisteredAsync(fdInv, changed: false, userAccountId: UserAccountID);
var imgcol = await _pdf.DocToImageCollectionAsync(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
@@ -73,12 +83,14 @@ public partial class IntranetController
case "sedit":
{
if (!HasForm("id", "invc")) return BadRequest400();
if (!HasForm("id", "invc")) { _logger.LogWarning("Requests sedit: missing form field 'id'/'invc', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Requests sedit: updating invoice draft {InvId}, user={User}", Form("id"), UserAccountID);
var fdInv = await _invoices.RegisterInvoiceAsync(
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
change: true, invId: Form("id"), UserAccountID, DbSec);
if (!string.IsNullOrEmpty(fdInv.Id))
{
_logger.LogInformation("Requests sedit: invoice draft {InvId} updated, user={User}", fdInv.Id, UserAccountID);
await _events.InvoiceDraftRegisteredAsync(fdInv, changed: true, userAccountId: UserAccountID);
var imgcol = await _pdf.DocToImageCollectionAsync(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
@@ -87,25 +99,36 @@ public partial class IntranetController
}
case "sdel":
if (!HasForm("id")) return BadRequest400();
await setSQLValue_async("EXECUTE [dbo].[fds__remInvoice] @Id, @authuser;",
{
if (!HasForm("id")) { _logger.LogWarning("Requests sdel: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("Requests sdel: deleting invoice draft {InvId}, user={User}", Form("id"), UserAccountID);
var ok = await setSQLValue_async("EXECUTE [dbo].[fds__remInvoice] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code));
if (!ok)
{
_logger.LogError("Requests sdel: SQL failed deleting invoice draft {InvId}, user={User}", Form("id"), UserAccountID);
return await InvoiceIssueResult("Die Rechnung konnte aufgrund eines Fehlers nicht gelöscht werden.", Form("id"));
}
return await JSONAsync(new { ok = true });
}
case "sconf": return await HandleRequestSconf(fn, id, code);
case "idoc": return await HandleRequestIdoc(fn, id, code);
case "resend": return await HandleRequestResend(fn, id, code);
default: return await JSONAsync(new { ok = true });
default:
_logger.LogWarning("Do_Process_Requests: unhandled action id={Id}, user={User}", id, UserAccountID);
return await JSONAsync(new { ok = true });
}
}
private async Task<IActionResult> HandleRequestList(string fn, string id, string code)
{
if (!HasForm("mode")) return BadRequest400();
if (!HasForm("mode")) { _logger.LogWarning("HandleRequestList: missing form field 'mode', user={User}", UserAccountID); return BadRequest400(); }
string mode = Form("mode").ToLower();
_logger.LogDebug("HandleRequestList mode={Mode} tgt={Tgt} user={User}", mode, Form("tgt"), UserAccountID);
if (mode == "s" && Form("tgt").Contains(':'))
{
var pl = StdParamlist(
@@ -126,7 +149,10 @@ public partial class IntranetController
}
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
{
_logger.LogWarning("HandleRequestList: invalid date format tgt='{Tgt}' user={User}", Form("tgt"), UserAccountID);
return BadRequest400();
}
{
var pl = StdParamlist(
SQL_Date("@tgtdate", tgtdate),
@@ -204,7 +230,8 @@ public partial class IntranetController
private async Task<IActionResult> HandleRequestGet(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("HandleRequestGet: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogDebug("HandleRequestGet requestId={ReqId} mode={Mode} user={User}", Form("id"), Form("mode"), UserAccountID);
string modeVal = Form("mode").ne("ov");
string[] tn = modeVal switch
{
@@ -231,7 +258,8 @@ public partial class IntranetController
private async Task<IActionResult> HandleRequestIget(string fn, string id, string code)
{
if (!HasForm("id", "typ")) return BadRequest400();
if (!HasForm("id", "typ")) { _logger.LogWarning("HandleRequestIget: missing form field 'id'/'typ', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogDebug("HandleRequestIget requestId={ReqId} typ={Typ} mode={Mode} user={User}", Form("id"), Form("typ"), Form("mode"), UserAccountID);
var pl = StdParamlist(
SQL_BigInt("@servicerequestid", Form("id")),
SQL_VarChar("@mode", Form("mode").ne("ov")),
@@ -273,12 +301,15 @@ public partial class IntranetController
private async Task<IActionResult> HandleRequestSconf(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
if (!HasForm("id")) { _logger.LogWarning("HandleRequestSconf: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
_logger.LogInformation("HandleRequestSconf: finalizing invoice {InvId} user={User}", Form("id"), UserAccountID);
var dt = await getSQLDatatable_async(
"EXECUTE [dbo].[fds__setInvoiceFinal] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code));
if (!string.IsNullOrEmpty(dt.Exception))
_logger.LogError("HandleRequestSconf: SQL error finalizing invoice {InvId}: {SqlError}, user={User}", Form("id"), dt.Exception, UserAccountID);
var frdic = dt.FirstRow.toObjectDictionary();
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
{
@@ -331,16 +362,19 @@ public partial class IntranetController
$"Die Rechnungs-PDF {frdic.nz("DocumentName").ne($"Rechnung_{invId}.pdf")} konnte nicht erstellt werden.",
UserAccountID, invId);
}
return await JSONAsync(new { ok = true });
// hasFile tells the frontend whether the PDF was actually stored, so it only opens the
// idoc preview popup when there is something to show (never on a failed render/store).
return await JSONAsync(new { ok = true, hasFile = filebyte.Length > 0 });
}
return await InvoiceIssueResult("Die Rechnung konnte aufgrund eines Fehlers nicht erstellt werden.");
}
private async Task<IActionResult> HandleRequestIdoc(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) { _logger.LogWarning("HandleRequestIdoc: missing/empty form field 'id', user={User}", UserAccountID); return StatusCode(404); }
_logger.LogDebug("HandleRequestIdoc: invoiceId={InvId} typ={Typ} create={Create} user={User}", Form("id"), Form("typ"), Form("create", "0"), UserAccountID);
var fdInv = await _invoices.LoadInvoiceAsync(Form("id"), UserAccountID, DbSec);
if (string.IsNullOrEmpty(fdInv.Id)) return StatusCode(404, new { error = "Rechnung wurde nicht gefunden" });
if (string.IsNullOrEmpty(fdInv.Id)) { _logger.LogWarning("HandleRequestIdoc: invoice not found id={InvId} user={User}", Form("id"), UserAccountID); return StatusCode(404, new { error = "Rechnung wurde nicht gefunden" }); }
string filename = fdInv.InvoiceRegistration!.nz("DocumentName").ne($"Rechnung_{fdInv.Id}.pdf");
if (Form("typ") != "img")
{
@@ -357,7 +391,8 @@ public partial class IntranetController
private async Task<IActionResult> HandleRequestResend(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) { _logger.LogWarning("HandleRequestResend: missing/empty form field 'id', user={User}", UserAccountID); return StatusCode(404); }
_logger.LogInformation("HandleRequestResend: resending invoice {InvId} user={User}", Form("id"), UserAccountID);
var dtset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoice] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
+13
View File
@@ -174,6 +174,19 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
{
_logger.LogError(ex, "Unhandled exception in Do fn={Fn} id={Id} code={Code} user={User}",
fn, id, code, UserAccountID);
// Standing rule: an exception that interrupts a user-initiated action must reach the
// user as a notification, not only the log. This is the catch-all safety net for any
// Do_Process_* action that throws without first publishing its own (more specific)
// issue event. Pre-auth flows (login/logout, unauthenticated GETs) are skipped — there
// is no user session to notify and the HTTP status already conveys the failure.
if (UserIdent.IsAuthenticated)
{
await _events.UserIssueAsync(
"Aktion fehlgeschlagen",
"Die Aktion konnte aufgrund eines unerwarteten Fehlers nicht abgeschlossen werden. Bitte versuchen Sie es erneut.",
UserAccountID,
new Dictionary<string, object?> { ["fn"] = fn });
}
return ServerError();
}
}
+21
View File
@@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Http;
namespace Fuchs.Controllers;
/// <summary>
/// Resolves a request value from the posted form, falling back to the query string.
/// Extracted as pure/testable logic: endpoints in <c>_allowedGet</c> (e.g. req/idoc, rem/idoc)
/// are invoked via a plain GET (window.open with '?id=...'), where <see cref="HttpRequest.Form"/>
/// has no Content-Type and throws <see cref="InvalidOperationException"/> if read directly.
/// </summary>
internal static class RequestValueHelper
{
internal static string? Resolve(bool hasFormContentType, IFormCollection form, IQueryCollection query, string key)
{
if (hasFormContentType && form.TryGetValue(key, out var formValue))
return formValue.ToString();
if (query.TryGetValue(key, out var queryValue))
return queryValue.ToString();
return null;
}
}