Complete DI migration: wire all business services end-to-end
Move the intranet off the static-helper / Active-Record pattern onto
constructor-injected services, removing controller coupling and the
sync-over-async (Task.Run().Wait()) hot spots in the data classes.
Services now registered and consumed via DI:
- IBankingService, IPdfService, IMfrClientFactory (singletons)
- IWidgetService, IReportService, IInvoiceService, IReminderService (scoped)
Key changes:
- FuchsWidgetService: real widget logic (sql_table/indicator/html +
rendering_options) ported from the static class, which is deleted.
- FuchsReportService + FuchsVisualization: report engine decoupled from
IntranetController (takes connStr/dbSec/userAccountId); static
FuchsReports deleted.
- InvoiceService / ReminderService: implement load/register/render/store
(previously NotImplementedException stubs). FdsInvoiceData /
FdsReminderData are now pure data holders — all DB + PDF work moved into
the services, async throughout (no Task.Run().Wait()).
- Controllers inject and call the services; all `new FdsMfrClient()` calls
go through IMfrClientFactory.
- Deleted dead code: static Banking, FuchsWidgets, FuchsReports, and the
unused IDbConnectionFactory.
- InternalsVisibleTo("Fuchs.Tests") for testing internal mapping logic.
Tests: 127 passing (Banking tests moved to the service; added data-holder
tests for FdsInvoiceData/FdsReminderData). Full solution builds clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ public partial class IntranetController
|
||||
_intranet.Intranet__SQLConnectionString,
|
||||
Security: DbSec, options: SqlOpt(fn, id, code))).DataTable;
|
||||
|
||||
var tbl = Banking.ParseToDatatable(stream, schemaDt);
|
||||
var tbl = _banking.ParseToDatatable(stream, schemaDt);
|
||||
var tmptbl = "bs_" + Guid.NewGuid().ToString().Replace("-", "");
|
||||
|
||||
var dtwa = new DatatableWriterAsync(tbl, _intranet.Intranet__SQLConnectionString)
|
||||
|
||||
@@ -146,7 +146,7 @@ public partial class IntranetController
|
||||
return BadRequest400();
|
||||
}
|
||||
_logger.LogInformation("mfrrel: resetting MFR relation for invoice {InvoiceId}, user={User}", relId, UserAccountID);
|
||||
using (var mfr = new fds.FdsMfrClient())
|
||||
using (var mfr = _mfrFactory.Create())
|
||||
await mfr.Update__entitytable(EntityTypes.Invoice,
|
||||
fds.FdsMfr.UpdateNeed.Reset, new[] { relId });
|
||||
return Ok();
|
||||
|
||||
@@ -22,7 +22,7 @@ public partial class IntranetController
|
||||
if (!long.TryParse(Form("id"), out long tgtid)) { _logger.LogWarning("HandleInvoicePget: invalid 'id' value='{Value}' user={User}", Form("id"), UserAccountID); return BadRequest400(); }
|
||||
_logger.LogDebug("HandleInvoicePget tgtid={TgtId} user={User}", tgtid, UserAccountID);
|
||||
|
||||
using (var mfr = new fds.FdsMfrClient())
|
||||
using (var mfr = _mfrFactory.Create())
|
||||
{
|
||||
_logger.LogDebug("HandleInvoicePget resetting invoice entity tgtid={TgtId}", tgtid);
|
||||
await mfr.Update__entitytable(EntityTypes.Invoice,
|
||||
@@ -50,7 +50,7 @@ public partial class IntranetController
|
||||
}
|
||||
}
|
||||
_logger.LogDebug("HandleInvoicePget resetting {InvCount} invoices and {SrqCount} service requests", invIds.Count, srqIds.Count);
|
||||
using var mfr2 = new fds.FdsMfrClient();
|
||||
using var mfr2 = _mfrFactory.Create();
|
||||
foreach (var iid in invIds)
|
||||
await mfr2.Update__entitytable(EntityTypes.Invoice, fds.FdsMfr.UpdateNeed.Reset, new[] { iid });
|
||||
foreach (var iid in srqIds)
|
||||
@@ -413,9 +413,9 @@ public partial class IntranetController
|
||||
return ldic;
|
||||
}
|
||||
|
||||
private static async Task<string[]> BuildPdfImageArray(byte[] content)
|
||||
private async Task<string[]> BuildPdfImageArray(byte[] content)
|
||||
{
|
||||
var imgcol = await FuchsPdf.BytesToImageCollection(content);
|
||||
var imgcol = await _pdf.BytesToImageCollectionAsync(content);
|
||||
return imgcol.ImgB64Array;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,11 +38,11 @@ public partial class IntranetController
|
||||
{
|
||||
if (!HasForm("remc")) return BadRequest400();
|
||||
var ctd = JsonConvert.DeserializeObject(Form("remc"))!;
|
||||
var fdRem = new FdsReminderData(ctd);
|
||||
fdRem.RegisterReminder(this, change: false, remId: "");
|
||||
var fdRem = await _reminders.RegisterReminderAsync(
|
||||
new FdsReminderData(ctd), change: false, remId: "", UserAccountID, DbSec);
|
||||
if (!string.IsNullOrEmpty(fdRem.Id))
|
||||
{
|
||||
var imgcol = await FuchsPdf.DocToImageCollection(fdRem.ReminderPDF(this));
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(_reminders.GenerateReminderPdf(fdRem, fdRem.IsDraft));
|
||||
return await JSONAsync(new { id = fdRem.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
return StatusCode(500, new { error = "Erinnerung wurde nicht registriert" });
|
||||
@@ -64,12 +64,11 @@ public partial class IntranetController
|
||||
case "rdoc":
|
||||
{
|
||||
if (!HasForm("id")) return BadRequest400();
|
||||
byte[]? fc = null;
|
||||
var file = FdsReminderData.GetStoredFile(ref fc, Form("id"), this);
|
||||
if (file == null) return StatusCode(404, new { error = "Dokument wurde nicht gefunden" });
|
||||
var (file, fc) = await _reminders.GetStoredFileAsync(Form("id"), UserAccountID, DbSec);
|
||||
if (file == null || fc == null) 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!) });
|
||||
? await FileContentResultAsync(fc, file.MimeType(), file.Name)
|
||||
: await JSONAsync(new { id = Form("id"), img = await BuildPdfImageArray(fc) });
|
||||
}
|
||||
|
||||
case "idoc": return await HandleReminderIdoc(fn, id, code);
|
||||
@@ -107,8 +106,8 @@ public partial class IntranetController
|
||||
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
|
||||
{
|
||||
string remId = frdic["Id"]?.ToString() ?? "";
|
||||
var fdRem = new FdsReminderData(remId, this);
|
||||
byte[] filebyte = await fdRem.StoreReminderDocumentFile(this);
|
||||
var fdRem = await _reminders.LoadReminderAsync(remId, UserAccountID, DbSec);
|
||||
byte[] filebyte = await _reminders.StoreReminderDocumentFileAsync(fdRem, fdRem.IsDraft, UserAccountID, DbSec);
|
||||
string email = frdic.nz("SendToEmail", "");
|
||||
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
|
||||
{
|
||||
@@ -140,17 +139,17 @@ 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);
|
||||
var fdRem = new FdsReminderData(Form("id"), this);
|
||||
var fdRem = await _reminders.LoadReminderAsync(Form("id"), UserAccountID, DbSec);
|
||||
if (string.IsNullOrEmpty(fdRem.Id)) return StatusCode(404, new { error = "Erinnerung wurde nicht gefunden" });
|
||||
string filename = fdRem.ReminderRegistration.nz("DocumentName").ne($"Zahlungserinnerung_{fdRem.Id}.pdf");
|
||||
string filename = fdRem.ReminderRegistration!.nz("DocumentName").ne($"Zahlungserinnerung_{fdRem.Id}.pdf");
|
||||
if (Form("typ") != "img")
|
||||
{
|
||||
byte[] ct = Form("create", "0") != "1"
|
||||
? (await fdRem.GetReminderFile(this)) is { Length: > 0 } f1 ? f1 : await fdRem.StoreReminderDocumentFile(this)
|
||||
: FuchsPdf.DocToPdfBytes(fdRem.ReminderPDF(this));
|
||||
? (await _reminders.GetReminderFileAsync(fdRem, fdRem.IsDraft, _mfr, UserAccountID, DbSec)) is { Length: > 0 } f1 ? f1 : await _reminders.StoreReminderDocumentFileAsync(fdRem, fdRem.IsDraft, UserAccountID, DbSec)
|
||||
: _pdf.DocToPdfBytes(_reminders.GenerateReminderPdf(fdRem, fdRem.IsDraft));
|
||||
return await FileContentResultAsync(ct, "application/pdf", filename, inline: true);
|
||||
}
|
||||
var imgcol = await FuchsPdf.DocToImageCollection(fdRem.ReminderPDF(this));
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(_reminders.GenerateReminderPdf(fdRem, fdRem.IsDraft));
|
||||
return await JSONAsync(new { id = fdRem.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial class IntranetController
|
||||
}
|
||||
|
||||
default:
|
||||
return await FuchsReports.ProcessFdsRequest(this, id.ToLower(), code);
|
||||
return await _reports.ProcessRequestAsync(id.ToLower(), code, UserAccountID, DbSec, RequestParamsDict());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,9 @@ public partial class IntranetController
|
||||
case "save":
|
||||
{
|
||||
if (!HasForm("invc")) return BadRequest400();
|
||||
var fdInv = new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!);
|
||||
fdInv.RegisterInvoice(this, change: !string.IsNullOrEmpty(Form("id")), invId: Form("id"));
|
||||
var fdInv = await _invoices.RegisterInvoiceAsync(
|
||||
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
|
||||
change: !string.IsNullOrEmpty(Form("id")), invId: Form("id"), UserAccountID, DbSec);
|
||||
return !string.IsNullOrEmpty(fdInv.Id)
|
||||
? await JSONAsync(new { id = fdInv.Id })
|
||||
: StatusCode(500, new { error = "Rechnung wurde nicht gespeichert" });
|
||||
@@ -55,11 +56,12 @@ public partial class IntranetController
|
||||
case "sprep":
|
||||
{
|
||||
if (!HasForm("invc")) return BadRequest400();
|
||||
var fdInv = new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!);
|
||||
fdInv.RegisterInvoice(this, change: false, invId: "");
|
||||
var fdInv = await _invoices.RegisterInvoiceAsync(
|
||||
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
|
||||
change: false, invId: "", UserAccountID, DbSec);
|
||||
if (!string.IsNullOrEmpty(fdInv.Id))
|
||||
{
|
||||
var imgcol = await FuchsPdf.DocToImageCollection(fdInv.InvoicePDF(this));
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
|
||||
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
return StatusCode(500, new { error = "Rechnung wurde nicht registriert" });
|
||||
@@ -68,11 +70,12 @@ public partial class IntranetController
|
||||
case "sedit":
|
||||
{
|
||||
if (!HasForm("id", "invc")) return BadRequest400();
|
||||
var fdInv = new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!);
|
||||
fdInv.RegisterInvoice(this, change: true, invId: Form("id"));
|
||||
var fdInv = await _invoices.RegisterInvoiceAsync(
|
||||
new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!),
|
||||
change: true, invId: Form("id"), UserAccountID, DbSec);
|
||||
if (!string.IsNullOrEmpty(fdInv.Id))
|
||||
{
|
||||
var imgcol = await FuchsPdf.DocToImageCollection(fdInv.InvoicePDF(this));
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
|
||||
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
return StatusCode(500, new { error = "Rechnung wurde nicht registriert" });
|
||||
@@ -171,7 +174,7 @@ public partial class IntranetController
|
||||
[EntityHelper.EntityName(EntityTypes.ServiceRequest)] =
|
||||
new fds.FdsMfrClient.DatabaseSchema(EntityTypes.ServiceRequest)
|
||||
};
|
||||
using var mfr = new fds.FdsMfrClient();
|
||||
using var mfr = _mfrFactory.Create();
|
||||
await mfr.Update__entitytable(EntityTypes.ServiceRequest,
|
||||
fds.FdsMfr.UpdateNeed.Reset, ids.ToArray(), schemaDic: schemaDic);
|
||||
return Ok();
|
||||
@@ -258,8 +261,8 @@ public partial class IntranetController
|
||||
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
|
||||
{
|
||||
string invId = frdic["Id"]?.ToString() ?? "";
|
||||
var fdInv = new FdsInvoiceData(invId, this);
|
||||
byte[] filebyte = await fdInv.StoreInvoiceDocumentFile(this);
|
||||
var fdInv = await _invoices.LoadInvoiceAsync(invId, UserAccountID, DbSec);
|
||||
byte[] filebyte = await _invoices.StoreInvoiceDocumentFileAsync(fdInv, fdInv.IsDraft, UserAccountID, DbSec);
|
||||
var dtset = await getSQLDataSet_async(
|
||||
"EXECUTE [dbo].[fds__getInvoice] @Id, @authuser;",
|
||||
_intranet.Intranet__SQLConnectionString,
|
||||
@@ -293,19 +296,19 @@ public partial class IntranetController
|
||||
private async Task<IActionResult> HandleRequestIdoc(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
|
||||
var fdInv = new FdsInvoiceData(Form("id"), this);
|
||||
var fdInv = await _invoices.LoadInvoiceAsync(Form("id"), UserAccountID, DbSec);
|
||||
if (string.IsNullOrEmpty(fdInv.Id)) return StatusCode(404, new { error = "Rechnung wurde nicht gefunden" });
|
||||
string filename = fdInv.InvoiceRegistration.nz("DocumentName").ne($"Rechnung_{fdInv.Id}.pdf");
|
||||
string filename = fdInv.InvoiceRegistration!.nz("DocumentName").ne($"Rechnung_{fdInv.Id}.pdf");
|
||||
if (Form("typ") != "img")
|
||||
{
|
||||
byte[]? ct = Form("create", "0") != "1"
|
||||
? await fdInv.GetInvoiceFile(this) is { Length: > 0 } f1 ? f1 : await fdInv.StoreInvoiceDocumentFile(this)
|
||||
: FuchsPdf.DocToPdfBytes(fdInv.InvoicePDF(this));
|
||||
? await _invoices.GetInvoiceFileAsync(fdInv, fdInv.IsDraft, _mfr) is { Length: > 0 } f1 ? f1 : await _invoices.StoreInvoiceDocumentFileAsync(fdInv, fdInv.IsDraft, UserAccountID, DbSec)
|
||||
: _pdf.DocToPdfBytes(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
|
||||
return ct != null
|
||||
? await FileContentResultAsync(ct, "application/pdf", filename, inline: true)
|
||||
: StatusCode(500, new { error = "Rechnungs-PDF konnte nicht erstellt werden" });
|
||||
}
|
||||
var imgcol = await FuchsPdf.DocToImageCollection(fdInv.InvoicePDF(this));
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(_invoices.GenerateInvoicePdf(fdInv, fdInv.IsDraft));
|
||||
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
|
||||
@@ -322,8 +325,8 @@ public partial class IntranetController
|
||||
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
|
||||
{
|
||||
string invId = frdic["Id"]?.ToString() ?? "";
|
||||
var fdInv = new FdsInvoiceData(invId, this);
|
||||
byte[] filebyte = FuchsPdf.DocToPdfBytes(fdInv.InvoicePDF(this));
|
||||
var fdInv = await _invoices.LoadInvoiceAsync(invId, UserAccountID, DbSec);
|
||||
byte[] filebyte = await _invoices.RenderInvoicePdfBytesAsync(fdInv, fdInv.IsDraft);
|
||||
string email = frdic.nz("SendToEmail", "");
|
||||
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,13 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
internal readonly fds.IFdsMfr _mfr;
|
||||
private readonly ILogger<IntranetController> _logger;
|
||||
private readonly IComService _comService;
|
||||
private readonly IBankingService _banking;
|
||||
private readonly IPdfService _pdf;
|
||||
private readonly IMfrClientFactory _mfrFactory;
|
||||
private readonly IWidgetService _widgets;
|
||||
private readonly IReportService _reports;
|
||||
private readonly IInvoiceService _invoices;
|
||||
private readonly IReminderService _reminders;
|
||||
private readonly List<string> _allowedNonAuth = new() { "spwc", "spw" };
|
||||
private readonly List<string> _allowedGet = new()
|
||||
{
|
||||
@@ -41,12 +48,40 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
public string UserAccountID => UserIdent.UserAccountId;
|
||||
public string AuthAccount => UserIdent.Email;
|
||||
|
||||
public IntranetController(Fuchs_intranet intranet, fds.IFdsMfr mfr, ILogger<IntranetController> logger, IComService comService)
|
||||
public IntranetController(
|
||||
Fuchs_intranet intranet,
|
||||
fds.IFdsMfr mfr,
|
||||
ILogger<IntranetController> logger,
|
||||
IComService comService,
|
||||
IBankingService banking,
|
||||
IPdfService pdf,
|
||||
IMfrClientFactory mfrFactory,
|
||||
IWidgetService widgets,
|
||||
IReportService reports,
|
||||
IInvoiceService invoices,
|
||||
IReminderService reminders)
|
||||
{
|
||||
_intranet = intranet;
|
||||
_mfr = mfr;
|
||||
_logger = logger;
|
||||
_comService = comService;
|
||||
_banking = banking;
|
||||
_pdf = pdf;
|
||||
_mfrFactory = mfrFactory;
|
||||
_widgets = widgets;
|
||||
_reports = reports;
|
||||
_invoices = invoices;
|
||||
_reminders = reminders;
|
||||
}
|
||||
|
||||
/// <summary>Merged query-string + form parameters (form wins) for report processing.</summary>
|
||||
internal Dictionary<string, string> RequestParamsDict()
|
||||
{
|
||||
var prms = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var kv in Request.Query) prms[kv.Key] = kv.Value.ToString();
|
||||
if (Request.HasFormContentType)
|
||||
foreach (var kv in Request.Form) prms[kv.Key] = kv.Value.ToString();
|
||||
return prms;
|
||||
}
|
||||
|
||||
// ── Standard param list (pre-populates @authuser) ────────────────────────
|
||||
@@ -106,7 +141,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
IActionResult? result = fn.ToLower() switch
|
||||
{
|
||||
"ping" => Ok(),
|
||||
"wdg" => await FuchsWidgets.IntranetWdg(this, id),
|
||||
"wdg" => await _widgets.GetWidgetAsync(id, UserAccountID, DbSec, Request),
|
||||
"todos" => new PhysicalFileResult(
|
||||
Path.Combine(Directory.GetCurrentDirectory(), "Data", "ProjectToDos.html"),
|
||||
"text/html"),
|
||||
@@ -374,7 +409,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
// Empty id → return the OData EDMX schema ($metadata), matching legacy fds.getSchema()
|
||||
using var mfrSchema = new fds.FdsMfrClient();
|
||||
using var mfrSchema = _mfrFactory.Create();
|
||||
string schema = await mfrSchema.ReadAnything(
|
||||
mfrSchema.ClientConfig.BaseUrl + "$metadata", throwErrorIfNotOk: false);
|
||||
return Content(schema, "text/xml", System.Text.Encoding.UTF8);
|
||||
@@ -383,7 +418,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
{
|
||||
string path = id + (!string.IsNullOrEmpty(code) ? "/" + code : HttpUtility.UrlDecode(Request.QueryString.Value ?? ""));
|
||||
_logger.LogDebug("HandleMfr reading OData path={Path} user={User}", path, UserAccountID);
|
||||
using var mfrRead = new fds.FdsMfrClient();
|
||||
using var mfrRead = _mfrFactory.Create();
|
||||
var result = await mfrRead.ReadOData(path, throwErrorIfNotOk: false);
|
||||
_logger.LogDebug("HandleMfr OData read complete for path={Path} user={User}", path, UserAccountID);
|
||||
return Content(JsonConvert.SerializeObject(result), "application/json");
|
||||
@@ -402,7 +437,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
if (et != EntityTypes.none && string.IsNullOrEmpty(Request.Form["need"]))
|
||||
{
|
||||
_logger.LogInformation("MfrUpdate entity={EntityType} need=Short user={User}", et, UserAccountID);
|
||||
using var mfrSingle = new fds.FdsMfrClient();
|
||||
using var mfrSingle = _mfrFactory.Create();
|
||||
await mfrSingle.Update__entitytable(et, fds.FdsMfr.UpdateNeed.Short);
|
||||
_logger.LogDebug("MfrUpdate Short completed for entity={EntityType}", et);
|
||||
return Ok();
|
||||
@@ -411,7 +446,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
{
|
||||
var need = fds.FdsMfr.UpdateNeedValue(needParam);
|
||||
_logger.LogInformation("MfrUpdate entity={EntityType} need={Need} user={User}", et, need, UserAccountID);
|
||||
using var mfr = new fds.FdsMfrClient();
|
||||
using var mfr = _mfrFactory.Create();
|
||||
await mfr.Update__entitytable(et, updateNeed: need, debugDetails: false);
|
||||
_logger.LogDebug("MfrUpdate completed for entity={EntityType} need={Need}", et, need);
|
||||
return Ok();
|
||||
|
||||
Reference in New Issue
Block a user