Restore legacy parity gaps lost in the VB.NET → C# migration

Close functional regressions found by comparing the legacy applications
(Intranet_Legacy/) against their C# counterparts:

- ProcessWebComService: send attachments inline (base64) with the
  push_com POST so invoice/reminder PDFs are attached again.
- FuchsPdf: wire GetPaycode into ApplyInvoice/ApplyReminder to restore
  the SEPA giro-code payment QR, and restore the full standard invoice
  text block (§35a labor-cost note, Akonto text, §14/§48 notes, AGB,
  Steuernummer, Verrechnungssätze, etc.).
- IntranetController: restore changepassword validation (password
  strength, confirmation match, current-password verification) and the
  mfr empty-id OData $metadata response.
- Reports: port the ocms_visualization engine to C# (FuchsVisualization)
  and wire FuchsReports.ProcessFdsRequest to render generic/
  generic_content/chart reports instead of returning an empty OK stub.

Adds smoke tests for the giro QR generator and report page builder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 16:41:46 +02:00
parent c8a4d18f1a
commit c81619fa53
6 changed files with 942 additions and 21 deletions
+36 -1
View File
@@ -317,11 +317,38 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
_logger.LogWarning("HandleAccount changepassword: missing required fields for user={User}", UserAccountID);
return BadRequest400();
}
if (npw != npwc)
{
_logger.LogWarning("HandleAccount changepassword: password confirmation mismatch for user={User}", UserAccountID);
return StatusCode(409, new { error = "match" });
}
if (!npw.ValidatePassword(minLength: 6, numSpecial: 0))
{
_logger.LogWarning("HandleAccount changepassword: new password does not meet requirements for user={User}", UserAccountID);
return StatusCode(409, new { error = "requirements" });
}
if (!OCORE.security.TFA.validateTotp_3h(_intranet.Intranet__TOTPsharedsecret_base + "3MDR", totpCode).isVerifiedInTime)
{
_logger.LogWarning("HandleAccount changepassword: TOTP verification failed for user={User}", UserAccountID);
return StatusCode(409, new { error = "sms" });
}
// Verify the supplied current password actually belongs to this user before changing it
string oldPw = Request.Form["opw"].ToString();
var authDt = await getSQLDatatable_async(
"SELECT TOP(1) * FROM [dbo].[fis_admin_authenticate_byID](@useraccount_id, @password);",
_intranet.Intranet__SQLConnectionString,
new List<SqlParameter>
{
SQL_VarChar("@useraccount_id", UserAccountID),
SQL_VarChar("@password", oldPw)
},
Security: DbSec, options: SqlOpt(fn, id, code));
var authRow = authDt.FirstRow.toObjectDictionary();
if (!string.IsNullOrEmpty(UserAccountID) && authRow.nz("useraccount_id") != UserAccountID)
{
_logger.LogWarning("HandleAccount changepassword: current password verification failed for user={User}", UserAccountID);
return StatusCode(409, new { error = "valid" });
}
_logger.LogInformation("Changing password for user={User}", UserAccountID);
await setSQLValue_async(
"EXECUTE [dbo].[fis_admin_setNewPassword] @useraccount_id, @oldpassword, @newpassword, @enc_key;",
@@ -329,7 +356,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
new List<SqlParameter>
{
SQL_VarChar("@useraccount_id", UserAccountID),
SQL_VarChar("@oldpassword", Request.Form["opw"].ToString()),
SQL_VarChar("@oldpassword", oldPw),
SQL_VarChar("@newpassword", npw)
},
Security: DbSec, options: SqlOpt(fn, id, code));
@@ -344,6 +371,14 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
{
_logger.LogDebug("HandleMfr id={Id} code={Code} user={User} auth={Auth}",
id, code, UserAccountID, UserIdent.Authorization);
if (string.IsNullOrEmpty(id))
{
// Empty id → return the OData EDMX schema ($metadata), matching legacy fds.getSchema()
using var mfrSchema = new fds.FdsMfrClient();
string schema = await mfrSchema.ReadAnything(
mfrSchema.ClientConfig.BaseUrl + "$metadata", throwErrorIfNotOk: false);
return Content(schema, "text/xml", System.Text.Encoding.UTF8);
}
if (!string.IsNullOrEmpty(UserAccountID) && UserIdent.Authorization > 3)
{
string path = id + (!string.IsNullOrEmpty(code) ? "/" + code : HttpUtility.UrlDecode(Request.QueryString.Value ?? ""));