Changed Reminder Systematics analogue to invoices
This commit is contained in:
@@ -97,6 +97,15 @@ public partial class IntranetController
|
||||
case "idoc": return await HandleReminderIdoc(fn, id, code);
|
||||
case "resend": return await HandleReminderResend(fn, id, code);
|
||||
|
||||
// ── Live backend-authoritative draft editing (ADR 0006) ───────────
|
||||
case "dopen": return await HandleReminderDraftOpen(fn, id, code);
|
||||
case "dstate": return await HandleReminderDraftState(fn, id, code);
|
||||
case "dpatch": return await HandleReminderDraftPatch(fn, id, code);
|
||||
case "dpreview": return await HandleReminderDraftPreview(fn, id, code);
|
||||
case "dsave": return await HandleReminderDraftSave(fn, id, code);
|
||||
case "dhistory": return await HandleReminderDraftHistory(fn, id, code);
|
||||
case "dclose": return await HandleReminderDraftClose(fn, id, code);
|
||||
|
||||
case "lrem":
|
||||
{
|
||||
if (!HasForm("id")) { _logger.LogWarning("Reminder lrem: missing form field 'id', user={User}", UserAccountID); return BadRequest400(); }
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
using Fuchs.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static OCORE.web.mvc_helper_async;
|
||||
|
||||
namespace Fuchs.Controllers;
|
||||
|
||||
// Partial class: live, backend-authoritative reminder draft editing (ADR 0006) — the
|
||||
// reminder mirror of IntranetController.InvoiceDraft.cs. The browser posts single edits
|
||||
// here; the server mutates the in-memory session (the source of truth), recomputes the
|
||||
// open amount / validates, and pings the editing browser over the shared DraftPreviewHub
|
||||
// (draftReady) to re-fetch. Commands are ordinary POSTs — the hub carries only signals.
|
||||
public partial class IntranetController
|
||||
{
|
||||
// POST rem/dopen — { payload } → { token, version }
|
||||
private async Task<IActionResult> HandleReminderDraftOpen(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("payload"))
|
||||
{
|
||||
_logger.LogWarning("Reminder draft dopen: 'payload' missing user={User}", UserAccountID);
|
||||
return BadRequest400();
|
||||
}
|
||||
JObject payload;
|
||||
try { payload = JObject.Parse(Form("payload")); }
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Reminder draft dopen: invalid payload JSON user={User}", UserAccountID);
|
||||
return BadRequest400();
|
||||
}
|
||||
var session = _reminderDrafts.OpenFromPayload(payload, UserAccountID);
|
||||
_logger.LogInformation("Reminder draft dopen: session {Token} (remId={RemId}) user={User}", session.Token, session.RemId, UserAccountID);
|
||||
// The browser holds the token from this response and fetches dstate directly; there is
|
||||
// no server 'draftReady' on open (it would race the client's group-join).
|
||||
return await JSONAsync(new { token = session.Token, version = session.Version });
|
||||
}
|
||||
|
||||
// POST rem/dstate — { token } → full view state
|
||||
private async Task<IActionResult> HandleReminderDraftState(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token")) return BadRequest400();
|
||||
var session = _reminderDrafts.Get(Form("token"));
|
||||
if (session == null) return DraftGone();
|
||||
return await JSONAsync(_reminderDrafts.BuildState(session));
|
||||
}
|
||||
|
||||
// POST rem/dpatch — { token, delta } → { ok, version }; signals draftReady
|
||||
private async Task<IActionResult> HandleReminderDraftPatch(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token", "delta")) return BadRequest400();
|
||||
ReminderDraftDelta? delta;
|
||||
try { delta = JsonConvert.DeserializeObject<ReminderDraftDelta>(Form("delta")); }
|
||||
catch (JsonException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Reminder draft dpatch: invalid delta JSON user={User}", UserAccountID);
|
||||
return BadRequest400();
|
||||
}
|
||||
if (delta == null || string.IsNullOrEmpty(delta.Target)) return BadRequest400();
|
||||
|
||||
var session = _reminderDrafts.ApplyPatch(Form("token"), delta);
|
||||
if (session == null) return DraftGone();
|
||||
await _draftNotifier.SignalDraftReadyAsync(session.Token, session.Version);
|
||||
return await JSONAsync(new { ok = true, version = session.Version });
|
||||
}
|
||||
|
||||
// POST rem/dpreview — { token } → { img[], total } (rendered straight from the cache)
|
||||
private async Task<IActionResult> HandleReminderDraftPreview(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token")) return BadRequest400();
|
||||
var doc = _reminderDrafts.RenderPreview(Form("token"));
|
||||
if (doc == null) return DraftGone();
|
||||
var imgcol = await _pdf.DocToImageCollectionAsync(doc);
|
||||
return await JSONAsync(new { img = imgcol.ImgB64Array, total = imgcol.TotalPages });
|
||||
}
|
||||
|
||||
// POST rem/dsave — { token } → { ok, remid }; flush cache→DB + business event + draftReady
|
||||
private async Task<IActionResult> HandleReminderDraftSave(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token")) return BadRequest400();
|
||||
string token = Form("token");
|
||||
var before = _reminderDrafts.Get(token);
|
||||
if (before == null) return DraftGone();
|
||||
bool existed = !string.IsNullOrEmpty(before.RemId);
|
||||
|
||||
var fdRem = await _reminderDrafts.FlushToDbAsync(token, UserAccountID, DbSec);
|
||||
if (fdRem == null) return DraftGone();
|
||||
if (string.IsNullOrEmpty(fdRem.Id))
|
||||
return await ReminderIssueResult("Der Zwischenstand konnte aufgrund eines Fehlers nicht gespeichert werden.");
|
||||
|
||||
await _events.ReminderDraftRegisteredAsync(fdRem, existed, UserAccountID);
|
||||
var after = _reminderDrafts.Get(token);
|
||||
if (after != null) await _draftNotifier.SignalDraftReadyAsync(after.Token, after.Version);
|
||||
return await JSONAsync(new { ok = true, remid = fdRem.Id });
|
||||
}
|
||||
|
||||
// POST rem/dhistory — { token } → { history[] }
|
||||
private async Task<IActionResult> HandleReminderDraftHistory(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token")) return BadRequest400();
|
||||
if (_reminderDrafts.Get(Form("token")) == null) return DraftGone();
|
||||
var history = _reminderDrafts.GetHistory(Form("token"))
|
||||
.Select(h => new
|
||||
{
|
||||
timestamp = h.TimestampUtc,
|
||||
target = h.Target,
|
||||
@ref = h.Ref,
|
||||
oldValue = h.OldValue,
|
||||
newValue = h.NewValue,
|
||||
version = h.Version
|
||||
});
|
||||
return await JSONAsync(new { history });
|
||||
}
|
||||
|
||||
// POST rem/dclose — { token } → { ok }
|
||||
private async Task<IActionResult> HandleReminderDraftClose(string fn, string id, string code)
|
||||
{
|
||||
if (!HasForm("token")) return BadRequest400();
|
||||
bool ok = _reminderDrafts.Close(Form("token"));
|
||||
_logger.LogDebug("Reminder draft dclose token={Token} removed={Removed} user={User}", Form("token"), ok, UserAccountID);
|
||||
return await JSONAsync(new { ok });
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
private readonly IReminderService _reminders;
|
||||
private readonly IEventService _events;
|
||||
private readonly IInvoiceDraftService _invoiceDrafts;
|
||||
private readonly IReminderDraftService _reminderDrafts;
|
||||
private readonly IDraftNotifier _draftNotifier;
|
||||
private readonly List<string> _allowedNonAuth = new() { "spwc", "spw" };
|
||||
private readonly List<string> _allowedGet = new()
|
||||
@@ -66,6 +67,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
IReminderService reminders,
|
||||
IEventService events,
|
||||
IInvoiceDraftService invoiceDrafts,
|
||||
IReminderDraftService reminderDrafts,
|
||||
IDraftNotifier draftNotifier)
|
||||
{
|
||||
_intranet = intranet;
|
||||
@@ -81,6 +83,7 @@ public partial class IntranetController : Microsoft.AspNetCore.Mvc.Controller
|
||||
_reminders = reminders;
|
||||
_events = events;
|
||||
_invoiceDrafts = invoiceDrafts;
|
||||
_reminderDrafts = reminderDrafts;
|
||||
_draftNotifier = draftNotifier;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user