Initial Commit after switching from SVN to git

This commit is contained in:
2026-05-03 01:43:52 +02:00
parent ab8638e5bb
commit a4284234b2
910 changed files with 359931 additions and 0 deletions
@@ -0,0 +1,161 @@
using System.Globalization;
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.io;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Banking processing
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_Bankings(string fn, string id, string code)
{
switch (id.ToLower())
{
case "auth":
return await JSONAsync(new { manage = 1 });
case "up":
foreach (var fle in Request.Form.Files)
{
using var stream = fle.OpenReadStream();
var schemaDt = (await getSQLDatatable_async(
"DECLARE @tmp [dbo].[fds__tt__bankingtransactions]; SELECT TOP(0) * FROM @tmp;",
_intranet.Intranet__SQLConnectionString,
Security: DbSec, options: SqlOpt(fn, id, code))).DataTable;
var tbl = Banking.ParseToDatatable(stream, schemaDt);
var tmptbl = "bs_" + Guid.NewGuid().ToString().Replace("-", "");
var dtwa = new DatatableWriterAsync(tbl, _intranet.Intranet__SQLConnectionString)
{
CommandAfter = new SqlCommand(
"EXECUTE [dbo].[fds__merge_bankingtransactions] @tblname, @authuser;")
};
dtwa.CommandAfter.Parameters.AddRange(new[]
{
new SqlParameter("@tblname", dtwa.DestinationTableName),
SQL_VarChar("@authuser", UserAccountID)
});
dtwa.CommandAfterError = new SqlCommand(
$"SELECT * INTO [{tmptbl}] FROM {dtwa.DestinationTableName};");
dtwa.OnError += (_, exc, _) =>
_intranet.debug_log("IntranetController.bam.up - sql exception",
exc, UserAccountID, new { uid = dtwa.InstanceGUID, tmptbl });
dtwa.OnCommandAfterError += (_, exc) =>
_intranet.debug_log("IntranetController.bam.up - command-after exception",
exc, UserAccountID, new { uid = dtwa.InstanceGUID, tmptbl });
dtwa.DoSubmit();
}
return Ok();
case "qtl":
{
if (!HasForm("mode")) return BadRequest400();
var pl = StdParamlist(SQL_VarChar("@mode", Form("mode")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getBankingtransfers_questionable] @mode, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "transfers" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
bs = dset.Tables("transfers").toArrayofObjectDictionaries()
});
}
case "btl":
{
if (!HasForm("mode")) return BadRequest400();
string mode = Form("mode").ToLower();
if (mode == "s" && Form("tgt").Contains(':'))
{
var pl = StdParamlist(
SQL_VarChar("@mode", Form("mode")),
SQL_VarChar("@search", Form("tgt")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getBankingtransactions_list2] @mode, @search, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "bank" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
bank = dset.Tables("bank").toArrayofObjectDictionaries()
});
}
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
return BadRequest400();
{
var pl = StdParamlist(
SQL_Date("@tgtdate", tgtdate),
SQL_VarChar("@mode", Form("mode").ne("m")),
SQL_VarChar("@includes", Form("includes").ne(Form("all") == "true" ? "all" : "")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getBankingtransfers_list] @tgtdate, @mode, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "bank" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
bank = dset.Tables("bank").toArrayofObjectDictionaries()
});
}
}
case "smd":
{
if (!HasForm("taid")) return BadRequest400();
var pl = StdParamlist(SQL_VarChar("@taID", Form("taid"), dbNull_IfEmpty: true));
var res = await getSQLValue_async(
"EXECUTE [dbo].[fds__setBankingtransaction_done] @taID, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
return res.Result is true ? Ok() : StatusCode(500, new { error = "not successful" });
}
case "ati":
{
if (!HasForm("taid", "iid")) return BadRequest400();
var pl = StdParamlist(
SQL_VarChar("@taid", Form("taid"), dbNull_IfEmpty: true),
SQL_VarChar("@invoice_id", Form("iid"), dbNull_IfEmpty: true));
var res = await getSQLValue_async(
"EXECUTE [dbo].[fds__setBankingtransaction_assignToIvoice] @taID, @invoice_id, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
return res.Result is true ? Ok() : StatusCode(500, new { error = "not successful" });
}
case "vfi":
{
if (!HasForm("invid")) return BadRequest400();
var pl = StdParamlist(SQL_VarChar("@InvoiceId", Form("invid"), dbNull_IfEmpty: true));
var res = await getSQLDatatable_async(
"SELECT [Id],[InvoiceId],[SendToAddress] FROM [dbo].[fds__invoices]" +
" WHERE [InvoiceId] LIKE ('%' + @InvoiceId) AND [InvoiceId] IS NOT NULL AND [DateFinalized] IS NOT NULL;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(res.DataTable.toArrayofObjectDictionaries());
}
default:
return Ok();
}
}
// ── Form helpers ─────────────────────────────────────────────────────────
protected bool HasForm(params string[] keys) =>
keys.All(k => Request.Form.ContainsKey(k) && !string.IsNullOrWhiteSpace(Request.Form[k]));
protected string Form(string key, string fallback = "") =>
Request.Form.TryGetValue(key, out var v) ? v.ToString() : fallback;
}
@@ -0,0 +1,94 @@
using System.Globalization;
using System.Web;
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using MFR_RESTClient.generic;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.io;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Invoice processing (dispatch + simple actions)
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_Invoices(string fn, string id, string code)
{
switch (id.ToLower())
{
case "auth":
return await JSONAsync(new { manage = 1 });
case "setpyd":
if (!HasForm("id")) return BadRequest400();
return await setSQLValue_async(
"EXECUTE [dbo].[fds__setInvoicePayed] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code))
? Ok() : StatusCode(500);
case "setupd":
if (!HasForm("id")) return BadRequest400();
return await setSQLValue_async(
"EXECUTE [dbo].[fds__setInvoiceUNPayed] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
Security: DbSec, options: SqlOpt(fn, id, code))
? Ok() : StatusCode(500);
case "setvat":
if (!float.TryParse(Form("val").Replace("%", "").Replace(",", ".").Trim(),
NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out float vatVal))
return BadRequest400();
{
var pl = StdParamlist(
SQL_BigInt("@id", Form("id")),
SQL_VarChar("@entitytype", "report"),
new SqlParameter("@vat", vatVal),
SQL_VarChar("@userid", UserAccountID));
string sqlEx = ""; int? sqlCode = null;
setSQLValue("EXECUTE [dbo].[fds__setReportVAT] @id, @entitytype, @vat, @authuser;",
_intranet.Intranet_SqlCon(), ref sqlEx, ref sqlCode, pl, Security: DbSec);
return string.IsNullOrEmpty(sqlEx) ? Ok() : StatusCode(500, new { error = sqlEx });
}
case "sis":
if (!HasForm("id")) return BadRequest400();
{
var pl = StdParamlist(SQL_VarChar("@Id", Form("id")), SQL_Bit("@auto", false));
var dt2 = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__setInvoiceSent] @Id, @auto, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
return string.IsNullOrEmpty(dt2.Exception) ? Ok() : StatusCode(500);
}
case "pget": return await HandleInvoicePget(fn, id, code);
case "get": return await HandleInvoiceGet(fn, id, code);
case "icget": return await HandleInvoiceIcGet(fn, id, code);
case "storno":
case "credit": return await HandleInvoiceStornoCredit(fn, id, code);
case "invl": return await HandleInvoiceList(fn, id, code);
case "rqi": return await HandleInvoiceRequestItems(fn, id, code);
case "pyi": return await HandleInvoicePayments(fn, id, code);
case "datev": return await HandleDatev(fn, id, code);
case "rdoc": return await HandleReportDoc(fn, id, code, Form("id"));
case "rdocn": return await HandleReportDocByName(fn, id, code);
case "datevzip": return await HandleDatevZip(fn, id, code);
case "getrem": return await HandleGetReminder(fn, id, code);
case "mfrrel":
if (!HasForm("id") || !long.TryParse(Form("id"), out long relId)) return BadRequest400();
using (var mfr = new fds.FdsMfrClient())
await mfr.Update__entitytable(EntityTypes.Invoice,
fds.FdsMfr.UpdateNeed.Reset, new[] { relId });
return Ok();
default: return Ok();
}
}
}
@@ -0,0 +1,323 @@
using System.Globalization;
using System.Web;
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using MFR_RESTClient.generic;
using OCORE.pdf;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.io;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Invoice sub-handlers
public partial class IntranetController
{
private async Task<IActionResult> HandleInvoicePget(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
if (!long.TryParse(Form("id"), out long tgtid)) return BadRequest400();
using (var mfr = new fds.FdsMfrClient())
await mfr.Update__entitytable(EntityTypes.Invoice,
fds.FdsMfr.UpdateNeed.Reset, new[] { tgtid });
var dt = await getSQLDatatable_async(
"SELECT * FROM [dbo].[fds__getInvoiceTreeIds](@srqid);",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_BigInt("@srqid", tgtid)),
Security: DbSec, options: SqlOpt(fn, id, code));
if (dt.Count > 0)
{
var invIds = new List<long>();
var srqIds = new List<long>();
foreach (System.Data.DataRow rw in dt.DataTable.Rows)
{
long iid = rw.nint64("Id", -1);
switch (rw.nz("type"))
{
case "invoice": if (iid > 0 && !invIds.Contains(iid)) invIds.Add(iid); break;
case "servicerequest": if (iid > 0 && !srqIds.Contains(iid)) srqIds.Add(iid); break;
}
}
using var mfr2 = new fds.FdsMfrClient();
foreach (var iid in invIds)
await mfr2.Update__entitytable(EntityTypes.Invoice, fds.FdsMfr.UpdateNeed.Reset, new[] { iid });
foreach (var iid in srqIds)
await mfr2.Update__entitytable(EntityTypes.ServiceRequest, fds.FdsMfr.UpdateNeed.Reset, new[] { iid });
}
return Ok();
}
private async Task<IActionResult> HandleInvoiceGet(string fn, string id, string code)
{
try
{
if (!HasForm("id")) return BadRequest400();
var sqldset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoice] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
tablenames: new[] { "admin", "inv", "req", "itm" },
Security: DbSec, options: SqlOpt(fn, id, code));
var ldic = BuildInvoiceRequestList(sqldset);
var adminDic = sqldset.Table("admin").FirstRow.toObjectDictionary();
var invDic = sqldset.Table("inv").FirstRow.toObjectDictionary();
if (invDic.nz("InvoiceOptions", "").Split(',').Contains("§13b"))
adminDic["p13b"] = true;
return await JSONAsync(new { admin = adminDic, inv = invDic, req = ldic });
}
catch { return StatusCode(500); }
}
private async Task<IActionResult> HandleInvoiceIcGet(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
var sqldset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__prepStorno_recreate] @InvId, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@InvId", Form("id"))),
tablenames: new[] { "admin", "requests", "items", "steps", "companies", "locations" },
Security: DbSec, options: SqlOpt(fn, id, code));
var ldic = BuildRequestItemList(sqldset);
return await JSONAsync(new
{
admin = sqldset.Table("admin").FirstRow.toObjectDictionary(),
requests = ldic,
companies = sqldset.Tables("companies").toArrayofObjectDictionaries(),
locations = sqldset.Tables("locations").toArrayofObjectDictionaries()
});
}
private async Task<IActionResult> HandleInvoiceStornoCredit(string fn, string id, string code)
{
if (!HasForm("id", "mode")) return BadRequest400();
string sqlcmd = Form("mode") switch
{
"credit" => "EXECUTE [dbo].[fds__createCredit_simple] @Id, @authuser;",
"simple" => "EXECUTE [dbo].[fds__createStorno_simple] @Id, @authuser;",
"copy" => "EXECUTE [dbo].[fds__createStorno_copy] @Id, @authuser;",
_ => ""
};
if (string.IsNullOrEmpty(sqlcmd)) return StatusCode(500, new { error = "function not allowed" });
var sqldset = await getSQLDataSet_async(sqlcmd,
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
tablenames: new[] { "admin", "inv", "req", "itm" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = sqldset.Table("admin").FirstRow.toObjectDictionary(),
inv = sqldset.Table("inv").FirstRow.toObjectDictionary(),
req = BuildInvoiceRequestList(sqldset)
});
}
private async Task<IActionResult> HandleInvoiceList(string fn, string id, string code)
{
if (!HasForm("mode")) return BadRequest400();
string mode = Form("mode").ToLower();
if (mode == "s" && Form("tgt").Contains(':'))
{
var pl = StdParamlist(
SQL_Date("@tgtdate", DBNull.Value),
SQL_VarChar("@mode", Form("mode").ne("m")),
SQL_Bit("@include_drafts", Form("all")),
SQL_VarChar("@search", Form("tgt")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoices_list2] @tgtdate, @mode, @include_drafts, @search, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "invoices" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
invoices = dset.Tables("invoices").toArrayofObjectDictionaries()
});
}
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
return BadRequest400();
{
var pl = StdParamlist(
SQL_Date("@tgtdate", tgtdate),
SQL_VarChar("@mode", Form("mode").ne("m")),
SQL_VarChar("@includes", Form("includes").ne(Form("all") == "true" ? "all" : "")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoices_list_vario] @tgtdate, @mode, @includes, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "invoices" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
invoices = dset.Tables("invoices").toArrayofObjectDictionaries()
});
}
}
private async Task<IActionResult> HandleInvoiceRequestItems(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
var sqldt = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvRequestItems] @invoiceid, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@invoiceid", Form("id"))),
tablenames: new[] { "requests", "items" },
Security: DbSec, options: SqlOpt(fn, id, code));
var ldic = new List<Dictionary<string, object?>>();
foreach (System.Data.DataRow sq in sqldt.Tables("requests").Select("",
sqldt.Tables("requests").Columns.Contains("order") ? "order" : ""))
{
var sdic = sq.toObjectDictionary();
sdic["items"] = sqldt.Tables("items")
.Select($"[ServiceRequestId] = {sdic["id"]}", sqldt.Tables("items").Columns.Contains("order") ? "order" : "")
.Select(r => r.toObjectDictionary()).ToList();
ldic.Add(sdic!);
}
return await JSONAsync(new { requests = ldic });
}
private async Task<IActionResult> HandleInvoicePayments(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
var sqldt = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvPayments] @invoiceid, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@invoiceid", Form("id"))),
tablenames: new[] { "items" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new { payments = sqldt.Tables("items").toArrayofObjectDictionaries() });
}
private async Task<IActionResult> HandleDatev(string fn, string id, string code)
{
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
return BadRequest400();
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getDatevExports] @tgtdate, @mode, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_Date("@tgtdate", tgtdate), SQL_VarChar("@mode", Form("mode").ne("m"))),
tablenames: new[] { "files", "invoices", "debits" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
files = dset.Tables("files").toArrayofObjectDictionaries(),
invoices = dset.Tables("invoices").toArrayofObjectDictionaries()
});
}
private async Task<IActionResult> HandleReportDoc(string fn, string id, string code, string reportid)
{
byte[]? content = null;
var file = _mfr.GetReportDoc(ref content, reportid);
if (file == null) return StatusCode(404, new { error = "Dokument wurde nicht gefunden" });
return Form("typ") != "img"
? await FileContentResultAsync(content!, file.MimeType(), file.Name)
: await JSONAsync(new { id = reportid, img = await BuildPdfImageArray(content!) });
}
private async Task<IActionResult> HandleReportDocByName(string fn, string id, string code)
{
if (!HasForm("name")) return BadRequest400();
string nme = Form("name").LeftToFirst("(").Trim();
if (string.IsNullOrEmpty(nme)) return StatusCode(404);
var so = await getSQLValue_async(
"SELECT [dbo].[fds__fn_InvoiceIdByName](@nme);",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@nme", nme)),
Security: DbSec, options: SqlOpt(fn, id, code));
string reportid = so.Result?.ToString() ?? "";
return string.IsNullOrEmpty(reportid)
? StatusCode(404)
: await HandleReportDoc(fn, id, code, reportid);
}
private async Task<IActionResult> HandleDatevZip(string fn, string id, string code)
{
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
return BadRequest400();
Stream? ms = new MemoryStream();
var file = _mfr.GetDatevZip(ref ms, tgtdate,
mode: Form("mode").ne("m"),
authUser: UserAccountID,
includeFiles: Form("files", "1") != "0");
if (file == null) return BadRequest400();
ms!.Position = 0;
return await FileStreamResultAsync(ms, file.MimeType(), file.Name);
}
private async Task<IActionResult> HandleGetReminder(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
var pl = StdParamlist(
SQL_VarChar("@InvId", Form("id")),
SQL_Bit("@include_drafts", Form("drafts")));
var sqldt = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoiceReminder] @InvId, @include_drafts, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "reminder" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(sqldt.Table("reminder").DataTable.toArrayofObjectDictionaries());
}
// -- Shared builder helpers ------------------------------------------------
private static List<Dictionary<string, object?>> BuildInvoiceRequestList(SQLDataSet sqldset)
{
var ldic = new List<Dictionary<string, object?>>();
foreach (System.Data.DataRow rq in sqldset.Tables("req").Select("",
sqldset.Tables("req").Columns.Contains("order") ? "order" : ""))
{
var rdic = rq.toObjectDictionary();
var sdic = new Dictionary<string, object?>
{
["Id"] = rdic["mfr__servicerequest"],
["InvRqId"] = rdic["Id"],
["text"] = HttpUtility.HtmlDecode(rdic["title"]?.ToString() ?? "")
};
if (sqldset.Contains("itm"))
{
var itm = new List<Dictionary<string, object?>>();
foreach (System.Data.DataRow sitm in sqldset.Tables("itm").Select(
$"[InvRqId] = '{rdic["Id"]}'",
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));
itm.Add(new Dictionary<string, object?>
{
["Id"] = d["Id"],
["net_val"] = net,
["vat_val"] = net * vat * 0.01,
["vat"] = vat == 0 ? "" : vat.ToString("0.00", FuchsPdf.DeCulture) + "%",
["svcnet_val"] = d.no("value_service", 0),
["net"] = d.no("value", 0),
["quantity"] = d["Quantity"],
["Type"] = d["Type"],
["Note"] = null,
["htmltext"] = d["Text"],
["position"] = d["Position"],
["SortOrder"] = d["SortOrder"]
});
}
sdic["items"] = itm;
}
ldic.Add(sdic);
}
return ldic;
}
private static async Task<string[]> BuildPdfImageArray(byte[] content)
{
var imgcol = await FuchsPdf.BytesToImageCollection(content);
return imgcol.ImgB64Array;
}
}
@@ -0,0 +1,198 @@
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using Newtonsoft.Json;
using OCORE.pdf;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.io;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Reminder processing
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_Reminder(string fn, string id, string code)
{
switch (id.ToLower())
{
case "get":
{
if (!HasForm("id")) return BadRequest400();
var pl = StdParamlist(
SQL_VarChar("@InvId", Form("id")),
SQL_VarChar("@type", Form("type")),
SQL_Int("@level", Form("level")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__prepReminder] @InvId, @authuser, @type, @level;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "rem" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new { rm = dset.Table("rem").FirstRow.toObjectDictionary() });
}
case "prep":
{
if (!HasForm("remc")) return BadRequest400();
var ctd = JsonConvert.DeserializeObject(Form("remc"))!;
var fdRem = new FdsReminderData(ctd);
fdRem.RegisterReminder(this, change: false, remId: "");
if (!string.IsNullOrEmpty(fdRem.Id))
{
var imgcol = await FuchsPdf.DocToImageCollection(fdRem.ReminderPDF(this));
return await JSONAsync(new { id = fdRem.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
}
return StatusCode(500, new { error = "Erinnerung wurde nicht registriert" });
}
case "conf": return await HandleReminderConf(fn, id, code);
case "srs":
{
if (!HasForm("id")) return BadRequest400();
var pl = StdParamlist(SQL_VarChar("@Id", Form("id")), SQL_Bit("@auto", false));
var dt2 = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__setReminderSent] @Id, @auto, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
Security: DbSec, options: SqlOpt(fn, id, code));
return string.IsNullOrEmpty(dt2.Exception) ? Ok() : StatusCode(500);
}
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" });
return Form("typ") != "img"
? 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);
case "resend": return await HandleReminderResend(fn, id, code);
case "lrem":
{
if (!HasForm("id")) return BadRequest400();
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));
return await JSONAsync(new
{
ov = dset.Table("ov").FirstRow.toStringDictionary(),
lst = dset.Tables("rem").toArrayofObjectDictionaries()
});
}
default: return Ok();
}
}
private async Task<IActionResult> HandleReminderConf(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
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));
var frdic = dt.FirstRow.toObjectDictionary();
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);
string email = frdic.nz("SendToEmail", "");
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
{
var remdoc = new Dictionary<string, byte[]>
{ [frdic.nz("DocumentName", "").ne("Zahlungserinnerung.pdf")] = filebyte };
if (!string.IsNullOrEmpty(frdic.nz("InvoiceFileName")) &&
frdic.no("InvoiceFile", null!) is byte[] invFile)
remdoc[frdic.nz("InvoiceFileName")] = invFile;
bool sent = await FuchsFdsEmail.SendEmail(
$"inv_{remId}",
$"SanitärFuchs - {frdic.nz("subject").ne(frdic.nz("DocumentName"))}",
BuildReminderBody(Convert.ToDouble(frdic.no("amount_open", 0))),
email.Trim(), "", remdoc, _intranet);
if (sent)
{
var pls = StdParamlist(SQL_VarChar("@Id", remId), SQL_Bit("@auto", true));
await getSQLDatatable_async(
"EXECUTE [dbo].[fds__setReminderSent] @Id, @auto, @authuser;",
_intranet.Intranet__SQLConnectionString, pls,
Security: DbSec, options: SqlOpt(fn, id, code));
}
}
return Ok();
}
return StatusCode(500, new { error = "Aktion war nicht erfolgreich" });
}
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);
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");
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));
return await FileContentResultAsync(ct, "application/pdf", filename, inline: true);
}
var imgcol = await FuchsPdf.DocToImageCollection(fdRem.ReminderPDF(this));
return await JSONAsync(new { id = fdRem.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
}
private async Task<IActionResult> HandleReminderResend(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
var pl = StdParamlist(SQL_VarChar("@Id", Form("id")), new SqlParameter("@includefile", true));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getReminder] @Id, @includefile, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "inv", "req", "itm" },
Security: DbSec, options: SqlOpt(fn, id, code));
var frdic = dset.Table("inv").FirstRow.toObjectDictionary();
if (frdic.TryGetValue("IsFinal", out var isFinal) && isFinal is true)
{
string remId = frdic["Id"]?.ToString() ?? "";
byte[] filebyte = frdic.no("file", null!) as byte[] ?? Array.Empty<byte>();
string email = frdic.nz("SendToEmail", "");
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
{
var remdoc = new Dictionary<string, byte[]>
{ [frdic.nz("DocumentName", "").ne("Zahlungserinnerung.pdf")] = filebyte };
if (!string.IsNullOrEmpty(frdic.nz("InvoiceFileName")) &&
frdic.no("InvoiceFile", null!) is byte[] invFile)
remdoc[frdic.nz("InvoiceFileName")] = invFile;
await FuchsFdsEmail.SendEmail($"rem_{remId}",
$"SanitärFuchs - {frdic.nz("subject").ne(frdic.nz("DocumentName"))}",
BuildReminderBody(Convert.ToDouble(frdic.no("amount_open", 0))),
email.Trim(), "", remdoc, _intranet);
}
return Ok();
}
return StatusCode(500, new { error = "Aktion war nicht erfolgreich" });
}
private static string BuildReminderBody(double amountOpen) =>
"<p>Sehr geehrte Damen und Herren,<br/>wir vermissen Ihren Zahlungseingang.</p>" +
(amountOpen != 0
? $"<p>Der offene Betrag beläuft sich auf (inkl. MwSt.): {((float)amountOpen).ToString("0.00 ", FuchsPdf.DeCulture)}</p>"
: "<p>Die Erinnerung mit allen Details finden Sie angehängt an diese Email.</p>") +
"<p>Bitte überweisen Sie den Rechnungsbetrag innerhalb von einer Woche auf unser Konto:<br/>" +
"IBAN: DE76300501100045014800, BIC DUSSSDEDDXXX (Stadtsparkasse Düsseldorf)</p>" +
"<p>Wenn Sie mit uns zufrieden waren, empfehlen Sie uns gerne weiter, " +
"wenn nicht, dann sagen Sie es uns unter " +
"<a href=\"mailto:info@sanitaerfuchs.de\">info@sanitaerfuchs.de</a>.</p>";
}
@@ -0,0 +1,52 @@
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Reports processing
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_Reports(string fn, string id, string code)
{
switch (id.ToLower())
{
case "auth":
return await JSONAsync(new { manage = 1 });
case "catalog":
{
var pl = StdParamlist(SQL_VarChar("@report_name", Form("report"), dbNull_IfEmpty: true));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__admin_getReportCatalog] @report_name, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "reports", "params", "categories", "tags" },
Security: DbSec, options: SqlOpt(fn, id, code));
var reps = dset.Tables("reports").toArrayofObjectDictionaries();
foreach (var ri in reps)
{
try
{
ri["params"] = dset.Tables("params")
.toArrayofObjectDictionaries($"[object_id] = {ri["object_id"]} AND [name] <> '@authuser'");
}
catch { ri["params"] = Array.Empty<Dictionary<string, object>>(); }
}
return await JSONAsync(new
{
reports = reps,
categories = dset.Tables("categories").toArrayofObjectDictionaries(),
tags = dset.Tables("tags").toArrayofObjectDictionaries()
});
}
default:
return await FuchsReports.ProcessFdsRequest(this, id.ToLower(), code);
}
}
}
@@ -0,0 +1,349 @@
using System.Globalization;
using Fuchs.intranet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using MFR_RESTClient.generic;
using Newtonsoft.Json;
using OCORE.pdf;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.io;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
// Partial class: Service request processing
public partial class IntranetController
{
private async Task<IActionResult> Do_Process_Requests(string fn, string id, string code)
{
switch (id.ToLower())
{
case "auth":
return await JSONAsync(new { manage = 1 });
case "rthd":
{
if (!HasForm("id")) return BadRequest400();
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" });
var dic = sqldt.FirstRow.toObjectDictionary();
return await JSONAsync(new { id = dic["EntityId"], visible = dic.no("hidden", false) is not true });
}
case "reql": return await HandleRequestList(fn, id, code);
case "pget": return await HandleRequestPget(fn, id, code);
case "get": return await HandleRequestGet(fn, id, code);
case "iget": return await HandleRequestIget(fn, id, code);
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"));
return !string.IsNullOrEmpty(fdInv.Id)
? await JSONAsync(new { id = fdInv.Id })
: StatusCode(500, new { error = "Rechnung wurde nicht gespeichert" });
}
case "sprep":
{
if (!HasForm("invc")) return BadRequest400();
var fdInv = new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!);
fdInv.RegisterInvoice(this, change: false, invId: "");
if (!string.IsNullOrEmpty(fdInv.Id))
{
var imgcol = await FuchsPdf.DocToImageCollection(fdInv.InvoicePDF(this));
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
}
return StatusCode(500, new { error = "Rechnung wurde nicht registriert" });
}
case "sedit":
{
if (!HasForm("id", "invc")) return BadRequest400();
var fdInv = new FdsInvoiceData(JsonConvert.DeserializeObject(Form("invc"))!);
fdInv.RegisterInvoice(this, change: true, invId: Form("id"));
if (!string.IsNullOrEmpty(fdInv.Id))
{
var imgcol = await FuchsPdf.DocToImageCollection(fdInv.InvoicePDF(this));
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
}
return StatusCode(500, new { error = "Rechnung wurde nicht registriert" });
}
case "sdel":
if (!HasForm("id")) return BadRequest400();
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));
return Ok();
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 Ok();
}
}
private async Task<IActionResult> HandleRequestList(string fn, string id, string code)
{
if (!HasForm("mode")) return BadRequest400();
string mode = Form("mode").ToLower();
if (mode == "s" && Form("tgt").Contains(':'))
{
var pl = StdParamlist(
SQL_Date("@tgtdate", DBNull.Value),
SQL_VarChar("@mode", mode),
SQL_Bit("@completed", true),
SQL_VarChar("@search", Form("tgt")));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getRequests_list2] @tgtdate, @mode, @completed, @search, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "requests", "reports" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
requests = AttachReports(dset)
});
}
if (!DateTime.TryParseExact(Form("tgt"), "yy-MM-dd",
CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var tgtdate))
return BadRequest400();
{
var pl = StdParamlist(
SQL_Date("@tgtdate", tgtdate),
SQL_VarChar("@mode", mode),
SQL_Bit("@completed", true));
var dset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getRequests_list] @tgtdate, @mode, @completed, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "requests", "reports" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = dset.Table("admin").FirstRow.toObjectDictionary(),
requests = AttachReports(dset)
});
}
}
private static List<Dictionary<string, object?>> AttachReports(SQLDataSet dset)
{
var req = new List<Dictionary<string, object?>>(dset.Tables("requests").toArrayofObjectDictionaries()!);
foreach (var r in req)
{
try { r["reports"] = dset.Tables("reports").toArrayofObjectDictionaries($"[requestID] = {r["Id"]}"); }
catch { /* no reports table */ }
}
return req;
}
private async Task<IActionResult> HandleRequestPget(string fn, string id, string code)
{
if (!HasForm("id") || !long.TryParse(Form("id"), out long tgtid)) return BadRequest400();
var dt = await getSQLDatatable_async(
"SELECT * FROM [dbo].[fds__getRequestTreeIds](@srqid);",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_BigInt("@srqid", tgtid)),
Security: DbSec, options: SqlOpt(fn, id, code));
var ids = new List<long> { tgtid };
if (dt.Count > 0)
{
foreach (System.Data.DataRow rw in dt.DataTable.Rows)
{
long iid = rw.nint64("Id", -1);
if (iid > 0 && !ids.Contains(iid)) ids.Add(iid);
}
}
var schemaDic = new Dictionary<string, fds.FdsMfrClient.DatabaseSchema>
{
[EntityHelper.EntityName(EntityTypes.ServiceRequest)] =
new fds.FdsMfrClient.DatabaseSchema(EntityTypes.ServiceRequest)
};
using var mfr = new fds.FdsMfrClient();
await mfr.Update__entitytable(EntityTypes.ServiceRequest,
fds.FdsMfr.UpdateNeed.Reset, ids.ToArray(), schemaDic: schemaDic);
return Ok();
}
private async Task<IActionResult> HandleRequestGet(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
string modeVal = Form("mode").ne("ov");
string[] tn = modeVal switch
{
"r" => new[] { "admin", "requests", "inv" },
"ful" => new[] { "admin", "requests", "items", "steps", "companies", "locations", "inv" },
_ => new[] { "admin", "requests", "items", "inv" }
};
var pl = StdParamlist(
SQL_BigInt("@servicerequestid", Form("id")),
SQL_VarChar("@mode", modeVal));
var sqldset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getRequest_details] @servicerequestid, @mode, @authuser;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: tn, Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = sqldset.Table("admin").FirstRow.toObjectDictionary(),
requests = BuildRequestItemList(sqldset),
companies = sqldset.Tables("companies").toArrayofObjectDictionaries(),
locations = sqldset.Tables("locations").toArrayofObjectDictionaries(),
inv = sqldset.Tables("inv").toArrayofObjectDictionaries()
});
}
private async Task<IActionResult> HandleRequestIget(string fn, string id, string code)
{
if (!HasForm("id", "typ")) return BadRequest400();
var pl = StdParamlist(
SQL_BigInt("@servicerequestid", Form("id")),
SQL_VarChar("@mode", Form("mode").ne("ov")),
SQL_Char("@type", Form("typ").Substr(0, 1).ToLower()),
SQL_VarChar("sel", Form("sel")));
var sqldset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__prepInvoice] @servicerequestid, @mode, @authuser, @type, @sel;",
_intranet.Intranet__SQLConnectionString, pl,
tablenames: new[] { "admin", "requests", "items", "steps", "companies", "locations" },
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new
{
admin = sqldset.Table("admin").FirstRow.toObjectDictionary(),
requests = BuildRequestItemList(sqldset),
companies = sqldset.Tables("companies").toArrayofObjectDictionaries(),
locations = sqldset.Tables("locations").toArrayofObjectDictionaries()
});
}
private static List<Dictionary<string, object?>> BuildRequestItemList(SQLDataSet sqldset)
{
var ldic = new List<Dictionary<string, object?>>();
foreach (System.Data.DataRow sq in sqldset.Tables("requests").Select("",
sqldset.Tables("requests").Columns.Contains("order") ? "order" : ""))
{
var sdic = sq.toObjectDictionary();
if (sqldset.Contains("items"))
sdic["items"] = sqldset.Tables("items")
.Select($"[ServiceRequestId] = {sdic["Id"]}", sqldset.Tables("items").Columns.Contains("order") ? "order" : "")
.Select(r => r.toObjectDictionary()).ToList();
if (sqldset.Contains("steps"))
sdic["steps"] = sqldset.Tables("steps")
.Select($"[ServiceRequestId] = {sdic["Id"]}", sqldset.Tables("steps").Columns.Contains("order") ? "order" : "")
.Select(r => r.toObjectDictionary()).ToList();
ldic.Add(sdic!);
}
return ldic;
}
private async Task<IActionResult> HandleRequestSconf(string fn, string id, string code)
{
if (!HasForm("id")) return BadRequest400();
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));
var frdic = dt.FirstRow.toObjectDictionary();
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 dtset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoice] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
tablenames: new[] { "admin", "inv", "req", "itm" },
Security: DbSec, options: SqlOpt(fn, id, code));
frdic = dtset.Table("inv").FirstRow.toObjectDictionary();
string email = frdic.nz("SendToEmail", "");
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
{
var inv = new Dictionary<string, byte[]> { [frdic.nz("DocumentName")] = filebyte };
double bal = Convert.ToDouble(frdic.no("InvoiceBalance", 0));
string terms = fdInv.PaymentTerms.Replace("wd", " Werktagen").Replace("d", " Tagen").Replace("wk", " Wochen").ne("10 Tagen");
string body = BuildInvoiceBody(bal, terms);
bool sent = await FuchsFdsEmail.SendEmail(
$"inv_{invId}", $"Sanit\u00e4rFuchs - {frdic.nz("DocumentName")}",
body, email.Trim(), "", inv, _intranet);
if (sent)
{
var pls = StdParamlist(SQL_VarChar("@Id", invId), SQL_Bit("@auto", true));
await getSQLDatatable_async("EXECUTE [dbo].[fds__setInvoiceSent] @Id, @auto, @authuser;",
_intranet.Intranet__SQLConnectionString, pls,
Security: DbSec, options: SqlOpt(fn, id, code));
}
}
return Ok();
}
return StatusCode(500, new { error = "Aktion war nicht erfolgreich" });
}
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);
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");
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));
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));
return await JSONAsync(new { id = fdInv.Id, img = imgcol.ImgB64Array, total = imgcol.TotalPages });
}
private async Task<IActionResult> HandleRequestResend(string fn, string id, string code)
{
if (!HasForm("id") || string.IsNullOrEmpty(Form("id"))) return StatusCode(404);
var dtset = await getSQLDataSet_async(
"EXECUTE [dbo].[fds__getInvoice] @Id, @authuser;",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@Id", Form("id"))),
tablenames: new[] { "admin", "inv", "req", "itm" },
Security: DbSec);
var frdic = dtset.Table("inv").FirstRow.toObjectDictionary();
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));
string email = frdic.nz("SendToEmail", "");
if (!string.IsNullOrEmpty(email) && filebyte.Length > 0)
{
double bal = Convert.ToDouble(frdic.no("InvoiceBalance", 0));
string terms = fdInv.PaymentTerms.Replace("wd", " Werktagen").Replace("d", " Tagen").Replace("wk", " Wochen").ne("10 Tagen");
await FuchsFdsEmail.SendEmail(
$"inv_{invId}", $"Sanit\u00e4rFuchs - {frdic.nz("DocumentName")}",
BuildInvoiceBody(bal, terms), email.Trim(), "",
new Dictionary<string, byte[]> { [frdic.nz("DocumentName")] = filebyte },
_intranet);
}
return Ok();
}
return StatusCode(500, new { error = "Aktion war nicht erfolgreich" });
}
private static string BuildInvoiceBody(double balance, string paymentTerms) =>
"<p>Sehr geehrte Damen und Herren,<br/>vielen Dank für Ihren Auftrag, wir haben gern für Sie gearbeitet.</p>" +
(balance != 0
? $"<p>Unsere Dienstleistung stellen wir wie folgt In Rechnung (inkl. MwSt.): {((float)balance).ToString("0.00 ", FuchsPdf.DeCulture)}</p>"
: "<p>Die Abrechnung unserer Dienstleistung finden Sie angehängt an diese Email.</p>") +
$"<p>Bitte überweisen Sie den Rechnungsbetrag innerhalb von {paymentTerms} auf unser Konto:<br/>" +
"IBAN: DE76300501100045014800, BIC DUSSSDEDDXXX (Stadtsparkasse Düsseldorf)</p>";
}
+307
View File
@@ -0,0 +1,307 @@
using System.Web;
using Fuchs.intranet;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using MFR_RESTClient.generic;
using Newtonsoft.Json;
using OCORE.security;
using OCORE.SQL;
using static OCORE.commons;
using static OCORE.OCORE_dictionaries;
using static OCORE.SQL.sql;
using static OCORE.web.mvc_helper_async;
namespace Fuchs.Controllers;
/// <summary>
/// Fuchs Intranet MVC Controller.
/// Handles all intranet requests: authentication, invoices, reminders, requests, banking, reports.
/// </summary>
public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
{
internal readonly Fuchs_intranet _intranet;
internal readonly fds.IFdsMfr _mfr;
private readonly ILogger<IntranetController> _logger;
private readonly List<string> _allowedNonAuth = new() { "spwc", "spw" };
private readonly List<string> _allowedGet = new()
{
"inv|datevzip", "inv|rdoc", "inv|rdocn",
"req|idoc", "req|resend",
"rem|idoc", "rem|resend",
"bam|up", "mfr", "mfr_update", "todos"
};
private FuchsUserIdentity? _userIdent;
private FuchsUserIdentity UserIdent => _userIdent ??= new FuchsUserIdentity(User);
public string UserAccountID => UserIdent.UserAccountId;
public string AuthAccount => UserIdent.Email;
public IntranetController(Fuchs_intranet intranet, fds.IFdsMfr mfr, ILogger<IntranetController> logger)
{
_intranet = intranet;
_mfr = mfr;
_logger = logger;
}
// ── Standard param list (pre-populates @authuser) ────────────────────────
public List<SqlParameter> StdParamlist(params SqlParameter[] extra)
{
var list = new List<SqlParameter> { SQL_VarChar("@authuser", UserAccountID) };
list.AddRange(extra);
return list;
}
public List<SqlParameter> StdParamlist(string key, object value, params SqlParameter[] extra)
{
var list = StdParamlist(extra);
list.Insert(0, SQL_VarChar(key, value?.ToString() ?? ""));
return list;
}
public DatabaseSecurity DbSec => _intranet.GetDbSecurity(UserAccountID);
public FIS_SQLOptions SqlOpt(string fn, string id, string code) =>
new(new Dictionary<string, object> { ["fn"] = fn, ["id"] = id, ["code"] = code });
// ── Action helpers ────────────────────────────────────────────────────────
protected IActionResult Unauthorized401() => StatusCode(401);
protected IActionResult BadRequest400() => BadRequest();
protected IActionResult ServerError(string msg = "") => StatusCode(500, new { error = msg });
// ── Index (GET /) ─────────────────────────────────────────────────────────
[AllowAnonymous]
public IActionResult Index(string? fn, string? id, string? code) =>
View("intranet");
// ── Do (POST+GET /do/{fn}/{id}/{code}) ─────────────────────────────────
[AllowAnonymous]
public async Task<IActionResult> Do(string? fn, string? id, string? code)
{
fn = (fn ?? "").ToLower();
id ??= "";
code ??= "";
bool isGet = HttpContext.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase);
if (!UserIdent.IsAuthenticated && !(new string[] { "login","logout" }).Contains(fn.ToLower()) && !_allowedNonAuth.Contains(fn.ToLower()))
{
if (!_allowedGet.Contains(fn.ToLower()) && !_allowedGet.Contains($"{fn.ToLower()}|{id.ToLower()}"))
_logger.LogInformation($"rejected function on do {fn}");
return Unauthorized401();
}
try
{
IActionResult? result = fn.ToLower() switch
{
"ping" => Ok(),
"wdg" => await FuchsWidgets.IntranetWdg(this, id),
"todos" => new PhysicalFileResult(
Path.Combine(Directory.GetCurrentDirectory(), "Data", "ProjectToDos.html"),
"text/html"),
"req" => await Do_Process_Requests(fn, id, code),
"inv" => await Do_Process_Invoices(fn, id, code),
"rem" => await Do_Process_Reminder(fn, id, code),
"rep" => await Do_Process_Reports(fn, id, code),
"bam" => await Do_Process_Bankings(fn, id, code),
"auth" => await HandleAuth(fn, id, code),
"spwc" => await HandleSendPasswordCode(fn, id, code),
"spw" => await HandleSendPassword(fn, id, code),
"account" => await HandleAccount(fn, id, code),
"mfr" => await HandleMfr(fn, id, code),
"mfr_update" => await HandleMfrUpdate(fn, id, code),
"login" => await HandleLogin(fn, id, code),
"logout" => await HandleLogout(),
_ => null
};
return result ?? Ok();
}
catch (Exception ex)
{
_intranet.debug_log("IntranetController.Do", ex, UserAccountID,
data: new { fn, id, code });
return ServerError();
}
}
// ── Auth helper ───────────────────────────────────────────────────────────
private async Task<IActionResult> HandleAuth(string fn, string id, string code)
{
if (!Request.Form.ContainsKey("module")) return BadRequest400();
string module = Request.Form["module"].ToString();
if (Request.Form["array"] == "1")
{
var dt = await getSQLDatatable_async(
"SELECT * FROM [dbo].[fis_getModuleAuthList](@module, @authuser);",
_intranet.Intranet__SQLConnectionString,
StdParamlist(SQL_VarChar("@module", module)),
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(dt.DataTable.ToDictionary(KeyColumn: "module", ValueColumn: "auth"));
}
else
{
var val = await getSQLValue_async<int>(
"SELECT [dbo].[fis_getModuleAuth](@module, @authuser);",
_intranet.Intranet__SQLConnectionString, -1,
StdParamlist(SQL_VarChar("@module", module)),
Security: DbSec, options: SqlOpt(fn, id, code));
return await JSONAsync(new { auth = val.Result });
}
}
// ── Login / Logout ────────────────────────────────────────────────────────
[AllowAnonymous]
private async Task<IActionResult> HandleLogin(string fn, string id, string code)
{
string email = Request.Form["userinfo"].ToString();
string password = Request.Form["userpass"].ToString();
var row = await _intranet.AuthenticateAsync(email, password);
if (row == null)
{
_logger.LogWarning("Login failed for '{Email}' from {IP}",
email, HttpContext.Connection.RemoteIpAddress);
_intranet.debug_log("HandleLogin: failed",
data: new { email, ip = HttpContext.Connection.RemoteIpAddress?.ToString() });
return Unauthorized401();
}
string userId = row.nz("useraccount_id");
string userEmail = row.nz("email");
int auth = int.TryParse(row.nz("authorization"), out var a) ? a : 0;
var identity = FuchsUserIdentity.BuildIdentity(userId, userEmail, auth, Fuchs_intranet.AuthScheme);
var principal = new System.Security.Claims.ClaimsPrincipal(identity);
await HttpContext.SignInAsync(Fuchs_intranet.AuthScheme, principal);
return await JSONAsync(new
{
login = userEmail,
useraccount_id = userId,
email = userEmail,
authorization = auth,
requestedaccount = "",
accountrequired = false
});
}
private async Task<IActionResult> HandleLogout()
{
await HttpContext.SignOutAsync(Fuchs_intranet.AuthScheme);
return Ok();
}
// ── Password helpers ──────────────────────────────────────────────────────
private async Task<IActionResult> HandleSendPasswordCode(string fn, string id, string code)
{
string? lastname = Request.Form["lastname"];
string? email = Request.Form["email"];
if (string.IsNullOrEmpty(lastname) || string.IsNullOrEmpty(email)) return BadRequest400();
var row = await _intranet.GetUserAccountByEmailAsync(email);
if (row != null && row.nz("email").Length > 5 &&
row.nz("name").ToLower().Trim() == lastname.ToLower().Trim() &&
row.nz("mobile").Length > 5 && !Request.Host.Host.ToLower().Contains("localhost"))
{
OCORE.sms.SMS77.Settings.APIKey = _intranet.Intranet__SMS_API_key;
using var sms = new OCORE.sms.SMS77.SMS("ProcessWeb");
string totp = OCORE.security.TFA.generateTotp_12h(_intranet.Intranet__TOTPsharedsecret_base);
if (long.TryParse(row.nz("mobile").Replace("+", "00").Replace(" ", ""), out long smsNum))
sms.SendSMS_sync(smsNum,
"Zur Bestätigung des Passwortversands auf sanitarfuchs.de, verwenden Sie bitte folgenden Code:" + totp);
}
return Ok(); // always OK to prevent enumeration
}
private async Task<IActionResult> HandleSendPassword(string fn, string id, string code)
{
string? lastname = Request.Form["lastname"];
string? email = Request.Form["email"];
string? totpCode = Request.Form["code"];
if (string.IsNullOrEmpty(lastname) || string.IsNullOrEmpty(email) || string.IsNullOrEmpty(totpCode))
return BadRequest400();
if (OCORE.security.TFA.validateTotp_12h(_intranet.Intranet__TOTPsharedsecret_base, totpCode).isVerifiedInTime)
{
var row = await _intranet.GetUserAccountByEmailAsync(email, includePassword: true);
if (row != null && row.nz("email").Length > 5)
{
await FuchsFdsEmail.SendEmail("pw_" + row.nz("email"),
"sanitaerfuchs.de Intranet Passwort",
$"<p>Guten Tag {row.nz("firstname")} {row.nz("name")},<br/>Ihr Passwort: {HttpUtility.HtmlEncode(row.nz("password"))}</p>",
row.nz("email"), $"{row.nz("firstname")} {row.nz("name")}", null, _intranet);
}
}
return Ok();
}
private async Task<IActionResult> HandleAccount(string fn, string id, string code)
{
switch (id.ToLower())
{
case "sms":
var row = await _intranet.GetUserAccountByEmailAsync(UserIdent.Email, includePassword: true);
if (row != null && row.nz("mobile").Length > 5 && !Request.Host.Host.Contains("localhost"))
{
OCORE.sms.SMS77.Settings.APIKey = _intranet.Intranet__SMS_API_key;
using var sms2 = new OCORE.sms.SMS77.SMS("ProcessWeb");
string totp2 = OCORE.security.TFA.generateTotp_3h(_intranet.Intranet__TOTPsharedsecret_base + "3MDR");
if (long.TryParse(row.nz("mobile").Replace("+", "00").Replace(" ", ""), out long mob2))
sms2.SendSMS_sync(mob2, "Zur Bestätigung der Passwortänderung auf sanitarfuchs.de: " + totp2);
}
return Ok();
case "changepassword":
string? npw = Request.Form["npw"];
string? npwc = Request.Form["npwc"];
string? totpCode = Request.Form["code"];
if (string.IsNullOrEmpty(npw) || string.IsNullOrEmpty(npwc) || string.IsNullOrEmpty(totpCode))
return BadRequest400();
if (!OCORE.security.TFA.validateTotp_3h(_intranet.Intranet__TOTPsharedsecret_base + "3MDR", totpCode).isVerifiedInTime)
return StatusCode(409, new { error = "sms" });
await setSQLValue_async(
"EXECUTE [dbo].[fis_admin_setNewPassword] @useraccount_id, @oldpassword, @newpassword, @enc_key;",
_intranet.Intranet__SQLConnectionString,
new List<SqlParameter>
{
SQL_VarChar("@useraccount_id", UserAccountID),
SQL_VarChar("@oldpassword", Request.Form["opw"].ToString()),
SQL_VarChar("@newpassword", npw)
},
Security: DbSec, options: SqlOpt(fn, id, code));
return Ok();
}
return Ok();
}
private async Task<IActionResult> HandleMfr(string fn, string id, string code)
{
if (!string.IsNullOrEmpty(UserAccountID) && UserIdent.Authorization > 3)
{
string path = id + (!string.IsNullOrEmpty(code) ? "/" + code : HttpUtility.UrlDecode(Request.QueryString.Value ?? ""));
using var mfrRead = new fds.FdsMfrClient();
var result = await mfrRead.ReadOData(path, throwErrorIfNotOk: false);
return Content(JsonConvert.SerializeObject(result), "application/json");
}
return Ok();
}
private async Task<IActionResult> HandleMfrUpdate(string fn, string id, string code)
{
var et = EntityHelper.EntityValue(Request.Form["type"].ToString());
if (et != EntityTypes.none && string.IsNullOrEmpty(Request.Form["need"]))
{
using var mfrSingle = new fds.FdsMfrClient();
await mfrSingle.Update__entitytable(et, fds.FdsMfr.UpdateNeed.Short);
return Ok();
}
if (et != EntityTypes.none && !string.IsNullOrEmpty(Request.Form["need"]))
{
var need = fds.FdsMfr.UpdateNeedValue(Request.Form["need"].ToString());
using var mfr = new fds.FdsMfrClient();
await mfr.Update__entitytable(et, updateNeed: need, debugDetails: false);
return Ok();
}
return BadRequest400();
}
}