From ecc74cf3c6a3cd7a4ffda6b9077fd76a5604e7d2 Mon Sep 17 00:00:00 2001 From: Stefan Date: Thu, 10 Sep 2026 08:55:05 +0200 Subject: [PATCH] Add user-defined invoice set structure dialog (ADR 0016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets a back-office user correct/define which invoice line belongs to which "set" and how deep it is nested, instead of relying only on the implicit "everything after a Set header until the next Set" grouping fds__prepInvoice derives. Sets may contain sets; every change applies immediately to the cached draft session and is reflected in the editor and PDF (ADR 0006/0008). Backend: - InvoiceSetStructure: pure parent-link forest (assign, change level, create, place/move-sibling, depth-first linearise + level recompute). - InvoiceDraftSetStructureDeltas: new inv/dpatch targets set.assign, set.level, set.create, set.move. - InvoiceSetPricing.Build is now order-preserving and nesting-aware (governed by the nearest converted ancestor); the set-item switch sums a header's transitive descendants so nested conversions never double-count. - FuchsPdf indents nested set members per level, mirroring the editor. Frontend: - New fis.inv_sets.js: "Set-Struktur bearbeiten" dialog — per-block table of all lines with drag & drop reorder (before/after/into), ▲▼ order and ◀▶ level buttons, fold/unfold per set (view-only), and a create bar for new Set/Text lines. Posts one delta per action; the dialog is a pure view of the server state (ADR 0006). - _Layout.cshtml / fis_main.js: cache-bust the dynamically imported per-module bundles (web/fis...js/.css) with an asset version derived from the newest bundle write time, so a gulp rebuild is picked up without a stale cached module. Docs: ADR 0016, live-draft-editing concept, INVOICE_SET_PRICING.md, USER_GUIDE.md, ARCHITECTURE.md updated. Tests: InvoiceSetStructureTests (new) + additions to InvoiceDraftServiceTests/InvoiceSetPricingTests. Co-Authored-By: Claude Sonnet 5 --- Fuchs.Tests/InvoiceDraftServiceTests.cs | 337 +++++++++++++++ Fuchs.Tests/InvoiceSetPricingTests.cs | 135 ++++++ Fuchs.Tests/InvoiceSetStructureTests.cs | 386 +++++++++++++++++ Fuchs/Docs/ARCHITECTURE.md | 2 + Fuchs/Docs/Concepts/live-draft-editing.md | 23 +- .../0016-user-defined-set-structure.md | 112 +++++ Fuchs/Docs/INVOICE_SET_PRICING.md | 57 ++- Fuchs/Docs/USER_GUIDE.md | 14 + Fuchs/Services/InvoiceDraftEditService.cs | 30 +- .../InvoiceDraftSetStructureDeltas.cs | 120 ++++++ Fuchs/Views/Shared/_Layout.cshtml | 12 + Fuchs/bdlconfig.json | 6 +- Fuchs/code/FuchsPdf.cs | 36 +- Fuchs/code/InvoiceDraftCalculator.cs | 32 +- Fuchs/code/InvoiceSetPricing.cs | 127 +++--- Fuchs/code/InvoiceSetStructure.cs | 404 ++++++++++++++++++ Fuchs/js/intranet/fis_main.js | 5 +- Fuchs/js/intranet/modules/fis.inv_sets.js | 200 +++++++++ Fuchs/js/intranet/modules/fis.inv_shared.js | 52 ++- Fuchs/js/intranet/modules/fis.inv_shared.scss | 105 +++++ Fuchs/js/intranet/modules/fis.inv_txt_de.js | 18 + Fuchs/wwwroot/web/fis.inv.css | 189 ++++++++ Fuchs/wwwroot/web/fis.inv.de.js | 272 +++++++++++- Fuchs/wwwroot/web/fis.inv.de.min.js | 2 +- Fuchs/wwwroot/web/fis.inv.min.css | 2 +- Fuchs/wwwroot/web/fis.js | 5 +- Fuchs/wwwroot/web/fis.min.js | 2 +- Fuchs/wwwroot/web/fis.req.css | 189 ++++++++ Fuchs/wwwroot/web/fis.req.de.js | 272 +++++++++++- Fuchs/wwwroot/web/fis.req.de.min.js | 2 +- Fuchs/wwwroot/web/fis.req.min.css | 2 +- 31 files changed, 3003 insertions(+), 147 deletions(-) create mode 100644 Fuchs.Tests/InvoiceSetStructureTests.cs create mode 100644 Fuchs/Docs/Decisions/0016-user-defined-set-structure.md create mode 100644 Fuchs/Services/InvoiceDraftSetStructureDeltas.cs create mode 100644 Fuchs/code/InvoiceSetStructure.cs create mode 100644 Fuchs/js/intranet/modules/fis.inv_sets.js diff --git a/Fuchs.Tests/InvoiceDraftServiceTests.cs b/Fuchs.Tests/InvoiceDraftServiceTests.cs index c870a82..e9bbf42 100644 --- a/Fuchs.Tests/InvoiceDraftServiceTests.cs +++ b/Fuchs.Tests/InvoiceDraftServiceTests.cs @@ -870,4 +870,341 @@ public class InvoiceDraftServiceTests Assert.Equal(4, final.History.Count); Assert.DoesNotContain(final.ValidationMessages, m => m.Severity == "error"); } + + // ── User-defined set structure (ADR 0016): set.assign / set.level / set.create ─────────── + // A(set) > B(set) > m1, m2 ; A > m3 ; x standalone. Delivered linearised, like fds__prepInvoice + Linearize would. + private static JObject NestedSetPayload() => JObject.Parse(@"{ + 'admin':{'p13b':false,'type':'r','paymentterms':'10wd'}, + 'new':{'invoiceemail':'a@b.de','invoiceaddress':'Weg 1','invoicetitle':'Rechnung'}, + 'req':[{'Id':'1','text':'Auftrag','itm':[ + {'id':'A','typ':'set','vt':0,'vv':0,'vs':0,'vsv':0,'vat':'19%','SetItmId':'A'}, + {'id':'B','typ':'set','vt':0,'vv':0,'vs':0,'vsv':0,'vat':'19%','SetItmId':'A'}, + {'id':'m1','typ':'material','vt':60,'vv':11.4,'vs':0,'vsv':0,'vat':'19%','SetItmId':'B'}, + {'id':'m2','typ':'material','vt':40,'vv':7.6,'vs':10,'vsv':1.9,'vat':'19%','SetItmId':'B'}, + {'id':'m3','typ':'material','vt':25,'vv':4.75,'vs':0,'vsv':0,'vat':'19%','SetItmId':'A'}, + {'id':'x','typ':'material','vt':10,'vv':1.9,'vs':0,'vsv':0,'vat':'19%','SetItmId':null}], + 'items':[{'id':'A','type':'set','total_net':0},{'id':'B','type':'set','total_net':0}, + {'id':'m1','type':'material','total_net':60,'vat':'19%'},{'id':'m2','type':'material','total_net':40,'vat':'19%'}, + {'id':'m3','type':'material','total_net':25,'vat':'19%'},{'id':'x','type':'material','total_net':10,'vat':'19%'}]}] + }"); + + private static string[] LineOrder(InvoiceDraftSession s, int block = 0) => + ((JArray)((JObject)s.Req[block])["itm"]!).OfType().Select(l => (string)l["id"]!).ToArray(); + private static JObject LineOf(InvoiceDraftSession s, string id, int block = 0) => + ((JArray)((JObject)s.Req[block])["itm"]!).OfType().Single(l => (string)l["id"]! == id); + private static JObject ItemOf(InvoiceDraftSession s, string id, int block = 0) => + ((JArray)((JObject)s.Req[block])["items"]!).OfType().Single(l => (string)l["id"]! == id); + + [Fact] + public void OpenFromPayload_NestedSets_PublishesLevelsAndMirrorsSetIdOntoItems() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + Assert.Equal(0, LineOf(s, "A")["lvl"]!.Value()); + Assert.Equal(1, LineOf(s, "B")["lvl"]!.Value()); + Assert.Equal(2, LineOf(s, "m1")["lvl"]!.Value()); + Assert.Equal(1, LineOf(s, "m3")["lvl"]!.Value()); + Assert.Equal(0, LineOf(s, "x")["lvl"]!.Value()); + Assert.Equal("A", (string)ItemOf(s, "B")["setId"]!); // a nested set is a member of its parent + Assert.Equal("B", (string)ItemOf(s, "m1")["setId"]!); + Assert.Equal(JTokenType.Null, ItemOf(s, "A")["setId"]!.Type); // self-reference -> no parent + Assert.Equal(2, ItemOf(s, "m1")["level"]!.Value()); + Assert.Equal(135m, s.Sums.TotalNet); + } + + [Fact] + public void ApplyPatch_SetAssign_DropOntoSet_BecomesLastMember_BumpsVersionRecordsHistory() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.assign", Ref = "x", Value = JObject.Parse("{'parent':'A'}") }); + + Assert.NotNull(s2); + Assert.Equal(1, s2!.Version); + Assert.Equal(new[] { "A", "B", "m1", "m2", "m3", "x" }, LineOrder(s2)); + Assert.Equal(1, LineOf(s2, "x")["lvl"]!.Value()); + Assert.Equal("A", (string)LineOf(s2, "x")["SetItmId"]!); + Assert.Equal("A", (string)ItemOf(s2, "x")["setId"]!); + Assert.Equal(135m, s2.Sums.TotalNet); // structure never changes the total + var h = Assert.Single(s2.History); + Assert.Equal("set.assign", h.Target); Assert.Equal("x", h.Ref); Assert.Equal("", h.OldValue); Assert.Equal("A", h.NewValue); + Assert.Equal("1.3", (string)LineOf(s2, "x")["p"]!); // positions renumbered along the new order (hierarchical, ADR 0017) + } + + [Fact] + public void ApplyPatch_SetAssign_PullSetOut_ItsMembersFollowOneLevelUp() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.assign", Ref = "B", Value = JToken.FromObject("") }); + + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2", "x" }, LineOrder(s2!)); + Assert.Equal(0, LineOf(s2!, "B")["lvl"]!.Value()); + Assert.Equal(1, LineOf(s2!, "m2")["lvl"]!.Value()); + Assert.Equal("B", (string)LineOf(s2!, "m2")["SetItmId"]!); + Assert.Equal("A", s2!.History[0].OldValue); + } + + [Theory] + [InlineData("A", "B")] // onto its own descendant — cycle + [InlineData("x", "m3")] // onto a non-set line + [InlineData("m1", "B")] // unchanged + [InlineData("nope", "A")] // unknown item + [InlineData("", "A")] // missing ref + public void ApplyPatch_SetAssign_Invalid_IsNoOp_NoVersionBumpNoHistory(string item, string parent) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + var before = LineOrder(s); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.assign", Ref = item, Value = JObject.Parse("{'parent':'" + parent + "'}") }); + + Assert.Equal(0, s2!.Version); + Assert.Empty(s2.History); + Assert.Equal(before, LineOrder(s2)); + } + + [Theory] + [InlineData("m3", 1, "1", "2", new[] { "A", "B", "m1", "m2", "m3", "x" })] // indent: joins preceding sibling set B + [InlineData("m1", -1, "2", "1", new[] { "A", "B", "m2", "m1", "m3", "x" })] // outdent: leaves B, placed after B's subtree + [InlineData("B", -1, "1", "0", new[] { "A", "m3", "B", "m1", "m2", "x" })] // outdent a set: members travel with it + public void ApplyPatch_SetLevel_IndentOutdent_ReordersAndRecordsLevels(string item, int delta, string oldLvl, string newLvl, string[] order) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.level", Ref = item, Value = JToken.FromObject(delta) }); + + Assert.Equal(1, s2!.Version); + Assert.Equal(order, LineOrder(s2)); + var h = Assert.Single(s2.History); + Assert.Equal("set.level", h.Target); Assert.Equal(oldLvl, h.OldValue); Assert.Equal(newLvl, h.NewValue); + Assert.Equal(int.Parse(newLvl), LineOf(s2, item)["lvl"]!.Value()); + Assert.Equal(135m, s2.Sums.TotalNet); + } + + [Theory] + [InlineData("x", "-1")] // already top level + [InlineData("A", "in")] // nothing precedes the first line + [InlineData("m1", "0")] // no direction + [InlineData("m1", "abc")] // unparseable + public void ApplyPatch_SetLevel_NoMove_IsNoOp(string item, string value) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + var before = LineOrder(s); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.level", Ref = item, Value = JToken.FromObject(value) }); + + Assert.Equal(0, s2!.Version); + Assert.Equal(before, LineOrder(s2)); + } + + [Fact] + public void ApplyPatch_SetCreate_Set_InsideParent_AppendsAsLastMember_UnpricedAndNumbered() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.create", Ref = "1", Value = JObject.Parse("{'typ':'set','title':'Unterset','parent':'B'}") }); + + Assert.Equal(1, s2!.Version); + var order = LineOrder(s2); + Assert.Equal(7, order.Length); + string id = order[4]; // A, B, m1, m2, NEW, m3, x + Assert.StartsWith("sN", id); + Assert.Equal(new[] { "A", "B", "m1", "m2", id, "m3", "x" }, order); + var co = LineOf(s2, id); + Assert.Equal("set", (string)co["typ"]!); + Assert.Equal("Unterset", (string)co["t"]!); + Assert.Equal(0m, co["vt"]!.Value()); // unconverted — the "Auf Setpreis umstellen" switch applies later + Assert.Equal(2, co["lvl"]!.Value()); + Assert.Equal("1.1.3", (string)co["p"]!); // a set header is numbered, hierarchically (ADR 0017) + Assert.Equal("set", (string)ItemOf(s2, id)["type"]!); + Assert.Equal("B", (string)ItemOf(s2, id)["setId"]!); + Assert.Equal(135m, s2.Sums.TotalNet); + var h = Assert.Single(s2.History); + Assert.Equal("set.create", h.Target); Assert.Equal("1", h.Ref); Assert.Equal("set:Unterset", h.NewValue); + } + + [Fact] + public void ApplyPatch_SetCreate_Text_TopLevel_IsUnnumberedFreeText() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.create", Ref = "1", Value = JObject.Parse("{'typ':'text','title':'Hinweis','parent':''}") }); + + string id = LineOrder(s2!).Last(); + var co = LineOf(s2!, id); + Assert.Equal("Text", (string)co["typ"]!); + Assert.Equal("

Hinweis

", (string)co["t"]!); + Assert.Equal("", (string)co["p"]!); // free text carries no position number + Assert.Equal(0, co["lvl"]!.Value()); + Assert.Equal("text", (string)ItemOf(s2!, id)["type"]!); + Assert.Equal("

Hinweis

", (string)ItemOf(s2!, id)["desc"]!); + } + + [Theory] + [InlineData("1", "{'typ':'material','title':'Neu','parent':''}")] // unsupported type + [InlineData("1", "{'typ':'set','title':' ','parent':''}")] // blank title + [InlineData("1", "{'typ':'set','title':'Neu','parent':'m3'}")] // parent is not a set + [InlineData("99", "{'typ':'set','title':'Neu','parent':''}")] // unknown block + [InlineData("1", "'not an object'")] // wrong value shape + public void ApplyPatch_SetCreate_Invalid_IsNoOp(string blockRef, string valueJson) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.create", Ref = blockRef, Value = JToken.Parse(valueJson) }); + + Assert.Equal(0, s2!.Version); + Assert.Equal(6, LineOrder(s2).Length); + Assert.Empty(s2.History); + } + + [Fact] + public void ApplyPatch_ItemSetPrice_NestedSet_SumsTransitiveDescendants_NullsAll_TotalUnchanged() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "item.setprice", Ref = "A" }); + + var a = LineOf(s2!, "A"); + Assert.Equal(125m, a["vt"]!.Value()); // 60 + 40 + 25 (B itself contributed 0 — unconverted) + Assert.Equal(23.75m, a["vv"]!.Value()); + Assert.Equal(10m, a["vs"]!.Value()); + foreach (var id in new[] { "B", "m1", "m2", "m3" }) + Assert.Equal(JTokenType.Null, LineOf(s2!, id)["vt"]!.Type); + Assert.Equal(10m, LineOf(s2!, "x")["vt"]!.Value()); // standalone untouched + Assert.Equal(135m, s2!.Sums.TotalNet); + Assert.Equal(125m, ItemOf(s2, "A")["total_net"]!.Value()); + Assert.Equal(JTokenType.Null, ItemOf(s2, "B")["total_net"]!.Type); + } + + [Fact] + public void ApplyPatch_ItemSetPrice_InnerThenOuter_NoDoubleCounting() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "item.setprice", Ref = "B" }); + Assert.Equal(100m, LineOf(svc.Get(s.Token)!, "B")["vt"]!.Value()); + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "item.setprice", Ref = "A" }); + + Assert.Equal(125m, LineOf(s2!, "A")["vt"]!.Value()); // B's set price (100) + m3 (25) + Assert.Equal(JTokenType.Null, LineOf(s2!, "B")["vt"]!.Type); + Assert.Equal(135m, s2!.Sums.TotalNet); + Assert.Equal(2, s2.Version); + } + + [Fact] + public void ApplyPatch_SetCreateThenAssignThenSetPrice_UserDefinedSetGetsMembersSum() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.create", Ref = "1", Value = JObject.Parse("{'typ':'set','title':'Extra','parent':''}") }); + string newId = LineOrder(svc.Get(s.Token)!).Last(); + svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.assign", Ref = "x", Value = JObject.Parse("{'parent':'" + newId + "'}") }); + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "item.setprice", Ref = newId }); + + Assert.Equal(3, s2!.Version); + Assert.Equal(10m, LineOf(s2, newId)["vt"]!.Value()); + Assert.Equal(JTokenType.Null, LineOf(s2, "x")["vt"]!.Type); + Assert.Equal(135m, s2.Sums.TotalNet); + Assert.Equal(new[] { "A", "B", "m1", "m2", "m3", newId, "x" }, LineOrder(s2)); + } + + [Fact] + public void BuildState_NestedConvertedSet_SetDisplayBlanksWholeSubtree_AndLinesCarryLevels() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "item.setprice", Ref = "A" }); + + var state = JObject.FromObject(svc.BuildState(s2!)); + var disp = (JObject)state["setDisplay"]!; + Assert.True(disp["A"]!["p"]!.Value()); Assert.True(disp["A"]!["h"]!.Value()); + Assert.False(disp["B"]!["p"]!.Value()); Assert.True(disp["B"]!["h"]!.Value()); // nested header inside a converted set: blank, still emphasised + Assert.False(disp["m1"]!["p"]!.Value()); + Assert.False(disp["m3"]!["p"]!.Value()); + Assert.True(disp["x"]!["p"]!.Value()); + Assert.Equal(2, state["req"]![0]!["itm"]![2]!["lvl"]!.Value()); + } + + [Fact] + public void ApplyPatch_BlockReplace_KeepsUserDefinedStructure_FromPostedParentLinks() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + // The editor re-posts the block after a local edit, carrying SetItmId per row (rrw -> co). + var nb = JObject.Parse(@"{'Id':'1','text':'Auftrag','itm':[ + {'id':'A','typ':'set','vt':0,'vat':'19%','SetItmId':'A'}, + {'id':'x','typ':'material','vt':10,'vv':1.9,'vat':'19%','SetItmId':'A'}], + 'items':[{'id':'A','type':'set'},{'id':'x','type':'material','total_net':10}]}"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "block.replace", Ref = "1", Value = nb }); + + Assert.Equal(1, LineOf(s2!, "x")["lvl"]!.Value()); + Assert.Equal("A", (string)ItemOf(s2!, "x")["setId"]!); + Assert.Equal(10m, s2!.Sums.TotalNet); + } + + [Theory] + [InlineData("B", "1", "0", "1", new[] { "A", "m3", "B", "m1", "m2", "x" })] // down among A's members, subtree travels + [InlineData("x", "-1", "1", "0", new[] { "x", "A", "B", "m1", "m2", "m3" })] // up at top level + [InlineData("m2", "up", "1", "0", new[] { "A", "B", "m2", "m1", "m3", "x" })] // word form + public void ApplyPatch_SetMove_Sibling_ReordersAndRecordsIndices(string item, string value, string oldIdx, string newIdx, string[] order) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.move", Ref = item, Value = JToken.Parse(value.StartsWith("-") || char.IsDigit(value[0]) ? value : "'" + value + "'") }); + + Assert.Equal(1, s2!.Version); + Assert.Equal(order, LineOrder(s2)); + var h = Assert.Single(s2.History); + Assert.Equal("set.move", h.Target); Assert.Equal(oldIdx, h.OldValue); Assert.Equal(newIdx, h.NewValue); + Assert.Equal(135m, s2.Sums.TotalNet); + } + + [Fact] + public void ApplyPatch_SetMove_Anchor_DragBetweenRows_AdoptsAnchorsParent() + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.move", Ref = "x", Value = JObject.Parse("{'anchor':'m1','where':'before'}") }); + + Assert.Equal(new[] { "A", "B", "x", "m1", "m2", "m3" }, LineOrder(s2!)); + Assert.Equal(2, LineOf(s2!, "x")["lvl"]!.Value()); + Assert.Equal("B", (string)LineOf(s2!, "x")["SetItmId"]!); + Assert.Equal("", s2!.History[0].OldValue); + Assert.StartsWith("before:m1", s2.History[0].NewValue); + Assert.Equal(3, LineOf(s2, "x")["p"]!.Value()); + } + + [Theory] + [InlineData("A", "-1")] // first line, no previous sibling + [InlineData("A", "{'anchor':'m1','where':'after'}")] // anchor inside own subtree + [InlineData("x", "{'anchor':'m1','where':'sideways'}")] // bad direction + [InlineData("x", "{'anchor':'','where':'before'}")] // missing anchor + [InlineData("nope", "1")] + public void ApplyPatch_SetMove_Invalid_IsNoOp(string item, string valueJson) + { + var (svc, _, _) = NewService(); + var s = svc.OpenFromPayload(NestedSetPayload(), "user1"); + var before = LineOrder(s); + + var s2 = svc.ApplyPatch(s.Token, new InvoiceDraftDelta { Target = "set.move", Ref = item, Value = JToken.Parse(valueJson) }); + + Assert.Equal(0, s2!.Version); + Assert.Equal(before, LineOrder(s2)); + Assert.Empty(s2.History); + } } diff --git a/Fuchs.Tests/InvoiceSetPricingTests.cs b/Fuchs.Tests/InvoiceSetPricingTests.cs index f8a6e42..d0ac698 100644 --- a/Fuchs.Tests/InvoiceSetPricingTests.cs +++ b/Fuchs.Tests/InvoiceSetPricingTests.cs @@ -201,4 +201,139 @@ public class InvoiceSetPricingTests .Sum(i => decimal.Parse(i["total_net"]!.ToString()!, System.Globalization.CultureInfo.InvariantCulture)); Assert.Equal(memberSum, setLine.TotalNet); } + + // ── Nested sets (ADR 0016): order preserved, levels reported, nearest converted ancestor rules ── + private static Dictionary NestedHeader(string id, string parent, string title, string total) => new() + { + ["type"] = "set", ["id"] = id, ["setId"] = parent, ["title"] = title, ["qty"] = "1", ["total_net"] = total, ["price_net"] = total + }; + + // A(converted 125) > B(unconverted) > m1, m2 ; A > m3 ; x standalone + private static List> NestedOuterConverted() => new() + { + SetHeader("A", "Komplett", "125.00"), + NestedHeader("B", "A", "Unterset", "0"), + Member("B", "m1", "60.00", "60.00"), + Member("B", "m2", "40.00", "40.00"), + Member("A", "m3", "25.00", "25.00"), + Standalone("x", "10.00", "10.00") + }; + + [Fact] + public void Build_Nested_OuterConverted_SetPrice_BlanksWholeSubtree_ReportsLevels() + { + var lines = InvoiceSetPricing.Build(NestedOuterConverted(), SetDisplayMode.SetPrice); + + Assert.Equal(new[] { "Komplett", "Unterset", "m1", "m2", "m3", "x" }, lines.Select(l => l.Title)); // input order kept + Assert.Equal(new[] { 0, 1, 2, 2, 1, 0 }, lines.Select(l => l.Level)); + Assert.True(lines[0].IsSetHeader); Assert.True(lines[0].ShowPrice); Assert.Equal(125m, lines[0].TotalNet); + Assert.True(lines[1].IsSetHeader); Assert.False(lines[1].ShowPrice); // nested header inside a converted set + Assert.False(lines[2].ShowPrice); Assert.False(lines[3].ShowPrice); Assert.False(lines[4].ShowPrice); + Assert.True(lines[5].ShowPrice); Assert.Equal(10m, lines[5].TotalNet); + } + + [Fact] + public void Build_Nested_OuterConverted_SetOnly_DropsWholeSubtree_KeepsStandalone() + { + var lines = InvoiceSetPricing.Build(NestedOuterConverted(), SetDisplayMode.SetOnly); + Assert.Equal(new[] { "Komplett", "x" }, lines.Select(l => l.Title)); + Assert.True(lines[0].ShowPrice); Assert.True(lines[1].ShowPrice); + } + + // A(unconverted) > B(converted 100) > m1, m2 ; A > m3(25) + private static List> NestedInnerConverted() => new() + { + SetHeader("A", "Komplett"), + NestedHeader("B", "A", "Unterset", "100.00"), + Member("B", "m1", "60.00", "60.00"), + Member("B", "m2", "40.00", "40.00"), + Member("A", "m3", "25.00", "25.00") + }; + + [Theory] + [InlineData(SetDisplayMode.SetPrice, 5)] + [InlineData(SetDisplayMode.SetOnly, 3)] + public void Build_Nested_InnerConvertedOnly_InnerSubtreeCollapses_OuterStaysIndividuallyPriced(SetDisplayMode mode, int count) + { + var lines = InvoiceSetPricing.Build(NestedInnerConverted(), mode); + + Assert.Equal(count, lines.Count); + var a = lines.Single(l => l.Title == "Komplett"); + Assert.True(a.IsSetHeader); Assert.False(a.ShowPrice); // outer still unconverted -> blank + var b = lines.Single(l => l.Title == "Unterset"); + Assert.True(b.IsSetHeader); Assert.True(b.ShowPrice); Assert.Equal(100m, b.TotalNet); Assert.Equal(1, b.Level); + var m3 = lines.Single(l => l.Title == "m3"); + Assert.True(m3.ShowPrice); Assert.Equal(25m, m3.TotalNet); Assert.Equal(1, m3.Level); + if (mode == SetDisplayMode.SetPrice) Assert.All(lines.Where(l => l.Title is "m1" or "m2"), l => Assert.False(l.ShowPrice)); + else Assert.DoesNotContain(lines, l => l.Title is "m1" or "m2"); + } + + [Fact] + public void Build_PreservesInputOrder_EvenWhenStandaloneSitsBetweenHeaderAndMember() + { + var items = new List> + { + SetHeader("10", "Set", "1000.00"), + Standalone("Anfahrt", "50.00", "50.00"), + Member("10", "Waschbecken", "600.00", "600.00") + }; + var lines = InvoiceSetPricing.Build(items, SetDisplayMode.SetPrice); + Assert.Equal(new[] { "Set", "Anfahrt", "Waschbecken" }, lines.Select(l => l.Title)); // editor == PDF sequence (ADR 0008) + Assert.True(lines[1].ShowPrice); + Assert.False(lines[2].ShowPrice); + } + + [Fact] + public void Build_HeaderSelfReferencingSetId_IsTopLevel_NotItsOwnMember() + { + var header = SetHeader("7", "Set", "100.00"); header["setId"] = "7"; // the shape fds__prepInvoice delivers + var lines = InvoiceSetPricing.Build(new List> { header, Member("7", "A", "100.00", "100.00") }, SetDisplayMode.SetOnly); + Assert.Single(lines); + Assert.Equal(0, lines[0].Level); + Assert.True(lines[0].ShowPrice); + } + + [Fact] + public void Build_MemberPointingAtUnknownSet_IsTreatedAsStandalone() + { + var lines = InvoiceSetPricing.Build(new List> { Member("ghost", "A", "5.00", "5.00") }, SetDisplayMode.SetOnly); + var l = Assert.Single(lines); + Assert.True(l.ShowPrice); Assert.Equal(0, l.Level); + } + + [Fact] + public void AssignPositionLabels_SetWithMembersPlusStandalone_IsHierarchical() + { + // Set(10) header + 2 members, then a standalone item — mirrors Sample(). + var lines = InvoiceSetPricing.Build(Sample(), SetDisplayMode.SetPrice); + InvoiceSetPricing.AssignPositionLabels(new[] { lines }); + + Assert.Equal(new[] { "1", "1.1", "1.2", "2" }, lines.Select(l => l.PositionLabel)); + } + + [Fact] + public void AssignPositionLabels_TextLineGetsNoLabel_AndDoesNotShiftFollowingNumbers() + { + var items = new List> + { + Standalone("A", "1.00", "1.00"), + new() { ["type"] = "text", ["title"] = "", ["desc"] = "Hinweis", ["qty"] = "" }, + Standalone("B", "2.00", "2.00") + }; + var lines = InvoiceSetPricing.Build(items, SetDisplayMode.SetPrice); + InvoiceSetPricing.AssignPositionLabels(new[] { lines }); + + Assert.Equal(new[] { "1", "", "2" }, lines.Select(l => l.PositionLabel)); + } + + [Fact] + public void AssignPositionLabels_SpansMultipleBlocks_Continuously() + { + var block1 = InvoiceSetPricing.Build(new List> { Standalone("A", "1.00", "1.00") }, SetDisplayMode.SetPrice); + var block2 = InvoiceSetPricing.Build(Sample(), SetDisplayMode.SetPrice); + InvoiceSetPricing.AssignPositionLabels(new[] { block1, block2 }); + + Assert.Equal(new[] { "1" }, block1.Select(l => l.PositionLabel)); + Assert.Equal(new[] { "2", "2.1", "2.2", "3" }, block2.Select(l => l.PositionLabel)); + } } diff --git a/Fuchs.Tests/InvoiceSetStructureTests.cs b/Fuchs.Tests/InvoiceSetStructureTests.cs new file mode 100644 index 0000000..72e185e --- /dev/null +++ b/Fuchs.Tests/InvoiceSetStructureTests.cs @@ -0,0 +1,386 @@ +using System.Collections.Generic; +using System.Linq; +using Fuchs.intranet; +using Newtonsoft.Json.Linq; +using Xunit; + +namespace Fuchs.Tests; + +/// +/// Exercises the pure set-structure logic (ADR 0016): parent links (SetItmId) on a block's +/// itm lines form a forest; levels, depth-first linearisation, assign / level / create +/// mutations and the mirror onto the items contract shape are all verified without a +/// service or database. Ids: A/B are sets, m* are members, x is standalone. +/// +public class InvoiceSetStructureTests +{ + // A(set) > B(set) > m1, m2 ; A > m3 ; x standalone — delivered already linearised. + private static JObject NestedBlock() => JObject.Parse(@"{ + 'Id':'1','text':'Auftrag', + 'itm':[ + {'id':'A','typ':'set','vt':0,'vat':'19%','SetItmId':'A'}, + {'id':'B','typ':'set','vt':0,'vat':'19%','SetItmId':'A'}, + {'id':'m1','typ':'material','vt':60,'vat':'19%','SetItmId':'B'}, + {'id':'m2','typ':'material','vt':40,'vat':'19%','SetItmId':'B'}, + {'id':'m3','typ':'material','vt':25,'vat':'19%','SetItmId':'A'}, + {'id':'x','typ':'material','vt':10,'vat':'19%','SetItmId':null}], + 'items':[ + {'id':'A','type':'set'},{'id':'B','type':'set'}, + {'id':'m1','type':'material','total_net':60},{'id':'m2','type':'material','total_net':40}, + {'id':'m3','type':'material','total_net':25},{'id':'x','type':'material','total_net':10}] + }"); + + private static string[] Order(JObject block) => InvoiceSetStructure.Lines(block).Select(InvoiceSetStructure.Id).ToArray(); + private static string[] ItemOrder(JObject block) => ((JArray)block["items"]!).OfType().Select(i => (string)i["id"]!).ToArray(); + private static int Lvl(JObject block, string id) => InvoiceSetStructure.Lines(block).Single(l => InvoiceSetStructure.Id(l) == id)[InvoiceSetStructure.LevelKey]!.Value(); + private static JObject Item(JObject block, string id) => ((JArray)block["items"]!).OfType().Single(i => (string)i["id"]! == id); + private static string? Parent(JObject block, string id) + { + var lines = InvoiceSetStructure.Lines(block); + return InvoiceSetStructure.ParentOf(lines.Single(l => InvoiceSetStructure.Id(l) == id), InvoiceSetStructure.ById(lines)); + } + + // ── Levels / parent normalisation ───────────────────────────────────────── + [Fact] + public void Levels_NestedForest_ReportsDepthPerLine() + { + var levels = InvoiceSetStructure.Levels(InvoiceSetStructure.Lines(NestedBlock())); + Assert.Equal(0, levels["A"]); + Assert.Equal(1, levels["B"]); + Assert.Equal(2, levels["m1"]); + Assert.Equal(2, levels["m2"]); + Assert.Equal(1, levels["m3"]); + Assert.Equal(0, levels["x"]); + } + + [Theory] + [InlineData("A")] // self-reference (the shape fds__prepInvoice delivers for a header) + [InlineData("nope")] // unknown id + [InlineData("m3")] // points at a non-set line + [InlineData("")] + public void ParentOf_SelfUnknownNonSetOrEmpty_IsTopLevel(string parent) + { + var block = NestedBlock(); + InvoiceSetStructure.Lines(block).Single(l => InvoiceSetStructure.Id(l) == "A")["SetItmId"] = parent; + Assert.Null(Parent(block, "A")); + Assert.Equal(0, InvoiceSetStructure.Levels(InvoiceSetStructure.Lines(block))["A"]); + } + + [Fact] + public void Levels_CyclicChain_DegradesToTopLevel_AndLinearizeKeepsEveryLine() + { + var block = JObject.Parse(@"{'Id':'1','itm':[ + {'id':'A','typ':'set','SetItmId':'B'},{'id':'B','typ':'set','SetItmId':'A'},{'id':'m','typ':'material','SetItmId':'A'}]}"); + var levels = InvoiceSetStructure.Levels(InvoiceSetStructure.Lines(block)); + Assert.Equal(0, levels["A"]); + Assert.Equal(0, levels["B"]); + InvoiceSetStructure.Linearize(block); + Assert.Equal(3, Order(block).Length); // nothing lost + Assert.Contains("m", Order(block)); + } + + [Fact] + public void Descendants_AreTransitive_ExcludeTheSetItself_AndUnrelatedLines() + { + var ids = InvoiceSetStructure.Descendants(InvoiceSetStructure.Lines(NestedBlock()), "A").Select(InvoiceSetStructure.Id).ToArray(); + Assert.Equal(new[] { "B", "m3", "m1", "m2" }, ids); // breadth-first: direct members first, then B's + Assert.DoesNotContain("A", ids); + Assert.DoesNotContain("x", ids); + Assert.Equal(new[] { "m1", "m2" }, InvoiceSetStructure.Descendants(InvoiceSetStructure.Lines(NestedBlock()), "B").Select(InvoiceSetStructure.Id)); + Assert.Empty(InvoiceSetStructure.Descendants(InvoiceSetStructure.Lines(NestedBlock()), "x")); + } + + // ── Linearize / RecomputeLevels ─────────────────────────────────────────── + [Fact] + public void Linearize_ScrambledInput_EmitsEachSetFollowedByItsMembersRecursively_AndMirrorsItems() + { + var block = JObject.Parse(@"{'Id':'1', + 'itm':[{'id':'m1','typ':'material','SetItmId':'B'},{'id':'x','typ':'material'},{'id':'B','typ':'set','SetItmId':'A'}, + {'id':'m3','typ':'material','SetItmId':'A'},{'id':'A','typ':'set'},{'id':'m2','typ':'material','SetItmId':'B'}], + 'items':[{'id':'m2'},{'id':'osum0','type':'osum'},{'id':'A'},{'id':'x'},{'id':'B'},{'id':'m1'},{'id':'m3'}]}"); + + InvoiceSetStructure.Linearize(block); + + Assert.Equal(new[] { "x", "A", "B", "m1", "m2", "m3" }, Order(block)); // roots keep their relative order (x came before A) + Assert.Equal(new[] { "x", "A", "B", "m1", "m2", "m3", "osum0" }, ItemOrder(block)); // items mirrored; unmatched entries kept at the end + Assert.Equal(0, Lvl(block, "A")); Assert.Equal(1, Lvl(block, "B")); Assert.Equal(2, Lvl(block, "m2")); Assert.Equal(1, Lvl(block, "m3")); + Assert.Equal("A", (string)Item(block, "B")["setId"]!); + Assert.Equal("B", (string)Item(block, "m1")["setId"]!); + Assert.Equal(JTokenType.Null, Item(block, "A")["setId"]!.Type); + Assert.Equal(2, Item(block, "m1")["level"]!.Value()); + Assert.Null(Item(block, "osum0")["level"]); // untouched + } + + [Fact] + public void RecomputeLevels_DoesNotReorder_ButPublishesLevelsAndMirrorsSetId() + { + var block = JObject.Parse(@"{'Id':'1', + 'itm':[{'id':'m1','typ':'material','SetItmId':'A'},{'id':'A','typ':'set','SetItmId':'A'}], + 'items':[{'id':'m1','setId':'stale'},{'id':'A','setId':'A'}]}"); + var s = new InvoiceDraftSession { Req = new JArray { block } }; + + InvoiceSetStructure.RecomputeLevels(s); + + Assert.Equal(new[] { "m1", "A" }, Order(block)); // order untouched + Assert.Equal(1, Lvl(block, "m1")); + Assert.Equal("A", (string)Item(block, "m1")["setId"]!); // stale mirror corrected + Assert.Equal(JTokenType.Null, Item(block, "A")["setId"]!.Type); // self-reference mirrored as "no parent" + } + + // ── Assign ──────────────────────────────────────────────────────────────── + [Fact] + public void Assign_StandaloneOntoSet_BecomesLastMember() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Assign(block, "x", "A", out string oldParent)); + Assert.Equal("", oldParent); + Assert.Equal(new[] { "A", "B", "m1", "m2", "m3", "x" }, Order(block)); + Assert.Equal(1, Lvl(block, "x")); + Assert.Equal("A", (string)Item(block, "x")["setId"]!); + } + + [Fact] + public void Assign_SetOntoSet_KeepsItsOwnMembersOneLevelDeeper() + { + var block = JObject.Parse(@"{'Id':'1','itm':[ + {'id':'A','typ':'set'},{'id':'m3','typ':'material','SetItmId':'A'}, + {'id':'B','typ':'set'},{'id':'m1','typ':'material','SetItmId':'B'},{'id':'m2','typ':'material','SetItmId':'B'}]}"); + Assert.True(InvoiceSetStructure.Assign(block, "B", "A", out _)); + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2" }, Order(block)); + Assert.Equal(1, Lvl(block, "B")); + Assert.Equal(2, Lvl(block, "m1")); + Assert.Equal("B", Parent(block, "m1")); // still B's member + } + + [Fact] + public void Assign_PullOutToTopLevel_PlacesLineAfterItsFormerSubtree_MembersFollow() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Assign(block, "B", "", out string oldParent)); + Assert.Equal("A", oldParent); + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2", "x" }, Order(block)); + Assert.Equal(0, Lvl(block, "B")); + Assert.Equal(1, Lvl(block, "m1")); + Assert.Equal(JTokenType.Null, Item(block, "B")["setId"]!.Type); + } + + [Fact] + public void Assign_DeepMemberToTopLevel_LandsAfterTheWholeRootSubtree() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Assign(block, "m1", "", out _)); + Assert.Equal(new[] { "A", "B", "m2", "m3", "m1", "x" }, Order(block)); + Assert.Equal(0, Lvl(block, "m1")); + } + + [Theory] + [InlineData("x", "")] // already top level + [InlineData("m1", "B")] // unchanged parent + [InlineData("A", "A")] // onto itself + [InlineData("A", "B")] // onto its own descendant (cycle) + [InlineData("x", "m3")] // onto a non-set line + [InlineData("x", "nope")] // unknown target + [InlineData("nope", "A")] // unknown item + public void Assign_InvalidOrUnchanged_IsNoOp(string item, string parent) + { + var block = NestedBlock(); + var before = Order(block); + Assert.False(InvoiceSetStructure.Assign(block, item, parent, out _)); + Assert.Equal(before, Order(block)); + } + + // ── ChangeLevel ─────────────────────────────────────────────────────────── + [Fact] + public void ChangeLevel_Indent_JoinsNearestPrecedingSiblingSetAsLastMember() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.ChangeLevel(block, "m3", +1, out int oldLevel, out int newLevel)); + Assert.Equal(1, oldLevel); Assert.Equal(2, newLevel); + Assert.Equal(new[] { "A", "B", "m1", "m2", "m3", "x" }, Order(block)); + Assert.Equal("B", Parent(block, "m3")); + } + + [Fact] + public void ChangeLevel_Outdent_JoinsGrandparentRightAfterFormerParentSubtree() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.ChangeLevel(block, "m1", -1, out int oldLevel, out int newLevel)); + Assert.Equal(2, oldLevel); Assert.Equal(1, newLevel); + Assert.Equal(new[] { "A", "B", "m2", "m1", "m3", "x" }, Order(block)); + Assert.Equal("A", Parent(block, "m1")); + } + + [Fact] + public void ChangeLevel_OutdentSet_TakesItsMembersAlong() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.ChangeLevel(block, "B", -1, out _, out int newLevel)); + Assert.Equal(0, newLevel); + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2", "x" }, Order(block)); + Assert.Equal(1, Lvl(block, "m2")); + } + + [Fact] + public void ChangeLevel_IndentTopLevelLineAfterASet_JoinsThatSet() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.ChangeLevel(block, "x", +1, out int oldLevel, out int newLevel)); + Assert.Equal(0, oldLevel); Assert.Equal(1, newLevel); + Assert.Equal(new[] { "A", "B", "m1", "m2", "m3", "x" }, Order(block)); + Assert.Equal("A", Parent(block, "x")); + } + + [Theory] + [InlineData("A", +1)] // first line — nothing precedes it + [InlineData("m1", +1)] // first member of B — no preceding sibling set inside B + [InlineData("x", -1)] // already top level + [InlineData("m1", 0)] // no direction + [InlineData("nope", -1)] // unknown + public void ChangeLevel_NoMovePossible_IsNoOp(string item, int delta) + { + var block = NestedBlock(); + var before = Order(block); + Assert.False(InvoiceSetStructure.ChangeLevel(block, item, delta, out _, out _)); + Assert.Equal(before, Order(block)); + } + + // ── Create ──────────────────────────────────────────────────────────────── + [Fact] + public void Create_Set_InsideParent_AppendsAsLastMember_WritesBothShapes() + { + var block = NestedBlock(); + string? id = InvoiceSetStructure.Create(block, "set", " Unterset ", "B"); + Assert.NotNull(id); + Assert.StartsWith("sN", id); + Assert.Equal(new[] { "A", "B", "m1", "m2", id!, "m3", "x" }, Order(block)); + var co = InvoiceSetStructure.Lines(block).Single(l => InvoiceSetStructure.Id(l) == id); + Assert.Equal("set", (string)co["typ"]!); + Assert.Equal("Unterset", (string)co["t"]!); + Assert.Equal(0m, co["vt"]!.Value()); + Assert.Equal("19%", (string)co["vat"]!); // adopts the block's rate + Assert.Equal(2, Lvl(block, id!)); + var io = Item(block, id!); + Assert.Equal("set", (string)io["type"]!); + Assert.Equal("Unterset", (string)io["title"]!); + Assert.Equal("B", (string)io["setId"]!); + Assert.Equal(0m, io["total_net"]!.Value()); + } + + [Fact] + public void Create_Text_TopLevel_AppendsAtBlockEnd_HtmlEncodesTitle() + { + var block = NestedBlock(); + string? id = InvoiceSetStructure.Create(block, "TEXT", "Hinweis fett", ""); + Assert.NotNull(id); + Assert.Equal(id, Order(block).Last()); + var co = InvoiceSetStructure.Lines(block).Last(); + Assert.Equal("Text", (string)co["typ"]!); + Assert.Equal("

Hinweis <b>fett</b>

", (string)co["t"]!); + Assert.Equal(0, Lvl(block, id!)); + Assert.Equal("text", (string)Item(block, id!)["type"]!); + Assert.Equal("

Hinweis <b>fett</b>

", (string)Item(block, id!)["desc"]!); + } + + [Fact] + public void Create_WhenBlockHasNoLinesOrItems_CreatesBothArrays_WithFallbackRate() + { + var block = JObject.Parse(@"{'Id':'9'}"); + string? id = InvoiceSetStructure.Create(block, "set", "Neu", ""); + Assert.NotNull(id); + Assert.Single((JArray)block["itm"]!); + Assert.Single((JArray)block["items"]!); + Assert.Equal("19%", (string)((JArray)block["itm"]!)[0]["vat"]!); + } + + [Theory] + [InlineData("material", "Neu", "")] // unsupported type + [InlineData("set", " ", "")] // blank title + [InlineData("set", "Neu", "m3")] // parent is not a set + [InlineData("set", "Neu", "nope")] // unknown parent + public void Create_Invalid_ReturnsNullAndLeavesBlockUntouched(string typ, string title, string parent) + { + var block = NestedBlock(); + var before = Order(block); + Assert.Null(InvoiceSetStructure.Create(block, typ, title, parent)); + Assert.Equal(before, Order(block)); + Assert.Equal(6, ((JArray)block["items"]!).Count); + } + + // ── Place / MoveSibling (reorder) ───────────────────────────────────────── + [Fact] + public void Place_BeforeAnchor_BecomesAnchorsSibling_SubtreeTravels() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Place(block, "B", "x", after: false, out string oldParent, out string newParent)); + Assert.Equal("A", oldParent); Assert.Equal("", newParent); + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2", "x" }, Order(block)); + Assert.Equal(0, Lvl(block, "B")); Assert.Equal(1, Lvl(block, "m1")); + } + + [Fact] + public void Place_AfterAnchor_LandsAfterAnchorsWholeSubtree() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Place(block, "x", "B", after: true, out _, out string newParent)); + Assert.Equal("A", newParent); // x is now B's sibling inside A + Assert.Equal(new[] { "A", "B", "m1", "m2", "x", "m3" }, Order(block)); + Assert.Equal(1, Lvl(block, "x")); + } + + [Fact] + public void Place_MemberBeforeItsOwnParent_MovesUpOneLevel() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.Place(block, "m1", "B", after: false, out _, out string newParent)); + Assert.Equal("A", newParent); + Assert.Equal(new[] { "A", "m1", "B", "m2", "m3", "x" }, Order(block)); + Assert.Equal(1, Lvl(block, "m1")); + } + + [Theory] + [InlineData("A", "m1", true)] // anchor inside own subtree + [InlineData("A", "A", false)] // itself + [InlineData("nope", "x", true)] // unknown item + [InlineData("x", "nope", false)] // unknown anchor + public void Place_Invalid_IsNoOp(string item, string anchor, bool after) + { + var block = NestedBlock(); + var before = Order(block); + Assert.False(InvoiceSetStructure.Place(block, item, anchor, after, out _, out _)); + Assert.Equal(before, Order(block)); + } + + [Fact] + public void MoveSibling_Down_SwapsWithNextSibling_SubtreeTravels() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.MoveSibling(block, "B", +1, out int oldIndex, out int newIndex)); + Assert.Equal(0, oldIndex); Assert.Equal(1, newIndex); + Assert.Equal(new[] { "A", "m3", "B", "m1", "m2", "x" }, Order(block)); + Assert.Equal(1, Lvl(block, "B")); Assert.Equal(2, Lvl(block, "m1")); // still inside A + } + + [Fact] + public void MoveSibling_Up_TopLevel_MovesWholeSetAboveStandalone() + { + var block = NestedBlock(); + Assert.True(InvoiceSetStructure.MoveSibling(block, "x", -1, out int oldIndex, out int newIndex)); + Assert.Equal(1, oldIndex); Assert.Equal(0, newIndex); + Assert.Equal(new[] { "x", "A", "B", "m1", "m2", "m3" }, Order(block)); + } + + [Theory] + [InlineData("A", -1)] // first top-level line + [InlineData("x", +1)] // last top-level line + [InlineData("m1", -1)] // first member of B + [InlineData("m2", +1)] // last member of B (m3 belongs to A, not B) + [InlineData("m1", 0)] + [InlineData("nope", 1)] + public void MoveSibling_AtEdge_IsNoOp(string item, int delta) + { + var block = NestedBlock(); + var before = Order(block); + Assert.False(InvoiceSetStructure.MoveSibling(block, item, delta, out _, out _)); + Assert.Equal(before, Order(block)); + } +} diff --git a/Fuchs/Docs/ARCHITECTURE.md b/Fuchs/Docs/ARCHITECTURE.md index efa71b7..ea0567d 100644 --- a/Fuchs/Docs/ARCHITECTURE.md +++ b/Fuchs/Docs/ARCHITECTURE.md @@ -177,6 +177,8 @@ disable/failure). See [`Concepts/erechnung-output.md`](Concepts/erechnung-output Stateless services (`IPdfService`, `IBankingService`, `IMfrClientFactory`) are singletons; DB/request-scoped services are scoped (see `Program.cs`). The **Admin** module (`Do_Process_Admin`, `ISystemStatusService`) surfaces a live system-status/diagnostics page (host, SQL/Key Vault/blob/MFR connectivity, email config, test-email) restricted to `fds_sys` > 4 — see ADR [0011](Decisions/0011-admin-module-system-status.md) and the [concept doc](Concepts/admin-system-status.md). `FdsInvoiceData` / `FdsReminderData` are now **pure data holders** (parse + properties); loading, persistence and PDF generation live in the services (fully async — no `Task.Run(...).Wait()`). + +Invoice/reminder drafts are edited **backend-authoritatively**: the truth is an in-memory `InvoiceDraftSession` mutated by single deltas (`IInvoiceDraftService`), including the user-defined **set structure** (`InvoiceSetStructure` — parent links, nesting, levels; ADR [0016](Decisions/0016-user-defined-set-structure.md)) and the set-price operations (`InvoiceSetPricing`, ADR [0009](Decisions/0009-block-setprice-modes-are-irreversible-mutations.md)). See the [live draft editing concept](Concepts/live-draft-editing.md) and ADRs [0006](Decisions/0006-backend-authoritative-draft-editing.md)/[0008](Decisions/0008-invoices-and-reminders-fully-backend-authoritative.md). `FuchsPdf` / `FuchsVisualization` remain as static rendering libraries used *by* the services. The earlier static, controller-coupled helpers (`FuchsWidgets`, `FuchsReports`, `Banking`, `FuchsFdsEmail`) have been removed. ### 4.4 SQL-First Data Access diff --git a/Fuchs/Docs/Concepts/live-draft-editing.md b/Fuchs/Docs/Concepts/live-draft-editing.md index 8524f8a..b1733d2 100644 --- a/Fuchs/Docs/Concepts/live-draft-editing.md +++ b/Fuchs/Docs/Concepts/live-draft-editing.md @@ -1,9 +1,10 @@ --- status: Active -lastUpdated: 2026-07-10 +lastUpdated: 2026-09-09 applyTo: - "Fuchs/Services/InvoiceDraft*" - "Fuchs/Services/IInvoiceDraft*" + - "Fuchs/code/InvoiceSetStructure.cs" - "Fuchs/Services/ReminderDraft*" - "Fuchs/Services/IReminderDraft*" - "Fuchs/code/InvoiceDraftSession.cs" @@ -18,6 +19,8 @@ applyTo: relatedDecisions: - "0006-backend-authoritative-draft-editing.md" - "0007-targeted-draft-signalr-groups.md" + - "0009-block-setprice-modes-are-irreversible-mutations.md" + - "0016-user-defined-set-structure.md" --- # Live draft editing (backend-authoritative invoice previews) @@ -83,6 +86,19 @@ Expiry: Server (timer) --SignalR draftExpiring{token,secondsLeft}--> warn "bit `BuildInvoiceRequestList`), applies deltas (`ApplyDelta`), builds the view-state DTO, flushes to the DB by reusing `IInvoiceService.RegisterInvoiceAsync` (no new persistence path), renders previews from a synthesised registration, and discards by reloading. +- **Set structure** (ADR 0016). Every cached line carries a parent link (`SetItmId` → the + owning `typ == "set"` line, null/self = top level); sets may nest. `fds__prepInvoice`'s + implicit tagging is the initial structure; the user edits it in the "Set-Struktur + bearbeiten" dialog (`$inv.sets`, `fis.inv_sets.js`) which posts one delta per action — + `set.assign` { parent } (drop onto a set / pull out), `set.level` ±1 (indent under the + preceding sibling set / outdent to the grandparent), `set.create` { typ: set|text, title, + parent }, `set.move` ±1 | { anchor, where } (reorder among siblings / drag before or after + a row; the subtree travels along). `InvoiceDraftSetStructureDeltas` delegates to the pure + `InvoiceSetStructure` (assign / change level / create / place, then **linearise** the block depth-first and mirror + `setId`/`level` onto the `items` contract). `Refresh` publishes `lvl` on every line + (`RecomputeLevels`), rendered as an indent class in the editor and a per-level indent in the + PDF. `item.setprice` sums a header's **transitive** descendants. Structure deltas never + change values — only order, membership, levels and therefore position numbers. - **Cache** (`InvoiceDraftCache`, singleton) stores sessions by token with an idle sliding TTL; `InvoiceDraftExpiryService` (a `BackgroundService`) warns before, and evicts after, the TTL. TTL/warn-lead are configurable under `Fuchs:DraftEditing`. @@ -97,12 +113,15 @@ Expiry: Server (timer) --SignalR draftExpiring{token,secondsLeft}--> warn "bit ## Key files - `Fuchs/code/InvoiceDraftSession.cs` — session + `ChangeHistoryEntry` + `InvoiceDraftSums`. - `Fuchs/code/InvoiceDraftCalculator.cs` — pure recompute + validate. +- `Fuchs/code/InvoiceSetStructure.cs` — pure set forest: parent links, levels, linearise, assign/level/create (ADR 0016). +- `Fuchs/Services/InvoiceDraftSetStructureDeltas.cs` — the `set.*` patch targets. - `Fuchs/Services/InvoiceDraftCache.cs` / `IInvoiceDraftCache.cs` — in-memory store + TTL. - `Fuchs/Services/InvoiceDraftEditService.cs` / `IInvoiceDraftService.cs` — orchestration + delta contract. - `Fuchs/Services/InvoiceDraftExpiryService.cs` — idle warn/evict monitor. - `Fuchs/Notifications/DraftPreviewHub.cs`, `DraftNotifier.cs`, `IDraftNotifier.cs` — targeted signals. - `Fuchs/Controllers/IntranetController.InvoiceDraft.cs` — `inv/d*` endpoints. - `Fuchs/js/intranet/fis_main.js`, `Fuchs/js/intranet/modules/fis.inv_shared.js` — client. +- `Fuchs/js/intranet/modules/fis.inv_sets.js` — the set-structure dialog (`$inv.sets`), a view of `dstate.req`. ## Reminders (Zahlungserinnerung) @@ -151,3 +170,5 @@ amount pair. ## Related decisions - [0006 — Backend-authoritative draft editing](../Decisions/0006-backend-authoritative-draft-editing.md) - [0007 — Targeted draft SignalR groups](../Decisions/0007-targeted-draft-signalr-groups.md) +- [0009 — Block set-price modes are irreversible mutations](../Decisions/0009-block-setprice-modes-are-irreversible-mutations.md) +- [0016 — User-defined set structure](../Decisions/0016-user-defined-set-structure.md) diff --git a/Fuchs/Docs/Decisions/0016-user-defined-set-structure.md b/Fuchs/Docs/Decisions/0016-user-defined-set-structure.md new file mode 100644 index 0000000..c3c785b --- /dev/null +++ b/Fuchs/Docs/Decisions/0016-user-defined-set-structure.md @@ -0,0 +1,112 @@ +--- +status: Accepted +date: 2026-09-09 +applyTo: + - "Fuchs/code/InvoiceSetStructure.cs" + - "Fuchs/code/InvoiceSetPricing.cs" + - "Fuchs/Services/InvoiceDraft*" + - "Fuchs/Services/IInvoiceDraft*" + - "Fuchs/code/InvoiceDraftCalculator.cs" + - "Fuchs/code/FuchsPdf.cs" + - "Fuchs/js/intranet/modules/fis.inv_sets.js" + - "Fuchs/js/intranet/modules/fis.inv_shared.js" + - "Fuchs/Docs/INVOICE_SET_PRICING.md" +supersededBy: "" +--- + +# 0016 — Set membership is an explicit, user-editable parent link on the cached lines; sets may nest; the server linearises + +## Context +Set membership on an invoice draft was purely **implicit**: `fds__prepInvoice` tags every item +that follows a zero-priced `Type = 'Set'` header (until the next such header) with that header's +id (`SetItmId`), and the set-item switch (ADR [0009](0009-block-setprice-modes-are-irreversible-mutations.md), +function 1) sums exactly those tagged lines. Users had no way to correct that grouping — a +trailing item that does not belong, a material line that should sit inside the set, a set that +should contain another set — nor to introduce a set (or a free-text line) that does not exist +in mfr. The product owner asked for a dialog that lets the user define which line belongs to +which set, create new set/text lines, change a line's level, and nest sets, with every change +applied **immediately** to the cached draft and reflected in the editor. + +## Decision +1. **The implicit derivation stays as the initial structure.** `fds__prepInvoice`'s + `SetItmId` window function is unchanged; a freshly opened draft shows exactly the grouping + it delivered. + +2. **Membership is an explicit parent link on every cached line.** Each line in a block's + `itm` array carries `SetItmId` = the id of the `typ == "set"` line that owns it (null, or a + self-reference as delivered for headers, = top level). The links form a forest per + service-request block; the block is the boundary — a line is never assigned to a set of + another block. `InvoiceSetStructure.ParentOf` normalises the link (self-reference, unknown + id, non-set target → top level). + +3. **Sets may contain sets.** A set's own `SetItmId` names its parent set. Moving a set keeps + its members attached — they simply sit one level deeper/shallower. The depth of every line is + published as `lvl` (co shape) / `level` (items contract) and mirrored onto `items[].setId`, + so the PDF and the editor read one structure. + +4. **Four new backend-authoritative deltas** (`inv/dpatch`, handled by + `InvoiceDraftSetStructureDeltas` → pure `InvoiceSetStructure`): + - `set.assign` (Ref = item, Value `{ parent }`; `""` = top level) — drop a line onto a set + (it becomes the set's **last** member) or pull it out (placed right after the subtree it + came from). Rejected as a no-op: unknown item, target not a set of the same block, target + is the item itself or one of its descendants (cycle), parent unchanged. + - `set.level` (Ref = item, Value ±1) — indent = join the nearest **preceding sibling set** + as its last member; outdent = join the grandparent right after the former parent's + subtree. No-op when no such move exists. + - `set.create` (Ref = block, Value `{ typ: "set" | "text", title, parent }`) — a new + zero-priced set header (convertible later via the item switch) or a free-text line, as the + last member of `parent` / end of the block. Both line shapes are written; the title is + HTML-encoded for the text line. + - `set.move` (Ref = item, Value ±1 or `{ anchor, where: "before" | "after" }`) — reorder: + one step up/down among the line's siblings, or become the anchor's sibling placed before + it / after its subtree (drag between rows). The line's own subtree travels with it, so a + set can be reordered folded or unfolded alike. Rejected: anchor inside the item's subtree. + +5. **The server linearises after every structural change**: the block's lines are reordered + depth-first (each set immediately followed by its members, recursively; siblings keep their + relative order), levels are recomputed and the `items` contract is mirrored (order, + `setId`, `level`). `InvoiceSetPricing.Build` is therefore **order-preserving**: it no longer + regroups members next to their header at render time; a line is governed by its nearest + converted ancestor (blank in SetPrice, dropped in SetOnly). Editor and PDF print the same + sequence and the same per-level indent (ADR 0008). + +6. **The set-item switch sums transitive descendants.** `item.setprice` on a set now sums and + nulls the header's whole subtree: an unconverted nested set contributes its members' + prices, an already-converted nested set contributes its own set price — so converting inner + then outer, or outer directly, both conserve the total and count nothing twice. + +7. **The dialog is a view, not a model.** `$inv.sets` (fis.inv_sets.js) renders + `state.req[].itm` (pos, type, quantity, title, indent, ▲▼ order and ◀▶ level buttons, fold + toggles, per-block create bar), posts one delta per action and re-renders from the next + `dstate`. Folding (`collapsed` set ids) is browser-only view state. The dialog performs no + restructuring, ordering or numbering itself (ADR 0006). + +## Consequences +- `Refresh` runs `InvoiceSetStructure.RecomputeLevels` on every patch, so a `block.replace` + posted by the editor (rows carry `SetItmId` from `rrw`) keeps the user-defined structure; the + editor never has to re-derive it. Levels are rendered as an indent class (`lvl1…`) in the + editor and a per-level left indent in the PDF. +- `invSumUpdate` now tags a nested **set** with its `setId` too (previously only non-set + members were tagged), and `BuildSetDisplay` blanks the whole subtree of a converted set — + including nested headers, which stay emphasised. +- Structural deltas never change a value: totals, VAT splits and `NetByBlock` are unaffected; + only positions are renumbered along the new order. +- Row drag in the editor (the existing `Sortable`) reorders rows without touching parent links; + a member dragged away from its set keeps its membership and indent. The structure dialog is + the place to change membership. +- Tests: `InvoiceSetStructureTests` (pure forest logic), `InvoiceDraftServiceTests` (deltas, + history, nested set price, state), `InvoiceSetPricingTests` (nesting, order preservation). + +## Alternatives considered +- **Derive membership from row order (everything after a set until the next set)** as the + live model: rejected — that is exactly what the user cannot correct, and it cannot express + nesting or an item that follows a set but does not belong to it. +- **A separate structure table (tree) beside the lines**: rejected — a parent link per line + keeps the single `itm`/`items` shape the calculator, the PDF, the persistence path and the + editor already consume, so nothing else had to be re-shaped. +- **Render-time regrouping (keep `Build` moving members next to their header)**: rejected — + with explicit links the server can guarantee contiguity by linearising once, and an + order-preserving `Build` is the only way the editor and the PDF are guaranteed to show the + same sequence. +- **Client-side restructuring with a full block re-post**: rejected per ADR 0006/0008 — one + delta per action, the server owns the mutation, the browser mirrors. diff --git a/Fuchs/Docs/INVOICE_SET_PRICING.md b/Fuchs/Docs/INVOICE_SET_PRICING.md index f65d62f..bf0c78c 100644 --- a/Fuchs/Docs/INVOICE_SET_PRICING.md +++ b/Fuchs/Docs/INVOICE_SET_PRICING.md @@ -1,8 +1,10 @@ # Invoice "Set" Pricing — Design & Front-/Back-end Contract > Governed by ADR [0009](Decisions/0009-block-setprice-modes-are-irreversible-mutations.md) -> (the two menu modes) and ADR [0008](Decisions/0008-invoices-and-reminders-fully-backend-authoritative.md) -> (everything server-side, PDF == editor). ADR 0009 **redefined** the two menu modes from the +> (the two menu modes), ADR [0008](Decisions/0008-invoices-and-reminders-fully-backend-authoritative.md) +> (everything server-side, PDF == editor) and ADR +> [0016](Decisions/0016-user-defined-set-structure.md) (set membership is an explicit, +> user-editable parent link; sets may nest — see "Set structure" below). ADR 0009 **redefined** the two menu modes from the > reversible, non-mutating display toggles this document previously described into irreversible, > per-service-request-block mutations — the text below reflects the redefinition. @@ -40,6 +42,37 @@ for the full breakdown. > or removes (mode 3), and the set-item switch (mode 1) writes exactly the members' sum onto the > header. +## Set structure (ADR 0016): which line belongs to which set + +`SetItmId` is no longer only what `fds__prepInvoice` derived from row order — it is the +**explicit parent link** of every cached line, and the user can edit it: + +- **Initial structure** = the implicit derivation (every item after a zero-priced Set header + until the next one). Nothing changes for drafts the user never touches. +- **"Set-Struktur bearbeiten"** (editor menu → `$inv.sets`, `fis.inv_sets.js`) shows every + line per service-request block (Pos., Typ, Menge, Bezeichnung, indented by level). Drag a + line onto a set → `set.assign { parent }` (it becomes the set's last member); drag it onto + the block heading / the "oberste Ebene" zone → `set.assign { parent: "" }` (pulled out, placed + after the subtree it came from). Drag a line to the **top/bottom edge** of another row → + `set.move { anchor, where: before|after }` (reorder: it becomes that row's sibling; a set + travels with its members, folded or not); ▲ / ▼ post `set.move` −1 / +1 (one step among + siblings). The ◀ / ▶ buttons post `set.level` −1 / +1 (outdent to the + grandparent / indent under the nearest preceding sibling set). Sets can be folded via the + triangle (view state only). The per-block bar creates a + new **Set** (zero-priced header, convertible via function 1) or **Text** line inside any set + or at the top level → `set.create { typ, title, parent }`. +- **Sets may contain sets.** A set dropped onto another set keeps its own members (one level + deeper). Function 1 (`item.setprice`) sums the header's **transitive** descendants and nulls + them all; converting inner-then-outer or outer directly conserves the total either way. +- **Only sets are drop targets; a line never leaves its block.** Cycles (a set onto its own + descendant) and no-op moves are rejected server-side (`InvoiceSetStructure`), the browser + merely greys them out. +- **The server linearises** after each structural delta (depth-first: set, then its members, + recursively; siblings keep their order) and publishes `lvl`/`level`; `InvoiceSetPricing.Build` + is order-preserving, so editor and PDF show the same sequence and the same per-level indent. + Every change is applied immediately to the cached session and pushed back via + `draftReady`/`dstate`; the dialog re-renders from that state. + ## Back-end > **Migration status (ADR 0009).** The code below still reflects the previous @@ -51,9 +84,14 @@ for the full breakdown. > migration lands so it stays a faithful description of the code. - `Fuchs/code/InvoiceSetPricing.cs` — the authoritative transformation: - `SetDisplayMode` + `Build(items, mode)` → ordered `InvoiceSetLine`s, each with - `ShowPrice` and `IsSetHeader`. `ModeFromInvoiceOptions(...)` reads the mode + `SetDisplayMode` + `Build(items, mode)` → `InvoiceSetLine`s **in input order**, each with + `ShowPrice`, `IsSetHeader` and `Level` (nesting depth). A line is governed by its nearest + *converted* ancestor set (blank in SetPrice, dropped in SetOnly); an unconverted header is + blank with individually priced members. `ModeFromInvoiceOptions(...)` reads the mode from the invoice options. Fully covered by `Fuchs.Tests/InvoiceSetPricingTests.cs`. +- `Fuchs/code/InvoiceSetStructure.cs` — the pure set forest (parent links, levels, depth-first + linearisation, assign / change level / create); `Fuchs/Services/InvoiceDraftSetStructureDeltas.cs` + maps the `set.*` patch targets onto it. Covered by `InvoiceSetStructureTests` + `InvoiceDraftServiceTests`. - `FuchsPdf.ApplyInvoice` renders through `InvoiceSetPricing.Build`: lines with `ShowPrice == false` render **blank** price/total cells (not `0,00 €`), and the set header line is rendered emphasised. Invoices without sets pass through @@ -82,11 +120,12 @@ Wired in `Fuchs/js/intranet/modules/fis.inv_shared.js` (bundled to C# PDF. This change closes that gap for **all** invoices, not just sets.) 3. **Set flags (function 1 only)** — `invSumUpdate` tags items as it builds `items[]`: - an item with `type === 'set'` is a header (`id` = its set id); a member item's - `setId` is taken directly from the server-computed `SetItmId` field on the row - (`rrx.SetItmId`, populated by `fds__prepInvoice`'s `[SetItmID]` window function, - anchored on the still-unconverted, zero-priced `'set'` header that owns it) — - **not** re-derived from row order in the browser. `mfr__items` itself still has no + an item with `type === 'set'` is a header (`id` = its set id); a member's + `setId` is taken directly from the `SetItmId` field on the row + (`rrx.SetItmId` — initially populated by `fds__prepInvoice`'s `[SetItmID]` window function, + anchored on the still-unconverted, zero-priced `'set'` header that owns it, and thereafter + maintained by the server per ADR 0016, including for a **nested set**, which is a member of + its parent set) — **not** re-derived from row order in the browser. `mfr__items` itself still has no explicit member link; `fds__prepInvoice` computes `SetItmId` per request from the item list, so only items the server actually attributes to a set are tagged, and unrelated items following a set in the list are never swept in. The header row's diff --git a/Fuchs/Docs/USER_GUIDE.md b/Fuchs/Docs/USER_GUIDE.md index af0eb13..4119204 100644 --- a/Fuchs/Docs/USER_GUIDE.md +++ b/Fuchs/Docs/USER_GUIDE.md @@ -50,6 +50,20 @@ Serviceaufträge kommen aus dem MFR-ERP-System und werden regelmäßig automatis 1. Aus einem Serviceauftrag eine Rechnung **vorbereiten** → Vorschau prüfen. 2. **Speichern** (Entwurf) oder weiter **bearbeiten**. Entwürfe tragen ein Wasserzeichen. +### Sets (Positionen zusammenfassen) +- Ein **Set** aus mfr fasst die darauf folgenden Positionen zusammen; mit **„Auf Setpreis + umstellen“** (Waage-Symbol an der Set-Zeile) erhält das Set die Summe seiner Positionen als + Preis, die Positionen selbst werden ohne Preis gedruckt (nicht rückgängig zu machen). +- **„Set-Struktur bearbeiten“** (Menü) zeigt alle Positionen je Auftrag. Positionen per + **Drag & Drop auf ein Set** ziehen, um sie zuzuordnen; auf die Auftragszeile oder den Bereich + „oberste Ebene“ ziehen, um sie aus dem Set zu lösen. **Reihenfolge**: Zeile an den oberen + bzw. unteren Rand einer anderen Zeile ziehen (davor/danach) oder ▲ / ▼ nutzen. Mit ◀ / ▶ eine + Ebene höher/tiefer. Sets lassen sich über das Dreieck zuklappen und auch zugeklappt verschieben. + Sets können Sets enthalten; ein verschobenes Set nimmt seine Positionen mit. Über die Leiste + „Neu“ lassen sich neue **Set**- oder **Text**-Zeilen anlegen (auch innerhalb eines Sets). +- Jede Änderung wird sofort übernommen und im Editor sowie in der Vorschau abgebildet + (eingerückt je Ebene); die Rechnungssumme ändert sich dadurch nicht. + ### Finalisieren & Versenden 1. **Bestätigen/Finalisieren**: Die Rechnung erhält eine Rechnungsnummer, das PDF wird erzeugt und gespeichert. 2. Ist eine E-Mail-Adresse hinterlegt, wird die Rechnung **automatisch als PDF-Anhang** per E-Mail versendet. diff --git a/Fuchs/Services/InvoiceDraftEditService.cs b/Fuchs/Services/InvoiceDraftEditService.cs index 293e0ab..c9ef2d8 100644 --- a/Fuchs/Services/InvoiceDraftEditService.cs +++ b/Fuchs/Services/InvoiceDraftEditService.cs @@ -95,6 +95,7 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService switch (d.Target) { case "email": return SetNewText(s, "invoiceemail", d, ref oldValue, ref newValue); + case "emailcc": return SetNewText(s, "invoiceemailcc", d, ref oldValue, ref newValue); // "Zusatzempfänger" — Cc recipient case "address": // Structured dialog form posts a JSON object; a plain string keeps the legacy // free-text path working (backward compatibility). @@ -124,6 +125,10 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService case "item.setprice": return ApplyItemSetPrice(s, d, ref oldValue, ref newValue); case "block.setprice": return ApplyBlockSetPricing(s, removeMembers: false, ref oldValue, ref newValue); case "block.setonly": return ApplyBlockSetPricing(s, removeMembers: true, ref oldValue, ref newValue); + case "set.assign": + case "set.level": + case "set.create": + case "set.move": return InvoiceDraftSetStructureDeltas.Apply(s, d, ref oldValue, ref newValue); // ADR 0016: user-defined set structure default: return false; } } @@ -226,9 +231,12 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService /// manually afterwards (ordinary item edit), which is why this is one-way. /// /// The header row's own SetItmId now self-references its own id (rather than being - /// null) once fds__prepInvoice stopped special-casing it, so membership is - /// determined by id == Ref first (captured as below and - /// continued past) — a header is never mistaken for its own member. + /// null) once fds__prepInvoice stopped special-casing it; membership is resolved + /// by , which never yields the header itself. + /// Because sets may contain sets (ADR 0016), the members are the header's transitive + /// descendants: a nested, still-unconverted set contributes its members' prices, a nested + /// already-converted set contributes its own set price — either way every descendant line is + /// price-nulled afterwards, so the total is conserved and nothing is counted twice. /// /// private static bool ApplyItemSetPrice(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) @@ -236,17 +244,12 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService if (string.IsNullOrEmpty(d.Ref)) return false; foreach (var blockTok in s.Req) { - if (blockTok is not JObject block || block["itm"] is not JArray lines) continue; - JObject? header = null; - var members = new List(); - foreach (var lineTok in lines) - { - if (lineTok is not JObject co) continue; - if (Str(co["id"]) == d.Ref) { header = co; continue; } // the header itself is never a member, even if its SetItmId self-references - if (Str(co["SetItmId"]) == d.Ref) members.Add(co); - } + if (blockTok is not JObject block || block["itm"] is not JArray) continue; + var lines = InvoiceSetStructure.Lines(block); + var header = lines.FirstOrDefault(co => Str(co["id"]) == d.Ref); if (header == null) continue; if (!string.Equals(Str(header["typ"]), "set", StringComparison.OrdinalIgnoreCase)) return false; + var members = InvoiceSetStructure.Descendants(lines, d.Ref); oldValue = InvoiceDraftCalculator.Dec(header["vt"]).ToString(CultureInfo.InvariantCulture); decimal sumNet = 0, sumVat = 0, sumSvcNet = 0, sumSvcVat = 0; @@ -256,6 +259,7 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService sumVat += InvoiceDraftCalculator.Dec(m["vv"]); sumSvcNet += InvoiceDraftCalculator.Dec(m["vs"]); sumSvcVat += InvoiceDraftCalculator.Dec(m["vsv"]); + if (Str(header["vat"]).Length == 0 && Str(m["vat"]).Length > 0) header["vat"] = Str(m["vat"]); // a user-created set adopts its members' rate NullLinePrice(m); // ADR 0009: null (empty cell), not 0 — excluded from the sum, distinct from a real 0,00 € } header["v"] = sumNet; header["vt"] = sumNet; header["vv"] = sumVat; header["vs"] = sumSvcNet; header["vsv"] = sumSvcVat; @@ -540,6 +544,7 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService { InvoiceDraftCalculator.RecomputeLineValues(session); InvoiceDraftCalculator.RecomputeTotals(session); + InvoiceSetStructure.RecomputeLevels(session); // ADR 0016: publish lvl + mirror setId/level onto the items contract — must run before RecomputePositions, which reads lvl (ADR 0017) InvoiceDraftCalculator.RecomputePositions(session); InvoiceDraftCalculator.Validate(session); } @@ -609,6 +614,7 @@ public sealed class InvoiceDraftEditService : IInvoiceDraftService ["InvoiceTitle"] = title, ["SendToAddress"] = Str(session.New["invoiceaddress"]), ["SendToEmail"] = Str(session.New["invoiceemail"]), + ["SendToEmailCc"] = Str(session.New["invoiceemailcc"]), ["ProvisionLocation"] = loc, ["ProvisionPeriod"] = Str(session.New["provisionperiod"]), ["PaymentTerm"] = Str(session.Admin["paymentterms"]).ne(Str(session.New["paymentterm"])), diff --git a/Fuchs/Services/InvoiceDraftSetStructureDeltas.cs b/Fuchs/Services/InvoiceDraftSetStructureDeltas.cs new file mode 100644 index 0000000..c590ea7 --- /dev/null +++ b/Fuchs/Services/InvoiceDraftSetStructureDeltas.cs @@ -0,0 +1,120 @@ +using System.Globalization; +using Fuchs.intranet; +using Newtonsoft.Json.Linq; + +namespace Fuchs.Services; + +/// +/// The set.* patch targets of inv/dpatch (ADR 0016) — the user-defined set structure +/// dialog's edits, applied to the authoritative cached session. Each handler locates the block, +/// delegates the actual tree mutation to the pure and reports the +/// old/new values for the change history. The browser never restructures anything itself: it posts +/// one delta, the server rewrites the block (parent links, depth-first order, levels) and the normal +/// draftReady refresh re-renders both the editor rows and the open structure dialog. +/// +/// set.assign — Ref = item id, Value = { parent: set id | "" }: drop a line onto a set / pull it out to the top level. +/// set.level — Ref = item id, Value = +1 | -1: indent under the preceding sibling set / outdent to the grandparent. +/// set.create — Ref = block id, Value = { typ: "set" | "text", title, parent: set id | "" }: add a new set header or free-text line. +/// set.move — Ref = item id, Value = -1 | +1 (one step up/down among its siblings) or { anchor: item id, where: "before" | "after" } (drag between rows: become the anchor's sibling, before it or after its subtree). The item's own subtree travels with it. +/// +/// +internal static class InvoiceDraftSetStructureDeltas +{ + public static bool Apply(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) => + d.Target switch + { + "set.assign" => Assign(s, d, ref oldValue, ref newValue), + "set.level" => Level(s, d, ref oldValue, ref newValue), + "set.create" => Create(s, d, ref oldValue, ref newValue), + "set.move" => Move(s, d, ref oldValue, ref newValue), + _ => false + }; + + private static bool Assign(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) + { + if (string.IsNullOrEmpty(d.Ref)) return false; + var block = BlockOfItem(s, d.Ref); + if (block == null) return false; + string parent = d.Value is JObject vo ? InvoiceSetStructure.Str(vo["parent"]) : d.ValueString; + if (!InvoiceSetStructure.Assign(block, d.Ref, parent.Trim(), out string oldParent)) return false; + oldValue = oldParent; + newValue = parent.Trim(); + return true; + } + + private static bool Level(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) + { + if (string.IsNullOrEmpty(d.Ref)) return false; + var block = BlockOfItem(s, d.Ref); + if (block == null) return false; + int delta = ParseDelta(d.Value); + if (!InvoiceSetStructure.ChangeLevel(block, d.Ref, delta, out int oldLevel, out int newLevel)) return false; + oldValue = oldLevel.ToString(CultureInfo.InvariantCulture); + newValue = newLevel.ToString(CultureInfo.InvariantCulture); + return true; + } + + private static bool Create(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) + { + if (d.Value is not JObject vo) return false; + var block = FindBlock(s, d.Ref); + if (block == null) return false; + string typ = InvoiceSetStructure.Str(vo["typ"]); + string title = InvoiceSetStructure.Str(vo["title"]); + string parent = InvoiceSetStructure.Str(vo["parent"]).Trim(); + string? id = InvoiceSetStructure.Create(block, typ, title, parent); + if (id == null) return false; + oldValue = ""; + newValue = typ.Trim().ToLowerInvariant() + ":" + title.Trim(); + return true; + } + + private static bool Move(InvoiceDraftSession s, InvoiceDraftDelta d, ref string oldValue, ref string newValue) + { + if (string.IsNullOrEmpty(d.Ref)) return false; + var block = BlockOfItem(s, d.Ref); + if (block == null) return false; + if (d.Value is JObject vo) + { + string anchor = InvoiceSetStructure.Str(vo["anchor"]).Trim(); + string where = InvoiceSetStructure.Str(vo["where"]).Trim().ToLowerInvariant(); + if (anchor.Length == 0 || where is not ("before" or "after")) return false; + if (!InvoiceSetStructure.Place(block, d.Ref, anchor, after: where == "after", out string oldParent, out string newParent)) return false; + oldValue = oldParent; + newValue = where + ":" + anchor + (newParent.Length > 0 ? " (" + newParent + ")" : ""); + return true; + } + int delta = ParseDelta(d.Value); + if (!InvoiceSetStructure.MoveSibling(block, d.Ref, delta, out int oldIndex, out int newIndex)) return false; + oldValue = oldIndex.ToString(CultureInfo.InvariantCulture); + newValue = newIndex.ToString(CultureInfo.InvariantCulture); + return true; + } + + /// Accepts a JSON number, a numeric string, or the words "in"/"out" (indent/outdent), "up"/"down". 0 when unparseable. + private static int ParseDelta(JToken? value) + { + if (value == null || value.Type == JTokenType.Null) return 0; + if (value.Type is JTokenType.Integer or JTokenType.Float) return Math.Sign(value.Value()); + string s = InvoiceSetStructure.Str(value).Trim().ToLowerInvariant(); + if (s is "in" or "indent" or "+" or "down") return 1; + if (s is "out" or "outdent" or "-" or "up") return -1; + return int.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out int n) ? Math.Sign(n) : 0; + } + + private static JObject? FindBlock(InvoiceDraftSession s, string blockId) + { + if (string.IsNullOrEmpty(blockId)) return null; + foreach (var b in s.Req) + if (b is JObject bo && InvoiceSetStructure.Str(bo["Id"]) == blockId) return bo; + return null; + } + + /// The block whose itm array contains the line . + private static JObject? BlockOfItem(InvoiceDraftSession s, string itemId) + { + foreach (var b in s.Req) + if (b is JObject bo && InvoiceSetStructure.Lines(bo).Any(l => InvoiceSetStructure.Id(l) == itemId)) return bo; + return null; + } +} diff --git a/Fuchs/Views/Shared/_Layout.cshtml b/Fuchs/Views/Shared/_Layout.cshtml index 8be0e95..fd2c0c6 100644 --- a/Fuchs/Views/Shared/_Layout.cshtml +++ b/Fuchs/Views/Shared/_Layout.cshtml @@ -15,6 +15,18 @@ } authinfo["requestedaccount"] = ""; authinfo["accountrequired"] = false; + // Cache-buster for the per-module bundles ($ocms.getScript loads web/fis...js and + // web/fis..css dynamically, so asp-append-version cannot cover them): the newest write + // time of any module bundle, appended as ?v=… by fis_main.js, so a gulp rebuild is picked up + // on the next page load instead of a stale cached module. + try + { + var webDir = new System.IO.DirectoryInfo(System.IO.Path.Combine(HostEnvironment.WebRootPath, "web")); + authinfo["assetversion"] = webDir.Exists + ? webDir.GetFiles("fis.*").Select(f => f.LastWriteTimeUtc.Ticks).DefaultIfEmpty(0L).Max().ToString() + : "0"; + } + catch { authinfo["assetversion"] = "0"; } string appName = ViewData["AppName"] as string ?? "Fuchs Intranet"; string fullName = ViewData["FullName"] as string ?? ""; diff --git a/Fuchs/bdlconfig.json b/Fuchs/bdlconfig.json index d98ed0a..5f6e188 100644 --- a/Fuchs/bdlconfig.json +++ b/Fuchs/bdlconfig.json @@ -77,7 +77,8 @@ "js/intranet/modules/fis.req_txt_de.js", "js/intranet/modules/fis.inv_txt_de.js", "js/intranet/modules/fis.inv.js", - "js/intranet/modules/fis.inv_shared.js" + "js/intranet/modules/fis.inv_shared.js", + "js/intranet/modules/fis.inv_sets.js" ], "minify": { "enabled": true } }, @@ -99,7 +100,8 @@ "js/intranet/modules/fis.req_txt_de.js", "js/intranet/modules/fis.inv_txt_de.js", "js/intranet/modules/fis.req.js", - "js/intranet/modules/fis.inv_shared.js" + "js/intranet/modules/fis.inv_shared.js", + "js/intranet/modules/fis.inv_sets.js" ], "minify": { "enabled": true } }, diff --git a/Fuchs/code/FuchsPdf.cs b/Fuchs/code/FuchsPdf.cs index 705eef0..5b14264 100644 --- a/Fuchs/code/FuchsPdf.cs +++ b/Fuchs/code/FuchsPdf.cs @@ -162,6 +162,9 @@ public static class FuchsPdf /// (type text/title) show neither a price nor a position number. This is the flat /// (non-collapsing) rendering used for every set mode except the explicit setonly. /// + /// Indent per nesting level of a set member on the invoice (mirrors the editor's per-level padding). + internal const double SetLevelIndentMm = 4; + private static InvoiceSetLine MapItemToLine(Dictionary i) { string type = i.nz("type", "").ToLowerInvariant(); @@ -180,7 +183,8 @@ public static class FuchsPdf TotalNet = total, ShowPrice = !isText && hasPrice, Numbered = !isText, // numbered like the editor: every line except free-text/heading, even price-blanked set members - IsSetHeader = type == "set" + IsSetHeader = type == "set", + Level = int.TryParse(i.nz("level", "0"), out int lvl) && lvl > 0 ? lvl : 0 // ADR 0016 nesting depth → indent }; } @@ -772,9 +776,22 @@ public static class FuchsPdf // and drops the members entirely. // Blocks without any set items are unaffected and always render flat. var setMode = InvoiceSetPricing.ModeFromInvoiceOptions(inv.InvoiceRegistration?.getString("InvoiceOptions")); - int pos = 0; - foreach (var block in inv.InvoiceBlocks) + + // Function-1 (SetItmId-linked) sets still collapse via Build; block-mode sets (ADR 0009) + // and plain blocks render flat — their set row is emphasised on its own and nulled + // members map to blank cells (see MapItemToLine). + var blockLines = inv.InvoiceBlocks + .Select(block => InvoiceSetPricing.HasSetMembers(block.Items) + ? InvoiceSetPricing.Build(block.Items, setMode) + : block.Items.Select(MapItemToLine).ToList()) + .ToList(); + // Position labels are hierarchical ("1", "1.1", "1.2", "2", …, ADR 0017) and span the + // whole invoice, so they must be computed once across every block, not per block. + InvoiceSetPricing.AssignPositionLabels(blockLines); + + for (int bi = 0; bi < inv.InvoiceBlocks.Count; bi++) { + var block = inv.InvoiceBlocks[bi]; if (!string.IsNullOrWhiteSpace(block.Heading)) { var hr = tbl.AddRow(); @@ -783,19 +800,12 @@ public static class FuchsPdf hr.Cells[1].AddParagraph().WithStyle("TblCell_RTitle").AddFormattedText(block.Heading, TextFormat.Bold); } - // Function-1 (SetItmId-linked) sets still collapse via Build; block-mode sets (ADR 0009) - // and plain blocks render flat — their set row is emphasised on its own and nulled - // members map to blank cells (see MapItemToLine). - var lines = InvoiceSetPricing.HasSetMembers(block.Items) - ? InvoiceSetPricing.Build(block.Items, setMode) - : block.Items.Select(MapItemToLine).ToList(); - - foreach (var line in lines) + foreach (var line in blockLines[bi]) { - bool numbered = line.Numbered; // every line except free-text/heading (mirrors the editor's RecomputePositions), even price-blanked set members var row = tbl.AddRow(); row.HeightRule = RowHeightRule.Auto; - row.Cells[0].AddParagraph(numbered ? (++pos).ToString() : "").Style = "TblCell_Base"; + row.Cells[0].AddParagraph(line.PositionLabel).Style = "TblCell_Base"; + if (line.Level > 0) row.Cells[1].Format.LeftIndent = Unit.FromMillimeter(SetLevelIndentMm * line.Level); // nested set member (ADR 0016) — indented like the editor if (!string.IsNullOrEmpty(line.Title)) // skip the empty paragraph that added a blank line before free text { var titleCell = row.Cells[1].AddParagraph(); diff --git a/Fuchs/code/InvoiceDraftCalculator.cs b/Fuchs/code/InvoiceDraftCalculator.cs index 9cdc653..cd74444 100644 --- a/Fuchs/code/InvoiceDraftCalculator.cs +++ b/Fuchs/code/InvoiceDraftCalculator.cs @@ -109,27 +109,35 @@ public static class InvoiceDraftCalculator /// /// Renumbers the visible line positions authoritatively (the port of the client-side - /// numbering in invSumUpdate): priced lines are numbered sequentially across the whole - /// invoice — matching the PDF's Pos. column — while heading/free-text lines - /// (typ = "text"/"title") carry no number. The result is written onto each line's - /// p field so it flows back to the browser (via the view state) and into the PDF; this - /// keeps the online editor and the PDF preview showing the same position numbers, including - /// after a reorder. + /// numbering in invSumUpdate): priced lines are numbered hierarchically across the + /// whole invoice — a set header gets a number at its own level, its members get subsidiary + /// numbers one level deeper (e.g. "3", "3.1", "3.2") — matching the PDF's Pos. column + /// (ADR 0017), while heading/free-text lines (typ = "text"/"title") carry no number. + /// Levels come from lvl (written by , default 0). The + /// result is written onto each line's p field (a string, since "3.1" isn't numeric) so + /// it flows back to the browser (via the view state) and into the PDF; this keeps the online + /// editor and the PDF preview showing the same position labels, including after a reorder. /// public static void RecomputePositions(InvoiceDraftSession session) { - int pos = 0; + var lines = new List(); + var input = new List<(bool Numbered, int Level)>(); foreach (var blockTok in session.Req) { - if (blockTok is not JObject block || block["itm"] is not JArray lines) continue; - foreach (var lineTok in lines) + if (blockTok is not JObject block || block["itm"] is not JArray itms) continue; + foreach (var lineTok in itms) { if (lineTok is not JObject co) continue; string typ = Str(co["typ"]).Trim().ToLowerInvariant(); bool numbered = typ is not ("text" or "title"); // only headings/free-text carry no number (mirrors invSumUpdate) - co["p"] = numbered ? (JToken)(++pos) : (JToken)""; + int level = (int?)co[InvoiceSetStructure.LevelKey] ?? 0; + lines.Add(co); + input.Add((numbered, level)); } } + var labels = InvoiceLineNumbering.Compute(input); + for (int i = 0; i < lines.Count; i++) + lines[i]["p"] = labels[i]; } /// @@ -149,6 +157,10 @@ public static class InvoiceDraftCalculator else if (!IsValidEmail(email)) Add("email", "error", "Die E-Mail-Adresse ist ungültig."); + string emailCc = Str(session.New["invoiceemailcc"]).Trim(); // "Zusatzempfänger" — optional, may stay empty + if (emailCc.Length > 0 && !IsValidEmail(emailCc)) + Add("emailcc", "error", "Die E-Mail-Adresse des Zusatzempfängers ist ungültig."); + if (Str(session.New["invoiceaddress"]).Trim().Length == 0) Add("address", "warning", "Es ist keine Rechnungsanschrift hinterlegt."); diff --git a/Fuchs/code/InvoiceSetPricing.cs b/Fuchs/code/InvoiceSetPricing.cs index fd3e345..8861591 100644 --- a/Fuchs/code/InvoiceSetPricing.cs +++ b/Fuchs/code/InvoiceSetPricing.cs @@ -1,4 +1,5 @@ -using static OCORE.commons; +using System.Linq; +using static OCORE.commons; using static OCORE.OCORE_dictionaries; namespace Fuchs.intranet; @@ -40,13 +41,24 @@ public sealed class InvoiceSetLine public bool Numbered { get; init; } = true; /// True for the set header line (rendered emphasised). public bool IsSetHeader { get; init; } + /// Nesting depth (0 = top level; a member of a top-level set is 1, a member of a set inside it is 2 …) — rendered as an indent (ADR 0016). + public int Level { get; init; } + /// + /// The hierarchical position label ("1", "1.1", "1.2", "2", …) as printed in the PDF's + /// Pos. column (ADR 0017), or "" for unnumbered lines. Assigned via + /// across the whole invoice, since a + /// single call only sees one block. + /// + public string PositionLabel { get; set; } = ""; } /// /// Transforms raw invoice line items into display lines according to a /// . A "set" item is identified by /// type == "set"; its members carry setId equal to the set -/// header's id. Items that belong to no set pass through unchanged. +/// header's id — a nested set is itself a member of its parent set +/// (ADR 0016), and every line reports its depth as . +/// Items that belong to no set pass through unchanged. /// /// The invoice total is taken from the registration balance, not from these /// lines, so calling is a pure, non-mutating transformation @@ -94,65 +106,81 @@ public static class InvoiceSetPricing items.Any(i => !string.IsNullOrEmpty(SetIdOf(i))); /// - /// Produces the ordered display lines for the given items and mode. - /// Standalone items are always shown with their price. A set header whose - /// own total_net is still zero (not yet converted via the set-item - /// switch) is rendered blank with its members individually priced, - /// regardless of . + /// Produces the display lines for the given items and mode, preserving the input order + /// (the draft service already linearises each block depth-first — set, then its members, + /// recursively — so editor and PDF print the very same sequence, ADR 0008/0016). + /// Standalone items are always shown with their price. A set header whose own + /// total_net is still zero (not yet converted via the set-item switch) is rendered + /// blank with its members individually priced, regardless of . + /// Sets may nest: a line is governed by its nearest converted ancestor — inside one, it + /// is price-blanked (SetPrice) or dropped (SetOnly); a nested set that is itself converted + /// but sits inside an unconverted one behaves like a converted top-level set for its own subtree. /// public static List Build(IReadOnlyList> items, SetDisplayMode mode) { var result = new List(); if (items.Count == 0) return result; - // Group members by their set id. - var membersBySet = items - .Where(i => !IsSetHeader(i) && !string.IsNullOrEmpty(SetIdOf(i))) - .GroupBy(SetIdOf) - .ToDictionary(g => g.Key!, g => g.ToList()); + var headers = new Dictionary>(); + foreach (var i in items) + if (IsSetHeader(i) && i.nz("id", "").Length > 0) headers.TryAdd(i.nz("id", ""), i); + + static bool Converted(Dictionary h) + { + FuchsPdf.ParseDec(h.no("total_net", 0), out decimal own); + return own != 0; + } + + // Walks the parent chain (setId → header) and reports depth + whether any ancestor is converted. + (int level, bool insideConverted) Ancestry(Dictionary i) + { + int level = 0; bool inside = false; + var seen = new HashSet { i.nz("id", "") }; + string? pid = ParentIdOf(i, headers); + while (pid != null && seen.Add(pid)) + { + level++; + var h = headers[pid]; + if (Converted(h)) inside = true; + pid = ParentIdOf(h, headers); + } + return (level, inside); + } foreach (var item in items) { + var (level, insideConverted) = Ancestry(item); + if (insideConverted && mode == SetDisplayMode.SetOnly) continue; // collapsed into its set's single line + if (IsSetHeader(item)) { - string setId = HeaderIdOf(item); - var members = membersBySet.TryGetValue(setId, out var m) ? m : new List>(); FuchsPdf.ParseDec(item.no("total_net", 0), out decimal headerOwnTotal); - - if (headerOwnTotal == 0) - { - // Not yet converted (the set-item switch, ApplyItemSetPrice, hasn't run): the - // header still carries its as-delivered zero price, so the chosen display mode - // does not apply yet — show the header blank and each member with its own, - // individual price, exactly as if there were no set grouping at all. - result.Add(HeaderLine(item, 0, showPrice: false)); - foreach (var mem in members) result.Add(MemberLine(mem, showPrice: true)); - continue; - } - - switch (mode) - { - case SetDisplayMode.SetPrice: - result.Add(HeaderLine(item, headerOwnTotal, showPrice: true)); - foreach (var mem in members) result.Add(MemberLine(mem, showPrice: false)); - break; - case SetDisplayMode.SetOnly: - result.Add(HeaderLine(item, headerOwnTotal, showPrice: true)); - break; - } - } - else if (!string.IsNullOrEmpty(SetIdOf(item))) - { - // Member — already emitted next to its header above; skip standalone emission. + // Unconverted (still zero-priced) header: blank price, members keep their own price. + // Converted: priced line; members blank (SetPrice) — or dropped above (SetOnly). + bool show = headerOwnTotal != 0 && !insideConverted; + result.Add(HeaderLine(item, show ? headerOwnTotal : 0, showPrice: show, level)); } else { - result.Add(MemberLine(item, showPrice: true)); // standalone item + result.Add(MemberLine(item, showPrice: !insideConverted, level)); } } return result; } + /// + /// Computes and assigns across every block's lines + /// (in the order the blocks are printed), so numbering spans the whole invoice exactly like + /// InvoiceDraftCalculator.RecomputePositions does for the online editor (ADR 0017). + /// + public static void AssignPositionLabels(IEnumerable> blocks) + { + var lines = blocks.SelectMany(b => b).ToList(); + var labels = InvoiceLineNumbering.Compute(lines.Select(l => (l.Numbered, l.Level)).ToList()); + for (int i = 0; i < lines.Count; i++) + lines[i].PositionLabel = labels[i]; + } + // ── helpers ────────────────────────────────────────────────────────────── private static bool IsSetHeader(Dictionary i) => string.Equals(i.nz("type", ""), "set", StringComparison.OrdinalIgnoreCase); @@ -174,13 +202,14 @@ public static class InvoiceSetPricing return string.IsNullOrEmpty(s) ? null : s; } - private static string HeaderIdOf(Dictionary i) + /// The owning set header's id: setId when it names another known header; null for top-level lines and self-references. + private static string? ParentIdOf(Dictionary i, Dictionary> headers) { - string s = i.nz("setId", ""); - return string.IsNullOrEmpty(s) ? i.nz("id", "") : s; + string s = SetIdOf(i) ?? ""; + return s.Length > 0 && s != i.nz("id", "") && headers.ContainsKey(s) ? s : null; } - private static InvoiceSetLine HeaderLine(Dictionary i, decimal setTotal, bool showPrice) => new() + private static InvoiceSetLine HeaderLine(Dictionary i, decimal setTotal, bool showPrice, int level) => new() { Id = i.nz("id", ""), Title = i.nz("title", ""), @@ -190,10 +219,11 @@ public static class InvoiceSetPricing TotalNet = setTotal, ShowPrice = showPrice, Numbered = true, // a set header is always numbered - IsSetHeader = true + IsSetHeader = true, + Level = level }; - private static InvoiceSetLine MemberLine(Dictionary i, bool showPrice) + private static InvoiceSetLine MemberLine(Dictionary i, bool showPrice, int level) { FuchsPdf.ParseDec(i.no("price_net", 0), out decimal price); FuchsPdf.ParseDec(i.no("total_net", 0), out decimal total); @@ -207,7 +237,8 @@ public static class InvoiceSetPricing TotalNet = total, ShowPrice = showPrice && !IsNoPriceLine(i), // headings/free text print no price Numbered = !IsNoPriceLine(i), // …but are still numbered unless heading/free-text - IsSetHeader = false + IsSetHeader = false, + Level = level }; } } diff --git a/Fuchs/code/InvoiceSetStructure.cs b/Fuchs/code/InvoiceSetStructure.cs new file mode 100644 index 0000000..ba6473d --- /dev/null +++ b/Fuchs/code/InvoiceSetStructure.cs @@ -0,0 +1,404 @@ +using Newtonsoft.Json.Linq; + +namespace Fuchs.intranet; + +/// +/// The explicit, user-editable set structure of an invoice draft (ADR 0016): which line +/// belongs to which set, and how deep it sits. Every line in a block's itm array carries a +/// parent link in SetItmId (the id of the typ == "set" line that owns it, or +/// null/self for a top-level line). Sets may contain sets, so the links form a forest per block; +/// the depth of each line is published as lvl (0 = top level) and mirrored onto the +/// items contract shape as setId/level. +/// +/// The implicit derivation (fds__prepInvoice tags every item following a zero-priced Set +/// header with that header's id) is kept as the initial structure; this class only applies +/// the user's explicit changes on top of it — assigning a line to a set, pulling it out, changing +/// its level, or creating a new set/text line — and then re-linearises the block depth-first so +/// every set is immediately followed by its members, recursively. Pure and static, so it is +/// exhaustively unit-tested (InvoiceSetStructureTests); the draft service merely delegates. +/// +/// +public static class InvoiceSetStructure +{ + /// Parent link on the editor co line shape. + public const string ParentKey = "SetItmId"; + /// Depth (0 = top level) on the editor co line shape. + public const string LevelKey = "lvl"; + + // ── Structural queries ─────────────────────────────────────────────────── + internal static string Id(JObject co) => Str(co["id"]); + + internal static bool IsSet(JObject co) => + string.Equals(Str(co["typ"]), "set", StringComparison.OrdinalIgnoreCase); + + /// + /// The effective parent set id of a line: its SetItmId when that names another + /// set line of the same block; null for top-level lines, a self-reference (the shape + /// fds__prepInvoice delivers for a header), an unknown id or a non-set target. + /// + internal static string? ParentOf(JObject co, IReadOnlyDictionary byId) + { + string p = Str(co[ParentKey]); + if (p.Length == 0 || p == Id(co)) return null; + return byId.TryGetValue(p, out var parent) && IsSet(parent) ? p : null; + } + + /// The block's line objects in their current order (non-object entries skipped). + internal static List Lines(JObject block) => + block["itm"] is JArray arr ? arr.OfType().ToList() : new List(); + + internal static Dictionary ById(IEnumerable lines) + { + var d = new Dictionary(); + foreach (var l in lines) { string id = Id(l); if (id.Length > 0) d.TryAdd(id, l); } + return d; + } + + /// Depth of every line (0 = top level). A cyclic chain is cut at its first repeat, so it degrades to a root. + internal static Dictionary Levels(List lines) + { + var byId = ById(lines); + var result = new Dictionary(); + foreach (var line in lines) + { + int depth = 0; + var seen = new HashSet { Id(line) }; + var cur = line; + while (ParentOf(cur, byId) is { } pid) + { + if (!seen.Add(pid)) { depth = 0; break; } // cyclic chain — degrade to top level (Linearize emits it as a root, too) + depth++; + cur = byId[pid]; + } + result[Id(line)] = depth; + } + return result; + } + + /// All lines transitively owned by the set (never the set itself), in current order. + public static List Descendants(List lines, string setId) + { + var byId = ById(lines); + var children = new Dictionary>(); + foreach (var l in lines) + if (ParentOf(l, byId) is { } p) + (children.TryGetValue(p, out var lst) ? lst : children[p] = new List()).Add(l); + + var result = new List(); + var seen = new HashSet { setId }; + var queue = new Queue(); + queue.Enqueue(setId); + while (queue.Count > 0) + { + string cur = queue.Dequeue(); + if (!children.TryGetValue(cur, out var kids)) continue; + foreach (var k in kids) + { + string kid = Id(k); + if (!seen.Add(kid)) continue; + result.Add(k); + queue.Enqueue(kid); + } + } + return result; + } + + /// The id of the top-level ancestor of a line (itself when it is top level). + private static string RootOf(JObject line, IReadOnlyDictionary byId) + { + var seen = new HashSet { Id(line) }; + var cur = line; + while (ParentOf(cur, byId) is { } pid && seen.Add(pid)) cur = byId[pid]; + return Id(cur); + } + + // ── Recompute / linearise ──────────────────────────────────────────────── + /// + /// Publishes the depth of every line (lvl) and mirrors parent + depth onto the + /// items contract shape (setId/level) for every block — without + /// reordering anything. Runs on every refresh so the view state and the PDF read one truth. + /// + public static void RecomputeLevels(InvoiceDraftSession session) + { + foreach (var blockTok in session.Req) + if (blockTok is JObject block) ApplyLevels(block); + } + + private static void ApplyLevels(JObject block) + { + var lines = Lines(block); + if (lines.Count == 0) return; + var byId = ById(lines); + var levels = Levels(lines); + foreach (var line in lines) line[LevelKey] = levels[Id(line)]; + if (block["items"] is not JArray items) return; + foreach (var io in items.OfType()) + { + string id = Str(io["id"]); + if (!byId.TryGetValue(id, out var co)) continue; // e.g. combined-sum rows use differing ids — leave untouched + io["level"] = levels[id]; + io["setId"] = ParentOf(co, byId) is { } p ? p : JValue.CreateNull(); + } + } + + /// + /// Reorders a block's lines depth-first — each set immediately followed by its members, + /// recursively, siblings keeping their current relative order — then republishes levels and + /// mirrors the order onto items. Lines caught in a cyclic chain are emitted as roots. + /// + public static void Linearize(JObject block) + { + var lines = Lines(block); + if (lines.Count == 0) return; + var byId = ById(lines); + var children = new Dictionary>(); + var roots = new List(); + foreach (var l in lines) + { + if (ParentOf(l, byId) is { } p) + (children.TryGetValue(p, out var lst) ? lst : children[p] = new List()).Add(l); + else roots.Add(l); + } + + var ordered = new List(); + var seen = new HashSet(); + void Visit(JObject l) + { + if (!seen.Add(Id(l))) return; + ordered.Add(l); + if (children.TryGetValue(Id(l), out var kids)) foreach (var k in kids) Visit(k); + } + foreach (var r in roots) Visit(r); + foreach (var l in lines) Visit(l); // anything unreachable (cycle) — keep, as a root + + var itm = (JArray)block["itm"]!; + itm.Clear(); + foreach (var l in ordered) itm.Add(l); + + if (block["items"] is JArray items) + { + var itemsById = new Dictionary(); + var rest = new List(); + foreach (var io in items.OfType()) + { + string id = Str(io["id"]); + if (id.Length > 0 && byId.ContainsKey(id) && itemsById.TryAdd(id, io)) continue; + rest.Add(io); + } + items.Clear(); + foreach (var l in ordered) if (itemsById.TryGetValue(Id(l), out var io)) items.Add(io); + foreach (var io in rest) items.Add(io); + } + ApplyLevels(block); + } + + // ── Mutations ──────────────────────────────────────────────────────────── + /// + /// Assigns line to the set ("" = pull it + /// out to the top level). The line's own descendants move with it (one level deeper/shallower). + /// A line dropped onto a set becomes its last member; a line pulled out is placed right after + /// the subtree it came from. No-op (false) when the item is unknown, the target is not a set + /// line of this block, the target is the item itself or one of its descendants (cycle), or + /// the parent is unchanged. + /// + public static bool Assign(JObject block, string itemId, string parentId, out string oldParent) + { + oldParent = ""; + var lines = Lines(block); + var byId = ById(lines); + if (!byId.TryGetValue(itemId, out var item)) return false; + string? current = ParentOf(item, byId); + oldParent = current ?? ""; + string? target = parentId.Length == 0 ? null : parentId; + if (target == current) return false; + if (target != null) + { + if (target == itemId || !byId.TryGetValue(target, out var parent) || !IsSet(parent)) return false; + if (Descendants(lines, itemId).Any(d => Id(d) == target)) return false; // would create a cycle + } + + var itm = (JArray)block["itm"]!; + if (target != null) + { + item.Remove(); + itm.Add(item); // last member of the target set + } + else + { + string root = RootOf(item, byId); + var subtree = new HashSet(Descendants(lines, root).Select(Id)) { root }; + var own = new HashSet(Descendants(lines, itemId).Select(Id)) { itemId }; + var last = lines.Where(l => subtree.Contains(Id(l)) && !own.Contains(Id(l))).LastOrDefault(); + item.Remove(); + if (last != null) last.AddAfterSelf(item); else itm.Add(item); + } + item[ParentKey] = ParentToken(target); + Linearize(block); + return true; + } + + /// + /// Moves a line one level deeper ( > 0: it becomes the last member of + /// the nearest preceding sibling set) or one level up ( < 0: it joins + /// its grandparent right after its former parent's subtree). Returns false when no such move + /// exists (no preceding sibling set / already top level) or the item is unknown. + /// + public static bool ChangeLevel(JObject block, string itemId, int delta, out int oldLevel, out int newLevel) + { + oldLevel = newLevel = 0; + if (delta == 0) return false; + Linearize(block); // guarantees contiguous subtrees + fresh levels + var lines = Lines(block); + var byId = ById(lines); + if (!byId.TryGetValue(itemId, out var item)) return false; + oldLevel = newLevel = Levels(lines)[itemId]; + string? parent = ParentOf(item, byId); + + if (delta > 0) + { + JObject? sibling = null; + foreach (var l in lines) + { + if (Id(l) == itemId) break; + if (ParentOf(l, byId) == parent && IsSet(l)) sibling = l; // nearest preceding sibling set + } + if (sibling == null) return false; + if (!Assign(block, itemId, Id(sibling), out _)) return false; + newLevel = Levels(Lines(block))[itemId]; + return true; + } + + if (parent == null) return false; + string grand = ParentOf(byId[parent], byId) ?? ""; + // Place the line (with its own subtree) right after its former parent's subtree. + var parentSubtree = new HashSet(Descendants(lines, parent).Select(Id)); + var own = new HashSet(Descendants(lines, itemId).Select(Id)) { itemId }; + var anchor = lines.Where(l => (parentSubtree.Contains(Id(l)) || Id(l) == parent) && !own.Contains(Id(l))).Last(); + var moving = lines.Where(l => own.Contains(Id(l))).ToList(); + foreach (var l in moving) l.Remove(); + JToken after = anchor; + foreach (var l in moving) { after.AddAfterSelf(l); after = l; } + item[ParentKey] = ParentToken(grand.Length > 0 ? grand : null); + Linearize(block); + newLevel = Levels(Lines(block))[itemId]; + return true; + } + + /// + /// Places line (with its whole subtree) immediately before the + /// anchor line or after the anchor's subtree, adopting the anchor's parent — i.e. it + /// becomes the anchor's sibling. This is the reorder primitive behind drag-between-rows and + /// the ▲/▼ buttons. Rejected (false) when the item or anchor is unknown, they are the same + /// line, or the anchor sits inside the item's own subtree. + /// + public static bool Place(JObject block, string itemId, string anchorId, bool after, out string oldParent, out string newParent) + { + oldParent = newParent = ""; + if (itemId == anchorId) return false; + Linearize(block); // contiguous subtrees + var lines = Lines(block); + var byId = ById(lines); + if (!byId.TryGetValue(itemId, out var item) || !byId.TryGetValue(anchorId, out var anchor)) return false; + var own = new HashSet(Descendants(lines, itemId).Select(Id)) { itemId }; + if (own.Contains(anchorId)) return false; // cannot place a set relative to its own member + oldParent = ParentOf(item, byId) ?? ""; + newParent = ParentOf(anchor, byId) ?? ""; + + var moving = lines.Where(l => own.Contains(Id(l))).ToList(); + foreach (var l in moving) l.Remove(); + JToken slot = after + ? lines.Where(l => !own.Contains(Id(l)) && (Id(l) == anchorId || Descendants(lines, anchorId).Any(d => Id(d) == Id(l)))).Last() + : anchor; + if (after) { foreach (var l in moving) { slot.AddAfterSelf(l); slot = l; } } + else { foreach (var l in moving) slot.AddBeforeSelf(l); } + item[ParentKey] = ParentToken(newParent.Length > 0 ? newParent : null); + Linearize(block); + return true; + } + + /// + /// Moves a line one step up ( < 0) or down (> 0) among its + /// siblings (same parent); its subtree travels with it. False at the first/last position or + /// for an unknown line. Reports the 0-based sibling index before and after. + /// + public static bool MoveSibling(JObject block, string itemId, int delta, out int oldIndex, out int newIndex) + { + oldIndex = newIndex = -1; + if (delta == 0) return false; + Linearize(block); + var lines = Lines(block); + var byId = ById(lines); + if (!byId.TryGetValue(itemId, out var item)) return false; + string? parent = ParentOf(item, byId); + var siblings = lines.Where(l => ParentOf(l, byId) == parent).ToList(); + oldIndex = siblings.FindIndex(l => Id(l) == itemId); + int target = oldIndex + Math.Sign(delta); + if (target < 0 || target >= siblings.Count) return false; + if (!Place(block, itemId, Id(siblings[target]), after: delta > 0, out _, out _)) return false; + newIndex = target; + return true; + } + + /// + /// Creates a new line of "set" (an unconverted, zero-priced set header + /// that can later be switched to a set price) or "text" (a free-text line) titled + /// , as the last member of ("" = end of the + /// block). Both line shapes (itm + items) are written. Returns the new id, or + /// null when the type is unknown, the title is blank or the parent is not a set of this block. + /// + public static string? Create(JObject block, string typ, string title, string parentId) + { + string kind = (typ ?? "").Trim().ToLowerInvariant(); + title = (title ?? "").Trim(); + if (kind is not ("set" or "text") || title.Length == 0) return null; + var lines = Lines(block); + var byId = ById(lines); + string? parent = parentId.Length == 0 ? null : parentId; + if (parent != null && (!byId.TryGetValue(parent, out var p) || !IsSet(p))) return null; + + string id = "sN" + Guid.NewGuid().ToString("N")[..8]; + string rate = lines.Select(l => Str(l["vat"])).FirstOrDefault(r => r.Length > 0) ?? "19%"; + JObject co, io; + if (kind == "set") + { + co = new JObject + { + ["id"] = id, ["typ"] = "set", ["p"] = "", ["q"] = "", ["t"] = title, ["tt"] = "", + ["v"] = 0, ["vt"] = 0, ["vv"] = 0, ["vs"] = 0, ["vsv"] = 0, ["vat"] = rate, ["det"] = false, + [ParentKey] = ParentToken(parent) + }; + io = new JObject + { + ["id"] = id, ["type"] = "set", ["title"] = title, ["desc"] = "", ["qty"] = "", + ["price_net"] = 0, ["total_net"] = 0, ["vat"] = rate, ["setId"] = ParentToken(parent) + }; + } + else + { + string html = "

" + System.Net.WebUtility.HtmlEncode(title) + "

"; + co = new JObject + { + ["id"] = id, ["typ"] = "Text", ["p"] = "", ["q"] = JValue.CreateNull(), ["t"] = html, ["tt"] = JValue.CreateNull(), + ["v"] = JValue.CreateNull(), ["vt"] = JValue.CreateNull(), ["vv"] = JValue.CreateNull(), // free text: no price at all (empty cell, ADR 0009 null semantics) + ["vs"] = JValue.CreateNull(), ["vsv"] = JValue.CreateNull(), ["vat"] = "", ["det"] = false, + [ParentKey] = ParentToken(parent) + }; + io = new JObject + { + ["id"] = id, ["type"] = "text", ["title"] = "", ["desc"] = html, ["qty"] = "", + ["price_net"] = "", ["total_net"] = "", ["vat"] = "", ["setId"] = ParentToken(parent) + }; + } + if (block["itm"] is not JArray itm) { itm = new JArray(); block["itm"] = itm; } + itm.Add(co); + if (block["items"] is JArray items) items.Add(io); else block["items"] = new JArray { io }; + Linearize(block); + return id; + } + + // ── helpers ────────────────────────────────────────────────────────────── + private static JToken ParentToken(string? parent) => parent != null ? new JValue(parent) : JValue.CreateNull(); + + internal static string Str(JToken? t) => + t == null || t.Type == JTokenType.Null ? "" : t.Type == JTokenType.String ? t.Value() ?? "" : t.ToString(); +} diff --git a/Fuchs/js/intranet/fis_main.js b/Fuchs/js/intranet/fis_main.js index 02f9b77..6e7ea0e 100644 --- a/Fuchs/js/intranet/fis_main.js +++ b/Fuchs/js/intranet/fis_main.js @@ -17,8 +17,11 @@ } $ocms[mdl].auth = auth; if (auth.manage > 0) { + /* ?v= (newest bundle write time, emitted by _Layout) busts the browser + cache of the dynamically imported module bundle + css after a gulp rebuild. */ + let av = '?v=' + encodeURIComponent($ocms.auth.assetversion || '0'); $ocms.getScript({ - module: mdl, script: ['/web/fis', mdl, $ocms.auth.locale || 'de', 'js'].join('.'), css: ['/web/fis', mdl, 'css'].join('.'), condition: typeof $ocms[mdl].init2 !== 'function' + module: mdl, script: ['/web/fis', mdl, $ocms.auth.locale || 'de', 'js'].join('.') + av, css: ['/web/fis', mdl, 'css'].join('.') + av, condition: typeof $ocms[mdl].init2 !== 'function' }, function () { $ocms[mdl].init2(); }); } }, error: function () { diff --git a/Fuchs/js/intranet/modules/fis.inv_sets.js b/Fuchs/js/intranet/modules/fis.inv_sets.js new file mode 100644 index 0000000..6dda6b1 --- /dev/null +++ b/Fuchs/js/intranet/modules/fis.inv_sets.js @@ -0,0 +1,200 @@ +/* ── User-defined set structure dialog (ADR 0016) ──────────────────────────── + Lets the user define which line belongs to which set (and how deep) instead of relying only + on the implicit "everything after a Set header until the next Set" derivation that + fds__prepInvoice delivers as the initial structure. The dialog is a pure view of the + authoritative server state (state.req[].itm — the co shape with SetItmId + lvl): every action + posts ONE delta (set.assign / set.level / set.create) via $inv.d.sync, the server rewrites the + cached block (parent links, depth-first order, levels) and the normal draftReady/dstate + refresh re-renders both the editor rows and this dialog (applyState -> $inv.sets.render). + The browser never restructures anything itself (ADR 0006/0008). */ +$inv.sets = { + o: null, /* open dialog handle ($ocms.dlg result) or null */ + c: null, /* dialog content container */ + drag: null, /* { id, bid } of the row currently being dragged */ + /* Reorder = set.move (▲▼ or drag before/after a row), membership = set.assign (drag into a set / + root zone), depth = set.level (◀▶). Folding is purely a view state kept in $inv.sets.collapsed. */ + isOpen: () => !!$inv.sets.o, + open: function () { + let tbl = $inv.d.tbl(); + if (tbl.length < 1 || $inv.sets.isOpen()) { return; } + if ($inv.d.token() === '' && !tbl.data('dstate')) { return; } /* no draft session (e.g. reminder editor) — nothing to structure */ + let c = $$.dc('setstruct'); + $inv.sets.c = c; + $inv.sets.o = $ocms.dlg(c, { + title: $ict.setst, size: [-1, 960], exclusive: false, + close: () => { $inv.sets.o = null; $inv.sets.c = null; $inv.sets.drag = null; } + }); + let state = tbl.data('dstate'); + if (state) { $inv.sets.render(state); } else { $inv.d.refresh(); } + }, + /* plain-text title of a co line (co.t is HTML for text lines / titles) */ + title: (co) => { + let t = $$.d().html(co.t || '').text().replace(/\s+/g, ' ').trim(); + return t !== '' ? t : ($ict.setsNoTitle || ''); + }, + typLbl: (co) => { + let k = (co.typ || '').toString().toLowerCase(); + return ($ict.settyp || {})[k] || co.typ || ''; + }, + isSet: (co) => (co.typ || '').toString().toLowerCase() === 'set', + /* Normalised parent id of a line, mirroring InvoiceSetStructure.ParentOf: SetItmId when it + names ANOTHER set line of the same block; '' for top level / self-reference / unknown. */ + parentOf: (co, byId) => { + let p = (co.SetItmId == null ? '' : co.SetItmId).toString(), id = (co.id || '').toString(); + if (p === '' || p === id) { return ''; } + let t = byId[p]; + return (t && $inv.sets.isSet(t)) ? p : ''; + }, + isDescendant: (candidateId, ancestorId, byId) => { + let seen = {}, cur = byId[candidateId]; + while (cur) { + let p = $inv.sets.parentOf(cur, byId); + if (p === '' || seen[p]) { return false; } + if (p === ancestorId) { return true; } + seen[p] = true; cur = byId[p]; + } + return false; + }, + /* Whether a line may be dropped onto the given target set (same block, not itself, no cycle). */ + canDrop: (drag, targetId, bid, byId) => + !!drag && drag.bid === bid && drag.id !== targetId && !$inv.sets.isDescendant(targetId, drag.id, byId), + sync: (delta) => { + if ($inv.sets.c) { $inv.sets.c.aC('busy'); } + return $inv.d.sync(delta); + }, + render: function (state) { + let c = $inv.sets.c; if (!c) { return; } + let bdy = c.closest('.modal-body'), scroll = bdy.length ? bdy.scrollTop() : 0; + c.empty().rC('busy'); + $$.dc('shint', c).text($ict.setsHint || ''); + (state.req || []).forEach((b) => { + if (!b) { return; } + let bid = (b.Id || '').toString(); if (bid === '') { return; } + let itms = (b.itm || []).filter((co) => co && (co.id || '') !== ''), byId = {}; + itms.forEach((co) => { byId[(co.id || '').toString()] = co; }); + let blk = $$.dc('sblock', c).data({ bid: bid }); + $inv.sets.rootZone($$.dc('sbhd', blk).append($$.sc('nme', b.text || b.nme || bid)), bid, byId); + let ts = $$.tblset({ class: 'stbl' }, blk); + let thr = $$.tr(ts.hd); + ($ict.setsHR || []).forEach((h) => $$.th(thr, h)); + itms.forEach((co) => $inv.sets.row(ts.bdy, co, bid, itms, byId)); + $inv.sets.rootZone($$.dc('sroot', blk).text($ict.setsRoot || ''), bid, byId); + $inv.sets.createBar(blk, bid, itms, byId); + }); + if (bdy.length) { bdy.scrollTop(scroll); } + }, + /* Ancestor-collapsed test: a row is hidden while any set above it is folded. */ + collapsed: {}, /* set id -> true (survives re-renders; default: everything expanded) */ + hiddenByCollapse: function (co, byId) { + let seen = {}, cur = co; + while (cur) { + let p = $inv.sets.parentOf(cur, byId); + if (p === '' || seen[p]) { return false; } + if ($inv.sets.collapsed[p]) { return true; } + seen[p] = true; cur = byId[p]; + } + return false; + }, + /* Drop zone of a pointer position within a row: top edge = before, bottom edge = after, + middle = into (sets only; for other rows the middle counts as "after"). */ + zone: function (ev, tr, isSet) { + let oe = ev.originalEvent || ev, r = tr[0].getBoundingClientRect(), y = (oe.clientY || 0) - r.top, h = r.height || 1; + if (y < h * 0.3) { return 'before'; } + if (y > h * 0.7 || !isSet) { return 'after'; } + return 'into'; + }, + clearDrop: () => { $('.setstruct .dropok, .setstruct .dropbefore, .setstruct .dropafter').rC('dropok dropbefore dropafter'); }, + /* One line of the structure table: drag handle, fold toggle, pos, type, qty, indented title, + order buttons (▲▼ among siblings) and level buttons (◀▶). */ + row: function (tbody, co, bid, itms, byId) { + let id = (co.id || '').toString(), lvl = parseInt(co.lvl || 0, 10) || 0, isSet = $inv.sets.isSet(co); + let parent = $inv.sets.parentOf(co, byId), folded = isSet && !!$inv.sets.collapsed[id]; + let tr = $$.tr(tbody, { draggable: 'true' }).aC('srow lvl' + lvl).tC('isset', isSet).tC('folded', folded).data({ id: id, bid: bid }); + if ($inv.sets.hiddenByCollapse(co, byId)) { tr.aC('hid'); } + $$.tdc('grip', tr).append(gi('move')).attr('title', $rct.mR); + $$.tdc('pos', tr).text(co.p != null ? co.p : ''); + let typ = $$.tdc('typ', tr); + if (isSet) { /* fold toggle sits in the type column, in front of "Set" */ + typ.append($$.sc('fold').attr('title', folded ? $ict.setsExpand : $ict.setsCollapse).append(gi(folded ? 'triangle-right' : 'triangle-bottom')).click((ev) => { + ev.stopPropagation(); + if (folded) { delete $inv.sets.collapsed[id]; } else { $inv.sets.collapsed[id] = true; } + let state = $inv.d.tbl().data('dstate'); if (state) { $inv.sets.render(state); } + })); + } + typ.append($$.s($inv.sets.typLbl(co))); + $$.tdc('qty', tr).text(co.q || ''); + let ttl = $$.tdc('ttl', tr).append($$.sc('ind').css('width', (lvl * 1.4).toString() + 'rem')).append($$.s($inv.sets.title(co))); + if (isSet) { + let n = itms.filter((x) => $inv.sets.parentOf(x, byId) === id).length; + ttl.append($$.sc('cnt', ' (' + n + ')')); + } + /* siblings (same parent, current order) drive the ▲▼ buttons; a preceding sibling set drives ▶ */ + let sibs = itms.filter((x) => $inv.sets.parentOf(x, byId) === parent), si = sibs.findIndex((x) => (x.id || '').toString() === id); + let hasSibSet = sibs.slice(0, si).some((x) => $inv.sets.isSet(x)); + let act = $$.tdc('act', tr); + let btn = (cls, glyph, title, enabled, delta) => $$.dc('ibtn ' + cls, act, { title: title }).append(gi(glyph)).tC('disabled', !enabled) + .click(() => { if (enabled) { $inv.sets.sync(delta); } }); + btn('up', 'arrow-up', $ict.setsUp, si > 0, { Target: 'set.move', Ref: id, Value: -1 }); + btn('down', 'arrow-down', $ict.setsDown, si >= 0 && si < sibs.length - 1, { Target: 'set.move', Ref: id, Value: 1 }); + btn('out', 'chevron-left', $ict.setsOut, lvl > 0, { Target: 'set.level', Ref: id, Value: -1 }); + btn('in', 'chevron-right', $ict.setsIn, hasSibSet, { Target: 'set.level', Ref: id, Value: 1 }); + + tr.on('dragstart', (ev) => { + $inv.sets.drag = { id: id, bid: bid }; + tr.aC('dragging'); + try { ev.originalEvent.dataTransfer.setData('text/plain', id); ev.originalEvent.dataTransfer.effectAllowed = 'move'; } catch (e) { /* ignore */ } + }).on('dragend', () => { tr.rC('dragging'); $inv.sets.drag = null; $inv.sets.clearDrop(); }); + /* Every row is a target: before / after (reorder, become the anchor's sibling — works on a + folded set too, its subtree travels along) and, for sets, into (become its last member). */ + let ok = () => $inv.sets.canDrop($inv.sets.drag, id, bid, byId); + tr.on('dragover', (ev) => { + if (!ok()) { return; } + ev.preventDefault(); + let z = $inv.sets.zone(ev, tr, isSet); + tr.rC('dropok dropbefore dropafter').aC(z === 'into' ? 'dropok' : (z === 'before' ? 'dropbefore' : 'dropafter')); + }).on('dragleave', () => { tr.rC('dropok dropbefore dropafter'); }).on('drop', (ev) => { + ev.preventDefault(); $inv.sets.clearDrop(); + let d = $inv.sets.drag; $inv.sets.drag = null; + if (!$inv.sets.canDrop(d, id, bid, byId)) { return; } + let z = $inv.sets.zone(ev, tr, isSet); + if (z === 'into') { $inv.sets.sync({ Target: 'set.assign', Ref: d.id, Value: { parent: id } }); } + else { $inv.sets.sync({ Target: 'set.move', Ref: d.id, Value: { anchor: id, where: z } }); } + }); + return tr; + }, + /* Drop target for "pull out of its set" (top level of the block). */ + rootZone: function (el, bid, byId) { + let ok = () => { let d = $inv.sets.drag; return !!d && d.bid === bid && !!byId[d.id] && $inv.sets.parentOf(byId[d.id], byId) !== ''; }; + el.on('dragover', (ev) => { if (ok()) { ev.preventDefault(); el.aC('dropok'); } }) + .on('dragleave', () => { el.rC('dropok'); }) + .on('drop', (ev) => { + ev.preventDefault(); el.rC('dropok'); + let d = $inv.sets.drag; $inv.sets.drag = null; + if (d && d.bid === bid && byId[d.id] && $inv.sets.parentOf(byId[d.id], byId) !== '') { + $inv.sets.sync({ Target: 'set.assign', Ref: d.id, Value: { parent: '' } }); + } + }); + return el; + }, + /* "New line" bar per block: type (Set/Text), target set (top level or any set), title. */ + createBar: function (blk, bid, itms, byId) { + let bar = $$.dc('screate', blk); + let typ = $('').attr({ name: 'typ' }).append([$$.opt('set', $ict.settyp.set), $$.opt('text', $ict.settyp.text)]); + let par = $('').attr({ name: 'parent' }).append($$.opt('', $ict.setsTop)); + itms.forEach((co) => { + if (!$inv.sets.isSet(co)) { return; } + let lvl = parseInt(co.lvl || 0, 10) || 0; + par.append($$.opt((co.id || '').toString(), ' '.repeat(lvl * 3) + $inv.sets.title(co))); + }); + let ttl = $('').attr({ name: 'title', placeholder: $ict.setsTitle, maxlength: 250 }); + let add = () => { + let t = (ttl.val() || '').toString().trim(); + if (t === '') { ttl.focus(); return; } + $inv.sets.sync({ Target: 'set.create', Ref: bid, Value: { typ: typ.val(), title: t, parent: par.val() || '' } }); + ttl.val(''); + }; + ttl.on('keydown', (ev) => { if (ev.key === 'Enter') { ev.preventDefault(); add(); } }); + bar.append([$$.sc('lbl', $ict.setsNew), typ, $$.sc('lbl', $ict.setsInto), par, ttl, $$.bbtn($ict.setsAdd, 'sadd').click(add)]); + return bar; + } +}; diff --git a/Fuchs/js/intranet/modules/fis.inv_shared.js b/Fuchs/js/intranet/modules/fis.inv_shared.js index 733a238..10ed336 100644 --- a/Fuchs/js/intranet/modules/fis.inv_shared.js +++ b/Fuchs/js/intranet/modules/fis.inv_shared.js @@ -86,7 +86,7 @@ $inv.iMn = (ix) => { yet been explicitly switched to a set-display mode (admin.setmode empty/undefined) — once set, changing it again is not supported (the user edits the line items directly instead); it reappears only if 'Änderungen verwerfen' reverts to a DB state that was never switched. */ -$inv.emOpt = (admin) => 'iss,p13b,ctp' + (((admin || {}).setmode || '') === '' ? ',setm' : ''); +$inv.emOpt = (admin) => 'iss,p13b,ctp,setst' + (((admin || {}).setmode || '') === '' ? ',setm' : ''); $inv.eM = (r, re, opt) => { let m = []; if (booln(r, false) === true || booln(re, false) === true) { @@ -104,6 +104,9 @@ $inv.eM = (r, re, opt) => { if ((opt || '').split(',').includes('setm') === true) { m.push({ lbl: $ict.setm, fnc: $inv.ssetmode }); } + if ((opt || '').split(',').includes('setst') === true) { /* user-defined set structure dialog (ADR 0016) */ + m.push({ lbl: $ict.setst, fnc: () => $inv.sets.open() }); + } if ((opt || '').split(',').includes('iss') === true) { /* backend-authoritative draft (ADR 0006) */ m.push({ lbl: 'Änderungshistorie', fnc: () => $inv.d.history() }); m.push({ lbl: 'Änderungen verwerfen', fnc: () => $inv.d.discard() }); @@ -172,12 +175,13 @@ $inv.d = { if (typeof curVer === 'number' && typeof state.version === 'number' && state.version < curVer) { return; } - tbl.data('dver', state.version).data('serverSums', state.sums); + tbl.data('dver', state.version).data('serverSums', state.sums).data('dstate', state); $inv.d.footer(tbl, state.sums || {}, state.admin || {}); $inv.d.notes(state.notes || []); $inv.d.validation(state.validation || []); $inv.d.applyItems(tbl, state.req || []); $inv.d.applySetDisplay(tbl, state.setDisplay || {}); + if ($inv.sets && $inv.sets.isOpen()) { $inv.sets.render(state); } /* the set-structure dialog is a view of the same state (ADR 0016) */ /* Keep the menu's "Set-Preisanzeige" entry in sync with the authoritative admin.setmode: hides it right after a mode switch, and restores it if a discard reverted the draft to a DB state that was never switched (ADR 0006 backend-authoritative refresh). */ @@ -223,13 +227,15 @@ $inv.d = { let changed = isNew || (dta.Type || '') !== (co.typ || '') || dta.net !== val(co.v) || dta.net_val !== val(co.vt) || dta.vat_val !== val(co.vv) || dta.svcnet_val !== val(co.vs) || dta.svcvat_val !== val(co.vsv) - || (dta.position || '') !== (co.p != null ? co.p : ''); + || (dta.position || '') !== (co.p != null ? co.p : '') + || (dta.lvl || 0) !== (co.lvl || 0) || (dta.SetItmId || null) !== (co.SetItmId != null ? co.SetItmId : null); $.extend(dta, { Id: id, Type: co.typ, net: val(co.v), net_val: val(co.vt), vat_val: val(co.vv), svcnet_val: val(co.vs), svcvat_val: val(co.vsv), vat: co.vat, position: (co.p != null ? co.p : ''), - SetItmId: (co.SetItmId != null ? co.SetItmId : null) + SetItmId: (co.SetItmId != null ? co.SetItmId : null), + lvl: (co.lvl || 0) /* nesting depth (ADR 0016) — rendered as an indent */ }); if (isNew) { $.extend(dta, { htmltext: co.t, quantity: co.q }); } // text only for server-created rows (e.g. the set row) rw.data(dta); @@ -311,7 +317,7 @@ $inv.d = { /* Map an inline recipient field to its delta target and send it. No token-guard here for the same reason as syncChanged: sync() waits for an in-flight seed instead of dropping the edit. */ syncField: function (nme, val) { - let map = { invoicetitle: 'title', invoiceaddress: 'address', invoiceemail: 'email', loc: 'provisionlocation', provisionlocation: 'provisionlocation', provisionperiod: 'provisionperiod' }; + let map = { invoicetitle: 'title', invoiceaddress: 'address', invoiceemail: 'email', invoiceemailcc: 'emailcc', loc: 'provisionlocation', provisionlocation: 'provisionlocation', provisionperiod: 'provisionperiod' }; let target = map[nme]; if (!target) { return; } $inv.d.sync({ Target: target, Value: val }); }, @@ -898,6 +904,7 @@ $inv.cntInv = function (data) { //invoice continuation fm('adrfrm', inv.SendToAddress, 'invoiceaddress', 0, null); fm('locfrm', inv.ProvisionLocation, 'loc', 1, null); fm('emailfrm', inv.SendToEmail, 'invoiceemail', 0, null); + fm('emailccfrm', inv.SendToEmailCc, 'invoiceemailcc', 0, null); $$.dc('sndfrm', frm).append($$.dc('content').text(response.admin.sender)); fm('admfrm', inv.ProvisionPeriod, 'provisionperiod', ((inv.ProvisionPeriod || '').includes('-') === true ? $rct.provP : $rct.provD), 1, null); @@ -960,6 +967,7 @@ $inv.cSt = function (data) { fm('adrfrm', inv.SendToAddress, 'invoiceaddress', 0, null); fm('locfrm', inv.ProvisionLocation, 'loc', 1, null); fm('emailfrm', inv.SendToEmail, 'invoiceemail', 0, null); + fm('emailccfrm', inv.SendToEmailCc, 'invoiceemailcc', 0, null); $$.dc('sndfrm', frm).append($$.dc('content').text(response.admin.sender)); fm('admfrm', inv.ProvisionPeriod, 'provisionperiod', ((inv.ProvisionPeriod || '').includes('-') === true ? $rct.provP : $rct.provD), 1, null); @@ -1101,7 +1109,7 @@ $inv.eHtml = function (ev) { Leistungsdatum). The backend sanitises HTML too (single source of truth, ADR 0006), but keeping these plain here avoids the UI briefly holding the wrapped value. Multi-line fields (invoiceaddress, loc) stay HTML-capable and are normalised to newlines server-side. */ - let isPlainText = ['invoiceemail', 'provisionperiod', 'invoicetitle'].includes(ev.data.nme); + let isPlainText = ['invoiceemail', 'invoiceemailcc', 'provisionperiod', 'invoicetitle'].includes(ev.data.nme); let flds = isPlainText ? [{ name: 'txt', label: 'Text', type: 'text', value: frmct.text() }] : [{ name: 'txt', label: 'Text', type: 'html', value: frmct.html(), tinymce: true, attr: { style: 'height: 300px' } }]; @@ -1234,7 +1242,7 @@ $inv.rrw = function () { } else if (rw.is('.itm.osum') === true) { co = { invrqid: dta.InvRqId, id: 'osum' + rw.index(), typ: 'osum', p: '', q: null, t: oHtml(dta.tbl.tbl), tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: false }; } else { - co = { invrqid: dta.InvRqId, id: dta.Id || '', typ: dta.Type || 'other', p: '', q: null, t: '', tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: (dta.Note || '') !== '' && hn === false, SetItmId: dta.SetItmId || null }; + co = { invrqid: dta.InvRqId, id: dta.Id || '', typ: dta.Type || 'other', p: '', q: null, t: '', tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: (dta.Note || '') !== '' && hn === false, SetItmId: dta.SetItmId || null, lvl: (dta.lvl || 0) }; $$.dc('ibtn ico move', axf, { title: $rct.mR }); /* should be the last added */ co.p = dta.position || (dta.SortOrder || ''); @@ -1274,6 +1282,7 @@ $inv.rrw = function () { } rw.empty().attr('class', ph ? 'placeholder' : 'itm').aC(co.Typ) .tC('sethdr', (co.typ || '').toString().toLowerCase() === 'set') /* emphasise set rows (fn1 header + block set row) */ + .aC((co.lvl || 0) > 0 ? 'lvl' + Math.min(co.lvl, 6) : '') /* indent nested set members (ADR 0016) */ .tC('hidenote', hn).append(tda); dta.co = co; }; @@ -1304,8 +1313,23 @@ $inv.invSumUpdate = function () { let tbl = $(this); let ba = []; let bds = tbl.children('tbody'); + /* Hierarchical position numbering (ADR 0017), spanning the whole invoice: a running counter + per nesting depth, ported from InvoiceLineNumbering.Compute. A set header gets a number at + its own level ("3"); its members get subsidiary numbers one level deeper ("3.1", "3.2"). A + row's numbering never depends on the MFR SortOrder value — including for a Text/Title + header row synced with SortOrder -1, which used to leak "-1" straight into the Pos. column + here because it never went through this loop at all. */ + let posCounters = []; + let nextPositionLabel = (numbered, lvl) => { + if (!numbered) { return ''; } + let l = Math.max(lvl || 0, 0); + while (posCounters.length <= l) { posCounters.push(0); } + posCounters.length = l + 1; + posCounters[l]++; + return posCounters.join('.'); + }; bds.each((bi, bdy) => { - let b = $(bdy), rx = b.data() || {}, i = [], citems = [], itm = b.find('tr.itm'), iso = 0, ipos = 0; + let b = $(bdy), rx = b.data() || {}, i = [], citems = [], itm = b.find('tr.itm'); b.tC('empty', itm.length < 1); itm.each((ti, tx) => { let rrx = $(tx).data() || {}; @@ -1318,14 +1342,12 @@ $inv.invSumUpdate = function () { by the id !== '' && !== citem.id check below). */ let citem = $inv.itemToContract(rrx); let sid = (rrx.SetItmId || '').toString(); - if (citem.type !== 'set' && sid !== '' && sid !== citem.id) { citem.setId = sid; } + /* a nested set (ADR 0016) is itself a member of its parent set, so the type is not excluded here */ + if (sid !== '' && sid !== citem.id) { citem.setId = sid; } citems.push(citem); - if (((typeof rrx.SortOrder === 'undefined' || rrx.SortOrder === null) ? -1 : rrx.SortOrder) > -1) { - if (['text', 'title'].includes((rrx.Type || 'other').toLowerCase()) === false) { ipos++; } - rrx.SortOrder = iso; - rrx.position = ipos; - $inv.rrw.call(tx); - } + let numbered = ['text', 'title'].includes((rrx.Type || 'other').toLowerCase()) === false; + rrx.position = nextPositionLabel(numbered, (rrx.co || {}).lvl || 0); + $inv.rrw.call(tx); }); ba.push({ Id: rx.Id, nme: rx.Name, text: rx.text, itm: i, items: citems }); }); diff --git a/Fuchs/js/intranet/modules/fis.inv_shared.scss b/Fuchs/js/intranet/modules/fis.inv_shared.scss index 6b44c2a..5cda505 100644 --- a/Fuchs/js/intranet/modules/fis.inv_shared.scss +++ b/Fuchs/js/intranet/modules/fis.inv_shared.scss @@ -81,6 +81,37 @@ } } + .emailccfrm { + position: absolute; + font-size: 0.7rem; + top: 2.2rem; + left: 0.5rem; + width: auto; + height: auto; + cursor: pointer; + + .content { + color: #000; + } + + /* Unlike the other optional fields (plain "-" placeholder), this one names the action — + there is no default "Zusatzempfänger" from MFR, so an empty state must read as an + offer to add one, not as missing data. */ + .content:empty::before { + content: 'Zusatzempfänger hinzufügen'; + min-width: 5rem; + height: 1rem; + display: block; + position: absolute; + } + + &:hover { + div.axf { + display: block; + } + } + } + .admfrm { position: absolute; right: 3rem; @@ -250,6 +281,12 @@ &.sethdr td.txt { font-weight: bold; } + /* Nested set members (ADR 0016): indent per level, mirroring the PDF's per-level indent. */ + @for $i from 1 through 6 { + &.lvl#{$i} td.txt { + padding-left: calc(0.4rem + #{$i * 1.4rem}); + } + } } > tfoot > tr { @@ -493,3 +530,71 @@ color: #445; } } + +/* ── Set structure dialog (ADR 0016, see $inv.sets in fis.inv_sets.js) ─────────────────── */ +.modal .setstruct { + &.busy { opacity: 0.6; pointer-events: none; } + + div.shint { font-size: 0.85rem; color: #666; margin: 0 0 0.8rem 0; } + + div.sblock { + margin: 0 0 1.4rem 0; + + div.sbhd { + font-weight: bold; + padding: 0.4rem 0.6rem; + border-top: 1.5px solid #000; + border-bottom: 1px solid #ddd; + &.dropok { background-color: #e6f2e6; outline: 2px dashed #5a5; } + } + + table.stbl { + width: 100%; + border-collapse: collapse; + + th { text-align: left; font-size: 0.8rem; color: #666; padding: 0.3rem 0.4rem; border-bottom: 1px solid #ddd; } + td { padding: 0.3rem 0.4rem; border-bottom: 1px solid #f0f0f0; vertical-align: middle; } + td.grip { width: 1.6rem; cursor: grab; color: #999; } + td.pos, td.qty { width: 4rem; white-space: nowrap; } + td.typ { width: 7rem; white-space: nowrap; color: #666; font-size: 0.85rem; + span.fold { cursor: pointer; margin-right: 0.35rem; color: #555; display: inline-block; width: 1rem; } + } + td.ttl { + span.ind { display: inline-block; } + span.seti { margin-right: 0.3rem; color: #888; } + span.cnt { color: #999; font-weight: normal; font-size: 0.85rem; } + } + td.act { width: 7.5rem; white-space: nowrap; text-align: right; + div.ibtn { display: inline-block; cursor: pointer; padding: 0 0.25rem; &.disabled { opacity: 0.25; cursor: default; } } + } + + tr.srow { + &.isset td.ttl { font-weight: bold; } + &.hid { display: none; } + &.folded td.ttl { font-style: italic; } + &.dragging { opacity: 0.4; } + &.dropok td { background-color: #e6f2e6; } + &.dropbefore td { border-top: 3px solid #5a5; } + &.dropafter td { border-bottom: 3px solid #5a5; } + } + } + + div.sroot { + margin: 0.3rem 0 0.5rem 0; + padding: 0.4rem 0.6rem; + border: 1px dashed #bbb; + color: #888; + font-size: 0.85rem; + &.dropok { background-color: #e6f2e6; border-color: #5a5; color: #363; } + } + + div.screate { + display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; + padding: 0.4rem 0.6rem; + background-color: #f7f7f7; + span.lbl { font-size: 0.85rem; color: #666; } + select, input[type="text"] { padding: 0.2rem 0.3rem; } + input[type="text"] { flex: 1 1 14rem; min-width: 10rem; } + } + } +} diff --git a/Fuchs/js/intranet/modules/fis.inv_txt_de.js b/Fuchs/js/intranet/modules/fis.inv_txt_de.js index 583bf94..85ab354 100644 --- a/Fuchs/js/intranet/modules/fis.inv_txt_de.js +++ b/Fuchs/js/intranet/modules/fis.inv_txt_de.js @@ -45,6 +45,24 @@ setprice: 'Set mit Preis – Positionen ohne Preis', setonly: 'Nur Set mit Preis – Positionen ausgeblendet' }, + /* user-defined set structure dialog (ADR 0016) */ + setst: 'Set-Struktur bearbeiten', + setsHint: 'Reihenfolge: Zeile per Drag & Drop an den oberen/unteren Rand einer anderen Zeile ziehen (davor/danach) oder ▲▼ nutzen. Zuordnung: Zeile auf die Mitte eines Sets ziehen (hinein) bzw. auf die Auftragszeile oder "oberste Ebene" (aus dem Set lösen); ◀▶ ändert die Ebene. Sets lassen sich über das Dreieck zu-/aufklappen und auch zugeklappt verschieben – ihre Positionen wandern mit. Änderungen werden sofort übernommen.', + setsUp: 'Nach oben (innerhalb der Ebene)', + setsDown: 'Nach unten (innerhalb der Ebene)', + setsCollapse: 'Set zuklappen', + setsExpand: 'Set aufklappen', + setsHR: ['', 'Pos.', 'Typ', 'Menge', 'Bezeichnung', 'Reihenfolge / Ebene'], + setsRoot: 'Hierher ziehen = aus dem Set lösen (oberste Ebene)', + setsOut: 'Eine Ebene höher (aus dem Set lösen)', + setsIn: 'Eine Ebene tiefer (dem vorangehenden Set zuordnen)', + setsNew: 'Neu:', + setsInto: 'in', + setsTop: 'oberste Ebene', + setsTitle: 'Bezeichnung', + setsAdd: 'Hinzufügen', + setsNoTitle: '(ohne Bezeichnung)', + settyp: { set: 'Set', text: 'Text', title: 'Text', material: 'Material', service: 'Arbeitsleistung', equipment: 'Ausrüstung', other: 'Position', osum: 'Zusammenfassung', intinvoiceallocation: 'Abschlag', intinvoiceallocationdiff: 'Abschlag (offen)' }, ctp: 'Ansprechpartner festlegen', mfr: 'Von MFR neu abrufen', rq1: 'Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.', diff --git a/Fuchs/wwwroot/web/fis.inv.css b/Fuchs/wwwroot/web/fis.inv.css index 9e13147..2a1b71b 100644 --- a/Fuchs/wwwroot/web/fis.inv.css +++ b/Fuchs/wwwroot/web/fis.inv.css @@ -1,3 +1,4 @@ +@charset "UTF-8"; /* basics */ /* Color SCHEME */ /* #8396bd */ @@ -169,6 +170,33 @@ table.if td.num { .edit_frm .invoice_layout .emailfrm:hover div.axf { display: block; } +.edit_frm .invoice_layout .emailccfrm { + position: absolute; + font-size: 0.7rem; + top: 2.2rem; + left: 0.5rem; + width: auto; + height: auto; + cursor: pointer; +} +.edit_frm .invoice_layout .emailccfrm .content { + color: #000; +} +.edit_frm .invoice_layout .emailccfrm { + /* Unlike the other optional fields (plain "-" placeholder), this one names the action — + there is no default "Zusatzempfänger" from MFR, so an empty state must read as an + offer to add one, not as missing data. */ +} +.edit_frm .invoice_layout .emailccfrm .content:empty::before { + content: "Zusatzempfänger hinzufügen"; + min-width: 5rem; + height: 1rem; + display: block; + position: absolute; +} +.edit_frm .invoice_layout .emailccfrm:hover div.axf { + display: block; +} .edit_frm .invoice_layout .admfrm { position: absolute; right: 3rem; @@ -302,6 +330,27 @@ table.if td.num { .edit_frm .invoice_layout table.invi > tbody > tr.sethdr td.txt { font-weight: bold; } +.edit_frm .invoice_layout table.invi > tbody > tr { + /* Nested set members (ADR 0016): indent per level, mirroring the PDF's per-level indent. */ +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl1 td.txt { + padding-left: calc(0.4rem + 1.4rem); +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl2 td.txt { + padding-left: calc(0.4rem + 2.8rem); +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl3 td.txt { + padding-left: calc(0.4rem + 4.2rem); +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl4 td.txt { + padding-left: calc(0.4rem + 5.6rem); +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl5 td.txt { + padding-left: calc(0.4rem + 7rem); +} +.edit_frm .invoice_layout table.invi > tbody > tr.lvl6 td.txt { + padding-left: calc(0.4rem + 8.4rem); +} .edit_frm .invoice_layout table.invi th.currency, .edit_frm .invoice_layout table.invi th.num, .edit_frm .invoice_layout table.invi td.currency, .edit_frm .invoice_layout table.invi td.num { text-align: right; white-space: nowrap; @@ -510,4 +559,144 @@ table.if td.num { } .adr-conformity .tx { color: #445; +} + +/* ── Set structure dialog (ADR 0016, see $inv.sets in fis.inv_sets.js) ─────────────────── */ +.modal .setstruct.busy { + opacity: 0.6; + pointer-events: none; +} +.modal .setstruct div.shint { + font-size: 0.85rem; + color: #666; + margin: 0 0 0.8rem 0; +} +.modal .setstruct div.sblock { + margin: 0 0 1.4rem 0; +} +.modal .setstruct div.sblock div.sbhd { + font-weight: bold; + padding: 0.4rem 0.6rem; + border-top: 1.5px solid #000; + border-bottom: 1px solid #ddd; +} +.modal .setstruct div.sblock div.sbhd.dropok { + background-color: #e6f2e6; + outline: 2px dashed #5a5; +} +.modal .setstruct div.sblock table.stbl { + width: 100%; + border-collapse: collapse; +} +.modal .setstruct div.sblock table.stbl th { + text-align: left; + font-size: 0.8rem; + color: #666; + padding: 0.3rem 0.4rem; + border-bottom: 1px solid #ddd; +} +.modal .setstruct div.sblock table.stbl td { + padding: 0.3rem 0.4rem; + border-bottom: 1px solid #f0f0f0; + vertical-align: middle; +} +.modal .setstruct div.sblock table.stbl td.grip { + width: 1.6rem; + cursor: grab; + color: #999; +} +.modal .setstruct div.sblock table.stbl td.pos, .modal .setstruct div.sblock table.stbl td.qty { + width: 4rem; + white-space: nowrap; +} +.modal .setstruct div.sblock table.stbl td.typ { + width: 7rem; + white-space: nowrap; + color: #666; + font-size: 0.85rem; +} +.modal .setstruct div.sblock table.stbl td.typ span.fold { + cursor: pointer; + margin-right: 0.35rem; + color: #555; + display: inline-block; + width: 1rem; +} +.modal .setstruct div.sblock table.stbl td.ttl span.ind { + display: inline-block; +} +.modal .setstruct div.sblock table.stbl td.ttl span.seti { + margin-right: 0.3rem; + color: #888; +} +.modal .setstruct div.sblock table.stbl td.ttl span.cnt { + color: #999; + font-weight: normal; + font-size: 0.85rem; +} +.modal .setstruct div.sblock table.stbl td.act { + width: 7.5rem; + white-space: nowrap; + text-align: right; +} +.modal .setstruct div.sblock table.stbl td.act div.ibtn { + display: inline-block; + cursor: pointer; + padding: 0 0.25rem; +} +.modal .setstruct div.sblock table.stbl td.act div.ibtn.disabled { + opacity: 0.25; + cursor: default; +} +.modal .setstruct div.sblock table.stbl tr.srow.isset td.ttl { + font-weight: bold; +} +.modal .setstruct div.sblock table.stbl tr.srow.hid { + display: none; +} +.modal .setstruct div.sblock table.stbl tr.srow.folded td.ttl { + font-style: italic; +} +.modal .setstruct div.sblock table.stbl tr.srow.dragging { + opacity: 0.4; +} +.modal .setstruct div.sblock table.stbl tr.srow.dropok td { + background-color: #e6f2e6; +} +.modal .setstruct div.sblock table.stbl tr.srow.dropbefore td { + border-top: 3px solid #5a5; +} +.modal .setstruct div.sblock table.stbl tr.srow.dropafter td { + border-bottom: 3px solid #5a5; +} +.modal .setstruct div.sblock div.sroot { + margin: 0.3rem 0 0.5rem 0; + padding: 0.4rem 0.6rem; + border: 1px dashed #bbb; + color: #888; + font-size: 0.85rem; +} +.modal .setstruct div.sblock div.sroot.dropok { + background-color: #e6f2e6; + border-color: #5a5; + color: #363; +} +.modal .setstruct div.sblock div.screate { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.4rem; + padding: 0.4rem 0.6rem; + background-color: #f7f7f7; +} +.modal .setstruct div.sblock div.screate span.lbl { + font-size: 0.85rem; + color: #666; +} +.modal .setstruct div.sblock div.screate select, .modal .setstruct div.sblock div.screate input[type=text] { + padding: 0.2rem 0.3rem; +} +.modal .setstruct div.sblock div.screate input[type=text] { + flex: 1 1 14rem; + min-width: 10rem; } \ No newline at end of file diff --git a/Fuchs/wwwroot/web/fis.inv.de.js b/Fuchs/wwwroot/web/fis.inv.de.js index 1b98b86..532dbbf 100644 --- a/Fuchs/wwwroot/web/fis.inv.de.js +++ b/Fuchs/wwwroot/web/fis.inv.de.js @@ -65,6 +65,7 @@ let $rct = { invoiceaddress: 'Adresse' , loc: 'Leistungsort / Lieferadresse' , invoiceemail: 'Email' + , invoiceemailcc: 'Zusatzempfänger (CC)' } }, $rcol = { @@ -154,6 +155,24 @@ let $ict = { setprice: 'Set mit Preis – Positionen ohne Preis', setonly: 'Nur Set mit Preis – Positionen ausgeblendet' }, + /* user-defined set structure dialog (ADR 0016) */ + setst: 'Set-Struktur bearbeiten', + setsHint: 'Reihenfolge: Zeile per Drag & Drop an den oberen/unteren Rand einer anderen Zeile ziehen (davor/danach) oder ▲▼ nutzen. Zuordnung: Zeile auf die Mitte eines Sets ziehen (hinein) bzw. auf die Auftragszeile oder "oberste Ebene" (aus dem Set lösen); ◀▶ ändert die Ebene. Sets lassen sich über das Dreieck zu-/aufklappen und auch zugeklappt verschieben – ihre Positionen wandern mit. Änderungen werden sofort übernommen.', + setsUp: 'Nach oben (innerhalb der Ebene)', + setsDown: 'Nach unten (innerhalb der Ebene)', + setsCollapse: 'Set zuklappen', + setsExpand: 'Set aufklappen', + setsHR: ['', 'Pos.', 'Typ', 'Menge', 'Bezeichnung', 'Reihenfolge / Ebene'], + setsRoot: 'Hierher ziehen = aus dem Set lösen (oberste Ebene)', + setsOut: 'Eine Ebene höher (aus dem Set lösen)', + setsIn: 'Eine Ebene tiefer (dem vorangehenden Set zuordnen)', + setsNew: 'Neu:', + setsInto: 'in', + setsTop: 'oberste Ebene', + setsTitle: 'Bezeichnung', + setsAdd: 'Hinzufügen', + setsNoTitle: '(ohne Bezeichnung)', + settyp: { set: 'Set', text: 'Text', title: 'Text', material: 'Material', service: 'Arbeitsleistung', equipment: 'Ausrüstung', other: 'Position', osum: 'Zusammenfassung', intinvoiceallocation: 'Abschlag', intinvoiceallocationdiff: 'Abschlag (offen)' }, ctp: 'Ansprechpartner festlegen', mfr: 'Von MFR neu abrufen', rq1: 'Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.', @@ -629,7 +648,7 @@ $inv.iMn = (ix) => { yet been explicitly switched to a set-display mode (admin.setmode empty/undefined) — once set, changing it again is not supported (the user edits the line items directly instead); it reappears only if 'Änderungen verwerfen' reverts to a DB state that was never switched. */ -$inv.emOpt = (admin) => 'iss,p13b,ctp' + (((admin || {}).setmode || '') === '' ? ',setm' : ''); +$inv.emOpt = (admin) => 'iss,p13b,ctp,setst' + (((admin || {}).setmode || '') === '' ? ',setm' : ''); $inv.eM = (r, re, opt) => { let m = []; if (booln(r, false) === true || booln(re, false) === true) { @@ -647,6 +666,9 @@ $inv.eM = (r, re, opt) => { if ((opt || '').split(',').includes('setm') === true) { m.push({ lbl: $ict.setm, fnc: $inv.ssetmode }); } + if ((opt || '').split(',').includes('setst') === true) { /* user-defined set structure dialog (ADR 0016) */ + m.push({ lbl: $ict.setst, fnc: () => $inv.sets.open() }); + } if ((opt || '').split(',').includes('iss') === true) { /* backend-authoritative draft (ADR 0006) */ m.push({ lbl: 'Änderungshistorie', fnc: () => $inv.d.history() }); m.push({ lbl: 'Änderungen verwerfen', fnc: () => $inv.d.discard() }); @@ -715,12 +737,13 @@ $inv.d = { if (typeof curVer === 'number' && typeof state.version === 'number' && state.version < curVer) { return; } - tbl.data('dver', state.version).data('serverSums', state.sums); + tbl.data('dver', state.version).data('serverSums', state.sums).data('dstate', state); $inv.d.footer(tbl, state.sums || {}, state.admin || {}); $inv.d.notes(state.notes || []); $inv.d.validation(state.validation || []); $inv.d.applyItems(tbl, state.req || []); $inv.d.applySetDisplay(tbl, state.setDisplay || {}); + if ($inv.sets && $inv.sets.isOpen()) { $inv.sets.render(state); } /* the set-structure dialog is a view of the same state (ADR 0016) */ /* Keep the menu's "Set-Preisanzeige" entry in sync with the authoritative admin.setmode: hides it right after a mode switch, and restores it if a discard reverted the draft to a DB state that was never switched (ADR 0006 backend-authoritative refresh). */ @@ -766,13 +789,15 @@ $inv.d = { let changed = isNew || (dta.Type || '') !== (co.typ || '') || dta.net !== val(co.v) || dta.net_val !== val(co.vt) || dta.vat_val !== val(co.vv) || dta.svcnet_val !== val(co.vs) || dta.svcvat_val !== val(co.vsv) - || (dta.position || '') !== (co.p != null ? co.p : ''); + || (dta.position || '') !== (co.p != null ? co.p : '') + || (dta.lvl || 0) !== (co.lvl || 0) || (dta.SetItmId || null) !== (co.SetItmId != null ? co.SetItmId : null); $.extend(dta, { Id: id, Type: co.typ, net: val(co.v), net_val: val(co.vt), vat_val: val(co.vv), svcnet_val: val(co.vs), svcvat_val: val(co.vsv), vat: co.vat, position: (co.p != null ? co.p : ''), - SetItmId: (co.SetItmId != null ? co.SetItmId : null) + SetItmId: (co.SetItmId != null ? co.SetItmId : null), + lvl: (co.lvl || 0) /* nesting depth (ADR 0016) — rendered as an indent */ }); if (isNew) { $.extend(dta, { htmltext: co.t, quantity: co.q }); } // text only for server-created rows (e.g. the set row) rw.data(dta); @@ -854,7 +879,7 @@ $inv.d = { /* Map an inline recipient field to its delta target and send it. No token-guard here for the same reason as syncChanged: sync() waits for an in-flight seed instead of dropping the edit. */ syncField: function (nme, val) { - let map = { invoicetitle: 'title', invoiceaddress: 'address', invoiceemail: 'email', loc: 'provisionlocation', provisionlocation: 'provisionlocation', provisionperiod: 'provisionperiod' }; + let map = { invoicetitle: 'title', invoiceaddress: 'address', invoiceemail: 'email', invoiceemailcc: 'emailcc', loc: 'provisionlocation', provisionlocation: 'provisionlocation', provisionperiod: 'provisionperiod' }; let target = map[nme]; if (!target) { return; } $inv.d.sync({ Target: target, Value: val }); }, @@ -1441,6 +1466,7 @@ $inv.cntInv = function (data) { //invoice continuation fm('adrfrm', inv.SendToAddress, 'invoiceaddress', 0, null); fm('locfrm', inv.ProvisionLocation, 'loc', 1, null); fm('emailfrm', inv.SendToEmail, 'invoiceemail', 0, null); + fm('emailccfrm', inv.SendToEmailCc, 'invoiceemailcc', 0, null); $$.dc('sndfrm', frm).append($$.dc('content').text(response.admin.sender)); fm('admfrm', inv.ProvisionPeriod, 'provisionperiod', ((inv.ProvisionPeriod || '').includes('-') === true ? $rct.provP : $rct.provD), 1, null); @@ -1503,6 +1529,7 @@ $inv.cSt = function (data) { fm('adrfrm', inv.SendToAddress, 'invoiceaddress', 0, null); fm('locfrm', inv.ProvisionLocation, 'loc', 1, null); fm('emailfrm', inv.SendToEmail, 'invoiceemail', 0, null); + fm('emailccfrm', inv.SendToEmailCc, 'invoiceemailcc', 0, null); $$.dc('sndfrm', frm).append($$.dc('content').text(response.admin.sender)); fm('admfrm', inv.ProvisionPeriod, 'provisionperiod', ((inv.ProvisionPeriod || '').includes('-') === true ? $rct.provP : $rct.provD), 1, null); @@ -1644,7 +1671,7 @@ $inv.eHtml = function (ev) { Leistungsdatum). The backend sanitises HTML too (single source of truth, ADR 0006), but keeping these plain here avoids the UI briefly holding the wrapped value. Multi-line fields (invoiceaddress, loc) stay HTML-capable and are normalised to newlines server-side. */ - let isPlainText = ['invoiceemail', 'provisionperiod', 'invoicetitle'].includes(ev.data.nme); + let isPlainText = ['invoiceemail', 'invoiceemailcc', 'provisionperiod', 'invoicetitle'].includes(ev.data.nme); let flds = isPlainText ? [{ name: 'txt', label: 'Text', type: 'text', value: frmct.text() }] : [{ name: 'txt', label: 'Text', type: 'html', value: frmct.html(), tinymce: true, attr: { style: 'height: 300px' } }]; @@ -1777,7 +1804,7 @@ $inv.rrw = function () { } else if (rw.is('.itm.osum') === true) { co = { invrqid: dta.InvRqId, id: 'osum' + rw.index(), typ: 'osum', p: '', q: null, t: oHtml(dta.tbl.tbl), tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: false }; } else { - co = { invrqid: dta.InvRqId, id: dta.Id || '', typ: dta.Type || 'other', p: '', q: null, t: '', tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: (dta.Note || '') !== '' && hn === false, SetItmId: dta.SetItmId || null }; + co = { invrqid: dta.InvRqId, id: dta.Id || '', typ: dta.Type || 'other', p: '', q: null, t: '', tt: null, v: null, vt: dta.net_val, vs: dta.svcnet_val, vat: dta.vat, vv: dta.vat_val, vsv: dta.svcvat_val, det: (dta.Note || '') !== '' && hn === false, SetItmId: dta.SetItmId || null, lvl: (dta.lvl || 0) }; $$.dc('ibtn ico move', axf, { title: $rct.mR }); /* should be the last added */ co.p = dta.position || (dta.SortOrder || ''); @@ -1817,6 +1844,7 @@ $inv.rrw = function () { } rw.empty().attr('class', ph ? 'placeholder' : 'itm').aC(co.Typ) .tC('sethdr', (co.typ || '').toString().toLowerCase() === 'set') /* emphasise set rows (fn1 header + block set row) */ + .aC((co.lvl || 0) > 0 ? 'lvl' + Math.min(co.lvl, 6) : '') /* indent nested set members (ADR 0016) */ .tC('hidenote', hn).append(tda); dta.co = co; }; @@ -1847,8 +1875,23 @@ $inv.invSumUpdate = function () { let tbl = $(this); let ba = []; let bds = tbl.children('tbody'); + /* Hierarchical position numbering (ADR 0017), spanning the whole invoice: a running counter + per nesting depth, ported from InvoiceLineNumbering.Compute. A set header gets a number at + its own level ("3"); its members get subsidiary numbers one level deeper ("3.1", "3.2"). A + row's numbering never depends on the MFR SortOrder value — including for a Text/Title + header row synced with SortOrder -1, which used to leak "-1" straight into the Pos. column + here because it never went through this loop at all. */ + let posCounters = []; + let nextPositionLabel = (numbered, lvl) => { + if (!numbered) { return ''; } + let l = Math.max(lvl || 0, 0); + while (posCounters.length <= l) { posCounters.push(0); } + posCounters.length = l + 1; + posCounters[l]++; + return posCounters.join('.'); + }; bds.each((bi, bdy) => { - let b = $(bdy), rx = b.data() || {}, i = [], citems = [], itm = b.find('tr.itm'), iso = 0, ipos = 0; + let b = $(bdy), rx = b.data() || {}, i = [], citems = [], itm = b.find('tr.itm'); b.tC('empty', itm.length < 1); itm.each((ti, tx) => { let rrx = $(tx).data() || {}; @@ -1861,14 +1904,12 @@ $inv.invSumUpdate = function () { by the id !== '' && !== citem.id check below). */ let citem = $inv.itemToContract(rrx); let sid = (rrx.SetItmId || '').toString(); - if (citem.type !== 'set' && sid !== '' && sid !== citem.id) { citem.setId = sid; } + /* a nested set (ADR 0016) is itself a member of its parent set, so the type is not excluded here */ + if (sid !== '' && sid !== citem.id) { citem.setId = sid; } citems.push(citem); - if (((typeof rrx.SortOrder === 'undefined' || rrx.SortOrder === null) ? -1 : rrx.SortOrder) > -1) { - if (['text', 'title'].includes((rrx.Type || 'other').toLowerCase()) === false) { ipos++; } - rrx.SortOrder = iso; - rrx.position = ipos; - $inv.rrw.call(tx); - } + let numbered = ['text', 'title'].includes((rrx.Type || 'other').toLowerCase()) === false; + rrx.position = nextPositionLabel(numbered, (rrx.co || {}).lvl || 0); + $inv.rrw.call(tx); }); ba.push({ Id: rx.Id, nme: rx.Name, text: rx.text, itm: i, items: citems }); }); @@ -2403,3 +2444,204 @@ $inv.mfrrel = (id) => { } }); }; + +/* ── User-defined set structure dialog (ADR 0016) ──────────────────────────── + Lets the user define which line belongs to which set (and how deep) instead of relying only + on the implicit "everything after a Set header until the next Set" derivation that + fds__prepInvoice delivers as the initial structure. The dialog is a pure view of the + authoritative server state (state.req[].itm — the co shape with SetItmId + lvl): every action + posts ONE delta (set.assign / set.level / set.create) via $inv.d.sync, the server rewrites the + cached block (parent links, depth-first order, levels) and the normal draftReady/dstate + refresh re-renders both the editor rows and this dialog (applyState -> $inv.sets.render). + The browser never restructures anything itself (ADR 0006/0008). */ +$inv.sets = { + o: null, /* open dialog handle ($ocms.dlg result) or null */ + c: null, /* dialog content container */ + drag: null, /* { id, bid } of the row currently being dragged */ + /* Reorder = set.move (▲▼ or drag before/after a row), membership = set.assign (drag into a set / + root zone), depth = set.level (◀▶). Folding is purely a view state kept in $inv.sets.collapsed. */ + isOpen: () => !!$inv.sets.o, + open: function () { + let tbl = $inv.d.tbl(); + if (tbl.length < 1 || $inv.sets.isOpen()) { return; } + if ($inv.d.token() === '' && !tbl.data('dstate')) { return; } /* no draft session (e.g. reminder editor) — nothing to structure */ + let c = $$.dc('setstruct'); + $inv.sets.c = c; + $inv.sets.o = $ocms.dlg(c, { + title: $ict.setst, size: [-1, 960], exclusive: false, + close: () => { $inv.sets.o = null; $inv.sets.c = null; $inv.sets.drag = null; } + }); + let state = tbl.data('dstate'); + if (state) { $inv.sets.render(state); } else { $inv.d.refresh(); } + }, + /* plain-text title of a co line (co.t is HTML for text lines / titles) */ + title: (co) => { + let t = $$.d().html(co.t || '').text().replace(/\s+/g, ' ').trim(); + return t !== '' ? t : ($ict.setsNoTitle || ''); + }, + typLbl: (co) => { + let k = (co.typ || '').toString().toLowerCase(); + return ($ict.settyp || {})[k] || co.typ || ''; + }, + isSet: (co) => (co.typ || '').toString().toLowerCase() === 'set', + /* Normalised parent id of a line, mirroring InvoiceSetStructure.ParentOf: SetItmId when it + names ANOTHER set line of the same block; '' for top level / self-reference / unknown. */ + parentOf: (co, byId) => { + let p = (co.SetItmId == null ? '' : co.SetItmId).toString(), id = (co.id || '').toString(); + if (p === '' || p === id) { return ''; } + let t = byId[p]; + return (t && $inv.sets.isSet(t)) ? p : ''; + }, + isDescendant: (candidateId, ancestorId, byId) => { + let seen = {}, cur = byId[candidateId]; + while (cur) { + let p = $inv.sets.parentOf(cur, byId); + if (p === '' || seen[p]) { return false; } + if (p === ancestorId) { return true; } + seen[p] = true; cur = byId[p]; + } + return false; + }, + /* Whether a line may be dropped onto the given target set (same block, not itself, no cycle). */ + canDrop: (drag, targetId, bid, byId) => + !!drag && drag.bid === bid && drag.id !== targetId && !$inv.sets.isDescendant(targetId, drag.id, byId), + sync: (delta) => { + if ($inv.sets.c) { $inv.sets.c.aC('busy'); } + return $inv.d.sync(delta); + }, + render: function (state) { + let c = $inv.sets.c; if (!c) { return; } + let bdy = c.closest('.modal-body'), scroll = bdy.length ? bdy.scrollTop() : 0; + c.empty().rC('busy'); + $$.dc('shint', c).text($ict.setsHint || ''); + (state.req || []).forEach((b) => { + if (!b) { return; } + let bid = (b.Id || '').toString(); if (bid === '') { return; } + let itms = (b.itm || []).filter((co) => co && (co.id || '') !== ''), byId = {}; + itms.forEach((co) => { byId[(co.id || '').toString()] = co; }); + let blk = $$.dc('sblock', c).data({ bid: bid }); + $inv.sets.rootZone($$.dc('sbhd', blk).append($$.sc('nme', b.text || b.nme || bid)), bid, byId); + let ts = $$.tblset({ class: 'stbl' }, blk); + let thr = $$.tr(ts.hd); + ($ict.setsHR || []).forEach((h) => $$.th(thr, h)); + itms.forEach((co) => $inv.sets.row(ts.bdy, co, bid, itms, byId)); + $inv.sets.rootZone($$.dc('sroot', blk).text($ict.setsRoot || ''), bid, byId); + $inv.sets.createBar(blk, bid, itms, byId); + }); + if (bdy.length) { bdy.scrollTop(scroll); } + }, + /* Ancestor-collapsed test: a row is hidden while any set above it is folded. */ + collapsed: {}, /* set id -> true (survives re-renders; default: everything expanded) */ + hiddenByCollapse: function (co, byId) { + let seen = {}, cur = co; + while (cur) { + let p = $inv.sets.parentOf(cur, byId); + if (p === '' || seen[p]) { return false; } + if ($inv.sets.collapsed[p]) { return true; } + seen[p] = true; cur = byId[p]; + } + return false; + }, + /* Drop zone of a pointer position within a row: top edge = before, bottom edge = after, + middle = into (sets only; for other rows the middle counts as "after"). */ + zone: function (ev, tr, isSet) { + let oe = ev.originalEvent || ev, r = tr[0].getBoundingClientRect(), y = (oe.clientY || 0) - r.top, h = r.height || 1; + if (y < h * 0.3) { return 'before'; } + if (y > h * 0.7 || !isSet) { return 'after'; } + return 'into'; + }, + clearDrop: () => { $('.setstruct .dropok, .setstruct .dropbefore, .setstruct .dropafter').rC('dropok dropbefore dropafter'); }, + /* One line of the structure table: drag handle, fold toggle, pos, type, qty, indented title, + order buttons (▲▼ among siblings) and level buttons (◀▶). */ + row: function (tbody, co, bid, itms, byId) { + let id = (co.id || '').toString(), lvl = parseInt(co.lvl || 0, 10) || 0, isSet = $inv.sets.isSet(co); + let parent = $inv.sets.parentOf(co, byId), folded = isSet && !!$inv.sets.collapsed[id]; + let tr = $$.tr(tbody, { draggable: 'true' }).aC('srow lvl' + lvl).tC('isset', isSet).tC('folded', folded).data({ id: id, bid: bid }); + if ($inv.sets.hiddenByCollapse(co, byId)) { tr.aC('hid'); } + $$.tdc('grip', tr).append(gi('move')).attr('title', $rct.mR); + $$.tdc('pos', tr).text(co.p != null ? co.p : ''); + let typ = $$.tdc('typ', tr); + if (isSet) { /* fold toggle sits in the type column, in front of "Set" */ + typ.append($$.sc('fold').attr('title', folded ? $ict.setsExpand : $ict.setsCollapse).append(gi(folded ? 'triangle-right' : 'triangle-bottom')).click((ev) => { + ev.stopPropagation(); + if (folded) { delete $inv.sets.collapsed[id]; } else { $inv.sets.collapsed[id] = true; } + let state = $inv.d.tbl().data('dstate'); if (state) { $inv.sets.render(state); } + })); + } + typ.append($$.s($inv.sets.typLbl(co))); + $$.tdc('qty', tr).text(co.q || ''); + let ttl = $$.tdc('ttl', tr).append($$.sc('ind').css('width', (lvl * 1.4).toString() + 'rem')).append($$.s($inv.sets.title(co))); + if (isSet) { + let n = itms.filter((x) => $inv.sets.parentOf(x, byId) === id).length; + ttl.append($$.sc('cnt', ' (' + n + ')')); + } + /* siblings (same parent, current order) drive the ▲▼ buttons; a preceding sibling set drives ▶ */ + let sibs = itms.filter((x) => $inv.sets.parentOf(x, byId) === parent), si = sibs.findIndex((x) => (x.id || '').toString() === id); + let hasSibSet = sibs.slice(0, si).some((x) => $inv.sets.isSet(x)); + let act = $$.tdc('act', tr); + let btn = (cls, glyph, title, enabled, delta) => $$.dc('ibtn ' + cls, act, { title: title }).append(gi(glyph)).tC('disabled', !enabled) + .click(() => { if (enabled) { $inv.sets.sync(delta); } }); + btn('up', 'arrow-up', $ict.setsUp, si > 0, { Target: 'set.move', Ref: id, Value: -1 }); + btn('down', 'arrow-down', $ict.setsDown, si >= 0 && si < sibs.length - 1, { Target: 'set.move', Ref: id, Value: 1 }); + btn('out', 'chevron-left', $ict.setsOut, lvl > 0, { Target: 'set.level', Ref: id, Value: -1 }); + btn('in', 'chevron-right', $ict.setsIn, hasSibSet, { Target: 'set.level', Ref: id, Value: 1 }); + + tr.on('dragstart', (ev) => { + $inv.sets.drag = { id: id, bid: bid }; + tr.aC('dragging'); + try { ev.originalEvent.dataTransfer.setData('text/plain', id); ev.originalEvent.dataTransfer.effectAllowed = 'move'; } catch (e) { /* ignore */ } + }).on('dragend', () => { tr.rC('dragging'); $inv.sets.drag = null; $inv.sets.clearDrop(); }); + /* Every row is a target: before / after (reorder, become the anchor's sibling — works on a + folded set too, its subtree travels along) and, for sets, into (become its last member). */ + let ok = () => $inv.sets.canDrop($inv.sets.drag, id, bid, byId); + tr.on('dragover', (ev) => { + if (!ok()) { return; } + ev.preventDefault(); + let z = $inv.sets.zone(ev, tr, isSet); + tr.rC('dropok dropbefore dropafter').aC(z === 'into' ? 'dropok' : (z === 'before' ? 'dropbefore' : 'dropafter')); + }).on('dragleave', () => { tr.rC('dropok dropbefore dropafter'); }).on('drop', (ev) => { + ev.preventDefault(); $inv.sets.clearDrop(); + let d = $inv.sets.drag; $inv.sets.drag = null; + if (!$inv.sets.canDrop(d, id, bid, byId)) { return; } + let z = $inv.sets.zone(ev, tr, isSet); + if (z === 'into') { $inv.sets.sync({ Target: 'set.assign', Ref: d.id, Value: { parent: id } }); } + else { $inv.sets.sync({ Target: 'set.move', Ref: d.id, Value: { anchor: id, where: z } }); } + }); + return tr; + }, + /* Drop target for "pull out of its set" (top level of the block). */ + rootZone: function (el, bid, byId) { + let ok = () => { let d = $inv.sets.drag; return !!d && d.bid === bid && !!byId[d.id] && $inv.sets.parentOf(byId[d.id], byId) !== ''; }; + el.on('dragover', (ev) => { if (ok()) { ev.preventDefault(); el.aC('dropok'); } }) + .on('dragleave', () => { el.rC('dropok'); }) + .on('drop', (ev) => { + ev.preventDefault(); el.rC('dropok'); + let d = $inv.sets.drag; $inv.sets.drag = null; + if (d && d.bid === bid && byId[d.id] && $inv.sets.parentOf(byId[d.id], byId) !== '') { + $inv.sets.sync({ Target: 'set.assign', Ref: d.id, Value: { parent: '' } }); + } + }); + return el; + }, + /* "New line" bar per block: type (Set/Text), target set (top level or any set), title. */ + createBar: function (blk, bid, itms, byId) { + let bar = $$.dc('screate', blk); + let typ = $('').attr({ name: 'typ' }).append([$$.opt('set', $ict.settyp.set), $$.opt('text', $ict.settyp.text)]); + let par = $('').attr({ name: 'parent' }).append($$.opt('', $ict.setsTop)); + itms.forEach((co) => { + if (!$inv.sets.isSet(co)) { return; } + let lvl = parseInt(co.lvl || 0, 10) || 0; + par.append($$.opt((co.id || '').toString(), ' '.repeat(lvl * 3) + $inv.sets.title(co))); + }); + let ttl = $('').attr({ name: 'title', placeholder: $ict.setsTitle, maxlength: 250 }); + let add = () => { + let t = (ttl.val() || '').toString().trim(); + if (t === '') { ttl.focus(); return; } + $inv.sets.sync({ Target: 'set.create', Ref: bid, Value: { typ: typ.val(), title: t, parent: par.val() || '' } }); + ttl.val(''); + }; + ttl.on('keydown', (ev) => { if (ev.key === 'Enter') { ev.preventDefault(); add(); } }); + bar.append([$$.sc('lbl', $ict.setsNew), typ, $$.sc('lbl', $ict.setsInto), par, ttl, $$.bbtn($ict.setsAdd, 'sadd').click(add)]); + return bar; + } +}; diff --git a/Fuchs/wwwroot/web/fis.inv.de.min.js b/Fuchs/wwwroot/web/fis.inv.de.min.js index b2adfe4..1afdd1f 100644 --- a/Fuchs/wwwroot/web/fis.inv.de.min.js +++ b/Fuchs/wwwroot/web/fis.inv.de.min.js @@ -1 +1 @@ -let $rct={mdl:"Aufträge",or:"offene Aufträge",orr:"offene Aufträge (4 W)",rn:"Auftragsnummer",iov:{all:"Auftragsübersicht (alle)","":"Auftragsübersicht"},wk:"Woche",nd:"Keine Daten gefunden.",h:"Uhr",rq1:"Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",rq2:"Auftragsdaten werden geladen",rq1f:"Die Auftragsdaten von MFR konnten nicht oder nicht schnell genug abgerufen werde.\nMöchten Sie mit den bestehenden Daten trotzdem weitermachen?",note1:"Im Bruttobetrag sind {0} Lohnkosten enthalten (netto {1}). Die darin enthaltene Umsatzsteuer beträgt {2}.",note2:"Bitte beachten Sie, nach §14 Abs. 1 Umsatzsteuergesetz ist diese Rechnung ein Zahlungsbeleg oder eine andere beweiskräftige Unterlage für 2 Jahre nach Ablauf des Kalenderjahres der Ausstellung dieser Rechnung aufzubewahren, soweit nicht aufgrund anderer gesetzlicher Regelungen andere ggf.längere Aufbewahrungsfristen gelten.",note3:"Privathaushalten erstattet das Finanzamt bis zu {0} des Arbeitslohns mit der nächsten Steuererklärung.",note4:"Für bereits erbrachte Arbeiten, Dienstleistungen, Materiallieferungen und getätigte Bestellvorgänge zum oben genannten Bauvorhaben, die sich aus dem mit Ihnen geschlossenen Vertrag ergeben, stellen wir Ihnen vertragsgemäß unsere Akontozahlung in Rechnung. Eine Endabrechnung erhalten Sie als Schlussrechnung nach Abschluss des gesamten Bauvorhabens. Das Ausführungsdatum entnehmen Sie bitte dem Schlusstext dieser Rechnung. Wir danken Ihnen herzlich für das entgegengebrachte Vertrauen und bitten Sie um kurzfristigen Ausgleich der Akontorechnung.",note13b:"Gem. §13b Umsatzsteuergesetz unterliegen Sie der Steuerschuldnerschaft des Leistungsempfängers zur Umsatzsteuer aus dieser Rechnung mit einem Steuersatz von 19%.",crI:"Rechnung erstellen",crII:"Abschlagsrechnung erstellen",dII:"Für eine Abschlagsrechnung darf nur ein Auftrag gewählt werden.",dnS:"Für eine Rechnung muss mindestens ein Auftrag gewählt werden.",inv:"Rechnung",invs:"Rechnungen",req:"Auftrag",provP:"Leistungszeitraum",provD:"Leistungsdatum",cP:"Position ändern",iRb:"Zeile darunter einfügen",dR:"Zeile löschen",sV:"USt festlegen",cD:"Löschen?",mR:"Zeile verschieben",svcPart:"Service-Anteil",vat:"Umsatzsteuer",combP:"Positionen zusammenfassen",toSetP:"Auf Setpreis umstellen (Positionen ohne Preis)",toSetPc:"Der Set-Preis wird auf die Summe seiner Positionen gesetzt und die Positionen selbst verlieren ihren Preis. Das ist nicht rückgängig zu machen (der Betrag kann danach nur noch manuell geändert werden). Fortfahren?",toSetMc:"Jeder Leistungsauftrag wird zu einer Set-Position mit der Summe als Preis zusammengefasst. Das ist nicht rückgängig zu machen (die Set-Preise können danach nur noch manuell geändert werden). Fortfahren?",iSum:"Zwischensumme",dtRel:"Freigegeben am: ",dtCr:"Erstellt am: ",rqV:"USt des Auftrags?",cthd:"wirklich aus-/einblenden ?",cst:{style:"currency",currency:"EUR"},sts:{IsWorkDone:"Arbeiten erledigt",Closed:"Auftrag geschlossen",SubcontractorPendingConfirmation:"Warten auf Bestätigung (Unterauftrag)",Scheduled:"Geplant",OfferIsRejected:"Angebot abgelehnt",OfferIsSend:"Offen (Angebot versandt)",CollaborationWaitingConfirmation:"Warten auf Bestätigung (Zusammenarbeit)",Released:"Freigegeben",OfferIsConfirmed:"Bestätigt",InProgress:"In Bearbeitung",ReadyForScheduling:"Zur Planung",Created:"Erstellt",Rejected:"Abgebrochen",Invoiced:"Rechnung gestellt","-":"-"},invHR:["Pos.","Menge","Artikelbezeichnung","VK","Summe"],frm:{invoiceaddress:"Adresse",loc:"Leistungsort / Lieferadresse",invoiceemail:"Email"}},$rcol={req:new fields_definition("Auftrag","Aufträge",[{name:"tags",label:"",type:"string",dfnc:function(e,t){""!==(e||"")&&($(this).aC("tags"),e.split(",").forEach((e=>{""!==e&&$(this).append($$.sc("tag "+e.replace(" ","_").replace("/","_").toLowerCase(),e))})))}},{name:"DateOfCreation",label:"Datum",type:"date",title:function(e){$(this).attr("title",$rct.dtCr+fdt(e.DateOfCreation).ne("-")+" \n"+$rct.dtRel+fdt(e.DateReleased).ne("-"))}},{name:"CustomerName",label:"Kunde (Firma)",type:"string"},{name:"Name",label:"Auftragsname",type:"string"},{name:"ExternalId",label:"Auftragsnummer",type:"string"},{name:"ParentExtenalId",label:"PAuftrag",type:"string"},{name:"InvoiceId",label:"RNummer",type:"string",dfnc:function(e,t){$(this).rwText(e," ").find("span").each((function(){$(this).aC("cla").click({id:$(this).text()},$inv.jdbn)}))}},{name:"State",label:"Status",type:"string"},{name:"WorkDoneAt",label:"Erledigt am",type:"date"},{name:"Description",label:"Beschreibung",type:"html"}]),itm:new fields_definition("Auftragsposition","Auftragspositionen",[{name:"NameOrNumber",label:"Bezeichnung",type:"string"},{name:"Type",label:"Typ",type:"select",required:!0,value:"Text",url:[{value:"Text",label:"Text"},{value:"Equipment",label:"Ausrüstung"},{value:"Material",label:"Material"},{value:"Service",label:"Arbeitsleistung"}],change:function(e){$req.quantChange.call(this,e)}},{name:"quantityhours",label:"Anzahl / Menge",type:"number",precision:"0.01",value:1,change:function(e){$inv.quantChange.call(this,e)}},{name:"UnitString",label:"Einheit",type:"select",url:["LFDM","Stck","Std.","QM","AW","Pauschal"],change:function(e){$inv.quantChange.call(this,e)}},{name:"net",label:"EinzelPreis netto",type:"number",precision:"0.01",value:0,change:function(e){$inv.quantChange.call(this,e)}},{name:"net_val",label:"GesamtPreis netto",type:"number",precision:"0.01",value:0},{name:"vat_val",label:"GesamtPreis USt",type:"number",precision:"0.01",value:0},{name:"svcnet_val",label:"Arbeitslohn netto",type:"number",precision:"0.01",value:0},{name:"svcvat_val",label:"Arbeitslohn USt",type:"number",precision:"0.01",value:0},{name:"net_pos",label:"Netto",type:"string"},{name:"bo_pos",label:"Brutto",type:"string"},{name:"vat",label:"USt",type:"string",value:"19,0%",change:function(e){$inv.quantChange.call(this,e)}},{name:"Note",label:"Details",type:"html",tinymce:!0}])},$ict={mdl:"Rechnungen",iov:{all:"Rechnungen (alle)","":"Rechnungen (nur fertige)","#d":"Rechnungen (nur Entwürfe)","#u":"Rechnungen (nur unbezahlt)","#r":"Rechnungen (nur angemahnt)","#a":"Rechnungen (nur Akonto)","#c":"Rechnungen (nur Storno)","#ru":"Rechnungen (nur angemahnt + unbez.)"},uba:", gesamter Zeitraum)",req:"Auftrag",inv:"Rechnung",rem:"Mahnung",in:"Rechnungsnummer",cc:"Kunde",wk:"Woche",nd:"Keine Daten gefunden.",dl:"Herunterladen",ed:"Bearbeiten",ced:"Bearbeitung fortsetzen",sItm:"Einzelheiten anzeigen",sPay:"Zahlungen anzeigen",cdI:"Entwurf der Rechnung löschen?",rel:"Neu Laden",relm:"Bitte laden Sie Liste manuell neu, um die Änderungen zu sehen.",dsp:"Rechnung anzeigen",storno:"Storno-Rechnung erstellen",credit:"Gutschrift erstellen",remd:"Mahnung erstellen",remdt:"Mahnung erstellen zur Rechnung {0}",remlst:"Mahnungen anzeigen",remdsp:"Mahnung anzeigen",remres:"Mahnung erneut senden",remresc:"Mahnung {0} wirklich erneut senden?",remresr:"Mahnung {0} wurde erfolgreich versandt.",setpyd:"Bezahlt markieren",cpyd:"Rechnung wirklich als bezahlt markieren?",setupd:"Bezahlt-Markierung aufheben",cupd:"Bezahlt-Markierung wirklich aufheben?",ivE:"Die Email-Adresse ist vermutlich nicht gültig.",ivEc:"\nMöchten Sie fortfahren?",pna:"Diese Seite ist in der Vorschau nicht verfügbar",tpe:"Die Anzahl von {0} Seiten wird aktuell nicht unterstützt",eis:"Der Rechnungsentwurf konnte nicht gespeichert werden.",iss:"Zwischenstand speichern.",p13b:"USt -> §13b",setm:"Set-Preisanzeige",setmo:{setprice:"Set mit Preis – Positionen ohne Preis",setonly:"Nur Set mit Preis – Positionen ausgeblendet"},ctp:"Ansprechpartner festlegen",mfr:"Von MFR neu abrufen",rq1:"Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",rq2:"Auftragsdaten werden geladen",iq1:"Rechnungsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",iq2:"Rechnungsdaten werden geladen",sis:"Rechnung als versandt markieren",srs:"Mahnung als versandt markieren",sisc:"Rechnung wirklich als versandt markieren?",srsc:"Mahnung wirklich als versandt markieren?",iSt:{dft:"Entwurf",uns:"nicht versandt",pyd:"bezahlt",cc:"storniert",op:"offen",due:"fällig",ovd:"überfällig",rem:"angemahnt"},rSt:["","Überfällig","2. Mahnung","3. Stufe"],pSt:{a:"Vollst.",p:"Teilz."},ivT:{i:"AbschlagsR.",f:"SchlussR",r:"Rechnung",c:"StornoR."},rovlh:"Übersicht der bisherigen Mahnungen",rovl:["Betreff","Betrag","Betrag gezahlt","fertiggestellt am"],remHR:["Rechnung","vom","Rechnungsbetrag","bereits bezahlt","noch offen"],translatePaymentTerm:e=>(e||"").replace("wd"," Werktagen").replace("d"," Tagen").replace("wk"," Wochen")||"10 Tagen",payi:{account:"Konto",name:"Zahler",text:"Verw.Zweck",InvoiceID:"Rechnung",amount:"Betrag",date:"Datum",manual:"Typ"}},$invcol={datev:new fields_definition("Rechnung","Rechnungen",[{name:"Umsatz (ohne Soll/Haben-Kz)",label:"Umsatz (ohne Soll/Haben-Kz)",type:"string"},{name:"vf",label:"vf",type:"string"},{name:"Soll/Haben-Kennzeichen",label:"Soll/Haben-Kennzeichen",type:"string"},{name:"Konto",label:"Konto",type:"string"},{name:"Gegenkonto",label:"Gegenkonto",type:"string"},{name:"BU-Schlüssel",label:"BU-Schlüssel",type:"string"},{name:"Belegdatum",label:"Belegdatum",type:"string"},{name:"Belegfeld 1",label:"Belegfeld 1",type:"string"},{name:"Belegfeld 2",label:"Belegfeld 2",type:"string"},{name:"Buchungstext",label:"Buchungstext",type:"string"}]),inv:new fields_definition("Rechnung","Rechnungen",[{name:"invstatus",label:"Status",type:"select",url:$ict.iSt},{name:"balance",label:"Umsatz",type:"string",dtype:"currency"},{name:"CustomerName",label:"Kunde",type:"string"},{name:"InvoiceId",label:"RNummer",type:"string"},{name:"InvoiceType",label:"Typ",type:"select",url:$ict.ivT},{name:"request",label:"Auftrag",type:"string",dtype:"num"},{name:"vat",label:"MwSt",type:"string",dtype:"num"},{name:"deb_cred",label:"Soll/Haben",type:"string"},{name:"customer",label:"Konto",type:"string",dtype:"num"},{name:"contra_account",label:"Gegenkonto",type:"string",dtype:"num"},{name:"Belegdatum",label:"Belegdatum",type:"date"},{name:"reminderstatus",label:"MahnStatus",type:"select",url:$ict.rSt},{name:"reminder",label:"# Mahnungen",type:"integer"},{name:"Buchungstext",label:"Buchungstext",type:"string"},{name:"Payment",label:"Zahlung",type:"string"}]),rem:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"amount",label:"Rechnungsbetrag",type:"number",precision:"0.01",value:1},{name:"amount_payed",label:"bereits bezahlt",type:"number",precision:"0.01",value:1}]),rem2:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"DocumentName",label:"Name",type:"string"},{name:"subject",label:"Betreff",type:"string"},{name:"DateSent",label:"Versanddatum",type:"date"},{name:"status",label:"Status",type:"string"},{name:"amount_open",label:"offener Betrag",type:"number",precision:"0.01"},{name:"InvoiceId",label:"RNummer",type:"string"}]),rid:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"type",label:"Typ",type:"select",url:[["f","einfache Zahlungserinnerung"],["m","Mahnung"],["l","letzte Mahnung"]],required:!0},{name:"level",label:"Stufe",type:"select",url:[["1","Stufe 1"],["2","Stufe 2"],["3","Stufe 3"],["4","Stufe 4"],["5","Stufe 5"],["6","Stufe 6"]],required:!0}]),ctp:new fields_definition("Ansprechpartner","Ansprechpartner",[{name:"name",label:"Name",type:"string"},{name:"email",label:"Email",type:"string"}])},gi=(e,t)=>$$.sc("glyphicon glyphicon-"+e).aC(t),$inv={init2:function(e,t){e=e||"inv",t=t||{},$ocms.getScript([],(function(){$inv.init3(e,t)}))},init3:async function(e,t){$fis.cf(!0);let n=$fis.lf(!0);$("#topbar").ocmsmenu([]),$("#activemodule").text($ict.mdl);let i=[(async()=>{await $fis.getAuth("fds_inv")>0&&($inv.prepLst(""),n.aC("fix"))})(),new Promise(((e,t)=>{$fis.prepAuth(["fds_reminder"])}))];await Promise.all(i)},prepLst:function(e){let t=new Date,n=$fis.lf(!0).ldng(1),i=new Date("2021-01-01");$fis.frm_list().IN((function(){}));let a=[];$.each($ict.iov,((e,t)=>{a.push({lbl:t,fnc:()=>{$inv.prepLst(e),n.aC("fix")}})})),$fis.lfm().ocmsmenu([{lbl:"Filter",itm:a}]);$$.i({placeholder:$ict.in}).appendTo($$.dc("mth ivn",n)).enterKey((function(e){let t=$(this),n=t.val()||"";e.stopPropagation(),t.parent().siblings().rC("selected"),n.length>3&&(t.parent().aC("selected"),$inv.renderinv("i:"+n,"s","all"),t.val(""))})),$$.i({placeholder:$ict.cc}).appendTo($$.dc("mth ivc",n)).enterKey((function(e){let t=$(this),n=t.val()||"";e.stopPropagation(),t.parent().siblings().rC("selected"),n.length>=3&&(t.parent().aC("selected"),$inv.renderinv("c:"+n,"s","all"),t.val(""))}));"#"===e.substr(0,1)&&$$.dc("mth extra",n).text($ict.iov[e].replace(")",$ict.uba)).click((function(t){let n=$(this);if(t.stopPropagation(),n.siblings().rC("selected"),!0===n.is(".selected")){n.toggleClass("selected");let t=fdt(new Date,"yy-MM-dd");$inv.renderinv(t,"a",e)}n.aC("selected")})),n.append("
");let r=$$.dc("mthl",n),d=t.getFullYear(),l=t.getMonth()+1;for(let t=i.getFullYear();t<=d;t++){let n=$$.dc("yr").prependTo(r).text($ict.iov[e]+" - "+t.toString()).toggleClass("selected",t===d);n.click({yr:t},(function(e){e.stopPropagation(),n.siblings().rC("selected"),n.aC("selected")}));let a=$$.dc("mfrm",n);for(let n=0;n<(t!==d?12:l);n++){i=new Date(t,n,1);let r=$$.dc("mth").prependTo(a).text($ict.iov[e]+" - "+fdt(i,"MMM yyyy"));if(r.click({yr:t,mt:n},(function(t){if(t.stopPropagation(),r.siblings().rC("selected"),!0===r.is(".selected")){r.toggleClass("selected");let n=fdt(new Date(t.data.yr,t.data.mt,1),"yy-MM-dd");$inv.renderinv(n,"m",e)}r.aC("selected")})),""===e){$$.dc("mthdl",r).append(gi("compressed","ico")).click({yr:t,mt:n},(function(e){e.stopPropagation();let t=fdt(new Date(e.data.yr,e.data.mt,1),"yy-MM-dd");$inv.downloadzip(t,"m")}))}let d=getMonday(i),l=new Date(i);l.setMonth(l.getMonth()+1),l.setDate(0),l=getMonday(l);let s=$$.dc("wfrm",r);for(;d<=l;){let t=$$.dc("wk",s).text(($ict.wk||"W")+" "+fdt(d,"dd.MM.yy"));t.click({rd:new Date(d)},(function(n){n.stopPropagation();let i=fdt(n.data.rd,"yy-MM-dd");$inv.renderinv(i,"w",e),r.siblings().rC("selected").find(".wk").rC("selected"),r.aC("selected").find(".wk").rC("selected"),t.aC("selected")})),$$.dc("wkdl",t).append(gi("compressed","ico")).click({rd:new Date(d)},(function(e){e.stopPropagation();let t=fdt(e.data.rd,"yy-MM-dd");$inv.downloadzip(t,"w")})),d.setDate(d.getDate()+7)}}}n.ldng(0)},rerenderinv:function(){let e=$("#contentframe .invfrm:first");if(e.length>0){let t=e.data("sets")||{};t.mode&&$inv.renderinv(t.tgt,t.mode,t.includes)}},renderinv:function(e,t,n){let i=$fis.frm_list(!0,!0).ldng(1),a=$$.dc("invfrm",i).aC("md"+t).data("sets",$.extend({},{tgt:e,mode:t,includes:n})),r=$fis.lf();$ocms.postXT({url:$ocms.url("inv/invl"),data:{mode:t,tgt:e,includes:n},success:i=>{r.rC("fix").aC("hd"),$$.dc("ovhd",a).text(i.admin.title);let d=$$.tblset({},a),l=$invcol.inv,s=$$.tr(d.hd);$$.th(s);$.each(l.fields||[],((e,t)=>{$$.th(s).text(t.label),"vat"===t.name&&$$.th(s)})),$.each(i.invoices||[],((s,c)=>{let o=$$.tr(d.bdy);o.click((function(){r.rC("fix").aC("hd"),o.toggleClass("selected").siblings().rC("selected").find("td.av").rC("av"),o.find("td.av").rC("av"),!0===o.is(".selected")?$inv.iMn(c):$inv.eM()}));let u=$$.td(o,{class:"raux"});c.hasFile?($$.dc("idl ilbtn",u,{title:$ict.dl+"\n"+c.DocumentName}).append(gi("save-file","ico")).click({id:c.Id},$inv.downloadinv),$$.dc("idl ilbtn",u,{title:$ict.dsp+"\n"+c.DocumentName}).append(gi("eye-open","ico")).click({id:c.Id,typ:"inv"},$inv.jdisp)):!1===c.isFinal&&!0===$fis.isAuth("fds_inv",2)&&$$.dc("idl ilbtn",u,{title:$ict.ed}).append(gi("edit","ico")).click({id:c.Id},$inv.doContInv),$$.dc("iitm ilbtn",u,{title:$ict.sItm}).append(gi("list","ico")).click({id:c.Id},$inv.showitm),$$.dc("iitm ilbtn",u,{title:$ict.sPay}).append(gi("euro","ico")).click({id:c.Id},$inv.showpay),$.each(l.fields||[],((r,d)=>{let l,s,u=$$.td(o).aC(d.dtype);switch("select"===(d.type||"")?u.text((d.url||{})[c[d.name]]||""):u.text(c[d.name]),d.name||""){case"vat":l=$$.sel().appendTo($$.td(o,{class:"vsel"})),s=(i.admin.ust_options||"19,0%;16,0%;0,0%").split(";"),$.each(s,((e,t)=>{$$.opt(t,t).appendTo(l)})),l.click((function(e){e.stopPropagation()})).val(c[d.name]).change().change({frm:a,tgt:e,mode:t,id:c.Id,td:u,includes:n},$inv.setvat),u.toggleClass("hl",c[d.name].substr(0,2)!==s[0].substr(0,2)).click((function(e){e.stopPropagation(),$(this).toggleClass("av")}));break;case"balance":u.aC("sh_"+(c.SollHaben||"").toLowerCase());break;case"invstatus":case"reminderstatus":u.aC(("invstatus"===d.name?"is_":"rs_")+c[d.name])}}))}))},complete:()=>{i.ldng(0)}})},setvat:function(e){let t=$(this),n=e.data||{};$ocms.postXT({url:$ocms.url("inv/setvat"),data:{id:n.id,val:t.val()},success:e=>{n.td.rC("av"),$inv.renderinv(n.tgt,n.mode,n.includes)}})},downloadzip:function(e,t){$(this).empty();window.open($ocms.url("inv/datevzip?mode="+t+"&tgt="+encodeURIComponent(e)),"_blank")},showitm:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$ocms.postXT({url:$ocms.url("inv/rqi"),data:{id:e.data.id},success:e=>{let t=$$.dc("rfrm");(e.requests||[]).length<1?t.text($ict.nd):$.each(e.requests||[],(function(e,n){let i=$$.dc("srq",t);$$.dc("nme",i).text(n.name);let a=$$.tblset({class:"if"},i);$.each(n.items||[],((e,t)=>{let n=$$.tr({id:"itm"+t.Id}).appendTo(a.bdy);$$.td(n).text(t.NameOrNumber),$$.td(n).text(t.Type),$$.td(n).aC("currency").text(t.net_pos),$$.td(n).aC("currency").text(t.bo_pos),$$.td(n).aC("num").text(t.vat)}))})),$ocms.dlg(t,{width:1e3})}})},showpay:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$ocms.postXT({url:$ocms.url("inv/pyi"),data:{id:e.data.id},success:e=>{let t=$$.dc("rfrm");if((e.payments||[]).length<1)t.text($ict.nd);else{let n=$$.tblset({class:"if"},t),i=$$.tr(n.hd);$.each(["date","account","name","text","InvoiceID","amount","manual"],((e,t)=>{$$.th(i,$ict.payi[t])})),$.each(e.payments,((e,t)=>{let i=$$.tr({id:"itm"+t.banking_uid}).appendTo(n.bdy);$$.td(i).aC("date").text(t.date),$$.td(i).text(t.account),$$.td(i).text(t.name),$$.td(i).text(t.text),$$.td(i).text(t.InvoiceID),$$.td(i).aC("currency").text(t.amount),$$.td(i).text(t.manual)}))}$ocms.dlg(t,{width:1e3,title:"Übersicht der Zahlungen"})}})},downloadinv:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&window.open($ocms.url("inv/rdoc?id="+e.data.id),"_blank")},doContInv:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$inv.cntInv({id:e.data.id})}},$$inv={init2:$inv.init2,auth:{}};export default $$inv;$inv.cInv=function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&!1!==$fis.isAuth("fds_inv",2)&&$inv.cInv2({id:e.data.id})},$inv.rMn=e=>{let t=[{lbl:$ict.req,itm:[]}];return!0===bool(e,!1)&&!0===$fis.isAuth("fds_inv",2)&&Array.prototype.push.apply(t[0].itm,[{lbl:$rct.crI,fnc:$inv.ccInv,data:{typ:"r"}},{lbl:$rct.crII,fnc:$inv.ccInv,data:{typ:"i"}}]),t.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(t)},$inv.iMnr=e=>{let t=booln(e.isFinal,!0),n=e.Id,i=booln(e.fds,!1),a=[{glyph:"glyphicon-menu-left",fnc:()=>{$fis.frm_edit().remove()}},{lbl:$ict.inv,itm:[]},{lbl:$ict.rem,itm:[]}];return!1===t&&!0===$fis.isAuth("fds_inv",2)?a[1].itm.push({lbl:$ict.ced,fnc:$inv.clCntInv}):!0===$fis.isAuth("fds_inv",1)&&a[1].itm.push({lbl:$ict.dsp,fnc:()=>$inv.disp(n,"inv")}),!0===i&&!0===t&&!0===$fis.isAuth("fds_inv",2)&&(a[1].itm.push({lbl:$ict.storno,fnc:()=>$inv.storno(n,i)}),a[1].itm.push({lbl:$ict.credit,fnc:()=>$inv.credit(n,i)})),!0===t&&!0===$fis.isAuth("fds_reminder",2)&&(a[2].itm.push({lbl:$ict.remd,fnc:()=>$inv.ccRem(n,e.InvoiceId)}),a[2].itm.push({lbl:$ict.remlst,fnc:()=>$inv.dspRem(n)})),!0===t&&!0===$fis.isAuth("fds_reminder",2)&&!1===booln(e.IsSent,!1)&&a[2].itm.push({lbl:$ict.srs,fnc:()=>$inv.srs(n)}),a.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(a)},$inv.iMn=e=>{let t=booln(e.isFinal,!0),n=e.Id,i=booln(e.fds,!1),a=[{glyph:"glyphicon-menu-left",fnc:()=>{$fis.frm_edit().remove()}},{lbl:$ict.inv,itm:[]},{lbl:$ict.rem,itm:[]}];return!1===t&&!0===$fis.isAuth("fds_inv",2)?a[1].itm.push({lbl:$ict.ced,fnc:()=>{$inv.cntInv({id:n})}}):!0===$fis.isAuth("fds_inv",1)&&a[1].itm.push({lbl:$ict.dsp,fnc:()=>$inv.disp(n,"inv")}),!0===$fis.isAuth("fds_inv",2)&&(a[1].itm.push({lbl:$ict.storno,fnc:()=>$inv.storno(n,i)}),a[1].itm.push({lbl:$ict.credit,fnc:()=>$inv.credit(n,i)})),!0===t&&!1===booln(e.IsPayed,!1)?(!0===$fis.isAuth("fds_reminder",2)&&a[2].itm.push({lbl:$ict.remd,fnc:()=>$inv.ccRem(n,e.InvoiceId)}),!0===$fis.isAuth("fds_inv",2)&&a[1].itm.push({lbl:$ict.setpyd,fnc:()=>$inv.setPyd(n)})):!0===t&&!0===booln(e.IsPayed,!1)&&"m"===(e.PaymentStatus||"")&&!0===$fis.isAuth("fds_inv",2)&&a[1].itm.push({lbl:$ict.setupd,fnc:()=>$inv.setUpd(n)}),!0===$fis.isAuth("fds_reminder",2)&&a[2].itm.push({lbl:$ict.remlst,fnc:()=>$inv.dspRem(n)}),!0===t&&!0===$fis.isAuth("fds_inv",2)&&!1===booln(e.IsSent,!1)&&a[1].itm.push({lbl:$ict.sis,fnc:()=>$inv.sis(n)}),!1===i&&a[1].itm.push({lbl:$ict.mfr,fnc:()=>$inv.mfrrel(n)}),$("#topbar").ocmsmenu(a)},$inv.emOpt=e=>"iss,p13b,ctp"+(""===((e||{}).setmode||"")?",setm":""),$inv.eM=(e,t,n)=>{let i=[];return!0!==booln(e,!1)&&!0!==booln(t,!1)||i.push({glyph:"glyphicon-menu-left",fnc:()=>{$fis.lf(!0),$fis.frm_edit().remove()}}),!0===(n||"").split(",").includes("iss")&&i.push({lbl:$ict.iss,fnc:$inv.ssave}),!0===(n||"").split(",").includes("ctp")&&i.push({lbl:$ict.ctp,fnc:$inv.sctp}),!0===(n||"").split(",").includes("p13b")&&i.push({lbl:$ict.p13b,fnc:$inv.sp13b}),!0===(n||"").split(",").includes("setm")&&i.push({lbl:$ict.setm,fnc:$inv.ssetmode}),!0===(n||"").split(",").includes("iss")&&(i.push({lbl:"Änderungshistorie",fnc:()=>$inv.d.history()}),i.push({lbl:"Änderungen verwerfen",fnc:()=>$inv.d.discard()})),!0===booln(e,!1)&&i.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(i)},$inv.d={tbl:()=>$("div.invoice_layout table.invi"),layout:()=>$("div.invoice_layout"),token:function(){return $inv.d.tbl().data("dtoken")||""},hashes:function(){let e=$inv.d.tbl().data("bai")||[],t={};return $.each(e,((e,n)=>{t[(n.Id||"").toString()]=JSON.stringify(n)})),t},seed:function(e){let t=$inv.d.layout();t.aC("freeze");let n=$ocms.postXT({url:$ocms.url("inv/dopen"),data:{payload:JSON.stringify(e)},success:e=>{$inv.d.tbl().data("dtoken",e.token).data("dver",e.version).data("dhashes",$inv.d.hashes()).data("dorder",$inv.d.order()),$fis.draft.bind(e.token,{onReady:()=>$inv.d.refresh(),onExpiring:e=>$inv.d.warnExpiry(e),onClosed:e=>$inv.d.closed(e)}),$inv.d.refresh()},error:()=>{t.rC("freeze")},complete:()=>{$inv.d.tbl().removeData("dseeding").removeData("dseedpromise")}});return $inv.d.tbl().data("dseedpromise",n),n},refresh:function(e){let t=$inv.d.token();""!==t&&$ocms.postXT({url:$ocms.url("inv/dstate"),data:{token:t},success:t=>{$inv.d.applyState(t),"function"==typeof e&&e(t)},error:e=>{e&&410===e.status&&$inv.d.closed("expired")},complete:()=>{$inv.d.layout().rC("freeze")}})},applyState:function(e){let t=$inv.d.tbl();if(t.length<1)return;let n=t.data("dver");"number"==typeof n&&"number"==typeof e.version&&e.version$$.dc("dnote").aC(e.style||"InvoiceNotes").text(e.text)))},applyItems:function(e,t){let n=e=>void 0===e?null:e;(t||[]).forEach((t=>{if(!t)return;let i=(t.Id||"").toString();if(""===i)return;let a=e.children("tbody").filter(((e,t)=>(($(t).data()||{}).Id||"").toString()===i)).first();if(a.length<1)return;let r=t.itm||[],d={},l=a.find("tr.title").first(),s=l.length?l:null;r.forEach((e=>{let t=(e.id||"").toString();if(""===t)return;d[t]=!0;let i=a.find("#itm"+t).first(),r=i.length<1;r&&(i=$$.tr(a,{id:"itm"+t,class:"itm"}));let l=i.data()||{},c=r||(l.Type||"")!==(e.typ||"")||l.net!==n(e.v)||l.net_val!==n(e.vt)||l.vat_val!==n(e.vv)||l.svcnet_val!==n(e.vs)||l.svcvat_val!==n(e.vsv)||(l.position||"")!==(null!=e.p?e.p:"");$.extend(l,{Id:t,Type:e.typ,net:n(e.v),net_val:n(e.vt),vat_val:n(e.vv),svcnet_val:n(e.vs),svcvat_val:n(e.vsv),vat:e.vat,position:null!=e.p?e.p:"",SetItmId:null!=e.SetItmId?e.SetItmId:null}),r&&$.extend(l,{htmltext:e.t,quantity:e.q}),i.data(l),s?i.prev()[0]!==s[0]&&i.insertAfter(s):a.children().first()[0]!==i[0]&&a.prepend(i),s=i,c&&$inv.rrw.call(i)})),a.find("tr.itm").each(((e,t)=>{let n=(($(t).data()||{}).Id||"").toString();""===n||d[n]||$(t).remove()}))}))},applySetDisplay:function(e,t){e.find("tr.itm").each(((e,n)=>{let i=$(n),a=((i.data()||{}).Id||"").toString();if(""===a||!Object.prototype.hasOwnProperty.call(t,a))return;let r=t[a]||{};i.tC("sethdr",bool(r.h,!1)),!1===bool(r.p,!0)&&i.find("td.currency").text("")}))},sync:function(e){let t=$inv.d.token();if(""===t){let t=$inv.d.tbl().data("dseedpromise");return t?$.when(t).then((()=>$inv.d.sync(e))):$.when()}return $inv.d.layout().aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dpatch"),data:{token:t,delta:JSON.stringify(e)},success:()=>{$inv.d.refresh()},error:e=>{$inv.d.layout().rC("freeze"),e&&410===e.status&&$inv.d.closed("expired")}})},order:function(){return($inv.d.tbl().data("bai")||[]).map((e=>(e.Id||"").toString()))},syncChanged:function(e){let t=e.data("bai")||[],n=e.data("dhashes")||{},i={},a=[],r=[],d=[];$.each(t,((e,t)=>{let r=(t.Id||"").toString(),d=JSON.stringify(t);i[r]=d,n[r]!==d&&a.push(t)})),$.each(n,(e=>{void 0===i[e]&&r.push(e)}));let l=$inv.d.order(),s=e.data("dorder")||[];return e.data("dhashes",i).data("dorder",l),a.forEach((e=>d.push($inv.d.sync({Target:"block.replace",Ref:(e.Id||"").toString(),Value:e})))),r.forEach((e=>d.push($inv.d.sync({Target:"block.remove",Ref:e})))),s.length===l.length&&s.slice().sort().join(",")===l.slice().sort().join(",")&&s.join(",")!==l.join(",")&&d.push($inv.d.sync({Target:"block.order",Value:l})),$.when.apply($,d).promise()},syncField:function(e,t){let n={invoicetitle:"title",invoiceaddress:"address",invoiceemail:"email",loc:"provisionlocation",provisionlocation:"provisionlocation",provisionperiod:"provisionperiod"}[e];n&&$inv.d.sync({Target:n,Value:t})},footer:function(e,t,n){let i=e.children("tfoot").empty();e.nextAll(".fnote").remove();let a=bool(n.p13b,!1),r=(e,t,n)=>$$.tdc("currency",$$.tr(i,{class:n||"tsum"}).append([$$.tdc("aux"),$$.td({colspan:4}).text(e)]),fnum(t,$rct.cst)),d=t=>$$.dc("fnote").insertAfter(e).rwText(t);r("Netto",t.total_net||0),!1===a&&$.each(t.vat||{},((e,t)=>r($rct.vat+" "+e+"%",t,"tvat"))),r("Summe",t.total_gross||0);let l=n.type||"";"i"===l?(d($rct.note2),d($rct.note4)):"c"===l?d($rct.note2):(d(string($rct.note3,[fnum(((t.service_net||0)+(t.service_vat||0))*(n.tax_servicerefund||0),$rct.cst)])).aC("ntax"),d($rct.note2),d(string($rct.note1,[fnum((t.service_net||0)+(t.service_vat||0),$rct.cst),fnum(t.service_net||0,$rct.cst),fnum(t.service_vat||0,$rct.cst)]))),!0===a&&d($rct.note13b)},validation:function(e){let t=$("div.invoice_layout");if(t.length<1)return;let n=t.children(".dvalidation");n.length<1&&(n=$$.dc("dvalidation"),t.prepend(n)),n.empty().tC("hidden",(e||[]).length<1),$.each(e||[],((e,t)=>$$.dc("dvmsg",n).aC(t.severity).text(t.message)))},preview:function(){let e=$inv.d.token();if(""===e)return;let t=$inv.d.layout(),n=($inv.d.tbl().data("new")||{}).invoiceemail||"";!1===$fis.ValidateEmail(n)&&!1===bool(confirm($ict.ivE+$ict.ivEc),!1)||(t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dpreview"),data:{token:e},success:n=>{t.rC("freeze");let i=$$.dc("imagecollection pdfpreview"),a=Math.round(.88*vh()),r=n.total;r>10&&$$.dc("note warn",i).text($ict.tpe),$.each(n.img||[],((e,t)=>{$$.dc("pdfp",i).append($$.img(t).css("max-height",(a-rpx(6)).toString()+"px"))}));for(let e=(n.img||[]).length+1;e<=r;e++)$$.dc("pdfp ph",i).append($$.dc("note",$ict.pna));$ocms.dlg(i,{size:[a,Math.round(.88*vw())],zindex:50,form:!1,button:$rct.crI,confirm:function(n){let i=$(this);t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dsave"),data:{token:e},success:e=>{$ocms.postXT({url:$ocms.url("req/sconf"),data:{id:e.invid},success:t=>{i.trigger("modal_close"),!0===t.hasFile&&window.open($ocms.url("req/idoc")+"?id="+e.invid,"_blank"),$inv.d.close(),$ocms.init("req"),$inv.rReload()},error:()=>{alert($t.f1),i.trigger("modal_close")},complete:()=>{t.rC("freeze")}})},error:()=>{t.rC("freeze"),alert($ict.eis)}})},cancel:function(e){confirm($ict.cdI)&&($inv.d.close(),$inv.rReload())}})},error:()=>{t.rC("freeze"),alert($ict.eis)}}))},save:function(){let e=$inv.d.token();if(""===e)return;let t=$inv.d.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dsave"),data:{token:e},success:e=>{$inv.d.tbl().data("invid",e.invid)},error:()=>{alert($ict.eis)},complete:()=>{t.rC("freeze")}})},history:function(){let e=$inv.d.token();""!==e&&$ocms.postXT({url:$ocms.url("inv/dhistory"),data:{token:e},success:e=>{let t=$$.dc("dhist");if((e.history||[]).length<1)$$.dc("note",t).text("Noch keine Änderungen erfasst.");else{let n=$$.tblset({class:"invtbl fullwidth"},t);$$.tr(n.hd).append([$$.th().text("Zeit"),$$.th().text("Feld"),$$.th().text("Alt"),$$.th().text("Neu")]),$.each(e.history,((e,t)=>$$.tr(n.bdy).append([$$.tdc("keep",fdt(t.timestamp)),$$.td().text(t.target),$$.td().text(t.oldValue),$$.td().text(t.newValue)])))}$ocms.dlg(t,{width:800,form:!1})}})},discard:function(){let e=$inv.d.tbl().data("invid")||"";""!==e?!1!==confirm("Alle Änderungen verwerfen und den zuletzt gespeicherten Stand neu laden?")&&($inv.d.close(),$inv.cntInv({id:e})):alert("Es wurde noch kein Zwischenstand gespeichert, der wiederhergestellt werden könnte.")},warnExpiry:function(e){let t=Math.max(1,Math.round((e||0)/60));$fis.notifications.push({severity:"info",title:"Entwurf läuft ab",message:"Der Rechnungsentwurf läuft in etwa "+t+" Minute(n) ab. Bitte zwischenspeichern, sonst gehen die Änderungen verloren."})},closed:function(e){let t=$inv.d.token();$inv.d.tbl().removeData("dtoken"),""!==t&&$fis.draft.release(t),$fis.frm_edit().remove(),$fis.lf(!0),$fis.notifications.push({severity:"error",title:"Entwurf geschlossen",message:"expired"===e?"Der Rechnungsentwurf ist wegen Inaktivität abgelaufen. Nicht gespeicherte Änderungen sind verloren.":"Der Rechnungsentwurf wurde geschlossen."});try{$inv.rReload()}catch(e){}},close:function(){let e=$inv.d.token();""!==e&&($ocms.postXT({url:$ocms.url("inv/dclose"),data:{token:e}}),$fis.draft.release(e)),$inv.d.tbl().removeData("dtoken")}},$inv.rd={tbl:()=>$("div.invoice_layout table.invi"),layout:()=>$("div.invoice_layout"),token:function(){return $inv.rd.tbl().data("rdtoken")||""},seed:function(e){let t=$inv.rd.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dopen"),data:{payload:JSON.stringify(e)},success:e=>{$inv.rd.tbl().data("rdtoken",e.token).data("rdver",e.version),$fis.draft.bind(e.token,{onReady:()=>$inv.rd.refresh(),onExpiring:e=>$inv.rd.warnExpiry(e),onClosed:e=>$inv.rd.closed(e)}),$inv.rd.refresh()},error:()=>{t.rC("freeze")}})},refresh:function(e){let t=$inv.rd.token();""!==t&&$ocms.postXT({url:$ocms.url("rem/dstate"),data:{token:t},success:t=>{$inv.rd.applyState(t),"function"==typeof e&&e(t)},error:e=>{e&&410===e.status&&$inv.rd.closed("expired")},complete:()=>{$inv.rd.layout().rC("freeze")}})},applyState:function(e){let t=$inv.rd.tbl();if(t.length<1)return;let n=t.data("rdver");"number"==typeof n&&"number"==typeof e.version&&e.version{let t=$$[0]("p").aC("dnote intro").rwText(e);a.length?t.insertBefore(a):t.appendTo(n)})),((e||{}).closing||[]).forEach((e=>$$[0]("p").aC("dnote closing").rwText(e).appendTo(n)))},sync:function(e){let t=$inv.rd.token();""!==t&&($inv.rd.layout().aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dpatch"),data:{token:t,delta:JSON.stringify(e)},success:()=>{$inv.rd.refresh()},error:e=>{$inv.rd.layout().rC("freeze"),e&&410===e.status&&$inv.rd.closed("expired")}}))},syncField:function(e,t){if(""===$inv.rd.token())return;let n={subject:"subject",invoiceaddress:"address",invoiceemail:"email",text:"text"}[e];n&&$inv.rd.sync({Target:n,Value:t})},syncAmount:function(e,t){""!==$inv.rd.token()&&($inv.rd.sync({Target:"amount",Value:(null!=e?e:0).toString()}),$inv.rd.sync({Target:"amount_payed",Value:(null!=t?t:0).toString()}))},footer:function(e,t){let n=e.children("tfoot").empty(),i=$$.tr(n,{class:"tsum"}).append([$$.tdc("aux"),$$.td({colspan:4}).text("Offener Betrag")]);$$.tdc("currency",i,fnum(t.amount_open||0,$rct.cst))},validation:function(e){let t=$inv.rd.layout();if(t.length<1)return;let n=t.children(".dvalidation");n.length<1&&(n=$$.dc("dvalidation"),t.prepend(n)),n.empty().tC("hidden",(e||[]).length<1),$.each(e||[],((e,t)=>$$.dc("dvmsg",n).aC(t.severity).text(t.message)))},preview:function(){let e=$inv.rd.token();if(""===e)return;let t=$inv.rd.layout(),n=($inv.rd.tbl().data("new")||{}).invoiceemail||"";!1===$fis.ValidateEmail(n)&&!1===bool(confirm($ict.ivE+$ict.ivEc),!1)||(t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dpreview"),data:{token:e},success:n=>{t.rC("freeze");let i=$$.dc("imagecollection pdfpreview"),a=Math.round(.88*vh());$.each(n.img||[],((e,t)=>{$$.dc("pdfp",i).append($$.img(t).css("max-height",(a-rpx(6)).toString()+"px"))})),$ocms.dlg(i,{size:[a,Math.round(.88*vw())],zindex:50,form:!1,button:$ict.remd,confirm:function(n){let i=$(this);t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dsave"),data:{token:e},success:e=>{$ocms.postXT({url:$ocms.url("rem/conf"),data:{id:e.remid},success:()=>{i.trigger("modal_close"),window.open($ocms.url("rem/idoc")+"?id="+e.remid,"_blank"),$inv.rd.close(),$ocms.init("req"),$inv.rReload()},error:()=>{alert($t.f1),i.trigger("modal_close")},complete:()=>{t.rC("freeze")}})},error:()=>{t.rC("freeze"),alert($t.f1)}})},cancel:function(e){confirm($ict.cdI)&&($inv.rd.close(),$inv.rReload())}})},error:()=>{t.rC("freeze"),alert($t.f1)}}))},save:function(){let e=$inv.rd.token();if(""===e)return;let t=$inv.rd.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dsave"),data:{token:e},success:e=>{$inv.rd.tbl().data("remid",e.remid)},error:()=>{alert($t.f1)},complete:()=>{t.rC("freeze")}})},history:function(){let e=$inv.rd.token();""!==e&&$ocms.postXT({url:$ocms.url("rem/dhistory"),data:{token:e},success:e=>{let t=$$.dc("dhist");if((e.history||[]).length<1)$$.dc("note",t).text("Noch keine Änderungen erfasst.");else{let n=$$.tblset({class:"invtbl fullwidth"},t);$$.tr(n.hd).append([$$.th().text("Zeit"),$$.th().text("Feld"),$$.th().text("Alt"),$$.th().text("Neu")]),$.each(e.history,((e,t)=>$$.tr(n.bdy).append([$$.tdc("keep",fdt(t.timestamp)),$$.td().text(t.target),$$.td().text(t.oldValue),$$.td().text(t.newValue)])))}$ocms.dlg(t,{width:800,form:!1})}})},warnExpiry:function(e){let t=Math.max(1,Math.round((e||0)/60));$fis.notifications.push({severity:"info",title:"Entwurf läuft ab",message:"Der Mahnentwurf läuft in etwa "+t+" Minute(n) ab. Bitte zwischenspeichern, sonst gehen die Änderungen verloren."})},closed:function(e){let t=$inv.rd.token();$inv.rd.tbl().removeData("rdtoken"),""!==t&&$fis.draft.release(t),$fis.frm_edit().remove(),$fis.lf(!0),$fis.notifications.push({severity:"error",title:"Entwurf geschlossen",message:"expired"===e?"Der Mahnentwurf ist wegen Inaktivität abgelaufen. Nicht gespeicherte Änderungen sind verloren.":"Der Mahnentwurf wurde geschlossen."});try{$inv.rReload()}catch(e){}},close:function(){let e=$inv.rd.token();""!==e&&($ocms.postXT({url:$ocms.url("rem/dclose"),data:{token:e}}),$fis.draft.release(e)),$inv.rd.tbl().removeData("rdtoken")}},$inv.cInv2=function(e){let t=$$.dc("rfrm").ldng(1),n=$ocms.dlg(t,{width:1e3});n&&n.ft.rwText($rct.rq1);let i=()=>{$ocms.postXT({url:$ocms.url("req/get"),timeout:60,data:{id:e.id,mode:"r"},success:t=>{t.admin=t.admin||{};let n=$fis.lf(!0).aC("fix").rC("hd");if($fis.frm_edit().IN(),$inv.eM(!0,!0),(t.requests||[]).length<1)n.aC("fix").text($rct.nd);else{$$.dc("lh",n,$rct.mdl);let i=$$.d(),a=$$.ul({class:"rql"}).data({search:e.id,parent:t.admin.parent}).appendTo(n),r={},d=$rcol.req.lbl();$.each(t.requests||[],(function(e,t){let n=$$.li({class:"cli rli"}).data($.extend({},t)).appendTo(a),l=$$.dc("lihd",n).addClass(t.state);!0===booln(t.open,!1)&&l.append($$.sc("cbox").click((()=>{n.tC("checked"),i.find("li").rC("checked"),!0===n.is(".checked")?$inv.rMn(t.open):$inv.eM(!0)}))),l.append([$$.sc("eid",t.ExternalId),$$.sc("nme",t.Name)]),$$.dc("lidt",n).append([$$.dc("rqs").append([$$.s(d.State+": "),$$.s($rct.sts[t.State||"-"])]),$$.dc("ivn").append([$$.s(d.InvoiceId+": "),$$.s(t.InvoiceId||"- -")]),$$.dc("wda").append([$$.s(d.WorkDoneAt+": "),$$.s(fdt(t.WorkDoneAt,"dd.MM.yyyy"))])]),r[t.Id]=n})),(t.inv||[]).length>0&&($$.dc("lh",n,$rct.invs),i=$$.ul({class:"ivl"}).appendTo(n),$.each(t.inv||[],((e,t)=>{let n=$$.li({class:"cli ili"}).data($.extend({},t)).appendTo(i),r=$$.dc("lihd",n).addClass(t.invstatus);!1===booln(t.isFinal,!0)?r.append($$.sc("cbox").click((()=>{""!==(t.Id||"")&&(n.tC("checked").siblings().rC("checked"),a.find("li").rC("checked"),!0===n.is(".checked")?$inv.iMnr(t):$inv.eM(!0))}))):["","dft"].indexOf(t.invstatus)<0&&r.append($$.sc("dli").click((function(){$inv.disp(t.Id,"inv")}))),r.append($$.sc("nme",t.DocumentName||t.Id)),$$.dc("lidt",n).append([$$.dc("wda").append([$$.s(fdt(t.DateCreated,"dd.MM.yyyy"))]),$$.d().text($ict.iSt[t.invstatus]||t.invstatus)])})))}},complete:()=>{n&&n.c.trigger("modal_close")}})};$ocms.postXT({url:$ocms.url("req/pget"),timeout:90,data:{id:e.id},success:e=>{n&&n.ft.rwText($rct.rq2),i()},error:()=>{confirm($rct.rq1f)?(n&&n.ft.rwText($rct.rq2),i()):n&&n.c.trigger("modal_close")}})},$inv.ccInv=function(e){let t=(e.data||{}).typ||"r",n=$fis.lf(),i=n.children("ul.rql"),a=i.data("parent"),r=[];if(i.find("li.rli.checked").each((function(){r.push($(this).data("Id"))})),r.length<1)return void alert($rct.dnS);if("i"===t&&r.length>1)return void alert($rct.dII);let d=$fis.frm_edit(),l=$$.dc("invoice_layout",d).append($$.dc("btn sprev").click($inv.sprev)),s=$fis.cf().width()>l.width()+n.width()+20;n.tC("fix",s).tC("hd",!s),$inv.eM(!1,!0);let c=$$.dc("rfrm").ldng(1),o=$ocms.dlg(c,{width:1e3});o.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("req/iget"),timeout:60,data:{id:a,mode:"ful",typ:t,sel:r.join(",")},success:e=>{let t=$$.dc("srq",l),n=$$.tblset({class:"invi"},t);n.bdy.remove(),n.ft=$$[0]("tfoot"),e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b")),n.tbl.data($.extend({new:{},sms:{},itm:{}},{admin:e.admin,companies:e.companies,locations:e.locations}));let i=$$.tr(n.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(i,e))),n.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.requests||[],(function(t,i){if(0!==(i.Id||0)){let t=$inv.worknotes(i);i.text="i"===e.admin.type?$rct.req+jine([i.ExternalId,i.Name],": ").eine(" ",""):jine([jine([fdt(i.WorkDoneAt,"dd.MM.yy"),i.ExternalId]," - "+$rct.req+" "),t.ne(i.Name)],": \n");let a=$$.tbody(n.tbl).data($.extend({},i));$inv.rendersrq.call(a)}}));let a=$$.tr($$.tbody(n.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(a),n.ft.appendTo(n.tbl);let r,d,s=e.admin||{},c=(e,t,i,a,r)=>{let d=$$.dc("inpfrm",l).aC(e).append("string"==typeof a?$$.dc("ahd",a):a>0?$$.dc("ahd",$rct.frm[i]):null),s=$$.dc("content",d).rwText(t);$$.dc("axf",d).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:s,nme:i,change:e=>{n.tbl.data("new")[i]=e}},r),$inv.eHtml)),n.tbl.data("new")[i]=t};c("tfrm",s.invoicetitle,"invoicetitle",0,null),c("adrfrm",s.invoiceaddress,"invoiceaddress",0,null),c("locfrm","","loc",1,{list:deepCopy(e.locations),lbl:"ref",property:"address"}),c("emailfrm",s.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",l).append($$.dc("content").text(s.sender)),s.provisionend&&(d=s.provisionstart?$rct.provP:$rct.provD,r=s.provisionstart?fdt(s.provisionstart,"dd.MM.yyyy")+" - "+fdt(s.provisionend,"dd.MM.yyyy"):fdt(s.provisionend,"dd.MM.yyyy")),c("admfrm",r,"provisionperiod",d,1),n.tbl.data("new").CustomValues=s.CustomValues||"",$$.dc("inpfrm ctpfrm",l).text(jObj(s.CustomValues,"contactName")),n.tbl.children("tbody").each($inv.bdysort),n.tbl.trigger("fds.inv"),$inv.eM(!1,!0,$inv.emOpt(s))},complete:()=>{o.c.trigger("modal_close")}})},$inv.ccStInv=function(e){let t=e.data||{},n=$fis.lf(),i=t.id,a=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sprev)),r=$fis.cf().width()>a.width()+n.width()+20;n.tC("fix",r).tC("hd",!r),$inv.eM(!1,!0);let d=$$.dc("rfrm").ldng(1),l=$ocms.dlg(d,{width:1e3});l.ft.rwText($ict.iq1),$ocms.postXT({url:$ocms.url("inv/pget"),timeout:90,data:{id:t.id},success:e=>{l&&l.ft.rwText($ict.iq2),$ocms.postXT({url:$ocms.url("inv/icget"),timeout:60,data:{id:i},success:e=>{let t=$$.dc("srq",a),n=$$.tblset({class:"invi"},t);n.bdy.remove(),n.ft=$$[0]("tfoot"),e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b")),n.tbl.data($.extend({new:{},sms:{},itm:{}},{admin:e.admin,companies:e.companies,locations:e.locations}));let i=$$.tr(n.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(i,e))),n.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.requests||[],(function(t,i){if(0!==(i.Id||0)){let t=$inv.worknotes(i);i.text="i"===e.admin.type?$rct.req+jine([i.ExternalId,i.Name],": ").eine(" ",""):jine([fdt(i.WorkDoneAt,"dd.MM.yy")+t.ne(i.Name)],": ");let a=$$.tbody(n.tbl).data($.extend({},i));$inv.rendersrq.call(a)}}));let r=$$.tr($$.tbody(n.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(r),n.ft.appendTo(n.tbl);let d,l,s=e.admin||{},c=(e,t,i,r,d)=>{let l=$$.dc("inpfrm",a).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),s=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:s,nme:i,change:e=>{n.tbl.data("new")[i]=e}},d),$inv.eHtml)),n.tbl.data("new")[i]=t};c("tfrm",s.invoicetitle,"invoicetitle",0,null),c("adrfrm",s.invoiceaddress,"invoiceaddress",0,null),c("locfrm",s.provisionlocation,"loc",1,{list:deepCopy(e.locations),lbl:"ref",property:"address"}),c("emailfrm",s.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",a).append($$.dc("content").text(s.sender)),s.provisionend&&(l=s.provisionstart?$rct.provP:$rct.provD,d=s.provisionstart?fdt(s.provisionstart,"dd.MM.yyyy")+" - "+fdt(s.provisionend,"dd.MM.yyyy"):fdt(s.provisionend,"dd.MM.yyyy")),c("admfrm",d,"provisionperiod",l,1),n.tbl.data("new").CustomValues=s.CustomValues||"",$$.dc("inpfrm ctpfrm",a).text(jObj(s.CustomValues,"contactName")),n.tbl.children("tbody").each($inv.bdysort),n.tbl.trigger("fds.inv")},complete:()=>{l.c.trigger("modal_close")}})},error:()=>{l&&l.c.trigger("modal_close")}})},$inv.clCntInv=function(e){let t=$fis.lf(!1),n=[];t.find("li.ili.checked").each((function(){n.push($(this).data("Id"))})),1===n.length&&$inv.cntInv({id:n[0]})},$inv.cntInv=function(e){e=e||{};$fis.lf(!1).rC("fix").aC("hd");let t=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sedit));$inv.eM(!1,!0);let n=$$.dc("rfrm").ldng(1),i=$ocms.dlg(n,{width:1e3});i.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("inv/get"),timeout:60,data:{id:e.id},success:e=>{e.admin=e.admin||{};let n=e.inv||{},i=$$.dc("srq",t),a=$$.tblset({class:"invi"},i);a.bdy.remove(),a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:n.Id,new:{},sms:{},itm:{},bai:[]},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(r,e))),a.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.req||[],(function(e,t){let n=$$.tbody(a.tbl).data($.extend({},t));$inv.rendersrq.call(n)}));let d=$$.tr($$.tbody(a.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(d),a.ft.appendTo(a.tbl);let l=(e,n,i,r,d)=>{let l=$$.dc("inpfrm",t).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),s=$$.dc("content",l).rwText(n);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:s,nme:i,change:e=>{a.tbl.data("new")[i]=e}},d),$inv.eHtml)),a.tbl.data("new")[i]=n};l("tfrm",n.InvoiceTitle,"invoicetitle",0,null),l("adrfrm",n.SendToAddress,"invoiceaddress",0,null),l("locfrm",n.ProvisionLocation,"loc",1,null),l("emailfrm",n.SendToEmail,"invoiceemail",0,null),$$.dc("sndfrm",t).append($$.dc("content").text(e.admin.sender)),l("admfrm",n.ProvisionPeriod,"provisionperiod",!0===(n.ProvisionPeriod||"").includes("-")?$rct.provP:$rct.provD,1),a.tbl.data("new").CustomValues=n.CustomValues||"",$$.dc("inpfrm ctpfrm",t).text(jObj(n.CustomValues,"contactName")),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv"),$inv.eM(!1,!0,$inv.emOpt(e.admin))},complete:()=>{i.c.trigger("modal_close")}})},$inv.cSt=function(e){e=e||{};let t=$fis.lf(),n=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sedit)),i=$fis.cf().width()>n.width()+t.width()+20;t.tC("fix",i).tC("hd",!i),$inv.eM(!1,!0);let a=$$.dc("rfrm").ldng(1),r=$ocms.dlg(a,{width:1e3});r.ft.rwText($ict.iq1),$ocms.postXT({url:$ocms.url("inv/pget"),data:{id:e.id},success:t=>{r&&r.ft.rwText($ict.iq2),$ocms.postXT({url:$ocms.url("inv/storno"),data:{id:e.id,mode:e.mode},success:e=>{e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b"));let t=e.inv||{},i=$$.dc("srq",n),a=$$.tblset({class:"invi"},i);a.bdy.remove(),a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:t.Id,new:{},sms:{},itm:{},bai:[]},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(r,e))),a.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.req||[],(function(e,t){let n=$$.tbody(a.tbl).data($.extend({},t));$inv.rendersrq.call(n)}));let d=$$.tr($$.tbody(a.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(d),a.ft.appendTo(a.tbl);let l=(e,t,i,r,d)=>{let l=$$.dc("inpfrm",n).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),s=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:s,nme:i,change:e=>{a.tbl.data("new")[i]=e}},d),$inv.eHtml)),a.tbl.data("new")[i]=t};l("tfrm",t.InvoiceTitle,"invoicetitle",0,null),l("adrfrm",t.SendToAddress,"invoiceaddress",0,null),l("locfrm",t.ProvisionLocation,"loc",1,null),l("emailfrm",t.SendToEmail,"invoiceemail",0,null),$$.dc("sndfrm",n).append($$.dc("content").text(e.admin.sender)),l("admfrm",t.ProvisionPeriod,"provisionperiod",!0===(t.ProvisionPeriod||"").includes("-")?$rct.provP:$rct.provD,1),a.tbl.data("new").CustomValues=t.CustomValues||"",$$.dc("inpfrm ctpfrm",n).text(jObj(t.CustomValues,"contactName")),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv")},complete:()=>{r.c.trigger("modal_close")}})},error:()=>{r&&r.c.trigger("modal_close")}})},$inv.adrCountries=[["DE","Deutschland"],["AT","Österreich"],["CH","Schweiz"],["FR","Frankreich"],["NL","Niederlande"],["BE","Belgien"],["LU","Luxemburg"],["IT","Italien"],["ES","Spanien"],["PL","Polen"],["DK","Dänemark"],["CZ","Tschechien"],["GB","Großbritannien"],["US","USA"]],$inv.composeAddress=function(e){let t=[],n=e=>{""!==(e=(""+(e||"")).trim())&&t.push(e)};n((e=e||{}).name),""!==(""+(e.contact||"")).trim()&&n("z.Hd. "+(""+e.contact).trim()),n(e.line2||e.addressLine2),n(e.street),n(((e.postalCode||"")+" "+(e.city||"")).trim());let i=(""+(e.countryCode||"")).toUpperCase();return""!==i&&"DE"!==i&&n(i),t.join("\n")},$inv.adrCurrent=function(e){let t=jObj((e.data("new")||{}).CustomValues,"sendToAddress");if("string"==typeof t&&"{"===t.trim().charAt(0))try{t=JSON.parse(t)}catch(e){t=null}if(t&&"object"==typeof t)return t;let n=(e.data("admin")||{}).invoiceaddressData;if("string"==typeof n&&"{"===n.trim().charAt(0))try{return JSON.parse(n)}catch(e){}else if(n&&"object"==typeof n)return n;return{}},$inv.eAddress=function(e){let t=$inv.d.tbl(),n=$inv.adrCurrent(t),i=[{name:"name",label:"Name / Firma",type:"text",value:n.name||"",required:!0},{name:"contact",label:"z.Hd. (optional)",type:"text",value:n.contact||""},{name:"street",label:"Straße + Nr.",type:"text",value:n.street||""},{name:"line2",label:"Adresszusatz (optional)",type:"text",value:n.line2||n.addressLine2||""},{name:"postalCode",label:"PLZ",type:"text",value:n.postalCode||""},{name:"city",label:"Ort",type:"text",value:n.city||""},{name:"countryCode",label:"Land",type:"select",url:$inv.adrCountries,value:n.countryCode||"DE"},{name:"vatId",label:"USt-IdNr. (nur bei Firmen)",type:"text",value:n.vatId||""},{name:"leitwegId",label:"Leitweg-ID (nur Behörden / B2G)",type:"text",value:n.leitwegId||""}],a=$$.dc("adr-conformity").append([$$.dc("hd").text("Hinweis zur DATEV-/eRechnungs-Konformität"),$$.dc("tx").text("Pflicht: Name, Straße, PLZ, Ort und Land. Die USt-IdNr. nur bei Firmen angeben — für eine Rechnung an eine Privatperson einfach leer lassen. Eine Leitweg-ID nur bei Behörden (B2G) — dann wird die Rechnung automatisch als XRechnung erzeugt.")]);$ocms.dlgform(i,{title:"Rechnungsempfänger",addcontent:a,success:function(t){let n={name:t.name||"",contact:t.contact||"",line2:t.line2||"",street:t.street||"",postalCode:t.postalCode||"",city:t.city||"",countryCode:t.countryCode||"DE",vatId:(""+(t.vatId||"")).trim(),leitwegId:(""+(t.leitwegId||"")).trim()},i=$inv.composeAddress(n);e.data&&e.data.t&&e.data.t.rwText(i),"function"==typeof(e.data||{}).change&&e.data.change(i),$inv.d.sync({Target:"address",Value:n})}})},$inv.provToIso=function(e){let t=(""+(e||"")).match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);return t?t[3]+"-"+("0"+t[2]).slice(-2)+"-"+("0"+t[1]).slice(-2):""},$inv.eProvisionPeriod=function(e){let t=(""+(e.data&&e.data.t?e.data.t.text():"")).trim().match(/\d{1,2}\.\d{1,2}\.\d{4}/g)||[],n=[{name:"von",label:"Leistungsdatum",type:"date",value:$inv.provToIso(t[0]||"")},{name:"bis",label:"Leistungszeitraum – bis (optional)",type:"date",value:$inv.provToIso(t[1]||"")}],i=$$.dc("adr-conformity").append([$$.dc("hd").text("Leistungsdatum / -zeitraum"),$$.dc("tx").text('Für ein einzelnes Datum nur „Leistungsdatum" ausfüllen; für einen Zeitraum zusätzlich das Bis-Datum. Nur strukturierte Datumsauswahl — für die E-Rechnung empfohlen.')]);$ocms.dlgform(n,{title:"Leistungsdatum / -zeitraum",addcontent:i,success:function(t){let n=(""+(t.von||"")).trim(),i=(""+(t.bis||"")).trim(),a=""===n?"":""===i||i===n?n:n+" - "+i;e.data&&e.data.t&&e.data.t.rwText(a),"function"==typeof(e.data||{}).change&&e.data.change(a),$inv.d.sync({Target:"provisionperiod",Value:a})}})},$inv.eHtml=function(e){if(e.data&&!(e.data instanceof jQuery)&&$inv.d&&"function"==typeof $inv.d.token&&""!==$inv.d.token()){if("invoiceaddress"===e.data.nme)return $inv.eAddress.call(this,e);if("provisionperiod"===e.data.nme)return $inv.eProvisionPeriod.call(this,e)}let t=$(this),n=e.data instanceof jQuery?e.data:e.data.t,i=["invoiceemail","provisionperiod","invoicetitle"].includes(e.data.nme),a=i?[{name:"txt",label:"Text",type:"text",value:n.text()}]:[{name:"txt",label:"Text",type:"html",value:n.html(),tinymce:!0,attr:{style:"height: 300px"}}],r=e.data.change||null,d={title:t.data("dialog")||"",success:function(t){i?n.text(t.txt||""):n.html(t.txt),"function"==typeof r&&r(t.txt),$inv.d.syncField(e.data.nme,i?t.txt||"":t.txt),$inv.rd.syncField(e.data.nme,i?t.txt||"":t.txt)},tinymce:{valid_elements:"br",hidemenu:!0,hidetoolbar:!0}};if(Array.isArray(e.data.list)){let t=$$.dc("lstfrm");$.each(e.data.list,((n,i)=>{let a=$$.dc("li",t).append(""!==(e.data.lbl||"")?$$.dc("lbl").rwText(i[e.data.lbl]):null);$$.dc("adr",a).rwText(i[e.data.property]).data("val",i[e.data.property]).click((function(){let e=$(this),t=e.closest(".modal-body").find(':input[name="txt"]');t.is(".tinymce")?tinymce.get(t.attr("id")).setContent($$.s().rwText(e.data("val")).html()):"TEXTAREA"===t.prop("tagName")?t.val(e.data("val")).change():t.rwText(e.data("val"))}))})),d.addcontent=t}$ocms.dlgform(a,d)},$inv.setVat=function(e){$(this);let t=e.data,n=prompt($rct.rqV);n&&(n=parseFloat(n.replace("%","")),n>1&&(n*=.01),!1===isNaN(n)&&(t.siblings(".itm").each((function(){$(this).data().vat=fnum(n,{style:"percent"}).replace(" ","")})),$inv.t_fds_inv()))},$inv.inRow=function(e){let t=$(this),n=e.data,i={},a=$rcol.itm.clone(["SortOrder","NameOrNumber","Type","quantityhours","UnitString","net","svcnet_val","svcvat_val","net_val","vat_val","vat","Note"]),r="N"+(65536*(1+Math.random())||0).toString(16).substr(6),d=$$.tr({id:"itm_"+r.toString(),class:"itm"});$ocms.dlgform(a,{title:t.data("dialog")||"",success:function(e){d.data($.extend({Id:r},i,e)),$inv.rrw.call(d),d.insertAfter(n),$inv.t_fds_inv()},typedvalues:!0})},$inv.eRow=function(e){let t=$(this),n=e.data,i=n.data()||{},a=["SortOrder","NameOrNumber","Type","quantityhours","UnitString","net","svcnet_val","svcvat_val","net_val","vat_val","vat","Note"];i.id||""!==(i.Type||"")||a.unshift("Type");let r=$rcol.itm.clone(a).applyValues(i);r.set("Type","hidden","type"),$inv.eRw.call(t,n,i,r)},$inv.eRw=function(e,t,n){let i=$(this);$ocms.dlgform(n,{title:i.data("dialog")||"",success:function(n){let i={};""===(t.Id||"")&&(i.Id="N"+(65536*(1+Math.random())||0).toString(16).substr(6),e.attr("id","itm_"+i.Id.toString())),i.quantity=((n.quantityhours||"").toString()+" "+(n.UnitString||"").toString()).trimEnd(),e.data($.extend({},t,n,i)),console.debug("eRw success %o",e.data()),$inv.rrw.call(e),$inv.t_fds_inv()},typedvalues:!0})},$inv.bdysort=(e,t)=>{$(t).Sortable({dragItem:!1,dragHandleClass:"ico",parentident:"tr",onend:()=>{$inv.t_fds_inv()}})},$inv.rrw=function(){let e=$(this),t=e.data(),n={},i=e.is(".placeholder"),a=e.is(".hidenote"),r=e=>$$.d().append(e).html(),d=[$$.dc("ibtn insb",{title:$rct.iRb}).append(gi("indent-left")).click(e,$inv.inRow)];!1===i&&(d.unshift($$.dc("ibtn edit",{title:$rct.cP}).append(gi("pencil")).click(e,$inv.eRow)),d.push($$.dc("ibtn del",{title:$rct.dR}).append(gi("trash")).click((function(t){confirm($rct.cD)&&(e.remove(),$inv.t_fds_inv())}))),"set"===(t.Type||"").toLowerCase()&&0===(t.net_val||0)&&d.push($$.dc("ibtn tosetp",{title:$rct.toSetP}).append(gi("scale")).click(e,$inv.toSetPrice)));let l=$$.dc("axf").append(d);!0===i?n={id:"",typ:"placeholder"}:!0===e.is(".itm.osum")?n={invrqid:t.InvRqId,id:"osum"+e.index(),typ:"osum",p:"",q:null,t:r(t.tbl.tbl),tt:null,v:null,vt:t.net_val,vs:t.svcnet_val,vat:t.vat,vv:t.vat_val,vsv:t.svcvat_val,det:!1}:(n={invrqid:t.InvRqId,id:t.Id||"",typ:t.Type||"other",p:"",q:null,t:"",tt:null,v:null,vt:t.net_val,vs:t.svcnet_val,vat:t.vat,vv:t.vat_val,vsv:t.svcvat_val,det:""!==(t.Note||"")&&!1===a,SetItmId:t.SetItmId||null},$$.dc("ibtn ico move",l,{title:$rct.mR}),n.p=t.position||t.SortOrder||"",""===n.id?n.t="":["Text","Title"].includes(n.typ)&&0===(t.net_val||0)?n.t=t.htmltext||("#"!==(t.NameOrNumber||"").substr(0,1)?r($$[0]("p").text(t.NameOrNumber)):"")+(t.Note||""):(n.tt=n.det?"":$$.s(t.Note||"").text(),n.q=t.quantity||(0!==(t.quantityhours||0)?fnum(t.quantityhours)+" "+(t.UnitString||""):""),n.t=t.htmltext||(n.det?r($$.s(t.NameOrNumber||""))+r($$.dc("desc").html(t.Note)):r($$.s(t.NameOrNumber||""))),n.v=t.net,n.vt=t.net_val)),""!==(t.Note||"")&&$$.dc("ibtn add",l).append(gi("object-align-left")).click((function(t){$inv.rrw.call(e.tC("hidenote"))}));let s=[$$.tdc("aux").append(l),$$.tdc("keep").text(n.p)];if(""===n.id)s.push($$.td(e,{colspan:4}).append(n.t));else{Array.prototype.push.apply(s,n.q?[$$.tdc("keep").text(n.q)]:[]);let e=e=>null==e?"":fnum(e,$rct.cst);Array.prototype.push.apply(s,[$$.tdc("txt",{colspan:n.q?1:2,title:n.tt}).append(n.t),$$.tdc("currency").text(e(n.v)),$$.tdc("currency inetval").text(e(n.vt)).attr("title",$rct.svcPart+": "+e(n.vs))])}e.empty().attr("class",i?"placeholder":"itm").aC(n.Typ).tC("sethdr","set"===(n.typ||"").toString().toLowerCase()).tC("hidenote",a).append(s),t.co=n},$inv.toSetPrice=function(e){let t=(e.data.data().Id||"").toString();""!==t&&!1!==confirm($rct.toSetPc)&&$.when($inv.t_fds_inv()).done((()=>{$inv.d.sync({Target:"item.setprice",Ref:t})}))},$inv.invSumUpdate=function(){let e=$(this),t=[],n=e.children("tbody");n.each(((e,n)=>{let i=$(n),a=i.data()||{},r=[],d=[],l=i.find("tr.itm"),s=0;i.tC("empty",l.length<1),l.each(((e,t)=>{let n=$(t).data()||{};r.push(n.co);let i=$inv.itemToContract(n),a=(n.SetItmId||"").toString();"set"!==i.type&&""!==a&&a!==i.id&&(i.setId=a),d.push(i),(void 0===n.SortOrder||null===n.SortOrder?-1:n.SortOrder)>-1&&(!1===["text","title"].includes((n.Type||"other").toLowerCase())&&s++,n.SortOrder=0,n.position=s,$inv.rrw.call(t))})),t.push({Id:a.Id,nme:a.Name,text:a.text,itm:r,items:d})}));let i=e.find("tbody:not(.empty)").length;n.find("tr.isum").tC("hidden",i<2),e.data("bai",t),""===(e.data("dtoken")||"")&&!1===bool(e.data("dseeding"),!1)&&null!=(e.data("admin")||{}).type&&(e.data("dseeding",!0),$inv.d.seed($.extend($inv.invcPayload(e.data()),{invid:e.data("invid")||""})))},$inv.worknotes=function(e){let t="";return e.steps.forEach(((e,n)=>{let i;try{i=JSON.parse(e.Data||{}).fields||[]}catch(e){console.debug(e),i=[]}!0!==Array.isArray(i||"")&&(i="object"==typeof i&&!0===Array.isArray(i.field||"")?i.field:[]),i.forEach(((e,n)=>{"Ausgeführte Arbeiten"===e.name&&(t=e.result||"")}))})),t},$inv.rendersrq=function(){let e=$(this).empty(),t=e.is(".onesum"),n=e.data(),i=$$.tr(e,{id:"srq"+n.Id}).aC("title nosort"),a=($rcol.itm.lbl(),$$.dc("axf").appendTo($$.tdc("aux",i)));$$.dc("ibtn osum",a,{title:$rct.combP}).append(gi("euro")).click((function(t){e.tC("onesum"),$inv.rendersrq.call(e),$inv.t_fds_inv()})),$$.dc("ibtn setvat",a,{title:$rct.sV}).append(gi("gbp")).click(i,$inv.setVat),$$.dc("ibtn insb",a,{title:$rct.iRb}).append(gi("indent-left")).click(i,$inv.inRow);let r,d=$$.sc("text",n.text),l=($$.td(i,{colspan:t?4:5}).append(d),["net_val","vat_val","svcnet_val","svcvat_val","net"]);if($$.dc("ibtn edit",a).data("dialog",$rcol.req.lbl().Name).append(gi("pencil")).click({t:d,change:e=>{n.text=e,$inv.t_fds_inv()}},$inv.eHtml),t&&($$.tdc("currency isumval",i),r={Id:n.Id.toString()+"_osum",net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0},r.tbl=$$.tblset({class:"stbl"})),$.each(n.items||[],((n,i)=>{let a,d={Id:i.Id,net_val:i.net_val||0,vat_val:i.vat_val||0,svcnet_val:0,svcvat_val:0,net:i.net||0,Note:i.Note||""};if("service"===i.Type.toLowerCase())d.svcnet_val=i.net_val||0,d.svcvat_val=i.vat_val||0;t?(a=$$.tr(r.tbl.bdy,{id:"itm"+i.Id,class:"sitm"}).aC(i.Type),"Text"===i.Type||"Title"===i.Type?$$.td(a,{colspan:2}).html(i.htmltext||i.Note):($$.tdc("keep",a).text(i.quantity||((i.quantityhours||0)>0?fnum(i.quantityhours)+(i.UnitString||"").eine(" ",""):"")),i.htmltext?$$.tdc("txt",a).html(i.htmltext):$$.tdc("txt",a).text(i.NameOrNumber).attr("title",i.Note)),$.each(l,((e,t)=>{r[t]+=d[t]})),a.data(d)):($.extend(d,i),a=$$.tr(e,{id:"itm"+i.Id,class:"itm"}),a.data(d),$inv.rrw.call(a))})),t){let t=$$.tr(e,{id:"itmsq"+n.Id,class:"itm osum"}).data(r);$inv.rrw.call(t)}else{let t=$$.tr(e).aC("isum nosort");$$.tdc("aux",t),$$.td(t,{colspan:4}).text($rct.iSum),$$.tdc("currency isumval",t)}},$inv.t_fds_inv=()=>{let e=$("div.invoice_layout table.invi");if(e.trigger("fds.inv"),""!==(e.data("dtoken")||""))return $inv.d.syncChanged(e);let t=e.data("dseedpromise");return t?$.when(t).then((()=>$inv.d.syncChanged(e))):$.when()},$inv.sedit=()=>{$inv.sprev(!0)},$inv.jdisp=function(e){e.stopPropagation(),e.data.id&&$inv.disp(e.data.id,e.data.typ||"")},$inv.disp=(e,t)=>{let n="";switch(t){case"inv":n="inv/rdoc";break;case"rem":n="rem/rdoc"}""!==n&&$ocms.postXT({url:$ocms.url(n),data:{id:e||"",typ:"img"},success:e=>{let t=$$.dc("imagecollection pdfpreview"),n=Math.round(.88*vh());e.id;$.each(e.img||[],(function(e,i){$$.dc("pdfp",t).append($$.img(i).css("max-height",(n-rpx(6)).toString()+"px"))})),$ocms.dlg(t,{size:[n,Math.round(.88*vw())],zindex_min:50,form:!1,exclusive:!1})}})},$inv.jdbn=function(e){$ocms.postXT({url:$ocms.url("inv/rdocn"),data:{name:e.data.id||"",typ:"img"},success:e=>{let t=$$.dc("imagecollection pdfpreview"),n=Math.round(.88*vh());e.id;$.each(e.img||[],(function(e,i){$$.dc("pdfp",t).append($$.img(i).css("max-height",(n-rpx(6)).toString()+"px"))})),$ocms.dlg(t,{size:[n,Math.round(.88*vw())],zindex:50,form:!1})}})},$inv.sp13b=()=>{var e=$("div.invoice_layout").find("table.invi"),t=e.data();t.admin.p13b=!0,!1===(t.inv.InvoiceOptions||"").split(",").includes("§13b")&&(t.inv.InvoiceOptions+=",§13b"),e.trigger("fds.inv"),$inv.d.sync({Target:"p13b",Value:t.admin.p13b})},$inv.itemToContract=function(e){let t=((e=e||{}).Type||"").toString().toLowerCase(),n=e=>e??null,i={id:(e.Id||"").toString(),type:t,title:"",desc:"",qty:"",price_net:"",total_net:n(e.net_val),vat:e.vat||""};var a;return e.co&&"osum"===e.co.typ?(i.desc=e.co.t||"",i.total_net=e.net_val||0):["text","title"].includes(t)&&0===(e.net_val||0)?(i.desc=e.htmltext||("#"!==(e.NameOrNumber||"").substr(0,1)?(a=$$[0]("p").text(e.NameOrNumber||""),$$.d().append(a).html()):"")+(e.Note||""),i.total_net=""):(e.htmltext?i.desc=e.htmltext:(i.title=e.NameOrNumber||"",i.desc=e.Note||""),i.qty=e.quantity||(0!==(e.quantityhours||0)?fnum(e.quantityhours)+(e.UnitString?" "+e.UnitString:""):""),i.price_net=n(e.net),i.total_net=n(e.net_val)),i},$inv.ssetmode=()=>{let e=$("div.invoice_layout").find("table.invi").data();e.admin=e.admin||{};let t,n=e.admin.setmode||"setprice",i=e=>$$.dc("btn",$ict.setmo[e]).tC("selected",n===e).click((()=>{t.c.trigger("modal_close"),$inv.setSetmode(e)})),a=$$.dc("choicefrm").append([i("setprice"),i("setonly")]);t=$ocms.dlg(a,{width:800})},$inv.setSetmode=e=>{if(!1===confirm($rct.toSetMc||$rct.toSetPc))return;let t="setonly"===e?"block.setonly":"block.setprice";$.when($inv.t_fds_inv()).done((()=>{$inv.d.sync({Target:t})}))},$inv.sctp=()=>{let e=$invcol.ctp;$ocms.dlgform(e,{title:$ict.ctp,success:function(e){var t=$("div.invoice_layout"),n=t.find("table.invi").data();let i={};void 0!==n.new&&"{"===(n.new.CustomValues||"").substr(0,1)&&(i=JSON.parse(n.inv.CustomValues)),i.contactName=e.name,i.contactEmail=e.email,n.new.CustomValues=JSON.stringify(i),t.find(".ctpfrm").text(ne(e.name,e.email)),$inv.d.sync({Target:"contact",Value:{name:e.name,email:e.email}})},typedvalues:!0})},$inv.invcPayload=function(e){e=e||{};let t=$.extend({},e.new),n=$.extend({},e.admin);return t.title=null!=t.invoicetitle?t.invoicetitle:t.title||"",t.provisionlocation=null!=t.loc?t.loc:t.provisionlocation||"",t.paymentterm=null!=n.paymentterms?n.paymentterms:t.paymentterm||"",n.customerid=null!=n.customerid?n.customerid:n.CustomerId,{admin:n,req:e.bai,new:t}},$inv.ssave=()=>{$inv.d.save()},$inv.sprev=e=>{$inv.d.preview()},$inv.rReload=()=>{try{let e=$("#listframe ul.rql:first").data();$inv.cInv2({id:e.search})}catch(e){}},$inv.quantChange=function(e){},$inv.storno=function(e,t){let n,i=$$.dc("choicefrm").append([$$.dc("btn","Storno ohne Details").click({id:e,mode:"simple"},(e=>{n.c.trigger("modal_close"),$inv.cSt(e.data)})),$$.dc("btn","Storno mit neuer Rechnung").click({id:e},(e=>{n.c.trigger("modal_close"),$inv.ccStInv(e)})),$$.dc("btn","Storno mit best. Rechnung").tC("inactive",!1===bool(t,!1)).click({id:e,mode:"copy"},(e=>{!0===bool(t,!1)&&(n.c.trigger("modal_close"),$inv.cSt(e.data))}))]);n=$ocms.dlg(i,{width:1e3})},$inv.credit=function(e,t){let n,i=$$.dc("choicefrm").append([$$.dc("btn","Gutschrift").click({id:e,mode:"credit"},(e=>{n.c.trigger("modal_close"),$inv.cSt(e.data)}))]);n=$ocms.dlg(i,{width:1e3})},$inv.setPyd=function(e){confirm($ict.cpyd)&&$ocms.postXT({url:$ocms.url("inv/setpyd"),timeout:60,data:{id:e},success:e=>{alert($ict.relm)},error:()=>{alert($t.f1)}})},$inv.setUpd=function(e){confirm($ict.cupd)&&$ocms.postXT({url:$ocms.url("inv/setupd"),timeout:60,data:{id:e},success:e=>{alert($ict.relm)},error:()=>{alert($t.f1)}})},$inv.resendRem=function(e){e.stopPropagation(),e.data.id&&confirm(string($ict.remresc,[e.data.name]))&&$ocms.postXT({url:$ocms.url("rem/resend"),timeout:60,data:{id:e.data.id},success:t=>{alert(string($ict.remresr,[e.data.name]))},error:()=>{alert($t.f1)}})},$inv.dspRem=function(e){let t=$$.dc("rfrm").ldng(1),n=$ocms.dlg(t,{width:1e3});n.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("inv/getrem"),timeout:60,data:{id:e,drafts:!1},success:e=>{n.ft.empty();let i=$$.tblset({class:"invtbl"},t.empty()),a=$invcol.rem2,r=$$.tr(i.hd);$$.th(r);$.each(a.fields||[],((e,t)=>{$$.th(r).text(t.label)}));let d=!1;$.each(e,((e,t)=>{d=!d;let n=$$.tr(i.bdy).tC("alt",d),r=$$.td(n);n.click((function(){n.tC("selected").siblings().rC("selected")})),!0===bool(t.hasFile,!1)&&($$.dc("idl ilbtn",r,{title:$ict.dl+"\n"+t.DocumentName}).append(gi("save-file","ico")).click({id:t.Id},$inv.downloadrem),$$.dc("idl ilbtn",r,{title:$ict.remdsp+"\n"+t.DocumentName}).append(gi("eye-open","ico")).click({id:t.Id,typ:"rem"},$inv.jdisp),$$.dc("idl ilbtn",r,{title:$ict.remres+"\n"+t.DocumentName}).append(gi("refresh","ico")).click({id:t.Id,typ:"rem",name:t.DocumentName},$inv.resendRem)),$.each(a.fields||[],((e,i)=>{let a=$$.td(n).aC(i.dtype),r=t[i.name];if("function"==typeof i.dfnc)i.dfnc.call(a,r,t);else switch(i.type||""){case"date":a.text(fdt(t[i.name],"dd.MM.yy"));break;case"datetime":a.text(fdt(t[i.name]));break;case"html":a.append($$.dc("ctw").html(r)),a.append($$.dc("ttip").html(r));break;default:a.text(t[i.name])}if("InvoiceId"===(i.name||""))a.aC("keep");switch(typeof i.title){case"function":i.title.call(a,t);break;case"string":a.attr("title",cs.title)}}))}))},error:()=>{t.empty(),n.ft.rwText($t.f1)},complete:()=>{t.ldng(0)}})},$inv.ccRem=function(e,t){$(this);$ocms.postXT({url:$ocms.url("rem/lrem"),timeout:60,data:{id:e},success:n=>{let i=$invcol.rid.clone();i.applyValues(n.ov);let a=$$.dc("ac"),r=$$.tblset({class:"fullgrid fullwidth"},a);if((n.lst||[]).length>0){$$.d({style:"margin: 1.5rem 0 1rem 0;font-size: 110%;text-decoration: underline;"}).prependTo(a).text($ict.rovlh);let e=$$.tr(r.hd);$ict.rovl.forEach(((t,n)=>$$.th(e,t))),$.each(n.lst,((e,t)=>{$$.tr(r.bdy).append([$$.tdc("keep",t.subject),$$.tdc("currency",fnum(t.amount,$rct.cst)),$$.tdc("currency",fnum(t.amount_payed,$rct.cst)),$$.tdc("keep",fdt(t.DateFinalized,"dd.MM.yy"))])}))}else $$.td($$.tr(r.bdy),$ict.nd);$ocms.dlgform(i,{addcontent:a,title:string($ict.remdt,[t||"?"]),success:function(t){$inv.ccRem_s2(e,t)},typedvalues:!0})}})},$inv.rRemRw=function(e){let t=$(this),n=e.rm||{};t.empty().data({invoiceid:n.invoiceid,invoicedate:n.invoicedate,amount:n.amount,amount_payed:n.amount_payed});let i=$$.dc("axf").append($$.dc("ibtn edit",{title:$rct.cP}).append(gi("pencil")).click(t,$inv.eRowR));t.append([$$.tdc("aux").append(i),$$.tdc("keep",n.invoiceid),$$.tdc("keep",fdt(n.invoicedate,"dd.MM.yy")),$$.tdc("currency",fnum(n.amount,$rct.cst)),$$.tdc("currency",fnum(n.amount_payed,$rct.cst)),$$.tdc("currency",fnum(n.amount-n.amount_payed,$rct.cst))])},$inv.eRowR=function(e){let t=$(this),n=e.data,i=n.data()||{},a=$invcol.rem.clone().applyValues(i);$ocms.dlgform(a,{title:t.data("dialog")||"",success:function(e){let i=t.closest("table"),a=i.data();$.extend(a.rm,e),i.data(a),$inv.rRemRw.call(n,a),$inv.rd.syncAmount(a.rm.amount,a.rm.amount_payed)},typedvalues:!0})},$inv.ccRem_s2=function(e,t){$fis.lf(!1).rC("fix").aC("hd");let n=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.rprev));$inv.eM(!1,!0);$$.dc("rfrm").ldng(1);$ocms.postXT({url:$ocms.url("rem/get"),timeout:60,data:$.extend({id:e},t),success:e=>{let t=e.rm||{},i=$$.dc("srq",n),a=$$.tblset({class:"invi"},i);a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:t.invid,new:{}},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$ict.remHR.forEach((e=>$$.th(r,e))),$inv.rRemRw.call($$.tr(a.bdy),a.tbl.data()),a.ft.appendTo(a.tbl);let d=(e,t,i,r,d)=>{let l=$$.dc("inpfrm",n).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),s=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:s,nme:i,change:e=>{a.tbl.data("new")[i]=e}},d),$inv.eHtml)),a.tbl.data("new")[i]=t};d("tfrm",t.subject,"subject",0,null),d("adrfrm",t.invoiceaddress,"invoiceaddress",0,null),d("emailfrm",t.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",n).append($$.dc("content").text(t.sender)),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv");let l=a.tbl.data("new");l.amount=t.amount,l.amount_payed=t.amount_payed,$inv.rd.seed({rem:{invid:t.invid,type:t.type,invoiceid:t.invoiceid,invoicedate:t.invoicedate},new:l})},complete:()=>{}})},$inv.rprev=()=>{$inv.rd.preview()},$inv.sis=e=>{confirm($ict.sisc)&&$ocms.postXT({url:$ocms.url("inv/sis"),data:{id:e||""},success:e=>{}})},$inv.srs=e=>{confirm($ict.srsc)&&$ocms.postXT({url:$ocms.url("rem/srs"),data:{id:e||""},success:e=>{}})},$inv.mfrrel=e=>{$("#contentframe").ldng(),$ocms.postXT({url:$ocms.url("inv/mfrrel"),data:{id:e||""},success:e=>{$inv.rerenderinv()},complete:()=>{$("#contentframe").ldng(0)}})}; \ No newline at end of file +let $rct={mdl:"Aufträge",or:"offene Aufträge",orr:"offene Aufträge (4 W)",rn:"Auftragsnummer",iov:{all:"Auftragsübersicht (alle)","":"Auftragsübersicht"},wk:"Woche",nd:"Keine Daten gefunden.",h:"Uhr",rq1:"Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",rq2:"Auftragsdaten werden geladen",rq1f:"Die Auftragsdaten von MFR konnten nicht oder nicht schnell genug abgerufen werde.\nMöchten Sie mit den bestehenden Daten trotzdem weitermachen?",note1:"Im Bruttobetrag sind {0} Lohnkosten enthalten (netto {1}). Die darin enthaltene Umsatzsteuer beträgt {2}.",note2:"Bitte beachten Sie, nach §14 Abs. 1 Umsatzsteuergesetz ist diese Rechnung ein Zahlungsbeleg oder eine andere beweiskräftige Unterlage für 2 Jahre nach Ablauf des Kalenderjahres der Ausstellung dieser Rechnung aufzubewahren, soweit nicht aufgrund anderer gesetzlicher Regelungen andere ggf.längere Aufbewahrungsfristen gelten.",note3:"Privathaushalten erstattet das Finanzamt bis zu {0} des Arbeitslohns mit der nächsten Steuererklärung.",note4:"Für bereits erbrachte Arbeiten, Dienstleistungen, Materiallieferungen und getätigte Bestellvorgänge zum oben genannten Bauvorhaben, die sich aus dem mit Ihnen geschlossenen Vertrag ergeben, stellen wir Ihnen vertragsgemäß unsere Akontozahlung in Rechnung. Eine Endabrechnung erhalten Sie als Schlussrechnung nach Abschluss des gesamten Bauvorhabens. Das Ausführungsdatum entnehmen Sie bitte dem Schlusstext dieser Rechnung. Wir danken Ihnen herzlich für das entgegengebrachte Vertrauen und bitten Sie um kurzfristigen Ausgleich der Akontorechnung.",note13b:"Gem. §13b Umsatzsteuergesetz unterliegen Sie der Steuerschuldnerschaft des Leistungsempfängers zur Umsatzsteuer aus dieser Rechnung mit einem Steuersatz von 19%.",crI:"Rechnung erstellen",crII:"Abschlagsrechnung erstellen",dII:"Für eine Abschlagsrechnung darf nur ein Auftrag gewählt werden.",dnS:"Für eine Rechnung muss mindestens ein Auftrag gewählt werden.",inv:"Rechnung",invs:"Rechnungen",req:"Auftrag",provP:"Leistungszeitraum",provD:"Leistungsdatum",cP:"Position ändern",iRb:"Zeile darunter einfügen",dR:"Zeile löschen",sV:"USt festlegen",cD:"Löschen?",mR:"Zeile verschieben",svcPart:"Service-Anteil",vat:"Umsatzsteuer",combP:"Positionen zusammenfassen",toSetP:"Auf Setpreis umstellen (Positionen ohne Preis)",toSetPc:"Der Set-Preis wird auf die Summe seiner Positionen gesetzt und die Positionen selbst verlieren ihren Preis. Das ist nicht rückgängig zu machen (der Betrag kann danach nur noch manuell geändert werden). Fortfahren?",toSetMc:"Jeder Leistungsauftrag wird zu einer Set-Position mit der Summe als Preis zusammengefasst. Das ist nicht rückgängig zu machen (die Set-Preise können danach nur noch manuell geändert werden). Fortfahren?",iSum:"Zwischensumme",dtRel:"Freigegeben am: ",dtCr:"Erstellt am: ",rqV:"USt des Auftrags?",cthd:"wirklich aus-/einblenden ?",cst:{style:"currency",currency:"EUR"},sts:{IsWorkDone:"Arbeiten erledigt",Closed:"Auftrag geschlossen",SubcontractorPendingConfirmation:"Warten auf Bestätigung (Unterauftrag)",Scheduled:"Geplant",OfferIsRejected:"Angebot abgelehnt",OfferIsSend:"Offen (Angebot versandt)",CollaborationWaitingConfirmation:"Warten auf Bestätigung (Zusammenarbeit)",Released:"Freigegeben",OfferIsConfirmed:"Bestätigt",InProgress:"In Bearbeitung",ReadyForScheduling:"Zur Planung",Created:"Erstellt",Rejected:"Abgebrochen",Invoiced:"Rechnung gestellt","-":"-"},invHR:["Pos.","Menge","Artikelbezeichnung","VK","Summe"],frm:{invoiceaddress:"Adresse",loc:"Leistungsort / Lieferadresse",invoiceemail:"Email",invoiceemailcc:"Zusatzempfänger (CC)"}},$rcol={req:new fields_definition("Auftrag","Aufträge",[{name:"tags",label:"",type:"string",dfnc:function(e,t){""!==(e||"")&&($(this).aC("tags"),e.split(",").forEach((e=>{""!==e&&$(this).append($$.sc("tag "+e.replace(" ","_").replace("/","_").toLowerCase(),e))})))}},{name:"DateOfCreation",label:"Datum",type:"date",title:function(e){$(this).attr("title",$rct.dtCr+fdt(e.DateOfCreation).ne("-")+" \n"+$rct.dtRel+fdt(e.DateReleased).ne("-"))}},{name:"CustomerName",label:"Kunde (Firma)",type:"string"},{name:"Name",label:"Auftragsname",type:"string"},{name:"ExternalId",label:"Auftragsnummer",type:"string"},{name:"ParentExtenalId",label:"PAuftrag",type:"string"},{name:"InvoiceId",label:"RNummer",type:"string",dfnc:function(e,t){$(this).rwText(e," ").find("span").each((function(){$(this).aC("cla").click({id:$(this).text()},$inv.jdbn)}))}},{name:"State",label:"Status",type:"string"},{name:"WorkDoneAt",label:"Erledigt am",type:"date"},{name:"Description",label:"Beschreibung",type:"html"}]),itm:new fields_definition("Auftragsposition","Auftragspositionen",[{name:"NameOrNumber",label:"Bezeichnung",type:"string"},{name:"Type",label:"Typ",type:"select",required:!0,value:"Text",url:[{value:"Text",label:"Text"},{value:"Equipment",label:"Ausrüstung"},{value:"Material",label:"Material"},{value:"Service",label:"Arbeitsleistung"}],change:function(e){$req.quantChange.call(this,e)}},{name:"quantityhours",label:"Anzahl / Menge",type:"number",precision:"0.01",value:1,change:function(e){$inv.quantChange.call(this,e)}},{name:"UnitString",label:"Einheit",type:"select",url:["LFDM","Stck","Std.","QM","AW","Pauschal"],change:function(e){$inv.quantChange.call(this,e)}},{name:"net",label:"EinzelPreis netto",type:"number",precision:"0.01",value:0,change:function(e){$inv.quantChange.call(this,e)}},{name:"net_val",label:"GesamtPreis netto",type:"number",precision:"0.01",value:0},{name:"vat_val",label:"GesamtPreis USt",type:"number",precision:"0.01",value:0},{name:"svcnet_val",label:"Arbeitslohn netto",type:"number",precision:"0.01",value:0},{name:"svcvat_val",label:"Arbeitslohn USt",type:"number",precision:"0.01",value:0},{name:"net_pos",label:"Netto",type:"string"},{name:"bo_pos",label:"Brutto",type:"string"},{name:"vat",label:"USt",type:"string",value:"19,0%",change:function(e){$inv.quantChange.call(this,e)}},{name:"Note",label:"Details",type:"html",tinymce:!0}])},$ict={mdl:"Rechnungen",iov:{all:"Rechnungen (alle)","":"Rechnungen (nur fertige)","#d":"Rechnungen (nur Entwürfe)","#u":"Rechnungen (nur unbezahlt)","#r":"Rechnungen (nur angemahnt)","#a":"Rechnungen (nur Akonto)","#c":"Rechnungen (nur Storno)","#ru":"Rechnungen (nur angemahnt + unbez.)"},uba:", gesamter Zeitraum)",req:"Auftrag",inv:"Rechnung",rem:"Mahnung",in:"Rechnungsnummer",cc:"Kunde",wk:"Woche",nd:"Keine Daten gefunden.",dl:"Herunterladen",ed:"Bearbeiten",ced:"Bearbeitung fortsetzen",sItm:"Einzelheiten anzeigen",sPay:"Zahlungen anzeigen",cdI:"Entwurf der Rechnung löschen?",rel:"Neu Laden",relm:"Bitte laden Sie Liste manuell neu, um die Änderungen zu sehen.",dsp:"Rechnung anzeigen",storno:"Storno-Rechnung erstellen",credit:"Gutschrift erstellen",remd:"Mahnung erstellen",remdt:"Mahnung erstellen zur Rechnung {0}",remlst:"Mahnungen anzeigen",remdsp:"Mahnung anzeigen",remres:"Mahnung erneut senden",remresc:"Mahnung {0} wirklich erneut senden?",remresr:"Mahnung {0} wurde erfolgreich versandt.",setpyd:"Bezahlt markieren",cpyd:"Rechnung wirklich als bezahlt markieren?",setupd:"Bezahlt-Markierung aufheben",cupd:"Bezahlt-Markierung wirklich aufheben?",ivE:"Die Email-Adresse ist vermutlich nicht gültig.",ivEc:"\nMöchten Sie fortfahren?",pna:"Diese Seite ist in der Vorschau nicht verfügbar",tpe:"Die Anzahl von {0} Seiten wird aktuell nicht unterstützt",eis:"Der Rechnungsentwurf konnte nicht gespeichert werden.",iss:"Zwischenstand speichern.",p13b:"USt -> §13b",setm:"Set-Preisanzeige",setmo:{setprice:"Set mit Preis – Positionen ohne Preis",setonly:"Nur Set mit Preis – Positionen ausgeblendet"},setst:"Set-Struktur bearbeiten",setsHint:'Reihenfolge: Zeile per Drag & Drop an den oberen/unteren Rand einer anderen Zeile ziehen (davor/danach) oder ▲▼ nutzen. Zuordnung: Zeile auf die Mitte eines Sets ziehen (hinein) bzw. auf die Auftragszeile oder "oberste Ebene" (aus dem Set lösen); ◀▶ ändert die Ebene. Sets lassen sich über das Dreieck zu-/aufklappen und auch zugeklappt verschieben – ihre Positionen wandern mit. Änderungen werden sofort übernommen.',setsUp:"Nach oben (innerhalb der Ebene)",setsDown:"Nach unten (innerhalb der Ebene)",setsCollapse:"Set zuklappen",setsExpand:"Set aufklappen",setsHR:["","Pos.","Typ","Menge","Bezeichnung","Reihenfolge / Ebene"],setsRoot:"Hierher ziehen = aus dem Set lösen (oberste Ebene)",setsOut:"Eine Ebene höher (aus dem Set lösen)",setsIn:"Eine Ebene tiefer (dem vorangehenden Set zuordnen)",setsNew:"Neu:",setsInto:"in",setsTop:"oberste Ebene",setsTitle:"Bezeichnung",setsAdd:"Hinzufügen",setsNoTitle:"(ohne Bezeichnung)",settyp:{set:"Set",text:"Text",title:"Text",material:"Material",service:"Arbeitsleistung",equipment:"Ausrüstung",other:"Position",osum:"Zusammenfassung",intinvoiceallocation:"Abschlag",intinvoiceallocationdiff:"Abschlag (offen)"},ctp:"Ansprechpartner festlegen",mfr:"Von MFR neu abrufen",rq1:"Auftragsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",rq2:"Auftragsdaten werden geladen",iq1:"Rechnungsdaten werden von MFR abgerufen.\nDer Vorgang kann bis zu 90Sek dauern.",iq2:"Rechnungsdaten werden geladen",sis:"Rechnung als versandt markieren",srs:"Mahnung als versandt markieren",sisc:"Rechnung wirklich als versandt markieren?",srsc:"Mahnung wirklich als versandt markieren?",iSt:{dft:"Entwurf",uns:"nicht versandt",pyd:"bezahlt",cc:"storniert",op:"offen",due:"fällig",ovd:"überfällig",rem:"angemahnt"},rSt:["","Überfällig","2. Mahnung","3. Stufe"],pSt:{a:"Vollst.",p:"Teilz."},ivT:{i:"AbschlagsR.",f:"SchlussR",r:"Rechnung",c:"StornoR."},rovlh:"Übersicht der bisherigen Mahnungen",rovl:["Betreff","Betrag","Betrag gezahlt","fertiggestellt am"],remHR:["Rechnung","vom","Rechnungsbetrag","bereits bezahlt","noch offen"],translatePaymentTerm:e=>(e||"").replace("wd"," Werktagen").replace("d"," Tagen").replace("wk"," Wochen")||"10 Tagen",payi:{account:"Konto",name:"Zahler",text:"Verw.Zweck",InvoiceID:"Rechnung",amount:"Betrag",date:"Datum",manual:"Typ"}},$invcol={datev:new fields_definition("Rechnung","Rechnungen",[{name:"Umsatz (ohne Soll/Haben-Kz)",label:"Umsatz (ohne Soll/Haben-Kz)",type:"string"},{name:"vf",label:"vf",type:"string"},{name:"Soll/Haben-Kennzeichen",label:"Soll/Haben-Kennzeichen",type:"string"},{name:"Konto",label:"Konto",type:"string"},{name:"Gegenkonto",label:"Gegenkonto",type:"string"},{name:"BU-Schlüssel",label:"BU-Schlüssel",type:"string"},{name:"Belegdatum",label:"Belegdatum",type:"string"},{name:"Belegfeld 1",label:"Belegfeld 1",type:"string"},{name:"Belegfeld 2",label:"Belegfeld 2",type:"string"},{name:"Buchungstext",label:"Buchungstext",type:"string"}]),inv:new fields_definition("Rechnung","Rechnungen",[{name:"invstatus",label:"Status",type:"select",url:$ict.iSt},{name:"balance",label:"Umsatz",type:"string",dtype:"currency"},{name:"CustomerName",label:"Kunde",type:"string"},{name:"InvoiceId",label:"RNummer",type:"string"},{name:"InvoiceType",label:"Typ",type:"select",url:$ict.ivT},{name:"request",label:"Auftrag",type:"string",dtype:"num"},{name:"vat",label:"MwSt",type:"string",dtype:"num"},{name:"deb_cred",label:"Soll/Haben",type:"string"},{name:"customer",label:"Konto",type:"string",dtype:"num"},{name:"contra_account",label:"Gegenkonto",type:"string",dtype:"num"},{name:"Belegdatum",label:"Belegdatum",type:"date"},{name:"reminderstatus",label:"MahnStatus",type:"select",url:$ict.rSt},{name:"reminder",label:"# Mahnungen",type:"integer"},{name:"Buchungstext",label:"Buchungstext",type:"string"},{name:"Payment",label:"Zahlung",type:"string"}]),rem:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"amount",label:"Rechnungsbetrag",type:"number",precision:"0.01",value:1},{name:"amount_payed",label:"bereits bezahlt",type:"number",precision:"0.01",value:1}]),rem2:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"DocumentName",label:"Name",type:"string"},{name:"subject",label:"Betreff",type:"string"},{name:"DateSent",label:"Versanddatum",type:"date"},{name:"status",label:"Status",type:"string"},{name:"amount_open",label:"offener Betrag",type:"number",precision:"0.01"},{name:"InvoiceId",label:"RNummer",type:"string"}]),rid:new fields_definition("Zahlungserinnerung","Zahlungserinnerung",[{name:"type",label:"Typ",type:"select",url:[["f","einfache Zahlungserinnerung"],["m","Mahnung"],["l","letzte Mahnung"]],required:!0},{name:"level",label:"Stufe",type:"select",url:[["1","Stufe 1"],["2","Stufe 2"],["3","Stufe 3"],["4","Stufe 4"],["5","Stufe 5"],["6","Stufe 6"]],required:!0}]),ctp:new fields_definition("Ansprechpartner","Ansprechpartner",[{name:"name",label:"Name",type:"string"},{name:"email",label:"Email",type:"string"}])},gi=(e,t)=>$$.sc("glyphicon glyphicon-"+e).aC(t),$inv={init2:function(e,t){e=e||"inv",t=t||{},$ocms.getScript([],(function(){$inv.init3(e,t)}))},init3:async function(e,t){$fis.cf(!0);let n=$fis.lf(!0);$("#topbar").ocmsmenu([]),$("#activemodule").text($ict.mdl);let i=[(async()=>{await $fis.getAuth("fds_inv")>0&&($inv.prepLst(""),n.aC("fix"))})(),new Promise(((e,t)=>{$fis.prepAuth(["fds_reminder"])}))];await Promise.all(i)},prepLst:function(e){let t=new Date,n=$fis.lf(!0).ldng(1),i=new Date("2021-01-01");$fis.frm_list().IN((function(){}));let a=[];$.each($ict.iov,((e,t)=>{a.push({lbl:t,fnc:()=>{$inv.prepLst(e),n.aC("fix")}})})),$fis.lfm().ocmsmenu([{lbl:"Filter",itm:a}]);$$.i({placeholder:$ict.in}).appendTo($$.dc("mth ivn",n)).enterKey((function(e){let t=$(this),n=t.val()||"";e.stopPropagation(),t.parent().siblings().rC("selected"),n.length>3&&(t.parent().aC("selected"),$inv.renderinv("i:"+n,"s","all"),t.val(""))})),$$.i({placeholder:$ict.cc}).appendTo($$.dc("mth ivc",n)).enterKey((function(e){let t=$(this),n=t.val()||"";e.stopPropagation(),t.parent().siblings().rC("selected"),n.length>=3&&(t.parent().aC("selected"),$inv.renderinv("c:"+n,"s","all"),t.val(""))}));"#"===e.substr(0,1)&&$$.dc("mth extra",n).text($ict.iov[e].replace(")",$ict.uba)).click((function(t){let n=$(this);if(t.stopPropagation(),n.siblings().rC("selected"),!0===n.is(".selected")){n.toggleClass("selected");let t=fdt(new Date,"yy-MM-dd");$inv.renderinv(t,"a",e)}n.aC("selected")})),n.append("
");let r=$$.dc("mthl",n),s=t.getFullYear(),l=t.getMonth()+1;for(let t=i.getFullYear();t<=s;t++){let n=$$.dc("yr").prependTo(r).text($ict.iov[e]+" - "+t.toString()).toggleClass("selected",t===s);n.click({yr:t},(function(e){e.stopPropagation(),n.siblings().rC("selected"),n.aC("selected")}));let a=$$.dc("mfrm",n);for(let n=0;n<(t!==s?12:l);n++){i=new Date(t,n,1);let r=$$.dc("mth").prependTo(a).text($ict.iov[e]+" - "+fdt(i,"MMM yyyy"));if(r.click({yr:t,mt:n},(function(t){if(t.stopPropagation(),r.siblings().rC("selected"),!0===r.is(".selected")){r.toggleClass("selected");let n=fdt(new Date(t.data.yr,t.data.mt,1),"yy-MM-dd");$inv.renderinv(n,"m",e)}r.aC("selected")})),""===e){$$.dc("mthdl",r).append(gi("compressed","ico")).click({yr:t,mt:n},(function(e){e.stopPropagation();let t=fdt(new Date(e.data.yr,e.data.mt,1),"yy-MM-dd");$inv.downloadzip(t,"m")}))}let s=getMonday(i),l=new Date(i);l.setMonth(l.getMonth()+1),l.setDate(0),l=getMonday(l);let d=$$.dc("wfrm",r);for(;s<=l;){let t=$$.dc("wk",d).text(($ict.wk||"W")+" "+fdt(s,"dd.MM.yy"));t.click({rd:new Date(s)},(function(n){n.stopPropagation();let i=fdt(n.data.rd,"yy-MM-dd");$inv.renderinv(i,"w",e),r.siblings().rC("selected").find(".wk").rC("selected"),r.aC("selected").find(".wk").rC("selected"),t.aC("selected")})),$$.dc("wkdl",t).append(gi("compressed","ico")).click({rd:new Date(s)},(function(e){e.stopPropagation();let t=fdt(e.data.rd,"yy-MM-dd");$inv.downloadzip(t,"w")})),s.setDate(s.getDate()+7)}}}n.ldng(0)},rerenderinv:function(){let e=$("#contentframe .invfrm:first");if(e.length>0){let t=e.data("sets")||{};t.mode&&$inv.renderinv(t.tgt,t.mode,t.includes)}},renderinv:function(e,t,n){let i=$fis.frm_list(!0,!0).ldng(1),a=$$.dc("invfrm",i).aC("md"+t).data("sets",$.extend({},{tgt:e,mode:t,includes:n})),r=$fis.lf();$ocms.postXT({url:$ocms.url("inv/invl"),data:{mode:t,tgt:e,includes:n},success:i=>{r.rC("fix").aC("hd"),$$.dc("ovhd",a).text(i.admin.title);let s=$$.tblset({},a),l=$invcol.inv,d=$$.tr(s.hd);$$.th(d);$.each(l.fields||[],((e,t)=>{$$.th(d).text(t.label),"vat"===t.name&&$$.th(d)})),$.each(i.invoices||[],((d,c)=>{let o=$$.tr(s.bdy);o.click((function(){r.rC("fix").aC("hd"),o.toggleClass("selected").siblings().rC("selected").find("td.av").rC("av"),o.find("td.av").rC("av"),!0===o.is(".selected")?$inv.iMn(c):$inv.eM()}));let u=$$.td(o,{class:"raux"});c.hasFile?($$.dc("idl ilbtn",u,{title:$ict.dl+"\n"+c.DocumentName}).append(gi("save-file","ico")).click({id:c.Id},$inv.downloadinv),$$.dc("idl ilbtn",u,{title:$ict.dsp+"\n"+c.DocumentName}).append(gi("eye-open","ico")).click({id:c.Id,typ:"inv"},$inv.jdisp)):!1===c.isFinal&&!0===$fis.isAuth("fds_inv",2)&&$$.dc("idl ilbtn",u,{title:$ict.ed}).append(gi("edit","ico")).click({id:c.Id},$inv.doContInv),$$.dc("iitm ilbtn",u,{title:$ict.sItm}).append(gi("list","ico")).click({id:c.Id},$inv.showitm),$$.dc("iitm ilbtn",u,{title:$ict.sPay}).append(gi("euro","ico")).click({id:c.Id},$inv.showpay),$.each(l.fields||[],((r,s)=>{let l,d,u=$$.td(o).aC(s.dtype);switch("select"===(s.type||"")?u.text((s.url||{})[c[s.name]]||""):u.text(c[s.name]),s.name||""){case"vat":l=$$.sel().appendTo($$.td(o,{class:"vsel"})),d=(i.admin.ust_options||"19,0%;16,0%;0,0%").split(";"),$.each(d,((e,t)=>{$$.opt(t,t).appendTo(l)})),l.click((function(e){e.stopPropagation()})).val(c[s.name]).change().change({frm:a,tgt:e,mode:t,id:c.Id,td:u,includes:n},$inv.setvat),u.toggleClass("hl",c[s.name].substr(0,2)!==d[0].substr(0,2)).click((function(e){e.stopPropagation(),$(this).toggleClass("av")}));break;case"balance":u.aC("sh_"+(c.SollHaben||"").toLowerCase());break;case"invstatus":case"reminderstatus":u.aC(("invstatus"===s.name?"is_":"rs_")+c[s.name])}}))}))},complete:()=>{i.ldng(0)}})},setvat:function(e){let t=$(this),n=e.data||{};$ocms.postXT({url:$ocms.url("inv/setvat"),data:{id:n.id,val:t.val()},success:e=>{n.td.rC("av"),$inv.renderinv(n.tgt,n.mode,n.includes)}})},downloadzip:function(e,t){$(this).empty();window.open($ocms.url("inv/datevzip?mode="+t+"&tgt="+encodeURIComponent(e)),"_blank")},showitm:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$ocms.postXT({url:$ocms.url("inv/rqi"),data:{id:e.data.id},success:e=>{let t=$$.dc("rfrm");(e.requests||[]).length<1?t.text($ict.nd):$.each(e.requests||[],(function(e,n){let i=$$.dc("srq",t);$$.dc("nme",i).text(n.name);let a=$$.tblset({class:"if"},i);$.each(n.items||[],((e,t)=>{let n=$$.tr({id:"itm"+t.Id}).appendTo(a.bdy);$$.td(n).text(t.NameOrNumber),$$.td(n).text(t.Type),$$.td(n).aC("currency").text(t.net_pos),$$.td(n).aC("currency").text(t.bo_pos),$$.td(n).aC("num").text(t.vat)}))})),$ocms.dlg(t,{width:1e3})}})},showpay:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$ocms.postXT({url:$ocms.url("inv/pyi"),data:{id:e.data.id},success:e=>{let t=$$.dc("rfrm");if((e.payments||[]).length<1)t.text($ict.nd);else{let n=$$.tblset({class:"if"},t),i=$$.tr(n.hd);$.each(["date","account","name","text","InvoiceID","amount","manual"],((e,t)=>{$$.th(i,$ict.payi[t])})),$.each(e.payments,((e,t)=>{let i=$$.tr({id:"itm"+t.banking_uid}).appendTo(n.bdy);$$.td(i).aC("date").text(t.date),$$.td(i).text(t.account),$$.td(i).text(t.name),$$.td(i).text(t.text),$$.td(i).text(t.InvoiceID),$$.td(i).aC("currency").text(t.amount),$$.td(i).text(t.manual)}))}$ocms.dlg(t,{width:1e3,title:"Übersicht der Zahlungen"})}})},downloadinv:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&window.open($ocms.url("inv/rdoc?id="+e.data.id),"_blank")},doContInv:function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&$inv.cntInv({id:e.data.id})}},$$inv={init2:$inv.init2,auth:{}};export default $$inv;$inv.cInv=function(e){let t=$(this).closest("tr");e.stopPropagation(),!1!==t.is(".selected")&&!1!==$fis.isAuth("fds_inv",2)&&$inv.cInv2({id:e.data.id})},$inv.rMn=e=>{let t=[{lbl:$ict.req,itm:[]}];return!0===bool(e,!1)&&!0===$fis.isAuth("fds_inv",2)&&Array.prototype.push.apply(t[0].itm,[{lbl:$rct.crI,fnc:$inv.ccInv,data:{typ:"r"}},{lbl:$rct.crII,fnc:$inv.ccInv,data:{typ:"i"}}]),t.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(t)},$inv.iMnr=e=>{let t=booln(e.isFinal,!0),n=e.Id,i=booln(e.fds,!1),a=[{glyph:"glyphicon-menu-left",fnc:()=>{$fis.frm_edit().remove()}},{lbl:$ict.inv,itm:[]},{lbl:$ict.rem,itm:[]}];return!1===t&&!0===$fis.isAuth("fds_inv",2)?a[1].itm.push({lbl:$ict.ced,fnc:$inv.clCntInv}):!0===$fis.isAuth("fds_inv",1)&&a[1].itm.push({lbl:$ict.dsp,fnc:()=>$inv.disp(n,"inv")}),!0===i&&!0===t&&!0===$fis.isAuth("fds_inv",2)&&(a[1].itm.push({lbl:$ict.storno,fnc:()=>$inv.storno(n,i)}),a[1].itm.push({lbl:$ict.credit,fnc:()=>$inv.credit(n,i)})),!0===t&&!0===$fis.isAuth("fds_reminder",2)&&(a[2].itm.push({lbl:$ict.remd,fnc:()=>$inv.ccRem(n,e.InvoiceId)}),a[2].itm.push({lbl:$ict.remlst,fnc:()=>$inv.dspRem(n)})),!0===t&&!0===$fis.isAuth("fds_reminder",2)&&!1===booln(e.IsSent,!1)&&a[2].itm.push({lbl:$ict.srs,fnc:()=>$inv.srs(n)}),a.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(a)},$inv.iMn=e=>{let t=booln(e.isFinal,!0),n=e.Id,i=booln(e.fds,!1),a=[{glyph:"glyphicon-menu-left",fnc:()=>{$fis.frm_edit().remove()}},{lbl:$ict.inv,itm:[]},{lbl:$ict.rem,itm:[]}];return!1===t&&!0===$fis.isAuth("fds_inv",2)?a[1].itm.push({lbl:$ict.ced,fnc:()=>{$inv.cntInv({id:n})}}):!0===$fis.isAuth("fds_inv",1)&&a[1].itm.push({lbl:$ict.dsp,fnc:()=>$inv.disp(n,"inv")}),!0===$fis.isAuth("fds_inv",2)&&(a[1].itm.push({lbl:$ict.storno,fnc:()=>$inv.storno(n,i)}),a[1].itm.push({lbl:$ict.credit,fnc:()=>$inv.credit(n,i)})),!0===t&&!1===booln(e.IsPayed,!1)?(!0===$fis.isAuth("fds_reminder",2)&&a[2].itm.push({lbl:$ict.remd,fnc:()=>$inv.ccRem(n,e.InvoiceId)}),!0===$fis.isAuth("fds_inv",2)&&a[1].itm.push({lbl:$ict.setpyd,fnc:()=>$inv.setPyd(n)})):!0===t&&!0===booln(e.IsPayed,!1)&&"m"===(e.PaymentStatus||"")&&!0===$fis.isAuth("fds_inv",2)&&a[1].itm.push({lbl:$ict.setupd,fnc:()=>$inv.setUpd(n)}),!0===$fis.isAuth("fds_reminder",2)&&a[2].itm.push({lbl:$ict.remlst,fnc:()=>$inv.dspRem(n)}),!0===t&&!0===$fis.isAuth("fds_inv",2)&&!1===booln(e.IsSent,!1)&&a[1].itm.push({lbl:$ict.sis,fnc:()=>$inv.sis(n)}),!1===i&&a[1].itm.push({lbl:$ict.mfr,fnc:()=>$inv.mfrrel(n)}),$("#topbar").ocmsmenu(a)},$inv.emOpt=e=>"iss,p13b,ctp,setst"+(""===((e||{}).setmode||"")?",setm":""),$inv.eM=(e,t,n)=>{let i=[];return!0!==booln(e,!1)&&!0!==booln(t,!1)||i.push({glyph:"glyphicon-menu-left",fnc:()=>{$fis.lf(!0),$fis.frm_edit().remove()}}),!0===(n||"").split(",").includes("iss")&&i.push({lbl:$ict.iss,fnc:$inv.ssave}),!0===(n||"").split(",").includes("ctp")&&i.push({lbl:$ict.ctp,fnc:$inv.sctp}),!0===(n||"").split(",").includes("p13b")&&i.push({lbl:$ict.p13b,fnc:$inv.sp13b}),!0===(n||"").split(",").includes("setm")&&i.push({lbl:$ict.setm,fnc:$inv.ssetmode}),!0===(n||"").split(",").includes("setst")&&i.push({lbl:$ict.setst,fnc:()=>$inv.sets.open()}),!0===(n||"").split(",").includes("iss")&&(i.push({lbl:"Änderungshistorie",fnc:()=>$inv.d.history()}),i.push({lbl:"Änderungen verwerfen",fnc:()=>$inv.d.discard()})),!0===booln(e,!1)&&i.push({lbl:$ict.rel,fnc:$inv.rReload}),$("#topbar").ocmsmenu(i)},$inv.d={tbl:()=>$("div.invoice_layout table.invi"),layout:()=>$("div.invoice_layout"),token:function(){return $inv.d.tbl().data("dtoken")||""},hashes:function(){let e=$inv.d.tbl().data("bai")||[],t={};return $.each(e,((e,n)=>{t[(n.Id||"").toString()]=JSON.stringify(n)})),t},seed:function(e){let t=$inv.d.layout();t.aC("freeze");let n=$ocms.postXT({url:$ocms.url("inv/dopen"),data:{payload:JSON.stringify(e)},success:e=>{$inv.d.tbl().data("dtoken",e.token).data("dver",e.version).data("dhashes",$inv.d.hashes()).data("dorder",$inv.d.order()),$fis.draft.bind(e.token,{onReady:()=>$inv.d.refresh(),onExpiring:e=>$inv.d.warnExpiry(e),onClosed:e=>$inv.d.closed(e)}),$inv.d.refresh()},error:()=>{t.rC("freeze")},complete:()=>{$inv.d.tbl().removeData("dseeding").removeData("dseedpromise")}});return $inv.d.tbl().data("dseedpromise",n),n},refresh:function(e){let t=$inv.d.token();""!==t&&$ocms.postXT({url:$ocms.url("inv/dstate"),data:{token:t},success:t=>{$inv.d.applyState(t),"function"==typeof e&&e(t)},error:e=>{e&&410===e.status&&$inv.d.closed("expired")},complete:()=>{$inv.d.layout().rC("freeze")}})},applyState:function(e){let t=$inv.d.tbl();if(t.length<1)return;let n=t.data("dver");"number"==typeof n&&"number"==typeof e.version&&e.version$$.dc("dnote").aC(e.style||"InvoiceNotes").text(e.text)))},applyItems:function(e,t){let n=e=>void 0===e?null:e;(t||[]).forEach((t=>{if(!t)return;let i=(t.Id||"").toString();if(""===i)return;let a=e.children("tbody").filter(((e,t)=>(($(t).data()||{}).Id||"").toString()===i)).first();if(a.length<1)return;let r=t.itm||[],s={},l=a.find("tr.title").first(),d=l.length?l:null;r.forEach((e=>{let t=(e.id||"").toString();if(""===t)return;s[t]=!0;let i=a.find("#itm"+t).first(),r=i.length<1;r&&(i=$$.tr(a,{id:"itm"+t,class:"itm"}));let l=i.data()||{},c=r||(l.Type||"")!==(e.typ||"")||l.net!==n(e.v)||l.net_val!==n(e.vt)||l.vat_val!==n(e.vv)||l.svcnet_val!==n(e.vs)||l.svcvat_val!==n(e.vsv)||(l.position||"")!==(null!=e.p?e.p:"")||(l.lvl||0)!==(e.lvl||0)||(l.SetItmId||null)!==(null!=e.SetItmId?e.SetItmId:null);$.extend(l,{Id:t,Type:e.typ,net:n(e.v),net_val:n(e.vt),vat_val:n(e.vv),svcnet_val:n(e.vs),svcvat_val:n(e.vsv),vat:e.vat,position:null!=e.p?e.p:"",SetItmId:null!=e.SetItmId?e.SetItmId:null,lvl:e.lvl||0}),r&&$.extend(l,{htmltext:e.t,quantity:e.q}),i.data(l),d?i.prev()[0]!==d[0]&&i.insertAfter(d):a.children().first()[0]!==i[0]&&a.prepend(i),d=i,c&&$inv.rrw.call(i)})),a.find("tr.itm").each(((e,t)=>{let n=(($(t).data()||{}).Id||"").toString();""===n||s[n]||$(t).remove()}))}))},applySetDisplay:function(e,t){e.find("tr.itm").each(((e,n)=>{let i=$(n),a=((i.data()||{}).Id||"").toString();if(""===a||!Object.prototype.hasOwnProperty.call(t,a))return;let r=t[a]||{};i.tC("sethdr",bool(r.h,!1)),!1===bool(r.p,!0)&&i.find("td.currency").text("")}))},sync:function(e){let t=$inv.d.token();if(""===t){let t=$inv.d.tbl().data("dseedpromise");return t?$.when(t).then((()=>$inv.d.sync(e))):$.when()}return $inv.d.layout().aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dpatch"),data:{token:t,delta:JSON.stringify(e)},success:()=>{$inv.d.refresh()},error:e=>{$inv.d.layout().rC("freeze"),e&&410===e.status&&$inv.d.closed("expired")}})},order:function(){return($inv.d.tbl().data("bai")||[]).map((e=>(e.Id||"").toString()))},syncChanged:function(e){let t=e.data("bai")||[],n=e.data("dhashes")||{},i={},a=[],r=[],s=[];$.each(t,((e,t)=>{let r=(t.Id||"").toString(),s=JSON.stringify(t);i[r]=s,n[r]!==s&&a.push(t)})),$.each(n,(e=>{void 0===i[e]&&r.push(e)}));let l=$inv.d.order(),d=e.data("dorder")||[];return e.data("dhashes",i).data("dorder",l),a.forEach((e=>s.push($inv.d.sync({Target:"block.replace",Ref:(e.Id||"").toString(),Value:e})))),r.forEach((e=>s.push($inv.d.sync({Target:"block.remove",Ref:e})))),d.length===l.length&&d.slice().sort().join(",")===l.slice().sort().join(",")&&d.join(",")!==l.join(",")&&s.push($inv.d.sync({Target:"block.order",Value:l})),$.when.apply($,s).promise()},syncField:function(e,t){let n={invoicetitle:"title",invoiceaddress:"address",invoiceemail:"email",invoiceemailcc:"emailcc",loc:"provisionlocation",provisionlocation:"provisionlocation",provisionperiod:"provisionperiod"}[e];n&&$inv.d.sync({Target:n,Value:t})},footer:function(e,t,n){let i=e.children("tfoot").empty();e.nextAll(".fnote").remove();let a=bool(n.p13b,!1),r=(e,t,n)=>$$.tdc("currency",$$.tr(i,{class:n||"tsum"}).append([$$.tdc("aux"),$$.td({colspan:4}).text(e)]),fnum(t,$rct.cst)),s=t=>$$.dc("fnote").insertAfter(e).rwText(t);r("Netto",t.total_net||0),!1===a&&$.each(t.vat||{},((e,t)=>r($rct.vat+" "+e+"%",t,"tvat"))),r("Summe",t.total_gross||0);let l=n.type||"";"i"===l?(s($rct.note2),s($rct.note4)):"c"===l?s($rct.note2):(s(string($rct.note3,[fnum(((t.service_net||0)+(t.service_vat||0))*(n.tax_servicerefund||0),$rct.cst)])).aC("ntax"),s($rct.note2),s(string($rct.note1,[fnum((t.service_net||0)+(t.service_vat||0),$rct.cst),fnum(t.service_net||0,$rct.cst),fnum(t.service_vat||0,$rct.cst)]))),!0===a&&s($rct.note13b)},validation:function(e){let t=$("div.invoice_layout");if(t.length<1)return;let n=t.children(".dvalidation");n.length<1&&(n=$$.dc("dvalidation"),t.prepend(n)),n.empty().tC("hidden",(e||[]).length<1),$.each(e||[],((e,t)=>$$.dc("dvmsg",n).aC(t.severity).text(t.message)))},preview:function(){let e=$inv.d.token();if(""===e)return;let t=$inv.d.layout(),n=($inv.d.tbl().data("new")||{}).invoiceemail||"";!1===$fis.ValidateEmail(n)&&!1===bool(confirm($ict.ivE+$ict.ivEc),!1)||(t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dpreview"),data:{token:e},success:n=>{t.rC("freeze");let i=$$.dc("imagecollection pdfpreview"),a=Math.round(.88*vh()),r=n.total;r>10&&$$.dc("note warn",i).text($ict.tpe),$.each(n.img||[],((e,t)=>{$$.dc("pdfp",i).append($$.img(t).css("max-height",(a-rpx(6)).toString()+"px"))}));for(let e=(n.img||[]).length+1;e<=r;e++)$$.dc("pdfp ph",i).append($$.dc("note",$ict.pna));$ocms.dlg(i,{size:[a,Math.round(.88*vw())],zindex:50,form:!1,button:$rct.crI,confirm:function(n){let i=$(this);t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dsave"),data:{token:e},success:e=>{$ocms.postXT({url:$ocms.url("req/sconf"),data:{id:e.invid},success:t=>{i.trigger("modal_close"),!0===t.hasFile&&window.open($ocms.url("req/idoc")+"?id="+e.invid,"_blank"),$inv.d.close(),$ocms.init("req"),$inv.rReload()},error:()=>{alert($t.f1),i.trigger("modal_close")},complete:()=>{t.rC("freeze")}})},error:()=>{t.rC("freeze"),alert($ict.eis)}})},cancel:function(e){confirm($ict.cdI)&&($inv.d.close(),$inv.rReload())}})},error:()=>{t.rC("freeze"),alert($ict.eis)}}))},save:function(){let e=$inv.d.token();if(""===e)return;let t=$inv.d.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("inv/dsave"),data:{token:e},success:e=>{$inv.d.tbl().data("invid",e.invid)},error:()=>{alert($ict.eis)},complete:()=>{t.rC("freeze")}})},history:function(){let e=$inv.d.token();""!==e&&$ocms.postXT({url:$ocms.url("inv/dhistory"),data:{token:e},success:e=>{let t=$$.dc("dhist");if((e.history||[]).length<1)$$.dc("note",t).text("Noch keine Änderungen erfasst.");else{let n=$$.tblset({class:"invtbl fullwidth"},t);$$.tr(n.hd).append([$$.th().text("Zeit"),$$.th().text("Feld"),$$.th().text("Alt"),$$.th().text("Neu")]),$.each(e.history,((e,t)=>$$.tr(n.bdy).append([$$.tdc("keep",fdt(t.timestamp)),$$.td().text(t.target),$$.td().text(t.oldValue),$$.td().text(t.newValue)])))}$ocms.dlg(t,{width:800,form:!1})}})},discard:function(){let e=$inv.d.tbl().data("invid")||"";""!==e?!1!==confirm("Alle Änderungen verwerfen und den zuletzt gespeicherten Stand neu laden?")&&($inv.d.close(),$inv.cntInv({id:e})):alert("Es wurde noch kein Zwischenstand gespeichert, der wiederhergestellt werden könnte.")},warnExpiry:function(e){let t=Math.max(1,Math.round((e||0)/60));$fis.notifications.push({severity:"info",title:"Entwurf läuft ab",message:"Der Rechnungsentwurf läuft in etwa "+t+" Minute(n) ab. Bitte zwischenspeichern, sonst gehen die Änderungen verloren."})},closed:function(e){let t=$inv.d.token();$inv.d.tbl().removeData("dtoken"),""!==t&&$fis.draft.release(t),$fis.frm_edit().remove(),$fis.lf(!0),$fis.notifications.push({severity:"error",title:"Entwurf geschlossen",message:"expired"===e?"Der Rechnungsentwurf ist wegen Inaktivität abgelaufen. Nicht gespeicherte Änderungen sind verloren.":"Der Rechnungsentwurf wurde geschlossen."});try{$inv.rReload()}catch(e){}},close:function(){let e=$inv.d.token();""!==e&&($ocms.postXT({url:$ocms.url("inv/dclose"),data:{token:e}}),$fis.draft.release(e)),$inv.d.tbl().removeData("dtoken")}},$inv.rd={tbl:()=>$("div.invoice_layout table.invi"),layout:()=>$("div.invoice_layout"),token:function(){return $inv.rd.tbl().data("rdtoken")||""},seed:function(e){let t=$inv.rd.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dopen"),data:{payload:JSON.stringify(e)},success:e=>{$inv.rd.tbl().data("rdtoken",e.token).data("rdver",e.version),$fis.draft.bind(e.token,{onReady:()=>$inv.rd.refresh(),onExpiring:e=>$inv.rd.warnExpiry(e),onClosed:e=>$inv.rd.closed(e)}),$inv.rd.refresh()},error:()=>{t.rC("freeze")}})},refresh:function(e){let t=$inv.rd.token();""!==t&&$ocms.postXT({url:$ocms.url("rem/dstate"),data:{token:t},success:t=>{$inv.rd.applyState(t),"function"==typeof e&&e(t)},error:e=>{e&&410===e.status&&$inv.rd.closed("expired")},complete:()=>{$inv.rd.layout().rC("freeze")}})},applyState:function(e){let t=$inv.rd.tbl();if(t.length<1)return;let n=t.data("rdver");"number"==typeof n&&"number"==typeof e.version&&e.version{let t=$$[0]("p").aC("dnote intro").rwText(e);a.length?t.insertBefore(a):t.appendTo(n)})),((e||{}).closing||[]).forEach((e=>$$[0]("p").aC("dnote closing").rwText(e).appendTo(n)))},sync:function(e){let t=$inv.rd.token();""!==t&&($inv.rd.layout().aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dpatch"),data:{token:t,delta:JSON.stringify(e)},success:()=>{$inv.rd.refresh()},error:e=>{$inv.rd.layout().rC("freeze"),e&&410===e.status&&$inv.rd.closed("expired")}}))},syncField:function(e,t){if(""===$inv.rd.token())return;let n={subject:"subject",invoiceaddress:"address",invoiceemail:"email",text:"text"}[e];n&&$inv.rd.sync({Target:n,Value:t})},syncAmount:function(e,t){""!==$inv.rd.token()&&($inv.rd.sync({Target:"amount",Value:(null!=e?e:0).toString()}),$inv.rd.sync({Target:"amount_payed",Value:(null!=t?t:0).toString()}))},footer:function(e,t){let n=e.children("tfoot").empty(),i=$$.tr(n,{class:"tsum"}).append([$$.tdc("aux"),$$.td({colspan:4}).text("Offener Betrag")]);$$.tdc("currency",i,fnum(t.amount_open||0,$rct.cst))},validation:function(e){let t=$inv.rd.layout();if(t.length<1)return;let n=t.children(".dvalidation");n.length<1&&(n=$$.dc("dvalidation"),t.prepend(n)),n.empty().tC("hidden",(e||[]).length<1),$.each(e||[],((e,t)=>$$.dc("dvmsg",n).aC(t.severity).text(t.message)))},preview:function(){let e=$inv.rd.token();if(""===e)return;let t=$inv.rd.layout(),n=($inv.rd.tbl().data("new")||{}).invoiceemail||"";!1===$fis.ValidateEmail(n)&&!1===bool(confirm($ict.ivE+$ict.ivEc),!1)||(t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dpreview"),data:{token:e},success:n=>{t.rC("freeze");let i=$$.dc("imagecollection pdfpreview"),a=Math.round(.88*vh());$.each(n.img||[],((e,t)=>{$$.dc("pdfp",i).append($$.img(t).css("max-height",(a-rpx(6)).toString()+"px"))})),$ocms.dlg(i,{size:[a,Math.round(.88*vw())],zindex:50,form:!1,button:$ict.remd,confirm:function(n){let i=$(this);t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dsave"),data:{token:e},success:e=>{$ocms.postXT({url:$ocms.url("rem/conf"),data:{id:e.remid},success:()=>{i.trigger("modal_close"),window.open($ocms.url("rem/idoc")+"?id="+e.remid,"_blank"),$inv.rd.close(),$ocms.init("req"),$inv.rReload()},error:()=>{alert($t.f1),i.trigger("modal_close")},complete:()=>{t.rC("freeze")}})},error:()=>{t.rC("freeze"),alert($t.f1)}})},cancel:function(e){confirm($ict.cdI)&&($inv.rd.close(),$inv.rReload())}})},error:()=>{t.rC("freeze"),alert($t.f1)}}))},save:function(){let e=$inv.rd.token();if(""===e)return;let t=$inv.rd.layout();t.aC("freeze"),$ocms.postXT({url:$ocms.url("rem/dsave"),data:{token:e},success:e=>{$inv.rd.tbl().data("remid",e.remid)},error:()=>{alert($t.f1)},complete:()=>{t.rC("freeze")}})},history:function(){let e=$inv.rd.token();""!==e&&$ocms.postXT({url:$ocms.url("rem/dhistory"),data:{token:e},success:e=>{let t=$$.dc("dhist");if((e.history||[]).length<1)$$.dc("note",t).text("Noch keine Änderungen erfasst.");else{let n=$$.tblset({class:"invtbl fullwidth"},t);$$.tr(n.hd).append([$$.th().text("Zeit"),$$.th().text("Feld"),$$.th().text("Alt"),$$.th().text("Neu")]),$.each(e.history,((e,t)=>$$.tr(n.bdy).append([$$.tdc("keep",fdt(t.timestamp)),$$.td().text(t.target),$$.td().text(t.oldValue),$$.td().text(t.newValue)])))}$ocms.dlg(t,{width:800,form:!1})}})},warnExpiry:function(e){let t=Math.max(1,Math.round((e||0)/60));$fis.notifications.push({severity:"info",title:"Entwurf läuft ab",message:"Der Mahnentwurf läuft in etwa "+t+" Minute(n) ab. Bitte zwischenspeichern, sonst gehen die Änderungen verloren."})},closed:function(e){let t=$inv.rd.token();$inv.rd.tbl().removeData("rdtoken"),""!==t&&$fis.draft.release(t),$fis.frm_edit().remove(),$fis.lf(!0),$fis.notifications.push({severity:"error",title:"Entwurf geschlossen",message:"expired"===e?"Der Mahnentwurf ist wegen Inaktivität abgelaufen. Nicht gespeicherte Änderungen sind verloren.":"Der Mahnentwurf wurde geschlossen."});try{$inv.rReload()}catch(e){}},close:function(){let e=$inv.rd.token();""!==e&&($ocms.postXT({url:$ocms.url("rem/dclose"),data:{token:e}}),$fis.draft.release(e)),$inv.rd.tbl().removeData("rdtoken")}},$inv.cInv2=function(e){let t=$$.dc("rfrm").ldng(1),n=$ocms.dlg(t,{width:1e3});n&&n.ft.rwText($rct.rq1);let i=()=>{$ocms.postXT({url:$ocms.url("req/get"),timeout:60,data:{id:e.id,mode:"r"},success:t=>{t.admin=t.admin||{};let n=$fis.lf(!0).aC("fix").rC("hd");if($fis.frm_edit().IN(),$inv.eM(!0,!0),(t.requests||[]).length<1)n.aC("fix").text($rct.nd);else{$$.dc("lh",n,$rct.mdl);let i=$$.d(),a=$$.ul({class:"rql"}).data({search:e.id,parent:t.admin.parent}).appendTo(n),r={},s=$rcol.req.lbl();$.each(t.requests||[],(function(e,t){let n=$$.li({class:"cli rli"}).data($.extend({},t)).appendTo(a),l=$$.dc("lihd",n).addClass(t.state);!0===booln(t.open,!1)&&l.append($$.sc("cbox").click((()=>{n.tC("checked"),i.find("li").rC("checked"),!0===n.is(".checked")?$inv.rMn(t.open):$inv.eM(!0)}))),l.append([$$.sc("eid",t.ExternalId),$$.sc("nme",t.Name)]),$$.dc("lidt",n).append([$$.dc("rqs").append([$$.s(s.State+": "),$$.s($rct.sts[t.State||"-"])]),$$.dc("ivn").append([$$.s(s.InvoiceId+": "),$$.s(t.InvoiceId||"- -")]),$$.dc("wda").append([$$.s(s.WorkDoneAt+": "),$$.s(fdt(t.WorkDoneAt,"dd.MM.yyyy"))])]),r[t.Id]=n})),(t.inv||[]).length>0&&($$.dc("lh",n,$rct.invs),i=$$.ul({class:"ivl"}).appendTo(n),$.each(t.inv||[],((e,t)=>{let n=$$.li({class:"cli ili"}).data($.extend({},t)).appendTo(i),r=$$.dc("lihd",n).addClass(t.invstatus);!1===booln(t.isFinal,!0)?r.append($$.sc("cbox").click((()=>{""!==(t.Id||"")&&(n.tC("checked").siblings().rC("checked"),a.find("li").rC("checked"),!0===n.is(".checked")?$inv.iMnr(t):$inv.eM(!0))}))):["","dft"].indexOf(t.invstatus)<0&&r.append($$.sc("dli").click((function(){$inv.disp(t.Id,"inv")}))),r.append($$.sc("nme",t.DocumentName||t.Id)),$$.dc("lidt",n).append([$$.dc("wda").append([$$.s(fdt(t.DateCreated,"dd.MM.yyyy"))]),$$.d().text($ict.iSt[t.invstatus]||t.invstatus)])})))}},complete:()=>{n&&n.c.trigger("modal_close")}})};$ocms.postXT({url:$ocms.url("req/pget"),timeout:90,data:{id:e.id},success:e=>{n&&n.ft.rwText($rct.rq2),i()},error:()=>{confirm($rct.rq1f)?(n&&n.ft.rwText($rct.rq2),i()):n&&n.c.trigger("modal_close")}})},$inv.ccInv=function(e){let t=(e.data||{}).typ||"r",n=$fis.lf(),i=n.children("ul.rql"),a=i.data("parent"),r=[];if(i.find("li.rli.checked").each((function(){r.push($(this).data("Id"))})),r.length<1)return void alert($rct.dnS);if("i"===t&&r.length>1)return void alert($rct.dII);let s=$fis.frm_edit(),l=$$.dc("invoice_layout",s).append($$.dc("btn sprev").click($inv.sprev)),d=$fis.cf().width()>l.width()+n.width()+20;n.tC("fix",d).tC("hd",!d),$inv.eM(!1,!0);let c=$$.dc("rfrm").ldng(1),o=$ocms.dlg(c,{width:1e3});o.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("req/iget"),timeout:60,data:{id:a,mode:"ful",typ:t,sel:r.join(",")},success:e=>{let t=$$.dc("srq",l),n=$$.tblset({class:"invi"},t);n.bdy.remove(),n.ft=$$[0]("tfoot"),e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b")),n.tbl.data($.extend({new:{},sms:{},itm:{}},{admin:e.admin,companies:e.companies,locations:e.locations}));let i=$$.tr(n.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(i,e))),n.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.requests||[],(function(t,i){if(0!==(i.Id||0)){let t=$inv.worknotes(i);i.text="i"===e.admin.type?$rct.req+jine([i.ExternalId,i.Name],": ").eine(" ",""):jine([jine([fdt(i.WorkDoneAt,"dd.MM.yy"),i.ExternalId]," - "+$rct.req+" "),t.ne(i.Name)],": \n");let a=$$.tbody(n.tbl).data($.extend({},i));$inv.rendersrq.call(a)}}));let a=$$.tr($$.tbody(n.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(a),n.ft.appendTo(n.tbl);let r,s,d=e.admin||{},c=(e,t,i,a,r)=>{let s=$$.dc("inpfrm",l).aC(e).append("string"==typeof a?$$.dc("ahd",a):a>0?$$.dc("ahd",$rct.frm[i]):null),d=$$.dc("content",s).rwText(t);$$.dc("axf",s).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:d,nme:i,change:e=>{n.tbl.data("new")[i]=e}},r),$inv.eHtml)),n.tbl.data("new")[i]=t};c("tfrm",d.invoicetitle,"invoicetitle",0,null),c("adrfrm",d.invoiceaddress,"invoiceaddress",0,null),c("locfrm","","loc",1,{list:deepCopy(e.locations),lbl:"ref",property:"address"}),c("emailfrm",d.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",l).append($$.dc("content").text(d.sender)),d.provisionend&&(s=d.provisionstart?$rct.provP:$rct.provD,r=d.provisionstart?fdt(d.provisionstart,"dd.MM.yyyy")+" - "+fdt(d.provisionend,"dd.MM.yyyy"):fdt(d.provisionend,"dd.MM.yyyy")),c("admfrm",r,"provisionperiod",s,1),n.tbl.data("new").CustomValues=d.CustomValues||"",$$.dc("inpfrm ctpfrm",l).text(jObj(d.CustomValues,"contactName")),n.tbl.children("tbody").each($inv.bdysort),n.tbl.trigger("fds.inv"),$inv.eM(!1,!0,$inv.emOpt(d))},complete:()=>{o.c.trigger("modal_close")}})},$inv.ccStInv=function(e){let t=e.data||{},n=$fis.lf(),i=t.id,a=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sprev)),r=$fis.cf().width()>a.width()+n.width()+20;n.tC("fix",r).tC("hd",!r),$inv.eM(!1,!0);let s=$$.dc("rfrm").ldng(1),l=$ocms.dlg(s,{width:1e3});l.ft.rwText($ict.iq1),$ocms.postXT({url:$ocms.url("inv/pget"),timeout:90,data:{id:t.id},success:e=>{l&&l.ft.rwText($ict.iq2),$ocms.postXT({url:$ocms.url("inv/icget"),timeout:60,data:{id:i},success:e=>{let t=$$.dc("srq",a),n=$$.tblset({class:"invi"},t);n.bdy.remove(),n.ft=$$[0]("tfoot"),e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b")),n.tbl.data($.extend({new:{},sms:{},itm:{}},{admin:e.admin,companies:e.companies,locations:e.locations}));let i=$$.tr(n.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(i,e))),n.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.requests||[],(function(t,i){if(0!==(i.Id||0)){let t=$inv.worknotes(i);i.text="i"===e.admin.type?$rct.req+jine([i.ExternalId,i.Name],": ").eine(" ",""):jine([fdt(i.WorkDoneAt,"dd.MM.yy")+t.ne(i.Name)],": ");let a=$$.tbody(n.tbl).data($.extend({},i));$inv.rendersrq.call(a)}}));let r=$$.tr($$.tbody(n.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(r),n.ft.appendTo(n.tbl);let s,l,d=e.admin||{},c=(e,t,i,r,s)=>{let l=$$.dc("inpfrm",a).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),d=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:d,nme:i,change:e=>{n.tbl.data("new")[i]=e}},s),$inv.eHtml)),n.tbl.data("new")[i]=t};c("tfrm",d.invoicetitle,"invoicetitle",0,null),c("adrfrm",d.invoiceaddress,"invoiceaddress",0,null),c("locfrm",d.provisionlocation,"loc",1,{list:deepCopy(e.locations),lbl:"ref",property:"address"}),c("emailfrm",d.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",a).append($$.dc("content").text(d.sender)),d.provisionend&&(l=d.provisionstart?$rct.provP:$rct.provD,s=d.provisionstart?fdt(d.provisionstart,"dd.MM.yyyy")+" - "+fdt(d.provisionend,"dd.MM.yyyy"):fdt(d.provisionend,"dd.MM.yyyy")),c("admfrm",s,"provisionperiod",l,1),n.tbl.data("new").CustomValues=d.CustomValues||"",$$.dc("inpfrm ctpfrm",a).text(jObj(d.CustomValues,"contactName")),n.tbl.children("tbody").each($inv.bdysort),n.tbl.trigger("fds.inv")},complete:()=>{l.c.trigger("modal_close")}})},error:()=>{l&&l.c.trigger("modal_close")}})},$inv.clCntInv=function(e){let t=$fis.lf(!1),n=[];t.find("li.ili.checked").each((function(){n.push($(this).data("Id"))})),1===n.length&&$inv.cntInv({id:n[0]})},$inv.cntInv=function(e){e=e||{};$fis.lf(!1).rC("fix").aC("hd");let t=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sedit));$inv.eM(!1,!0);let n=$$.dc("rfrm").ldng(1),i=$ocms.dlg(n,{width:1e3});i.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("inv/get"),timeout:60,data:{id:e.id},success:e=>{e.admin=e.admin||{};let n=e.inv||{},i=$$.dc("srq",t),a=$$.tblset({class:"invi"},i);a.bdy.remove(),a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:n.Id,new:{},sms:{},itm:{},bai:[]},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(r,e))),a.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.req||[],(function(e,t){let n=$$.tbody(a.tbl).data($.extend({},t));$inv.rendersrq.call(n)}));let s=$$.tr($$.tbody(a.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(s),a.ft.appendTo(a.tbl);let l=(e,n,i,r,s)=>{let l=$$.dc("inpfrm",t).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),d=$$.dc("content",l).rwText(n);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:d,nme:i,change:e=>{a.tbl.data("new")[i]=e}},s),$inv.eHtml)),a.tbl.data("new")[i]=n};l("tfrm",n.InvoiceTitle,"invoicetitle",0,null),l("adrfrm",n.SendToAddress,"invoiceaddress",0,null),l("locfrm",n.ProvisionLocation,"loc",1,null),l("emailfrm",n.SendToEmail,"invoiceemail",0,null),l("emailccfrm",n.SendToEmailCc,"invoiceemailcc",0,null),$$.dc("sndfrm",t).append($$.dc("content").text(e.admin.sender)),l("admfrm",n.ProvisionPeriod,"provisionperiod",!0===(n.ProvisionPeriod||"").includes("-")?$rct.provP:$rct.provD,1),a.tbl.data("new").CustomValues=n.CustomValues||"",$$.dc("inpfrm ctpfrm",t).text(jObj(n.CustomValues,"contactName")),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv"),$inv.eM(!1,!0,$inv.emOpt(e.admin))},complete:()=>{i.c.trigger("modal_close")}})},$inv.cSt=function(e){e=e||{};let t=$fis.lf(),n=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.sedit)),i=$fis.cf().width()>n.width()+t.width()+20;t.tC("fix",i).tC("hd",!i),$inv.eM(!1,!0);let a=$$.dc("rfrm").ldng(1),r=$ocms.dlg(a,{width:1e3});r.ft.rwText($ict.iq1),$ocms.postXT({url:$ocms.url("inv/pget"),data:{id:e.id},success:t=>{r&&r.ft.rwText($ict.iq2),$ocms.postXT({url:$ocms.url("inv/storno"),data:{id:e.id,mode:e.mode},success:e=>{e.admin=e.admin||{},e.admin.p13b=bool(e.admin.p13b||"",!0===((e.inv||{}).InvoiceOptions||"").split(",").includes("§13b"));let t=e.inv||{},i=$$.dc("srq",n),a=$$.tblset({class:"invi"},i);a.bdy.remove(),a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:t.Id,new:{},sms:{},itm:{},bai:[]},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$rct.invHR.forEach((e=>$$.th(r,e))),a.tbl.on("fds.inv",$inv.invSumUpdate),$.each(e.req||[],(function(e,t){let n=$$.tbody(a.tbl).data($.extend({},t));$inv.rendersrq.call(n)}));let s=$$.tr($$.tbody(a.tbl),{class:"placeholder"}).data({net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0});$inv.rrw.call(s),a.ft.appendTo(a.tbl);let l=(e,t,i,r,s)=>{let l=$$.dc("inpfrm",n).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),d=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:d,nme:i,change:e=>{a.tbl.data("new")[i]=e}},s),$inv.eHtml)),a.tbl.data("new")[i]=t};l("tfrm",t.InvoiceTitle,"invoicetitle",0,null),l("adrfrm",t.SendToAddress,"invoiceaddress",0,null),l("locfrm",t.ProvisionLocation,"loc",1,null),l("emailfrm",t.SendToEmail,"invoiceemail",0,null),l("emailccfrm",t.SendToEmailCc,"invoiceemailcc",0,null),$$.dc("sndfrm",n).append($$.dc("content").text(e.admin.sender)),l("admfrm",t.ProvisionPeriod,"provisionperiod",!0===(t.ProvisionPeriod||"").includes("-")?$rct.provP:$rct.provD,1),a.tbl.data("new").CustomValues=t.CustomValues||"",$$.dc("inpfrm ctpfrm",n).text(jObj(t.CustomValues,"contactName")),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv")},complete:()=>{r.c.trigger("modal_close")}})},error:()=>{r&&r.c.trigger("modal_close")}})},$inv.adrCountries=[["DE","Deutschland"],["AT","Österreich"],["CH","Schweiz"],["FR","Frankreich"],["NL","Niederlande"],["BE","Belgien"],["LU","Luxemburg"],["IT","Italien"],["ES","Spanien"],["PL","Polen"],["DK","Dänemark"],["CZ","Tschechien"],["GB","Großbritannien"],["US","USA"]],$inv.composeAddress=function(e){let t=[],n=e=>{""!==(e=(""+(e||"")).trim())&&t.push(e)};n((e=e||{}).name),""!==(""+(e.contact||"")).trim()&&n("z.Hd. "+(""+e.contact).trim()),n(e.line2||e.addressLine2),n(e.street),n(((e.postalCode||"")+" "+(e.city||"")).trim());let i=(""+(e.countryCode||"")).toUpperCase();return""!==i&&"DE"!==i&&n(i),t.join("\n")},$inv.adrCurrent=function(e){let t=jObj((e.data("new")||{}).CustomValues,"sendToAddress");if("string"==typeof t&&"{"===t.trim().charAt(0))try{t=JSON.parse(t)}catch(e){t=null}if(t&&"object"==typeof t)return t;let n=(e.data("admin")||{}).invoiceaddressData;if("string"==typeof n&&"{"===n.trim().charAt(0))try{return JSON.parse(n)}catch(e){}else if(n&&"object"==typeof n)return n;return{}},$inv.eAddress=function(e){let t=$inv.d.tbl(),n=$inv.adrCurrent(t),i=[{name:"name",label:"Name / Firma",type:"text",value:n.name||"",required:!0},{name:"contact",label:"z.Hd. (optional)",type:"text",value:n.contact||""},{name:"street",label:"Straße + Nr.",type:"text",value:n.street||""},{name:"line2",label:"Adresszusatz (optional)",type:"text",value:n.line2||n.addressLine2||""},{name:"postalCode",label:"PLZ",type:"text",value:n.postalCode||""},{name:"city",label:"Ort",type:"text",value:n.city||""},{name:"countryCode",label:"Land",type:"select",url:$inv.adrCountries,value:n.countryCode||"DE"},{name:"vatId",label:"USt-IdNr. (nur bei Firmen)",type:"text",value:n.vatId||""},{name:"leitwegId",label:"Leitweg-ID (nur Behörden / B2G)",type:"text",value:n.leitwegId||""}],a=$$.dc("adr-conformity").append([$$.dc("hd").text("Hinweis zur DATEV-/eRechnungs-Konformität"),$$.dc("tx").text("Pflicht: Name, Straße, PLZ, Ort und Land. Die USt-IdNr. nur bei Firmen angeben — für eine Rechnung an eine Privatperson einfach leer lassen. Eine Leitweg-ID nur bei Behörden (B2G) — dann wird die Rechnung automatisch als XRechnung erzeugt.")]);$ocms.dlgform(i,{title:"Rechnungsempfänger",addcontent:a,success:function(t){let n={name:t.name||"",contact:t.contact||"",line2:t.line2||"",street:t.street||"",postalCode:t.postalCode||"",city:t.city||"",countryCode:t.countryCode||"DE",vatId:(""+(t.vatId||"")).trim(),leitwegId:(""+(t.leitwegId||"")).trim()},i=$inv.composeAddress(n);e.data&&e.data.t&&e.data.t.rwText(i),"function"==typeof(e.data||{}).change&&e.data.change(i),$inv.d.sync({Target:"address",Value:n})}})},$inv.provToIso=function(e){let t=(""+(e||"")).match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/);return t?t[3]+"-"+("0"+t[2]).slice(-2)+"-"+("0"+t[1]).slice(-2):""},$inv.eProvisionPeriod=function(e){let t=(""+(e.data&&e.data.t?e.data.t.text():"")).trim().match(/\d{1,2}\.\d{1,2}\.\d{4}/g)||[],n=[{name:"von",label:"Leistungsdatum",type:"date",value:$inv.provToIso(t[0]||"")},{name:"bis",label:"Leistungszeitraum – bis (optional)",type:"date",value:$inv.provToIso(t[1]||"")}],i=$$.dc("adr-conformity").append([$$.dc("hd").text("Leistungsdatum / -zeitraum"),$$.dc("tx").text('Für ein einzelnes Datum nur „Leistungsdatum" ausfüllen; für einen Zeitraum zusätzlich das Bis-Datum. Nur strukturierte Datumsauswahl — für die E-Rechnung empfohlen.')]);$ocms.dlgform(n,{title:"Leistungsdatum / -zeitraum",addcontent:i,success:function(t){let n=(""+(t.von||"")).trim(),i=(""+(t.bis||"")).trim(),a=""===n?"":""===i||i===n?n:n+" - "+i;e.data&&e.data.t&&e.data.t.rwText(a),"function"==typeof(e.data||{}).change&&e.data.change(a),$inv.d.sync({Target:"provisionperiod",Value:a})}})},$inv.eHtml=function(e){if(e.data&&!(e.data instanceof jQuery)&&$inv.d&&"function"==typeof $inv.d.token&&""!==$inv.d.token()){if("invoiceaddress"===e.data.nme)return $inv.eAddress.call(this,e);if("provisionperiod"===e.data.nme)return $inv.eProvisionPeriod.call(this,e)}let t=$(this),n=e.data instanceof jQuery?e.data:e.data.t,i=["invoiceemail","invoiceemailcc","provisionperiod","invoicetitle"].includes(e.data.nme),a=i?[{name:"txt",label:"Text",type:"text",value:n.text()}]:[{name:"txt",label:"Text",type:"html",value:n.html(),tinymce:!0,attr:{style:"height: 300px"}}],r=e.data.change||null,s={title:t.data("dialog")||"",success:function(t){i?n.text(t.txt||""):n.html(t.txt),"function"==typeof r&&r(t.txt),$inv.d.syncField(e.data.nme,i?t.txt||"":t.txt),$inv.rd.syncField(e.data.nme,i?t.txt||"":t.txt)},tinymce:{valid_elements:"br",hidemenu:!0,hidetoolbar:!0}};if(Array.isArray(e.data.list)){let t=$$.dc("lstfrm");$.each(e.data.list,((n,i)=>{let a=$$.dc("li",t).append(""!==(e.data.lbl||"")?$$.dc("lbl").rwText(i[e.data.lbl]):null);$$.dc("adr",a).rwText(i[e.data.property]).data("val",i[e.data.property]).click((function(){let e=$(this),t=e.closest(".modal-body").find(':input[name="txt"]');t.is(".tinymce")?tinymce.get(t.attr("id")).setContent($$.s().rwText(e.data("val")).html()):"TEXTAREA"===t.prop("tagName")?t.val(e.data("val")).change():t.rwText(e.data("val"))}))})),s.addcontent=t}$ocms.dlgform(a,s)},$inv.setVat=function(e){$(this);let t=e.data,n=prompt($rct.rqV);n&&(n=parseFloat(n.replace("%","")),n>1&&(n*=.01),!1===isNaN(n)&&(t.siblings(".itm").each((function(){$(this).data().vat=fnum(n,{style:"percent"}).replace(" ","")})),$inv.t_fds_inv()))},$inv.inRow=function(e){let t=$(this),n=e.data,i={},a=$rcol.itm.clone(["SortOrder","NameOrNumber","Type","quantityhours","UnitString","net","svcnet_val","svcvat_val","net_val","vat_val","vat","Note"]),r="N"+(65536*(1+Math.random())||0).toString(16).substr(6),s=$$.tr({id:"itm_"+r.toString(),class:"itm"});$ocms.dlgform(a,{title:t.data("dialog")||"",success:function(e){s.data($.extend({Id:r},i,e)),$inv.rrw.call(s),s.insertAfter(n),$inv.t_fds_inv()},typedvalues:!0})},$inv.eRow=function(e){let t=$(this),n=e.data,i=n.data()||{},a=["SortOrder","NameOrNumber","Type","quantityhours","UnitString","net","svcnet_val","svcvat_val","net_val","vat_val","vat","Note"];i.id||""!==(i.Type||"")||a.unshift("Type");let r=$rcol.itm.clone(a).applyValues(i);r.set("Type","hidden","type"),$inv.eRw.call(t,n,i,r)},$inv.eRw=function(e,t,n){let i=$(this);$ocms.dlgform(n,{title:i.data("dialog")||"",success:function(n){let i={};""===(t.Id||"")&&(i.Id="N"+(65536*(1+Math.random())||0).toString(16).substr(6),e.attr("id","itm_"+i.Id.toString())),i.quantity=((n.quantityhours||"").toString()+" "+(n.UnitString||"").toString()).trimEnd(),e.data($.extend({},t,n,i)),console.debug("eRw success %o",e.data()),$inv.rrw.call(e),$inv.t_fds_inv()},typedvalues:!0})},$inv.bdysort=(e,t)=>{$(t).Sortable({dragItem:!1,dragHandleClass:"ico",parentident:"tr",onend:()=>{$inv.t_fds_inv()}})},$inv.rrw=function(){let e=$(this),t=e.data(),n={},i=e.is(".placeholder"),a=e.is(".hidenote"),r=e=>$$.d().append(e).html(),s=[$$.dc("ibtn insb",{title:$rct.iRb}).append(gi("indent-left")).click(e,$inv.inRow)];!1===i&&(s.unshift($$.dc("ibtn edit",{title:$rct.cP}).append(gi("pencil")).click(e,$inv.eRow)),s.push($$.dc("ibtn del",{title:$rct.dR}).append(gi("trash")).click((function(t){confirm($rct.cD)&&(e.remove(),$inv.t_fds_inv())}))),"set"===(t.Type||"").toLowerCase()&&0===(t.net_val||0)&&s.push($$.dc("ibtn tosetp",{title:$rct.toSetP}).append(gi("scale")).click(e,$inv.toSetPrice)));let l=$$.dc("axf").append(s);!0===i?n={id:"",typ:"placeholder"}:!0===e.is(".itm.osum")?n={invrqid:t.InvRqId,id:"osum"+e.index(),typ:"osum",p:"",q:null,t:r(t.tbl.tbl),tt:null,v:null,vt:t.net_val,vs:t.svcnet_val,vat:t.vat,vv:t.vat_val,vsv:t.svcvat_val,det:!1}:(n={invrqid:t.InvRqId,id:t.Id||"",typ:t.Type||"other",p:"",q:null,t:"",tt:null,v:null,vt:t.net_val,vs:t.svcnet_val,vat:t.vat,vv:t.vat_val,vsv:t.svcvat_val,det:""!==(t.Note||"")&&!1===a,SetItmId:t.SetItmId||null,lvl:t.lvl||0},$$.dc("ibtn ico move",l,{title:$rct.mR}),n.p=t.position||t.SortOrder||"",""===n.id?n.t="":["Text","Title"].includes(n.typ)&&0===(t.net_val||0)?n.t=t.htmltext||("#"!==(t.NameOrNumber||"").substr(0,1)?r($$[0]("p").text(t.NameOrNumber)):"")+(t.Note||""):(n.tt=n.det?"":$$.s(t.Note||"").text(),n.q=t.quantity||(0!==(t.quantityhours||0)?fnum(t.quantityhours)+" "+(t.UnitString||""):""),n.t=t.htmltext||(n.det?r($$.s(t.NameOrNumber||""))+r($$.dc("desc").html(t.Note)):r($$.s(t.NameOrNumber||""))),n.v=t.net,n.vt=t.net_val)),""!==(t.Note||"")&&$$.dc("ibtn add",l).append(gi("object-align-left")).click((function(t){$inv.rrw.call(e.tC("hidenote"))}));let d=[$$.tdc("aux").append(l),$$.tdc("keep").text(n.p)];if(""===n.id)d.push($$.td(e,{colspan:4}).append(n.t));else{Array.prototype.push.apply(d,n.q?[$$.tdc("keep").text(n.q)]:[]);let e=e=>null==e?"":fnum(e,$rct.cst);Array.prototype.push.apply(d,[$$.tdc("txt",{colspan:n.q?1:2,title:n.tt}).append(n.t),$$.tdc("currency").text(e(n.v)),$$.tdc("currency inetval").text(e(n.vt)).attr("title",$rct.svcPart+": "+e(n.vs))])}e.empty().attr("class",i?"placeholder":"itm").aC(n.Typ).tC("sethdr","set"===(n.typ||"").toString().toLowerCase()).aC((n.lvl||0)>0?"lvl"+Math.min(n.lvl,6):"").tC("hidenote",a).append(d),t.co=n},$inv.toSetPrice=function(e){let t=(e.data.data().Id||"").toString();""!==t&&!1!==confirm($rct.toSetPc)&&$.when($inv.t_fds_inv()).done((()=>{$inv.d.sync({Target:"item.setprice",Ref:t})}))},$inv.invSumUpdate=function(){let e=$(this),t=[],n=e.children("tbody"),i=[];n.each(((e,n)=>{let a=$(n),r=a.data()||{},s=[],l=[],d=a.find("tr.itm");a.tC("empty",d.length<1),d.each(((e,t)=>{let n=$(t).data()||{};s.push(n.co);let a=$inv.itemToContract(n),r=(n.SetItmId||"").toString();""!==r&&r!==a.id&&(a.setId=r),l.push(a);let d=!1===["text","title"].includes((n.Type||"other").toLowerCase());n.position=((e,t)=>{if(!e)return"";let n=Math.max(t||0,0);for(;i.length<=n;)i.push(0);return i.length=n+1,i[n]++,i.join(".")})(d,(n.co||{}).lvl||0),$inv.rrw.call(t)})),t.push({Id:r.Id,nme:r.Name,text:r.text,itm:s,items:l})}));let a=e.find("tbody:not(.empty)").length;n.find("tr.isum").tC("hidden",a<2),e.data("bai",t),""===(e.data("dtoken")||"")&&!1===bool(e.data("dseeding"),!1)&&null!=(e.data("admin")||{}).type&&(e.data("dseeding",!0),$inv.d.seed($.extend($inv.invcPayload(e.data()),{invid:e.data("invid")||""})))},$inv.worknotes=function(e){let t="";return e.steps.forEach(((e,n)=>{let i;try{i=JSON.parse(e.Data||{}).fields||[]}catch(e){console.debug(e),i=[]}!0!==Array.isArray(i||"")&&(i="object"==typeof i&&!0===Array.isArray(i.field||"")?i.field:[]),i.forEach(((e,n)=>{"Ausgeführte Arbeiten"===e.name&&(t=e.result||"")}))})),t},$inv.rendersrq=function(){let e=$(this).empty(),t=e.is(".onesum"),n=e.data(),i=$$.tr(e,{id:"srq"+n.Id}).aC("title nosort"),a=($rcol.itm.lbl(),$$.dc("axf").appendTo($$.tdc("aux",i)));$$.dc("ibtn osum",a,{title:$rct.combP}).append(gi("euro")).click((function(t){e.tC("onesum"),$inv.rendersrq.call(e),$inv.t_fds_inv()})),$$.dc("ibtn setvat",a,{title:$rct.sV}).append(gi("gbp")).click(i,$inv.setVat),$$.dc("ibtn insb",a,{title:$rct.iRb}).append(gi("indent-left")).click(i,$inv.inRow);let r,s=$$.sc("text",n.text),l=($$.td(i,{colspan:t?4:5}).append(s),["net_val","vat_val","svcnet_val","svcvat_val","net"]);if($$.dc("ibtn edit",a).data("dialog",$rcol.req.lbl().Name).append(gi("pencil")).click({t:s,change:e=>{n.text=e,$inv.t_fds_inv()}},$inv.eHtml),t&&($$.tdc("currency isumval",i),r={Id:n.Id.toString()+"_osum",net_val:0,vat_val:0,svcnet_val:0,svcvat_val:0,net:0},r.tbl=$$.tblset({class:"stbl"})),$.each(n.items||[],((n,i)=>{let a,s={Id:i.Id,net_val:i.net_val||0,vat_val:i.vat_val||0,svcnet_val:0,svcvat_val:0,net:i.net||0,Note:i.Note||""};if("service"===i.Type.toLowerCase())s.svcnet_val=i.net_val||0,s.svcvat_val=i.vat_val||0;t?(a=$$.tr(r.tbl.bdy,{id:"itm"+i.Id,class:"sitm"}).aC(i.Type),"Text"===i.Type||"Title"===i.Type?$$.td(a,{colspan:2}).html(i.htmltext||i.Note):($$.tdc("keep",a).text(i.quantity||((i.quantityhours||0)>0?fnum(i.quantityhours)+(i.UnitString||"").eine(" ",""):"")),i.htmltext?$$.tdc("txt",a).html(i.htmltext):$$.tdc("txt",a).text(i.NameOrNumber).attr("title",i.Note)),$.each(l,((e,t)=>{r[t]+=s[t]})),a.data(s)):($.extend(s,i),a=$$.tr(e,{id:"itm"+i.Id,class:"itm"}),a.data(s),$inv.rrw.call(a))})),t){let t=$$.tr(e,{id:"itmsq"+n.Id,class:"itm osum"}).data(r);$inv.rrw.call(t)}else{let t=$$.tr(e).aC("isum nosort");$$.tdc("aux",t),$$.td(t,{colspan:4}).text($rct.iSum),$$.tdc("currency isumval",t)}},$inv.t_fds_inv=()=>{let e=$("div.invoice_layout table.invi");if(e.trigger("fds.inv"),""!==(e.data("dtoken")||""))return $inv.d.syncChanged(e);let t=e.data("dseedpromise");return t?$.when(t).then((()=>$inv.d.syncChanged(e))):$.when()},$inv.sedit=()=>{$inv.sprev(!0)},$inv.jdisp=function(e){e.stopPropagation(),e.data.id&&$inv.disp(e.data.id,e.data.typ||"")},$inv.disp=(e,t)=>{let n="";switch(t){case"inv":n="inv/rdoc";break;case"rem":n="rem/rdoc"}""!==n&&$ocms.postXT({url:$ocms.url(n),data:{id:e||"",typ:"img"},success:e=>{let t=$$.dc("imagecollection pdfpreview"),n=Math.round(.88*vh());e.id;$.each(e.img||[],(function(e,i){$$.dc("pdfp",t).append($$.img(i).css("max-height",(n-rpx(6)).toString()+"px"))})),$ocms.dlg(t,{size:[n,Math.round(.88*vw())],zindex_min:50,form:!1,exclusive:!1})}})},$inv.jdbn=function(e){$ocms.postXT({url:$ocms.url("inv/rdocn"),data:{name:e.data.id||"",typ:"img"},success:e=>{let t=$$.dc("imagecollection pdfpreview"),n=Math.round(.88*vh());e.id;$.each(e.img||[],(function(e,i){$$.dc("pdfp",t).append($$.img(i).css("max-height",(n-rpx(6)).toString()+"px"))})),$ocms.dlg(t,{size:[n,Math.round(.88*vw())],zindex:50,form:!1})}})},$inv.sp13b=()=>{var e=$("div.invoice_layout").find("table.invi"),t=e.data();t.admin.p13b=!0,!1===(t.inv.InvoiceOptions||"").split(",").includes("§13b")&&(t.inv.InvoiceOptions+=",§13b"),e.trigger("fds.inv"),$inv.d.sync({Target:"p13b",Value:t.admin.p13b})},$inv.itemToContract=function(e){let t=((e=e||{}).Type||"").toString().toLowerCase(),n=e=>e??null,i={id:(e.Id||"").toString(),type:t,title:"",desc:"",qty:"",price_net:"",total_net:n(e.net_val),vat:e.vat||""};var a;return e.co&&"osum"===e.co.typ?(i.desc=e.co.t||"",i.total_net=e.net_val||0):["text","title"].includes(t)&&0===(e.net_val||0)?(i.desc=e.htmltext||("#"!==(e.NameOrNumber||"").substr(0,1)?(a=$$[0]("p").text(e.NameOrNumber||""),$$.d().append(a).html()):"")+(e.Note||""),i.total_net=""):(e.htmltext?i.desc=e.htmltext:(i.title=e.NameOrNumber||"",i.desc=e.Note||""),i.qty=e.quantity||(0!==(e.quantityhours||0)?fnum(e.quantityhours)+(e.UnitString?" "+e.UnitString:""):""),i.price_net=n(e.net),i.total_net=n(e.net_val)),i},$inv.ssetmode=()=>{let e=$("div.invoice_layout").find("table.invi").data();e.admin=e.admin||{};let t,n=e.admin.setmode||"setprice",i=e=>$$.dc("btn",$ict.setmo[e]).tC("selected",n===e).click((()=>{t.c.trigger("modal_close"),$inv.setSetmode(e)})),a=$$.dc("choicefrm").append([i("setprice"),i("setonly")]);t=$ocms.dlg(a,{width:800})},$inv.setSetmode=e=>{if(!1===confirm($rct.toSetMc||$rct.toSetPc))return;let t="setonly"===e?"block.setonly":"block.setprice";$.when($inv.t_fds_inv()).done((()=>{$inv.d.sync({Target:t})}))},$inv.sctp=()=>{let e=$invcol.ctp;$ocms.dlgform(e,{title:$ict.ctp,success:function(e){var t=$("div.invoice_layout"),n=t.find("table.invi").data();let i={};void 0!==n.new&&"{"===(n.new.CustomValues||"").substr(0,1)&&(i=JSON.parse(n.inv.CustomValues)),i.contactName=e.name,i.contactEmail=e.email,n.new.CustomValues=JSON.stringify(i),t.find(".ctpfrm").text(ne(e.name,e.email)),$inv.d.sync({Target:"contact",Value:{name:e.name,email:e.email}})},typedvalues:!0})},$inv.invcPayload=function(e){e=e||{};let t=$.extend({},e.new),n=$.extend({},e.admin);return t.title=null!=t.invoicetitle?t.invoicetitle:t.title||"",t.provisionlocation=null!=t.loc?t.loc:t.provisionlocation||"",t.paymentterm=null!=n.paymentterms?n.paymentterms:t.paymentterm||"",n.customerid=null!=n.customerid?n.customerid:n.CustomerId,{admin:n,req:e.bai,new:t}},$inv.ssave=()=>{$inv.d.save()},$inv.sprev=e=>{$inv.d.preview()},$inv.rReload=()=>{try{let e=$("#listframe ul.rql:first").data();$inv.cInv2({id:e.search})}catch(e){}},$inv.quantChange=function(e){},$inv.storno=function(e,t){let n,i=$$.dc("choicefrm").append([$$.dc("btn","Storno ohne Details").click({id:e,mode:"simple"},(e=>{n.c.trigger("modal_close"),$inv.cSt(e.data)})),$$.dc("btn","Storno mit neuer Rechnung").click({id:e},(e=>{n.c.trigger("modal_close"),$inv.ccStInv(e)})),$$.dc("btn","Storno mit best. Rechnung").tC("inactive",!1===bool(t,!1)).click({id:e,mode:"copy"},(e=>{!0===bool(t,!1)&&(n.c.trigger("modal_close"),$inv.cSt(e.data))}))]);n=$ocms.dlg(i,{width:1e3})},$inv.credit=function(e,t){let n,i=$$.dc("choicefrm").append([$$.dc("btn","Gutschrift").click({id:e,mode:"credit"},(e=>{n.c.trigger("modal_close"),$inv.cSt(e.data)}))]);n=$ocms.dlg(i,{width:1e3})},$inv.setPyd=function(e){confirm($ict.cpyd)&&$ocms.postXT({url:$ocms.url("inv/setpyd"),timeout:60,data:{id:e},success:e=>{alert($ict.relm)},error:()=>{alert($t.f1)}})},$inv.setUpd=function(e){confirm($ict.cupd)&&$ocms.postXT({url:$ocms.url("inv/setupd"),timeout:60,data:{id:e},success:e=>{alert($ict.relm)},error:()=>{alert($t.f1)}})},$inv.resendRem=function(e){e.stopPropagation(),e.data.id&&confirm(string($ict.remresc,[e.data.name]))&&$ocms.postXT({url:$ocms.url("rem/resend"),timeout:60,data:{id:e.data.id},success:t=>{alert(string($ict.remresr,[e.data.name]))},error:()=>{alert($t.f1)}})},$inv.dspRem=function(e){let t=$$.dc("rfrm").ldng(1),n=$ocms.dlg(t,{width:1e3});n.ft.rwText($rct.rq2),$ocms.postXT({url:$ocms.url("inv/getrem"),timeout:60,data:{id:e,drafts:!1},success:e=>{n.ft.empty();let i=$$.tblset({class:"invtbl"},t.empty()),a=$invcol.rem2,r=$$.tr(i.hd);$$.th(r);$.each(a.fields||[],((e,t)=>{$$.th(r).text(t.label)}));let s=!1;$.each(e,((e,t)=>{s=!s;let n=$$.tr(i.bdy).tC("alt",s),r=$$.td(n);n.click((function(){n.tC("selected").siblings().rC("selected")})),!0===bool(t.hasFile,!1)&&($$.dc("idl ilbtn",r,{title:$ict.dl+"\n"+t.DocumentName}).append(gi("save-file","ico")).click({id:t.Id},$inv.downloadrem),$$.dc("idl ilbtn",r,{title:$ict.remdsp+"\n"+t.DocumentName}).append(gi("eye-open","ico")).click({id:t.Id,typ:"rem"},$inv.jdisp),$$.dc("idl ilbtn",r,{title:$ict.remres+"\n"+t.DocumentName}).append(gi("refresh","ico")).click({id:t.Id,typ:"rem",name:t.DocumentName},$inv.resendRem)),$.each(a.fields||[],((e,i)=>{let a=$$.td(n).aC(i.dtype),r=t[i.name];if("function"==typeof i.dfnc)i.dfnc.call(a,r,t);else switch(i.type||""){case"date":a.text(fdt(t[i.name],"dd.MM.yy"));break;case"datetime":a.text(fdt(t[i.name]));break;case"html":a.append($$.dc("ctw").html(r)),a.append($$.dc("ttip").html(r));break;default:a.text(t[i.name])}if("InvoiceId"===(i.name||""))a.aC("keep");switch(typeof i.title){case"function":i.title.call(a,t);break;case"string":a.attr("title",cs.title)}}))}))},error:()=>{t.empty(),n.ft.rwText($t.f1)},complete:()=>{t.ldng(0)}})},$inv.ccRem=function(e,t){$(this);$ocms.postXT({url:$ocms.url("rem/lrem"),timeout:60,data:{id:e},success:n=>{let i=$invcol.rid.clone();i.applyValues(n.ov);let a=$$.dc("ac"),r=$$.tblset({class:"fullgrid fullwidth"},a);if((n.lst||[]).length>0){$$.d({style:"margin: 1.5rem 0 1rem 0;font-size: 110%;text-decoration: underline;"}).prependTo(a).text($ict.rovlh);let e=$$.tr(r.hd);$ict.rovl.forEach(((t,n)=>$$.th(e,t))),$.each(n.lst,((e,t)=>{$$.tr(r.bdy).append([$$.tdc("keep",t.subject),$$.tdc("currency",fnum(t.amount,$rct.cst)),$$.tdc("currency",fnum(t.amount_payed,$rct.cst)),$$.tdc("keep",fdt(t.DateFinalized,"dd.MM.yy"))])}))}else $$.td($$.tr(r.bdy),$ict.nd);$ocms.dlgform(i,{addcontent:a,title:string($ict.remdt,[t||"?"]),success:function(t){$inv.ccRem_s2(e,t)},typedvalues:!0})}})},$inv.rRemRw=function(e){let t=$(this),n=e.rm||{};t.empty().data({invoiceid:n.invoiceid,invoicedate:n.invoicedate,amount:n.amount,amount_payed:n.amount_payed});let i=$$.dc("axf").append($$.dc("ibtn edit",{title:$rct.cP}).append(gi("pencil")).click(t,$inv.eRowR));t.append([$$.tdc("aux").append(i),$$.tdc("keep",n.invoiceid),$$.tdc("keep",fdt(n.invoicedate,"dd.MM.yy")),$$.tdc("currency",fnum(n.amount,$rct.cst)),$$.tdc("currency",fnum(n.amount_payed,$rct.cst)),$$.tdc("currency",fnum(n.amount-n.amount_payed,$rct.cst))])},$inv.eRowR=function(e){let t=$(this),n=e.data,i=n.data()||{},a=$invcol.rem.clone().applyValues(i);$ocms.dlgform(a,{title:t.data("dialog")||"",success:function(e){let i=t.closest("table"),a=i.data();$.extend(a.rm,e),i.data(a),$inv.rRemRw.call(n,a),$inv.rd.syncAmount(a.rm.amount,a.rm.amount_payed)},typedvalues:!0})},$inv.ccRem_s2=function(e,t){$fis.lf(!1).rC("fix").aC("hd");let n=$$.dc("invoice_layout",$fis.frm_edit()).append($$.dc("btn sprev").click($inv.rprev));$inv.eM(!1,!0);$$.dc("rfrm").ldng(1);$ocms.postXT({url:$ocms.url("rem/get"),timeout:60,data:$.extend({id:e},t),success:e=>{let t=e.rm||{},i=$$.dc("srq",n),a=$$.tblset({class:"invi"},i);a.ft=$$[0]("tfoot"),a.tbl.data($.extend({invid:t.invid,new:{}},e));let r=$$.tr(a.hd).aC("shd").append([$$.th().aC("aux")]);$ict.remHR.forEach((e=>$$.th(r,e))),$inv.rRemRw.call($$.tr(a.bdy),a.tbl.data()),a.ft.appendTo(a.tbl);let s=(e,t,i,r,s)=>{let l=$$.dc("inpfrm",n).aC(e).append("string"==typeof r?$$.dc("ahd",r):r>0?$$.dc("ahd",$rct.frm[i]):null),d=$$.dc("content",l).rwText(t);$$.dc("axf",l).append($$.dc("ibtn edit").data("dialog",$rct.frm[i]).append(gi("pencil")).click($.extend({t:d,nme:i,change:e=>{a.tbl.data("new")[i]=e}},s),$inv.eHtml)),a.tbl.data("new")[i]=t};s("tfrm",t.subject,"subject",0,null),s("adrfrm",t.invoiceaddress,"invoiceaddress",0,null),s("emailfrm",t.invoiceemail,"invoiceemail",0,null),$$.dc("sndfrm",n).append($$.dc("content").text(t.sender)),a.tbl.children("tbody").each($inv.bdysort),a.tbl.trigger("fds.inv");let l=a.tbl.data("new");l.amount=t.amount,l.amount_payed=t.amount_payed,$inv.rd.seed({rem:{invid:t.invid,type:t.type,invoiceid:t.invoiceid,invoicedate:t.invoicedate},new:l})},complete:()=>{}})},$inv.rprev=()=>{$inv.rd.preview()},$inv.sis=e=>{confirm($ict.sisc)&&$ocms.postXT({url:$ocms.url("inv/sis"),data:{id:e||""},success:e=>{}})},$inv.srs=e=>{confirm($ict.srsc)&&$ocms.postXT({url:$ocms.url("rem/srs"),data:{id:e||""},success:e=>{}})},$inv.mfrrel=e=>{$("#contentframe").ldng(),$ocms.postXT({url:$ocms.url("inv/mfrrel"),data:{id:e||""},success:e=>{$inv.rerenderinv()},complete:()=>{$("#contentframe").ldng(0)}})},$inv.sets={o:null,c:null,drag:null,isOpen:()=>!!$inv.sets.o,open:function(){let e=$inv.d.tbl();if(e.length<1||$inv.sets.isOpen())return;if(""===$inv.d.token()&&!e.data("dstate"))return;let t=$$.dc("setstruct");$inv.sets.c=t,$inv.sets.o=$ocms.dlg(t,{title:$ict.setst,size:[-1,960],exclusive:!1,close:()=>{$inv.sets.o=null,$inv.sets.c=null,$inv.sets.drag=null}});let n=e.data("dstate");n?$inv.sets.render(n):$inv.d.refresh()},title:e=>{let t=$$.d().html(e.t||"").text().replace(/\s+/g," ").trim();return""!==t?t:$ict.setsNoTitle||""},typLbl:e=>{let t=(e.typ||"").toString().toLowerCase();return($ict.settyp||{})[t]||e.typ||""},isSet:e=>"set"===(e.typ||"").toString().toLowerCase(),parentOf:(e,t)=>{let n=(null==e.SetItmId?"":e.SetItmId).toString(),i=(e.id||"").toString();if(""===n||n===i)return"";let a=t[n];return a&&$inv.sets.isSet(a)?n:""},isDescendant:(e,t,n)=>{let i={},a=n[e];for(;a;){let e=$inv.sets.parentOf(a,n);if(""===e||i[e])return!1;if(e===t)return!0;i[e]=!0,a=n[e]}return!1},canDrop:(e,t,n,i)=>!!e&&e.bid===n&&e.id!==t&&!$inv.sets.isDescendant(t,e.id,i),sync:e=>($inv.sets.c&&$inv.sets.c.aC("busy"),$inv.d.sync(e)),render:function(e){let t=$inv.sets.c;if(!t)return;let n=t.closest(".modal-body"),i=n.length?n.scrollTop():0;t.empty().rC("busy"),$$.dc("shint",t).text($ict.setsHint||""),(e.req||[]).forEach((e=>{if(!e)return;let n=(e.Id||"").toString();if(""===n)return;let i=(e.itm||[]).filter((e=>e&&""!==(e.id||""))),a={};i.forEach((e=>{a[(e.id||"").toString()]=e}));let r=$$.dc("sblock",t).data({bid:n});$inv.sets.rootZone($$.dc("sbhd",r).append($$.sc("nme",e.text||e.nme||n)),n,a);let s=$$.tblset({class:"stbl"},r),l=$$.tr(s.hd);($ict.setsHR||[]).forEach((e=>$$.th(l,e))),i.forEach((e=>$inv.sets.row(s.bdy,e,n,i,a))),$inv.sets.rootZone($$.dc("sroot",r).text($ict.setsRoot||""),n,a),$inv.sets.createBar(r,n,i,a)})),n.length&&n.scrollTop(i)},collapsed:{},hiddenByCollapse:function(e,t){let n={},i=e;for(;i;){let e=$inv.sets.parentOf(i,t);if(""===e||n[e])return!1;if($inv.sets.collapsed[e])return!0;n[e]=!0,i=t[e]}return!1},zone:function(e,t,n){let i=e.originalEvent||e,a=t[0].getBoundingClientRect(),r=(i.clientY||0)-a.top,s=a.height||1;return r<.3*s?"before":r>.7*s||!n?"after":"into"},clearDrop:()=>{$(".setstruct .dropok, .setstruct .dropbefore, .setstruct .dropafter").rC("dropok dropbefore dropafter")},row:function(e,t,n,i,a){let r=(t.id||"").toString(),s=parseInt(t.lvl||0,10)||0,l=$inv.sets.isSet(t),d=$inv.sets.parentOf(t,a),c=l&&!!$inv.sets.collapsed[r],o=$$.tr(e,{draggable:"true"}).aC("srow lvl"+s).tC("isset",l).tC("folded",c).data({id:r,bid:n});$inv.sets.hiddenByCollapse(t,a)&&o.aC("hid"),$$.tdc("grip",o).append(gi("move")).attr("title",$rct.mR),$$.tdc("pos",o).text(null!=t.p?t.p:"");let $=$$.tdc("typ",o);l&&$.append($$.sc("fold").attr("title",c?$ict.setsExpand:$ict.setsCollapse).append(gi(c?"triangle-right":"triangle-bottom")).click((e=>{e.stopPropagation(),c?delete $inv.sets.collapsed[r]:$inv.sets.collapsed[r]=!0;let t=$inv.d.tbl().data("dstate");t&&$inv.sets.render(t)}))),$.append($$.s($inv.sets.typLbl(t))),$$.tdc("qty",o).text(t.q||"");let u=$$.tdc("ttl",o).append($$.sc("ind").css("width",(1.4*s).toString()+"rem")).append($$.s($inv.sets.title(t)));if(l){let e=i.filter((e=>$inv.sets.parentOf(e,a)===r)).length;u.append($$.sc("cnt"," ("+e+")"))}let m=i.filter((e=>$inv.sets.parentOf(e,a)===d)),v=m.findIndex((e=>(e.id||"").toString()===r)),p=m.slice(0,v).some((e=>$inv.sets.isSet(e))),f=$$.tdc("act",o),h=(e,t,n,i,a)=>$$.dc("ibtn "+e,f,{title:n}).append(gi(t)).tC("disabled",!i).click((()=>{i&&$inv.sets.sync(a)}));h("up","arrow-up",$ict.setsUp,v>0,{Target:"set.move",Ref:r,Value:-1}),h("down","arrow-down",$ict.setsDown,v>=0&&v0,{Target:"set.level",Ref:r,Value:-1}),h("in","chevron-right",$ict.setsIn,p,{Target:"set.level",Ref:r,Value:1}),o.on("dragstart",(e=>{$inv.sets.drag={id:r,bid:n},o.aC("dragging");try{e.originalEvent.dataTransfer.setData("text/plain",r),e.originalEvent.dataTransfer.effectAllowed="move"}catch(e){}})).on("dragend",(()=>{o.rC("dragging"),$inv.sets.drag=null,$inv.sets.clearDrop()}));return o.on("dragover",(e=>{if(!$inv.sets.canDrop($inv.sets.drag,r,n,a))return;e.preventDefault();let t=$inv.sets.zone(e,o,l);o.rC("dropok dropbefore dropafter").aC("into"===t?"dropok":"before"===t?"dropbefore":"dropafter")})).on("dragleave",(()=>{o.rC("dropok dropbefore dropafter")})).on("drop",(e=>{e.preventDefault(),$inv.sets.clearDrop();let t=$inv.sets.drag;if($inv.sets.drag=null,!$inv.sets.canDrop(t,r,n,a))return;let i=$inv.sets.zone(e,o,l);"into"===i?$inv.sets.sync({Target:"set.assign",Ref:t.id,Value:{parent:r}}):$inv.sets.sync({Target:"set.move",Ref:t.id,Value:{anchor:r,where:i}})})),o},rootZone:function(e,t,n){return e.on("dragover",(i=>{(()=>{let e=$inv.sets.drag;return!!e&&e.bid===t&&!!n[e.id]&&""!==$inv.sets.parentOf(n[e.id],n)})()&&(i.preventDefault(),e.aC("dropok"))})).on("dragleave",(()=>{e.rC("dropok")})).on("drop",(i=>{i.preventDefault(),e.rC("dropok");let a=$inv.sets.drag;$inv.sets.drag=null,a&&a.bid===t&&n[a.id]&&""!==$inv.sets.parentOf(n[a.id],n)&&$inv.sets.sync({Target:"set.assign",Ref:a.id,Value:{parent:""}})})),e},createBar:function(e,t,n,i){let a=$$.dc("screate",e),r=$("").attr({name:"typ"}).append([$$.opt("set",$ict.settyp.set),$$.opt("text",$ict.settyp.text)]),s=$("").attr({name:"parent"}).append($$.opt("",$ict.setsTop));n.forEach((e=>{if(!$inv.sets.isSet(e))return;let t=parseInt(e.lvl||0,10)||0;s.append($$.opt((e.id||"").toString()," ".repeat(3*t)+$inv.sets.title(e)))}));let l=$('').attr({name:"title",placeholder:$ict.setsTitle,maxlength:250}),d=()=>{let e=(l.val()||"").toString().trim();""!==e?($inv.sets.sync({Target:"set.create",Ref:t,Value:{typ:r.val(),title:e,parent:s.val()||""}}),l.val("")):l.focus()};return l.on("keydown",(e=>{"Enter"===e.key&&(e.preventDefault(),d())})),a.append([$$.sc("lbl",$ict.setsNew),r,$$.sc("lbl",$ict.setsInto),s,l,$$.bbtn($ict.setsAdd,"sadd").click(d)]),a}}; \ No newline at end of file diff --git a/Fuchs/wwwroot/web/fis.inv.min.css b/Fuchs/wwwroot/web/fis.inv.min.css index 1829908..f1b1fce 100644 --- a/Fuchs/wwwroot/web/fis.inv.min.css +++ b/Fuchs/wwwroot/web/fis.inv.min.css @@ -1 +1 @@ -.invfrm>table td.sh_s,table.invtbl td.sh_s{color:#bf4b06}.invfrm>table td.is_cc,table.invtbl td.is_cc{background-color:#777!important;color:#fff!important}.invfrm>table td.is_pyd,table.invtbl td.is_pyd{color:#60cc08}.invfrm>table td.is_due,table.invtbl td.is_due{background-color:#f2f23f!important}.invfrm>table td.is_ovd,table.invtbl td.is_ovd{background-color:#de4e09!important}.invfrm>table td.is_rem,table.invtbl td.is_rem{background-color:#ce1900!important}.invfrm>table td.av,table.invtbl td.av{border:2px solid red}.invfrm>table td.av+td.vsel select,table.invtbl td.av+td.vsel select{display:block}.rfrm{min-width:200px;min-height:200px}table.if{border-collapse:collapse;margin:1rem 0;background:#fff}table.if tr:nth-child(2n+1) td{background-color:#f9f9f9}table.if tr.title td{background-color:#56a532;color:#fff}table.if tr.title td .eid{font-weight:700;margin:0 1rem 0 .5rem;font-size:140%}table.if tr.title td .nme{font-size:110%}table.if tr.shd td{font-style:italic;color:#bbb;background-color:#fff!important;font-size:90%}table.if td,table.if th{padding:.2rem .35rem;border:1px solid #ddd}table.if td.currency{text-align:right;white-space:nowrap}table.if td.num{white-space:nowrap}.pdfpreview .pdfp.ph>.note{background:#fff;display:inline-block;padding:3rem;margin:.2rem 0}@keyframes fis_spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}.edit_frm .invoice_layout{margin:2rem 2rem 2rem auto;width:50rem;border:1px solid #eee;padding:25.5rem 3rem 3rem;box-shadow:4px 4px 9px rgba(50,50,50,.3);position:relative}.edit_frm .invoice_layout .axf{font-size:1rem;display:none;position:absolute;right:100%;height:100%;min-height:1.5rem;min-width:2rem;background:#f0ffe2;border:1px solid #ccc;border-top-left-radius:.2rem;border-bottom-left-radius:.2rem;top:0;width:auto;max-height:1.5rem;overflow:hidden;padding:.1rem}.edit_frm .invoice_layout .axf>.ibtn{cursor:pointer;height:100%;width:1.5rem;display:inline-block;position:relative}.edit_frm .invoice_layout .sndfrm{position:absolute;font-size:.7rem;color:#727272;top:4.7rem;left:3rem;width:auto;height:auto;user-select:none}.edit_frm .invoice_layout .emailfrm{position:absolute;font-size:.7rem;top:.5rem;left:.5rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .emailfrm .content{color:#000}.edit_frm .invoice_layout .emailfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .emailfrm:hover div.axf{display:block}.edit_frm .invoice_layout .admfrm{position:absolute;right:3rem;top:9rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .admfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .admfrm .content{color:#000}.edit_frm .invoice_layout .admfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .admfrm:hover div.axf{display:block}.edit_frm .invoice_layout .ctpfrm{position:absolute;right:3rem;top:14rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .ctpfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .ctpfrm .content{color:#000}.edit_frm .invoice_layout .ctpfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .ctpfrm:hover div.axf{display:block}.edit_frm .invoice_layout .adrfrm{position:absolute;left:3rem;top:6rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .adrfrm:hover div.axf{display:block}.edit_frm .invoice_layout .locfrm{position:absolute;left:3rem;top:18.5rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .locfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .locfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .locfrm:hover div.axf{display:block}.edit_frm .invoice_layout .tfrm,.edit_frm .invoice_layout>div.title{position:absolute;left:3rem;top:15rem;font-size:2rem;cursor:pointer}.edit_frm .invoice_layout .tfrm:hover div.axf,.edit_frm .invoice_layout>div.title:hover div.axf{display:block}.edit_frm .invoice_layout div.fnote{margin:1rem 0}.edit_frm .invoice_layout p+table.invi,.edit_frm .invoice_layout table.invi+p{margin-top:2.5rem}.edit_frm .invoice_layout table.invi{border-collapse:separate;border-spacing:0;border-color:#fff;width:100%;max-width:100%}.edit_frm .invoice_layout table.invi>tbody>tr.hidden{display:none}.edit_frm .invoice_layout table.invi>tbody>tr:nth-child(2n+1) td{background-color:#fbfbfb;border-color:#fbfbfb}.edit_frm .invoice_layout table.invi>tbody>tr.title>td{background:#fff;border-color:#fff;border-top:1.5px solid #000}.edit_frm .invoice_layout table.invi>tbody>tr.sethdr td.txt{font-weight:700}.edit_frm .invoice_layout table.invi td.currency,.edit_frm .invoice_layout table.invi td.num,.edit_frm .invoice_layout table.invi th.currency,.edit_frm .invoice_layout table.invi th.num{text-align:right;white-space:nowrap}.edit_frm .invoice_layout table.invi td.keep,.edit_frm .invoice_layout table.invi th.keep{white-space:nowrap}.edit_frm .invoice_layout table.invi table.stbl td,.edit_frm .invoice_layout table.invi table.stbl th{padding:.3rem .5rem}.edit_frm .invoice_layout table.invi table.stbl td:first-child,.edit_frm .invoice_layout table.invi table.stbl th:first-child{padding-left:0}.edit_frm .invoice_layout table.invi table.stbl td:last-child,.edit_frm .invoice_layout table.invi table.stbl th:last-child{padding-right:0}.edit_frm .invoice_layout table.invi>tbody>tr>td,.edit_frm .invoice_layout table.invi>tbody>tr>th,.edit_frm .invoice_layout table.invi>tfoot>tr>td,.edit_frm .invoice_layout table.invi>tfoot>tr>th,.edit_frm .invoice_layout table.invi>thead>tr>td,.edit_frm .invoice_layout table.invi>thead>tr>th,.edit_frm .invoice_layout table.invi>tr>td,.edit_frm .invoice_layout table.invi>tr>th{border-width:1px;padding:.3rem .5rem;border-left:none!important;border-right:none!important}.edit_frm .invoice_layout table.invi>tbody>tr>td.aux,.edit_frm .invoice_layout table.invi>tbody>tr>th.aux,.edit_frm .invoice_layout table.invi>tfoot>tr>td.aux,.edit_frm .invoice_layout table.invi>tfoot>tr>th.aux,.edit_frm .invoice_layout table.invi>thead>tr>td.aux,.edit_frm .invoice_layout table.invi>thead>tr>th.aux,.edit_frm .invoice_layout table.invi>tr>td.aux,.edit_frm .invoice_layout table.invi>tr>th.aux{width:0;max-width:0;min-width:0;position:relative;padding:0}.edit_frm .invoice_layout table.invi>tbody>tr>th,.edit_frm .invoice_layout table.invi>tfoot>tr>th,.edit_frm .invoice_layout table.invi>thead>tr>th,.edit_frm .invoice_layout table.invi>tr>th{text-align:left;font-weight:300;background-color:#fff;border-color:#fff;font-size:.8rem;font-style:italic;border-top-style:none;border-bottom-style:none}.edit_frm .invoice_layout table.invi>tbody>tr>td,.edit_frm .invoice_layout table.invi>tfoot>tr>td,.edit_frm .invoice_layout table.invi>thead>tr>td,.edit_frm .invoice_layout table.invi>tr>td{vertical-align:top;font-weight:400;background-color:#f8f8f8;border-color:#f8f8f8;border-top-style:solid;border-bottom-style:solid;user-select:none}.edit_frm .invoice_layout table.invi>tbody>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>tfoot>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>thead>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>tr:hover>td.aux>.axf{display:inline-flex}.edit_frm .invoice_layout table.invi>tbody>tr.isum>td,.edit_frm .invoice_layout table.invi>tfoot>tr.isum>td,.edit_frm .invoice_layout table.invi>thead>tr.isum>td,.edit_frm .invoice_layout table.invi>tr.isum>td{border-top:1px solid #000;background:#fff}.edit_frm .invoice_layout table.invi>tbody>tr.sep>td,.edit_frm .invoice_layout table.invi>tfoot>tr.sep>td,.edit_frm .invoice_layout table.invi>thead>tr.sep>td,.edit_frm .invoice_layout table.invi>tr.sep>td{background:#fff}.edit_frm .invoice_layout table.invi>tbody>tr.tsum td,.edit_frm .invoice_layout table.invi>tfoot>tr.tsum td,.edit_frm .invoice_layout table.invi>thead>tr.tsum td,.edit_frm .invoice_layout table.invi>tr.tsum td{font-weight:600;border-top:1px solid #000;border-bottom:1px solid #000}.edit_frm .invoice_layout table.invi .sortable>tr>td.aux .ico.move{background:url(data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAzMiAzMiIgaWQ9IkxheWVyXzQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjE2LDMxIDgsMTggMjQsMTggICAgICIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iI0RERERERCIvPjxwb2x5Z29uIGZpbGw9Im5vbmUiIHBvaW50cz0iMTYsMSA4LDE0IDI0LDE0ICAgICAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiNEREREREQiLz48L2c+PC9zdmc+) center center/contain no-repeat border-box}.edit_frm .invoice_layout table.invi .sortable>tr>td.aux .ico.move:hover{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzIgMzIiIGlkPSJMYXllcl80IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGc+PHBvbHlnb24gZmlsbD0ibm9uZSIgcG9pbnRzPSIxNiwzMSA4LDE4IDI0LDE4ICAgICAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjE2LDEgOCwxNCAyNCwxNCAgICAgIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9nPjwvc3ZnPg==)}.edit_frm .invoice_layout table.invi .sortable>tr.sortactive>td.ico{cursor:move}.edit_frm .invoice_layout .btn{background:url("") center center/contain no-repeat content-box #fff;position:absolute;top:.2rem;right:.2rem;height:3rem;width:3rem;padding:.1rem;z-index:11}.edit_frm .invoice_layout .btn.sprev{background-image:url(data:image/png;base64,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)}.edit_frm .invoice_layout .btn.sdoc{background-image:url(data:image/png;base64,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)}.edit_frm .invoice_layout.freeze::before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(50,50,50,.3);user-select:none;cursor:wait;z-index:10;pointer-events:none}.edit_frm .invoice_layout.freeze::before tr:hover>td.aux>.axf{display:inline-flex}.edit_frm .invoice_layout.freeze::after{content:"";position:absolute;top:50%;left:50%;width:3rem;height:3rem;margin:-1.5rem 0 0 -1.5rem;border:.4rem solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;z-index:11;pointer-events:none;animation:.8s linear infinite fis_spin}.edit_frm .invoice_layout .dvalidation{margin:0 0 1rem}.edit_frm .invoice_layout .dvalidation.hidden{display:none}.edit_frm .invoice_layout .dvalidation .dvmsg{padding:.4rem .7rem;margin:.25rem 0;border-radius:.2rem;font-size:.9rem;border-left:4px solid #999;background:#f5f5f5}.edit_frm .invoice_layout .dvalidation .dvmsg.error{border-left-color:#c0392b;background:#fdecea;color:#922}.edit_frm .invoice_layout .dvalidation .dvmsg.warning{border-left-color:#e0a800;background:#fff8e1;color:#7a5c00}.edit_frm .invoice_layout .dvalidation .dvmsg.info{border-left-color:#3498db;background:#eaf4fb;color:#1c5a82}.dhist .invtbl{width:100%}.dhist td,.dhist th{padding:.3rem .6rem;text-align:left;vertical-align:top}.dhist tbody tr:nth-child(even){background:#fafafa}.modal-body .lstfrm{display:block;position:relative}.modal-body .lstfrm .li{display:inline-block;background-color:#fff;border:1px solid #e0e0e0;padding:1.7rem .5rem .5rem;border-radius:.2rem;cursor:pointer;position:relative;margin:0 .5rem .5rem 0;box-shadow:1px 1px 3px rgba(50,50,50,.1)}.modal-body .lstfrm .li .lbl{position:absolute;background:#727272;color:#fff;font-size:.8rem;left:0;top:0;right:0;padding:.2rem;border-top-left-radius:inherit;border-top-right-radius:inherit}.adr-conformity{margin-top:.75rem;padding:.6rem .75rem;background:#f3f7fb;border:1px solid #cfe0f0;border-radius:4px;font-size:.85rem;line-height:1.35}.adr-conformity .hd{font-weight:600;margin-bottom:.2rem}.adr-conformity .tx{color:#445} \ No newline at end of file +@charset "UTF-8";.invfrm>table td.sh_s,table.invtbl td.sh_s{color:#bf4b06}.invfrm>table td.is_cc,table.invtbl td.is_cc{background-color:#777!important;color:#fff!important}.invfrm>table td.is_pyd,table.invtbl td.is_pyd{color:#60cc08}.invfrm>table td.is_due,table.invtbl td.is_due{background-color:#f2f23f!important}.invfrm>table td.is_ovd,table.invtbl td.is_ovd{background-color:#de4e09!important}.invfrm>table td.is_rem,table.invtbl td.is_rem{background-color:#ce1900!important}.invfrm>table td.av,table.invtbl td.av{border:2px solid red}.invfrm>table td.av+td.vsel select,table.invtbl td.av+td.vsel select{display:block}.rfrm{min-width:200px;min-height:200px}table.if{border-collapse:collapse;margin:1rem 0;background:#fff}table.if tr:nth-child(2n+1) td{background-color:#f9f9f9}table.if tr.title td{background-color:#56a532;color:#fff}table.if tr.title td .eid{font-weight:700;margin:0 1rem 0 .5rem;font-size:140%}table.if tr.title td .nme{font-size:110%}table.if tr.shd td{font-style:italic;color:#bbb;background-color:#fff!important;font-size:90%}table.if td,table.if th{padding:.2rem .35rem;border:1px solid #ddd}table.if td.currency{text-align:right;white-space:nowrap}table.if td.num{white-space:nowrap}.pdfpreview .pdfp.ph>.note{background:#fff;display:inline-block;padding:3rem;margin:.2rem 0}@keyframes fis_spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}.edit_frm .invoice_layout{margin:2rem 2rem 2rem auto;width:50rem;border:1px solid #eee;padding:25.5rem 3rem 3rem;box-shadow:4px 4px 9px rgba(50,50,50,.3);position:relative}.edit_frm .invoice_layout .axf{font-size:1rem;display:none;position:absolute;right:100%;height:100%;min-height:1.5rem;min-width:2rem;background:#f0ffe2;border:1px solid #ccc;border-top-left-radius:.2rem;border-bottom-left-radius:.2rem;top:0;width:auto;max-height:1.5rem;overflow:hidden;padding:.1rem}.edit_frm .invoice_layout .axf>.ibtn{cursor:pointer;height:100%;width:1.5rem;display:inline-block;position:relative}.edit_frm .invoice_layout .sndfrm{position:absolute;font-size:.7rem;color:#727272;top:4.7rem;left:3rem;width:auto;height:auto;user-select:none}.edit_frm .invoice_layout .emailfrm{position:absolute;font-size:.7rem;top:.5rem;left:.5rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .emailfrm .content{color:#000}.edit_frm .invoice_layout .emailfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .emailfrm:hover div.axf{display:block}.edit_frm .invoice_layout .emailccfrm{position:absolute;font-size:.7rem;top:2.2rem;left:.5rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .emailccfrm .content{color:#000}.edit_frm .invoice_layout .emailccfrm .content:empty::before{content:"Zusatzempfänger hinzufügen";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .emailccfrm:hover div.axf{display:block}.edit_frm .invoice_layout .admfrm{position:absolute;right:3rem;top:9rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .admfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .admfrm .content{color:#000}.edit_frm .invoice_layout .admfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .admfrm:hover div.axf{display:block}.edit_frm .invoice_layout .ctpfrm{position:absolute;right:3rem;top:14rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .ctpfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .ctpfrm .content{color:#000}.edit_frm .invoice_layout .ctpfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .ctpfrm:hover div.axf{display:block}.edit_frm .invoice_layout .adrfrm{position:absolute;left:3rem;top:6rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .adrfrm:hover div.axf{display:block}.edit_frm .invoice_layout .locfrm{position:absolute;left:3rem;top:18.5rem;width:auto;height:auto;cursor:pointer}.edit_frm .invoice_layout .locfrm .ahd{display:block;color:#727272;margin:0 0 .2rem}.edit_frm .invoice_layout .locfrm .content:empty::before{content:"-";min-width:5rem;height:1rem;display:block;position:absolute}.edit_frm .invoice_layout .locfrm:hover div.axf{display:block}.edit_frm .invoice_layout .tfrm,.edit_frm .invoice_layout>div.title{position:absolute;left:3rem;top:15rem;font-size:2rem;cursor:pointer}.edit_frm .invoice_layout .tfrm:hover div.axf,.edit_frm .invoice_layout>div.title:hover div.axf{display:block}.edit_frm .invoice_layout div.fnote{margin:1rem 0}.edit_frm .invoice_layout p+table.invi,.edit_frm .invoice_layout table.invi+p{margin-top:2.5rem}.edit_frm .invoice_layout table.invi{border-collapse:separate;border-spacing:0;border-color:#fff;width:100%;max-width:100%}.edit_frm .invoice_layout table.invi>tbody>tr.hidden{display:none}.edit_frm .invoice_layout table.invi>tbody>tr:nth-child(2n+1) td{background-color:#fbfbfb;border-color:#fbfbfb}.edit_frm .invoice_layout table.invi>tbody>tr.title>td{background:#fff;border-color:#fff;border-top:1.5px solid #000}.edit_frm .invoice_layout table.invi>tbody>tr.sethdr td.txt{font-weight:700}.edit_frm .invoice_layout table.invi>tbody>tr.lvl1 td.txt{padding-left:calc(.4rem + 1.4rem)}.edit_frm .invoice_layout table.invi>tbody>tr.lvl2 td.txt{padding-left:calc(.4rem + 2.8rem)}.edit_frm .invoice_layout table.invi>tbody>tr.lvl3 td.txt{padding-left:calc(.4rem + 4.2rem)}.edit_frm .invoice_layout table.invi>tbody>tr.lvl4 td.txt{padding-left:calc(.4rem + 5.6rem)}.edit_frm .invoice_layout table.invi>tbody>tr.lvl5 td.txt{padding-left:calc(.4rem + 7rem)}.edit_frm .invoice_layout table.invi>tbody>tr.lvl6 td.txt{padding-left:calc(.4rem + 8.4rem)}.edit_frm .invoice_layout table.invi td.currency,.edit_frm .invoice_layout table.invi td.num,.edit_frm .invoice_layout table.invi th.currency,.edit_frm .invoice_layout table.invi th.num{text-align:right;white-space:nowrap}.edit_frm .invoice_layout table.invi td.keep,.edit_frm .invoice_layout table.invi th.keep{white-space:nowrap}.edit_frm .invoice_layout table.invi table.stbl td,.edit_frm .invoice_layout table.invi table.stbl th{padding:.3rem .5rem}.edit_frm .invoice_layout table.invi table.stbl td:first-child,.edit_frm .invoice_layout table.invi table.stbl th:first-child{padding-left:0}.edit_frm .invoice_layout table.invi table.stbl td:last-child,.edit_frm .invoice_layout table.invi table.stbl th:last-child{padding-right:0}.edit_frm .invoice_layout table.invi>tbody>tr>td,.edit_frm .invoice_layout table.invi>tbody>tr>th,.edit_frm .invoice_layout table.invi>tfoot>tr>td,.edit_frm .invoice_layout table.invi>tfoot>tr>th,.edit_frm .invoice_layout table.invi>thead>tr>td,.edit_frm .invoice_layout table.invi>thead>tr>th,.edit_frm .invoice_layout table.invi>tr>td,.edit_frm .invoice_layout table.invi>tr>th{border-width:1px;padding:.3rem .5rem;border-left:none!important;border-right:none!important}.edit_frm .invoice_layout table.invi>tbody>tr>td.aux,.edit_frm .invoice_layout table.invi>tbody>tr>th.aux,.edit_frm .invoice_layout table.invi>tfoot>tr>td.aux,.edit_frm .invoice_layout table.invi>tfoot>tr>th.aux,.edit_frm .invoice_layout table.invi>thead>tr>td.aux,.edit_frm .invoice_layout table.invi>thead>tr>th.aux,.edit_frm .invoice_layout table.invi>tr>td.aux,.edit_frm .invoice_layout table.invi>tr>th.aux{width:0;max-width:0;min-width:0;position:relative;padding:0}.edit_frm .invoice_layout table.invi>tbody>tr>th,.edit_frm .invoice_layout table.invi>tfoot>tr>th,.edit_frm .invoice_layout table.invi>thead>tr>th,.edit_frm .invoice_layout table.invi>tr>th{text-align:left;font-weight:300;background-color:#fff;border-color:#fff;font-size:.8rem;font-style:italic;border-top-style:none;border-bottom-style:none}.edit_frm .invoice_layout table.invi>tbody>tr>td,.edit_frm .invoice_layout table.invi>tfoot>tr>td,.edit_frm .invoice_layout table.invi>thead>tr>td,.edit_frm .invoice_layout table.invi>tr>td{vertical-align:top;font-weight:400;background-color:#f8f8f8;border-color:#f8f8f8;border-top-style:solid;border-bottom-style:solid;user-select:none}.edit_frm .invoice_layout table.invi>tbody>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>tfoot>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>thead>tr:hover>td.aux>.axf,.edit_frm .invoice_layout table.invi>tr:hover>td.aux>.axf{display:inline-flex}.edit_frm .invoice_layout table.invi>tbody>tr.isum>td,.edit_frm .invoice_layout table.invi>tfoot>tr.isum>td,.edit_frm .invoice_layout table.invi>thead>tr.isum>td,.edit_frm .invoice_layout table.invi>tr.isum>td{border-top:1px solid #000;background:#fff}.edit_frm .invoice_layout table.invi>tbody>tr.sep>td,.edit_frm .invoice_layout table.invi>tfoot>tr.sep>td,.edit_frm .invoice_layout table.invi>thead>tr.sep>td,.edit_frm .invoice_layout table.invi>tr.sep>td{background:#fff}.edit_frm .invoice_layout table.invi>tbody>tr.tsum td,.edit_frm .invoice_layout table.invi>tfoot>tr.tsum td,.edit_frm .invoice_layout table.invi>thead>tr.tsum td,.edit_frm .invoice_layout table.invi>tr.tsum td{font-weight:600;border-top:1px solid #000;border-bottom:1px solid #000}.edit_frm .invoice_layout table.invi .sortable>tr>td.aux .ico.move{background:url(data:image/svg+xml;base64,PHN2ZyBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAzMiAzMiIgaWQ9IkxheWVyXzQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjE2LDMxIDgsMTggMjQsMTggICAgICIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iI0RERERERCIvPjxwb2x5Z29uIGZpbGw9Im5vbmUiIHBvaW50cz0iMTYsMSA4LDE0IDI0LDE0ICAgICAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2U9IiNEREREREQiLz48L2c+PC9zdmc+) center center/contain no-repeat border-box}.edit_frm .invoice_layout table.invi .sortable>tr>td.aux .ico.move:hover{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMzIgMzIiIGlkPSJMYXllcl80IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGc+PHBvbHlnb24gZmlsbD0ibm9uZSIgcG9pbnRzPSIxNiwzMSA4LDE4IDI0LDE4ICAgICAiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2Utd2lkdGg9IjIiLz48cG9seWdvbiBmaWxsPSJub25lIiBwb2ludHM9IjE2LDEgOCwxNCAyNCwxNCAgICAgIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9nPjwvc3ZnPg==)}.edit_frm .invoice_layout table.invi .sortable>tr.sortactive>td.ico{cursor:move}.edit_frm .invoice_layout .btn{background:url("") center center/contain no-repeat content-box #fff;position:absolute;top:.2rem;right:.2rem;height:3rem;width:3rem;padding:.1rem;z-index:11}.edit_frm .invoice_layout .btn.sprev{background-image:url(data:image/png;base64,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)}.edit_frm .invoice_layout .btn.sdoc{background-image:url(data:image/png;base64,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)}.edit_frm .invoice_layout.freeze::before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(50,50,50,.3);user-select:none;cursor:wait;z-index:10;pointer-events:none}.edit_frm .invoice_layout.freeze::before tr:hover>td.aux>.axf{display:inline-flex}.edit_frm .invoice_layout.freeze::after{content:"";position:absolute;top:50%;left:50%;width:3rem;height:3rem;margin:-1.5rem 0 0 -1.5rem;border:.4rem solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;z-index:11;pointer-events:none;animation:.8s linear infinite fis_spin}.edit_frm .invoice_layout .dvalidation{margin:0 0 1rem}.edit_frm .invoice_layout .dvalidation.hidden{display:none}.edit_frm .invoice_layout .dvalidation .dvmsg{padding:.4rem .7rem;margin:.25rem 0;border-radius:.2rem;font-size:.9rem;border-left:4px solid #999;background:#f5f5f5}.edit_frm .invoice_layout .dvalidation .dvmsg.error{border-left-color:#c0392b;background:#fdecea;color:#922}.edit_frm .invoice_layout .dvalidation .dvmsg.warning{border-left-color:#e0a800;background:#fff8e1;color:#7a5c00}.edit_frm .invoice_layout .dvalidation .dvmsg.info{border-left-color:#3498db;background:#eaf4fb;color:#1c5a82}.dhist .invtbl{width:100%}.dhist td,.dhist th{padding:.3rem .6rem;text-align:left;vertical-align:top}.dhist tbody tr:nth-child(even){background:#fafafa}.modal-body .lstfrm{display:block;position:relative}.modal-body .lstfrm .li{display:inline-block;background-color:#fff;border:1px solid #e0e0e0;padding:1.7rem .5rem .5rem;border-radius:.2rem;cursor:pointer;position:relative;margin:0 .5rem .5rem 0;box-shadow:1px 1px 3px rgba(50,50,50,.1)}.modal-body .lstfrm .li .lbl{position:absolute;background:#727272;color:#fff;font-size:.8rem;left:0;top:0;right:0;padding:.2rem;border-top-left-radius:inherit;border-top-right-radius:inherit}.adr-conformity{margin-top:.75rem;padding:.6rem .75rem;background:#f3f7fb;border:1px solid #cfe0f0;border-radius:4px;font-size:.85rem;line-height:1.35}.adr-conformity .hd{font-weight:600;margin-bottom:.2rem}.adr-conformity .tx{color:#445}.modal .setstruct.busy{opacity:.6;pointer-events:none}.modal .setstruct div.shint{font-size:.85rem;color:#666;margin:0 0 .8rem}.modal .setstruct div.sblock{margin:0 0 1.4rem}.modal .setstruct div.sblock div.sbhd{font-weight:700;padding:.4rem .6rem;border-top:1.5px solid #000;border-bottom:1px solid #ddd}.modal .setstruct div.sblock div.sbhd.dropok{background-color:#e6f2e6;outline:#5a5 dashed 2px}.modal .setstruct div.sblock table.stbl{width:100%;border-collapse:collapse}.modal .setstruct div.sblock table.stbl th{text-align:left;font-size:.8rem;color:#666;padding:.3rem .4rem;border-bottom:1px solid #ddd}.modal .setstruct div.sblock table.stbl td{padding:.3rem .4rem;border-bottom:1px solid #f0f0f0;vertical-align:middle}.modal .setstruct div.sblock table.stbl td.grip{width:1.6rem;cursor:grab;color:#999}.modal .setstruct div.sblock table.stbl td.pos,.modal .setstruct div.sblock table.stbl td.qty{width:4rem;white-space:nowrap}.modal .setstruct div.sblock table.stbl td.typ{width:7rem;white-space:nowrap;color:#666;font-size:.85rem}.modal .setstruct div.sblock table.stbl td.typ span.fold{cursor:pointer;margin-right:.35rem;color:#555;display:inline-block;width:1rem}.modal .setstruct div.sblock table.stbl td.ttl span.ind{display:inline-block}.modal .setstruct div.sblock table.stbl td.ttl span.seti{margin-right:.3rem;color:#888}.modal .setstruct div.sblock table.stbl td.ttl span.cnt{color:#999;font-weight:400;font-size:.85rem}.modal .setstruct div.sblock table.stbl td.act{width:7.5rem;white-space:nowrap;text-align:right}.modal .setstruct div.sblock table.stbl td.act div.ibtn{display:inline-block;cursor:pointer;padding:0 .25rem}.modal .setstruct div.sblock table.stbl td.act div.ibtn.disabled{opacity:.25;cursor:default}.modal .setstruct div.sblock table.stbl tr.srow.isset td.ttl{font-weight:700}.modal .setstruct div.sblock table.stbl tr.srow.hid{display:none}.modal .setstruct div.sblock table.stbl tr.srow.folded td.ttl{font-style:italic}.modal .setstruct div.sblock table.stbl tr.srow.dragging{opacity:.4}.modal .setstruct div.sblock table.stbl tr.srow.dropok td{background-color:#e6f2e6}.modal .setstruct div.sblock table.stbl tr.srow.dropbefore td{border-top:3px solid #5a5}.modal .setstruct div.sblock table.stbl tr.srow.dropafter td{border-bottom:3px solid #5a5}.modal .setstruct div.sblock div.sroot{margin:.3rem 0 .5rem;padding:.4rem .6rem;border:1px dashed #bbb;color:#888;font-size:.85rem}.modal .setstruct div.sblock div.sroot.dropok{background-color:#e6f2e6;border-color:#5a5;color:#363}.modal .setstruct div.sblock div.screate{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;padding:.4rem .6rem;background-color:#f7f7f7}.modal .setstruct div.sblock div.screate span.lbl{font-size:.85rem;color:#666}.modal .setstruct div.sblock div.screate input[type=text],.modal .setstruct div.sblock div.screate select{padding:.2rem .3rem}.modal .setstruct div.sblock div.screate input[type=text]{flex:1 1 14rem;min-width:10rem} \ No newline at end of file diff --git a/Fuchs/wwwroot/web/fis.js b/Fuchs/wwwroot/web/fis.js index 787d162..cb9035a 100644 --- a/Fuchs/wwwroot/web/fis.js +++ b/Fuchs/wwwroot/web/fis.js @@ -2882,8 +2882,11 @@ $ocms.init = function (ev) { } $ocms[mdl].auth = auth; if (auth.manage > 0) { + /* ?v= (newest bundle write time, emitted by _Layout) busts the browser + cache of the dynamically imported module bundle + css after a gulp rebuild. */ + let av = '?v=' + encodeURIComponent($ocms.auth.assetversion || '0'); $ocms.getScript({ - module: mdl, script: ['/web/fis', mdl, $ocms.auth.locale || 'de', 'js'].join('.'), css: ['/web/fis', mdl, 'css'].join('.'), condition: typeof $ocms[mdl].init2 !== 'function' + module: mdl, script: ['/web/fis', mdl, $ocms.auth.locale || 'de', 'js'].join('.') + av, css: ['/web/fis', mdl, 'css'].join('.') + av, condition: typeof $ocms[mdl].init2 !== 'function' }, function () { $ocms[mdl].init2(); }); } }, error: function () { diff --git a/Fuchs/wwwroot/web/fis.min.js b/Fuchs/wwwroot/web/fis.min.js index 3c61008..9585cc6 100644 --- a/Fuchs/wwwroot/web/fis.min.js +++ b/Fuchs/wwwroot/web/fis.min.js @@ -1,4 +1,4 @@ var t,e,$t={lng:"de-DE",dn:["So","Mo","Di","Mi","Do","Fr","Sa"],mn:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],ma:["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],datepattern:"(0[1-9]|1[0-9]|2[0-9]|3[01]).(0[1-9]|1[012]).[0-9]{4}",datetimepattern:"(0[1-9]|1[0-9]|2[0-9]|3[01]).(0[1-9]|1[012]).[0-9]{4}\\s([0-5][0-9]):([0-5][0-9])",dateplaceholder:"dd.MM.yyyy",datetimeplaceholder:"dd.MM.yyyy HH:mm",dateformat:"dd.MM.yyyy",datetimeformat:"dd.MM.yyyy HH:mm",f1:"Der Server hat einen Fehler gemeldet: \n",f2:"Bitte versuchen Sie es erneut.",m0:"Diese Internet-Seite benötigt einen html5-kompatiblen Browser.",m0b:"Unterstützt werden bspw: Internet Explorer ab Version 10, Firefox ab Version 31, Chrome ab Version 31, Safari ab Version 7, Opera ab Version 27",m1:"Dieser Datensatz ist momentan von jemand anderem zur Bearbeitung gesperrt.",m2:"Diese Funktion ist zur Zeit nicht verfügbar",t1:"Eingabe erforderlich.",t2:"Eingabe ist nicht erforderlich.",true:"Ja",false:"Nein",alert:"Hinweis",confirm:"Bestätigen",open:"Öffnen","not implemented":"Diese Funktion in zur Zeit noch nicht verfügbar.",l0:"Anmeldung",l1:"Email / Anmeldename",l2:"Email-Adresse / Anmeldename",l3:"Passwort",l4:"Benutzer",l5:"Wird vom System ermittelt...",l6:"Anmelden",l7:"Passwort vergessen?",l7a:'Die "Passwort vergessen"-Funktion läuft in zwei Schritten ab:\n \nIm ersten Schritt wird eine SMS mit einem Code an die hinterlegte Mobilfunk-Nummer versandt.\nIm zweiten Schritt geben Sie bitte diesen Code in das Formular ein und übermitteln es erneut.\n \nIn beiden Schritten wird aus Sicherheitsgründen kein Fehler angezeigt und auch dann ein erfolgreicher Versand bestätigt, wenn die Kombination aus Email-Adresse und Nachname nicht gefunden wurde und/oder der code falsch ist.',l8:"Keinen Account?",l9:"Anmeldenamen der Email-Adresse wurde nicht erkannt.",l10:"Nachname",l11:"Email-Adresse",l12:"Passwort zusenden",l13:"Das Passwort wurde erfolgreich verschickt",l14:"Das Passwort konnte nicht verschickt werden",l15:"Sie sind nicht berechtigt, diese Funktion auszuführen.",l16:"Sie müssen zunächst einen Account angeben.",l17:"Die Kombination aus Anmeldenamen und Passwort konnte nicht bestätigt werden.",l18:"Es gibt ein Problem mit dem Formular.\nEs kann momentan nicht verarbeitet und versendet werden.",name:"Name",submit:"Senden",cancel:"Abbrechen",noop:"Diese Funktion is noch nicht verfügar."};t=self,e=()=>(()=>{var t={d:(e,n)=>{for(var i in n)t.o(n,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:n[i]})}};t.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),t.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),t.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"t",{value:!0})};var e,n={};t.r(n),t.d(n,{AbortError:()=>r,DefaultHttpClient:()=>D,HttpClient:()=>h,HttpError:()=>i,HttpResponse:()=>d,HttpTransportType:()=>U,HubConnection:()=>O,HubConnectionBuilder:()=>tt,HubConnectionState:()=>j,JsonHubProtocol:()=>K,LogLevel:()=>e,MessageType:()=>N,NullLogger:()=>p,Subject:()=>q,TimeoutError:()=>o,TransferFormat:()=>B,VERSION:()=>f});class i extends Error{constructor(t,e){const n=new.target.prototype;super(`${t}: Status code '${e}'`),this.statusCode=e,this.__proto__=n}}class o extends Error{constructor(t="A timeout occurred."){const e=new.target.prototype;super(t),this.__proto__=e}}class r extends Error{constructor(t="An abort occurred."){const e=new.target.prototype;super(t),this.__proto__=e}}class s extends Error{constructor(t,e){const n=new.target.prototype;super(t),this.transport=e,this.errorType="UnsupportedTransportError",this.__proto__=n}}class a extends Error{constructor(t,e){const n=new.target.prototype;super(t),this.transport=e,this.errorType="DisabledTransportError",this.__proto__=n}}class c extends Error{constructor(t,e){const n=new.target.prototype;super(t),this.transport=e,this.errorType="FailedToStartTransportError",this.__proto__=n}}class l extends Error{constructor(t){const e=new.target.prototype;super(t),this.errorType="FailedToNegotiateWithServerError",this.__proto__=e}}class u extends Error{constructor(t,e){const n=new.target.prototype;super(t),this.innerErrors=e,this.__proto__=n}}class d{constructor(t,e,n){this.statusCode=t,this.statusText=e,this.content=n}}class h{get(t,e){return this.send({...e,method:"GET",url:t})}post(t,e){return this.send({...e,method:"POST",url:t})}delete(t,e){return this.send({...e,method:"DELETE",url:t})}getCookieString(t){return""}}!function(t){t[t.Trace=0]="Trace",t[t.Debug=1]="Debug",t[t.Information=2]="Information",t[t.Warning=3]="Warning",t[t.Error=4]="Error",t[t.Critical=5]="Critical",t[t.None=6]="None"}(e||(e={}));class p{constructor(){}log(t,e){}}p.instance=new p;const f="10.0.0";class m{static isRequired(t,e){if(null==t)throw new Error(`The '${e}' argument is required.`)}static isNotEmpty(t,e){if(!t||t.match(/^\s*$/))throw new Error(`The '${e}' argument should not be empty.`)}static isIn(t,e,n){if(!(t in e))throw new Error(`Unknown ${n} value: ${t}.`)}}class g{static get isBrowser(){return!g.isNode&&"object"==typeof window&&"object"==typeof window.document}static get isWebWorker(){return!g.isNode&&"object"==typeof self&&"importScripts"in self}static get isReactNative(){return!g.isNode&&"object"==typeof window&&void 0===window.document}static get isNode(){return"undefined"!=typeof process&&process.release&&"node"===process.release.name}}function y(t,e){let n="";return v(t)?(n=`Binary data of length ${t.byteLength}`,e&&(n+=`. Content: '${function(t){const e=new Uint8Array(t);let n="";return e.forEach((t=>{n+=`0x${t<16?"0":""}${t.toString(16)} `})),n.substring(0,n.length-1)}(t)}'`)):"string"==typeof t&&(n=`String data of length ${t.length}`,e&&(n+=`. Content: '${t}'`)),n}function v(t){return t&&"undefined"!=typeof ArrayBuffer&&(t instanceof ArrayBuffer||t.constructor&&"ArrayBuffer"===t.constructor.name)}async function b(t,n,i,o,r,s){const a={},[c,l]=C();a[c]=l,t.log(e.Trace,`(${n} transport) sending data. ${y(r,s.logMessageContent)}.`);const u=v(r)?"arraybuffer":"text",d=await i.post(o,{content:r,headers:{...a,...s.headers},responseType:u,timeout:s.timeout,withCredentials:s.withCredentials});t.log(e.Trace,`(${n} transport) request complete. Response status: ${d.statusCode}.`)}class ${constructor(t,e){this.i=t,this.h=e}dispose(){const t=this.i.observers.indexOf(this.h);t>-1&&this.i.observers.splice(t,1),0===this.i.observers.length&&this.i.cancelCallback&&this.i.cancelCallback().catch((t=>{}))}}class w{constructor(t){this.l=t,this.out=console}log(t,n){if(t>=this.l){const i=`[${(new Date).toISOString()}] ${e[t]}: ${n}`;switch(t){case e.Critical:case e.Error:this.out.error(i);break;case e.Warning:this.out.warn(i);break;case e.Information:this.out.info(i);break;default:this.out.log(i)}}}}function C(){let t="X-SignalR-User-Agent";return g.isNode&&(t="User-Agent"),[t,S(f,_(),g.isNode?"NodeJS":"Browser",x())]}function S(t,e,n,i){let o="Microsoft SignalR/";const r=t.split(".");return o+=`${r[0]}.${r[1]}`,o+=` (${t}; `,o+=e&&""!==e?`${e}; `:"Unknown OS; ",o+=`${n}`,o+=i?`; ${i}`:"; Unknown Runtime Version",o+=")",o}function _(){if(!g.isNode)return"";switch(process.platform){case"win32":return"Windows NT";case"darwin":return"macOS";case"linux":return"Linux";default:return process.platform}}function x(){if(g.isNode)return process.versions.node}function T(t){return t.stack?t.stack:t.message?t.message:`${t}`}class E extends h{constructor(e){if(super(),this.u=e,"undefined"==typeof fetch||g.isNode){const t=require;this.p=new(t("tough-cookie").CookieJar),"undefined"==typeof fetch?this.m=t("node-fetch"):this.m=fetch,this.m=t("fetch-cookie")(this.m,this.p)}else this.m=fetch.bind(function(){if("undefined"!=typeof globalThis)return globalThis;if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==t.g)return t.g;throw new Error("could not find global")}());if("undefined"==typeof AbortController){const t=require;this.v=t("abort-controller")}else this.v=AbortController}async send(t){if(t.abortSignal&&t.abortSignal.aborted)throw new r;if(!t.method)throw new Error("No method defined.");if(!t.url)throw new Error("No url defined.");const n=new this.v;let s;t.abortSignal&&(t.abortSignal.onabort=()=>{n.abort(),s=new r});let a,c=null;if(t.timeout){const i=t.timeout;c=setTimeout((()=>{n.abort(),this.u.log(e.Warning,"Timeout from HTTP request."),s=new o}),i)}""===t.content&&(t.content=void 0),t.content&&(t.headers=t.headers||{},v(t.content)?t.headers["Content-Type"]="application/octet-stream":t.headers["Content-Type"]="text/plain;charset=UTF-8");try{a=await this.m(t.url,{body:t.content,cache:"no-cache",credentials:!0===t.withCredentials?"include":"same-origin",headers:{"X-Requested-With":"XMLHttpRequest",...t.headers},method:t.method,mode:"cors",redirect:"follow",signal:n.signal})}catch(t){if(s)throw s;throw this.u.log(e.Warning,`Error from HTTP request. ${t}.`),t}finally{c&&clearTimeout(c),t.abortSignal&&(t.abortSignal.onabort=null)}if(!a.ok){const t=await k(a,"text");throw new i(t||a.statusText,a.status)}const l=k(a,t.responseType),u=await l;return new d(a.status,a.statusText,u)}getCookieString(t){let e="";return g.isNode&&this.p&&this.p.getCookies(t,((t,n)=>e=n.join("; "))),e}}function k(t,e){let n;switch(e){case"arraybuffer":n=t.arrayBuffer();break;case"text":default:n=t.text();break;case"blob":case"document":case"json":throw new Error(`${e} is not supported.`)}return n}class I extends h{constructor(t){super(),this.u=t}send(t){return t.abortSignal&&t.abortSignal.aborted?Promise.reject(new r):t.method?t.url?new Promise(((n,s)=>{const a=new XMLHttpRequest;a.open(t.method,t.url,!0),a.withCredentials=void 0===t.withCredentials||t.withCredentials,a.setRequestHeader("X-Requested-With","XMLHttpRequest"),""===t.content&&(t.content=void 0),t.content&&(v(t.content)?a.setRequestHeader("Content-Type","application/octet-stream"):a.setRequestHeader("Content-Type","text/plain;charset=UTF-8"));const c=t.headers;c&&Object.keys(c).forEach((t=>{a.setRequestHeader(t,c[t])})),t.responseType&&(a.responseType=t.responseType),t.abortSignal&&(t.abortSignal.onabort=()=>{a.abort(),s(new r)}),t.timeout&&(a.timeout=t.timeout),a.onload=()=>{t.abortSignal&&(t.abortSignal.onabort=null),a.status>=200&&a.status<300?n(new d(a.status,a.statusText,a.response||a.responseText)):s(new i(a.response||a.responseText||a.statusText,a.status))},a.onerror=()=>{this.u.log(e.Warning,`Error from HTTP request. ${a.status}: ${a.statusText}.`),s(new i(a.statusText,a.status))},a.ontimeout=()=>{this.u.log(e.Warning,"Timeout from HTTP request."),s(new o)},a.send(t.content)})):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}}class D extends h{constructor(t){if(super(),"undefined"!=typeof fetch||g.isNode)this.$=new E(t);else{if("undefined"==typeof XMLHttpRequest)throw new Error("No usable HttpClient found.");this.$=new I(t)}}send(t){return t.abortSignal&&t.abortSignal.aborted?Promise.reject(new r):t.method?t.url?this.$.send(t):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}getCookieString(t){return this.$.getCookieString(t)}}class A{static write(t){return`${t}${A.RecordSeparator}`}static parse(t){if(t[t.length-1]!==A.RecordSeparator)throw new Error("Message is incomplete.");const e=t.split(A.RecordSeparator);return e.pop(),e}}A.RecordSeparatorCode=30,A.RecordSeparator=String.fromCharCode(A.RecordSeparatorCode);class P{writeHandshakeRequest(t){return A.write(JSON.stringify(t))}parseHandshakeResponse(t){let e,n;if(v(t)){const i=new Uint8Array(t),o=i.indexOf(A.RecordSeparatorCode);if(-1===o)throw new Error("Message is incomplete.");const r=o+1;e=String.fromCharCode.apply(null,Array.prototype.slice.call(i.slice(0,r))),n=i.byteLength>r?i.slice(r).buffer:null}else{const i=t,o=i.indexOf(A.RecordSeparator);if(-1===o)throw new Error("Message is incomplete.");const r=o+1;e=i.substring(0,r),n=i.length>r?i.substring(r):null}const i=A.parse(e),o=JSON.parse(i[0]);if(o.type)throw new Error("Expected a handshake response from the server.");return[n,o]}}var N,j;!function(t){t[t.Invocation=1]="Invocation",t[t.StreamItem=2]="StreamItem",t[t.Completion=3]="Completion",t[t.StreamInvocation=4]="StreamInvocation",t[t.CancelInvocation=5]="CancelInvocation",t[t.Ping=6]="Ping",t[t.Close=7]="Close",t[t.Ack=8]="Ack",t[t.Sequence=9]="Sequence"}(N||(N={}));class q{constructor(){this.observers=[]}next(t){for(const e of this.observers)e.next(t)}error(t){for(const e of this.observers)e.error&&e.error(t)}complete(){for(const t of this.observers)t.complete&&t.complete()}subscribe(t){return this.observers.push(t),new $(this,t)}}class M{constructor(t,e,n){this.C=1e5,this.S=[],this.k=0,this.P=!1,this.T=1,this.I=0,this._=0,this.H=!1,this.D=t,this.R=e,this.C=n}async A(t){const e=this.D.writeMessage(t);let n=Promise.resolve();if(this.U(t)){this.k++;let t=()=>{},i=()=>{};v(e)?this._+=e.byteLength:this._+=e.length,this._>=this.C&&(n=new Promise(((e,n)=>{t=e,i=n}))),this.S.push(new R(e,this.k,t,i))}try{this.H||await this.R.send(e)}catch{this.L()}await n}N(t){let e=-1;for(let n=0;nthis.T?this.R.stop(new Error("Sequence ID greater than amount of messages we've received.")):this.T=t.sequenceId}L(){this.H=!0,this.P=!0}async B(){const t=0!==this.S.length?this.S[0].q:this.k+1;await this.R.send(this.D.writeMessage({type:N.Sequence,sequenceId:t}));const e=this.S;for(const t of e)await this.R.send(t.M);this.H=!1}X(t){null!=t||(t=new Error("Unable to reconnect to server."));for(const e of this.S)e.J(t)}U(t){switch(t.type){case N.Invocation:case N.StreamItem:case N.Completion:case N.StreamInvocation:case N.CancelInvocation:return!0;case N.Close:case N.Sequence:case N.Ping:case N.Ack:return!1}}O(){void 0===this.V&&(this.V=setTimeout((async()=>{try{this.H||await this.R.send(this.D.writeMessage({type:N.Ack,sequenceId:this.I}))}catch{}clearTimeout(this.V),this.V=void 0}),1e3))}}class R{constructor(t,e,n,i){this.M=t,this.q=e,this.j=n,this.J=i}}!function(t){t.Disconnected="Disconnected",t.Connecting="Connecting",t.Connected="Connected",t.Disconnecting="Disconnecting",t.Reconnecting="Reconnecting"}(j||(j={}));class O{static create(t,e,n,i,o,r,s){return new O(t,e,n,i,o,r,s)}constructor(t,n,i,o,r,s,a){this.K=0,this.G=()=>{this.u.log(e.Warning,"The page is being frozen, this will likely lead to the connection being closed and messages being lost. For more information see the docs at https://learn.microsoft.com/aspnet/core/signalr/javascript-client#bsleep")},m.isRequired(t,"connection"),m.isRequired(n,"logger"),m.isRequired(i,"protocol"),this.serverTimeoutInMilliseconds=null!=r?r:3e4,this.keepAliveIntervalInMilliseconds=null!=s?s:15e3,this.Y=null!=a?a:1e5,this.u=n,this.D=i,this.connection=t,this.Z=o,this.tt=new P,this.connection.onreceive=t=>this.et(t),this.connection.onclose=t=>this.st(t),this.it={},this.nt={},this.rt=[],this.ot=[],this.ht=[],this.ct=0,this.lt=!1,this.ut=j.Disconnected,this.dt=!1,this.ft=this.D.writeMessage({type:N.Ping})}get state(){return this.ut}get connectionId(){return this.connection&&this.connection.connectionId||null}get baseUrl(){return this.connection.baseUrl||""}set baseUrl(t){if(this.ut!==j.Disconnected&&this.ut!==j.Reconnecting)throw new Error("The HubConnection must be in the Disconnected or Reconnecting state to change the url.");if(!t)throw new Error("The HubConnection url must be a valid url.");this.connection.baseUrl=t}start(){return this.wt=this.gt(),this.wt}async gt(){if(this.ut!==j.Disconnected)return Promise.reject(new Error("Cannot start a HubConnection that is not in the 'Disconnected' state."));this.ut=j.Connecting,this.u.log(e.Debug,"Starting HubConnection.");try{await this.yt(),g.isBrowser&&window.document.addEventListener("freeze",this.G),this.ut=j.Connected,this.dt=!0,this.u.log(e.Debug,"HubConnection connected successfully.")}catch(t){return this.ut=j.Disconnected,this.u.log(e.Debug,`HubConnection failed to start successfully because of error '${t}'.`),Promise.reject(t)}}async yt(){this.vt=void 0,this.lt=!1;const t=new Promise(((t,e)=>{this.bt=t,this.Et=e}));await this.connection.start(this.D.transferFormat);try{let n=this.D.version;this.connection.features.reconnect||(n=1);const i={protocol:this.D.name,version:n};if(this.u.log(e.Debug,"Sending handshake request."),await this.$t(this.tt.writeHandshakeRequest(i)),this.u.log(e.Information,`Using HubProtocol '${this.D.name}'.`),this.Ct(),this.St(),this.kt(),await t,this.vt)throw this.vt;!!this.connection.features.reconnect&&(this.Pt=new M(this.D,this.connection,this.Y),this.connection.features.disconnected=this.Pt.L.bind(this.Pt),this.connection.features.resend=()=>{if(this.Pt)return this.Pt.B()}),this.connection.features.inherentKeepAlive||await this.$t(this.ft)}catch(t){throw this.u.log(e.Debug,`Hub handshake failed with error '${t}' during start(). Stopping HubConnection.`),this.Ct(),this.Tt(),await this.connection.stop(t),t}}async stop(){const t=this.wt;this.connection.features.reconnect=!1,this.It=this._t(),await this.It;try{await t}catch(t){}}_t(t){if(this.ut===j.Disconnected)return this.u.log(e.Debug,`Call to HubConnection.stop(${t}) ignored because it is already in the disconnected state.`),Promise.resolve();if(this.ut===j.Disconnecting)return this.u.log(e.Debug,`Call to HttpConnection.stop(${t}) ignored because the connection is already in the disconnecting state.`),this.It;const n=this.ut;return this.ut=j.Disconnecting,this.u.log(e.Debug,"Stopping HubConnection."),this.Ht?(this.u.log(e.Debug,"Connection stopped during reconnect delay. Done reconnecting."),clearTimeout(this.Ht),this.Ht=void 0,this.Dt(),Promise.resolve()):(n===j.Connected&&this.Rt(),this.Ct(),this.Tt(),this.vt=t||new r("The connection was stopped before the hub handshake could complete."),this.connection.stop(t))}async Rt(){try{await this.xt(this.At())}catch{}}stream(t,...e){const[n,i]=this.Ut(e),o=this.Lt(t,e,i);let r;const s=new q;return s.cancelCallback=()=>{const t=this.Nt(o.invocationId);return delete this.it[o.invocationId],r.then((()=>this.xt(t)))},this.it[o.invocationId]=(t,e)=>{e?s.error(e):t&&(t.type===N.Completion?t.error?s.error(new Error(t.error)):s.complete():s.next(t.item))},r=this.xt(o).catch((t=>{s.error(t),delete this.it[o.invocationId]})),this.qt(n,r),s}$t(t){return this.kt(),this.connection.send(t)}xt(t){return this.Pt?this.Pt.A(t):this.$t(this.D.writeMessage(t))}send(t,...e){const[n,i]=this.Ut(e),o=this.xt(this.Mt(t,e,!0,i));return this.qt(n,o),o}invoke(t,...e){const[n,i]=this.Ut(e),o=this.Mt(t,e,!1,i);return new Promise(((t,e)=>{this.it[o.invocationId]=(n,i)=>{i?e(i):n&&(n.type===N.Completion?n.error?e(new Error(n.error)):t(n.result):e(new Error(`Unexpected message type: ${n.type}`)))};const i=this.xt(o).catch((t=>{e(t),delete this.it[o.invocationId]}));this.qt(n,i)}))}on(t,e){t&&e&&(t=t.toLowerCase(),this.nt[t]||(this.nt[t]=[]),-1===this.nt[t].indexOf(e)&&this.nt[t].push(e))}off(t,e){if(!t)return;t=t.toLowerCase();const n=this.nt[t];if(n)if(e){const i=n.indexOf(e);-1!==i&&(n.splice(i,1),0===n.length&&delete this.nt[t])}else delete this.nt[t]}onclose(t){t&&this.rt.push(t)}onreconnecting(t){t&&this.ot.push(t)}onreconnected(t){t&&this.ht.push(t)}et(t){if(this.Ct(),this.lt||(t=this.jt(t),this.lt=!0),t){const n=this.D.parseMessages(t,this.u);for(const i of n)if(!this.Pt||this.Pt.W(i))switch(i.type){case N.Invocation:this.Wt(i).catch((t=>{this.u.log(e.Error,`Invoke client method threw error: ${T(t)}`)}));break;case N.StreamItem:case N.Completion:{const n=this.it[i.invocationId];if(n){i.type===N.Completion&&delete this.it[i.invocationId];try{n(i)}catch(t){this.u.log(e.Error,`Stream callback threw error: ${T(t)}`)}}break}case N.Ping:break;case N.Close:{this.u.log(e.Information,"Close message received from server.");const t=i.error?new Error("Server returned an error on close: "+i.error):void 0;!0===i.allowReconnect?this.connection.stop(t):this.It=this._t(t);break}case N.Ack:this.Pt&&this.Pt.N(i);break;case N.Sequence:this.Pt&&this.Pt.F(i);break;default:this.u.log(e.Warning,`Invalid message type: ${i.type}.`)}}this.St()}jt(t){let n,i;try{[i,n]=this.tt.parseHandshakeResponse(t)}catch(t){const n="Error parsing handshake response: "+t;this.u.log(e.Error,n);const i=new Error(n);throw this.Et(i),i}if(n.error){const t="Server returned handshake error: "+n.error;this.u.log(e.Error,t);const i=new Error(t);throw this.Et(i),i}return this.u.log(e.Debug,"Server handshake complete."),this.bt(),i}kt(){this.connection.features.inherentKeepAlive||(this.K=(new Date).getTime()+this.keepAliveIntervalInMilliseconds,this.Tt())}St(){if(!this.connection.features||!this.connection.features.inherentKeepAlive){this.Ot=setTimeout((()=>this.serverTimeout()),this.serverTimeoutInMilliseconds);let t=this.K-(new Date).getTime();if(t<0)return void(this.ut===j.Connected&&this.Ft());void 0===this.Bt&&(t<0&&(t=0),this.Bt=setTimeout((async()=>{this.ut===j.Connected&&await this.Ft()}),t))}}serverTimeout(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))}async Wt(t){const n=t.target.toLowerCase(),i=this.nt[n];if(!i)return this.u.log(e.Warning,`No client method with the name '${n}' found.`),void(t.invocationId&&(this.u.log(e.Warning,`No result given for '${n}' method and invocation ID '${t.invocationId}'.`),await this.xt(this.Xt(t.invocationId,"Client didn't provide a result.",null))));const o=i.slice(),r=!!t.invocationId;let s,a,c;for(const i of o)try{const o=s;s=await i.apply(this,t.arguments),r&&s&&o&&(this.u.log(e.Error,`Multiple results provided for '${n}'. Sending error to server.`),c=this.Xt(t.invocationId,"Client provided multiple results.",null)),a=void 0}catch(t){a=t,this.u.log(e.Error,`A callback for the method '${n}' threw error '${t}'.`)}c?await this.xt(c):r?(a?c=this.Xt(t.invocationId,`${a}`,null):void 0!==s?c=this.Xt(t.invocationId,null,s):(this.u.log(e.Warning,`No result given for '${n}' method and invocation ID '${t.invocationId}'.`),c=this.Xt(t.invocationId,"Client didn't provide a result.",null)),await this.xt(c)):s&&this.u.log(e.Error,`Result given for '${n}' method but server is not expecting a result.`)}st(t){this.u.log(e.Debug,`HubConnection.connectionClosed(${t}) called while in state ${this.ut}.`),this.vt=this.vt||t||new r("The underlying connection was closed before the hub handshake could complete."),this.bt&&this.bt(),this.Jt(t||new Error("Invocation canceled due to the underlying connection being closed.")),this.Ct(),this.Tt(),this.ut===j.Disconnecting?this.Dt(t):this.ut===j.Connected&&this.Z?this.zt(t):this.ut===j.Connected&&this.Dt(t)}Dt(t){if(this.dt){this.ut=j.Disconnected,this.dt=!1,this.Pt&&(this.Pt.X(null!=t?t:new Error("Connection closed.")),this.Pt=void 0),g.isBrowser&&window.document.removeEventListener("freeze",this.G);try{this.rt.forEach((e=>e.apply(this,[t])))}catch(n){this.u.log(e.Error,`An onclose callback called with error '${t}' threw error '${n}'.`)}}}async zt(t){const n=Date.now();let i=0,o=void 0!==t?t:new Error("Attempting to reconnect due to a unknown error."),r=this.Vt(i,0,o);if(null===r)return this.u.log(e.Debug,"Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt."),void this.Dt(t);if(this.ut=j.Reconnecting,t?this.u.log(e.Information,`Connection reconnecting because of error '${t}'.`):this.u.log(e.Information,"Connection reconnecting."),0!==this.ot.length){try{this.ot.forEach((e=>e.apply(this,[t])))}catch(n){this.u.log(e.Error,`An onreconnecting callback called with error '${t}' threw error '${n}'.`)}if(this.ut!==j.Reconnecting)return void this.u.log(e.Debug,"Connection left the reconnecting state in onreconnecting callback. Done reconnecting.")}for(;null!==r;){if(this.u.log(e.Information,`Reconnect attempt number ${i+1} will start in ${r} ms.`),await new Promise((t=>{this.Ht=setTimeout(t,r)})),this.Ht=void 0,this.ut!==j.Reconnecting)return void this.u.log(e.Debug,"Connection left the reconnecting state during reconnect delay. Done reconnecting.");try{if(await this.yt(),this.ut=j.Connected,this.u.log(e.Information,"HubConnection reconnected successfully."),0!==this.ht.length)try{this.ht.forEach((t=>t.apply(this,[this.connection.connectionId])))}catch(t){this.u.log(e.Error,`An onreconnected callback called with connectionId '${this.connection.connectionId}; threw error '${t}'.`)}return}catch(t){if(this.u.log(e.Information,`Reconnect attempt failed because of error '${t}'.`),this.ut!==j.Reconnecting)return this.u.log(e.Debug,`Connection moved to the '${this.ut}' from the reconnecting state during reconnect attempt. Done reconnecting.`),void(this.ut===j.Disconnecting&&this.Dt());i++,o=t instanceof Error?t:new Error(t.toString()),r=this.Vt(i,Date.now()-n,o)}}this.u.log(e.Information,`Reconnect retries have been exhausted after ${Date.now()-n} ms and ${i} failed attempts. Connection disconnecting.`),this.Dt()}Vt(t,n,i){try{return this.Z.nextRetryDelayInMilliseconds({elapsedMilliseconds:n,previousRetryCount:t,retryReason:i})}catch(i){return this.u.log(e.Error,`IRetryPolicy.nextRetryDelayInMilliseconds(${t}, ${n}) threw error '${i}'.`),null}}Jt(t){const n=this.it;this.it={},Object.keys(n).forEach((i=>{const o=n[i];try{o(null,t)}catch(n){this.u.log(e.Error,`Stream 'error' callback called with '${t}' threw error: ${T(n)}`)}}))}Tt(){this.Bt&&(clearTimeout(this.Bt),this.Bt=void 0)}Ct(){this.Ot&&clearTimeout(this.Ot)}Mt(t,e,n,i){if(n)return 0!==i.length?{target:t,arguments:e,streamIds:i,type:N.Invocation}:{target:t,arguments:e,type:N.Invocation};{const n=this.ct;return this.ct++,0!==i.length?{target:t,arguments:e,invocationId:n.toString(),streamIds:i,type:N.Invocation}:{target:t,arguments:e,invocationId:n.toString(),type:N.Invocation}}}qt(t,e){if(0!==t.length){e||(e=Promise.resolve());for(const n in t)t[n].subscribe({complete:()=>{e=e.then((()=>this.xt(this.Xt(n))))},error:t=>{let i;i=t instanceof Error?t.message:t&&t.toString?t.toString():"Unknown error",e=e.then((()=>this.xt(this.Xt(n,i))))},next:t=>{e=e.then((()=>this.xt(this.Kt(n,t))))}})}}Ut(t){const e=[],n=[];for(let i=0;i0)&&(e=!1,this.te=await this.Zt()),this.ee(t);const n=await this.Yt.send(t);return e&&401===n.statusCode&&this.Zt?(this.te=await this.Zt(),this.ee(t),await this.Yt.send(t)):n}ee(t){t.headers||(t.headers={}),this.te?t.headers[L.Authorization]=`Bearer ${this.te}`:this.Zt&&t.headers[L.Authorization]&&delete t.headers[L.Authorization]}getCookieString(t){return this.Yt.getCookieString(t)}}var U,B;!function(t){t[t.None=0]="None",t[t.WebSockets=1]="WebSockets",t[t.ServerSentEvents=2]="ServerSentEvents",t[t.LongPolling=4]="LongPolling"}(U||(U={})),function(t){t[t.Text=1]="Text",t[t.Binary=2]="Binary"}(B||(B={}));class W{constructor(){this.se=!1,this.onabort=null}abort(){this.se||(this.se=!0,this.onabort&&this.onabort())}get signal(){return this}get aborted(){return this.se}}class X{get pollAborted(){return this.ie.aborted}constructor(t,e,n){this.$=t,this.u=e,this.ie=new W,this.ne=n,this.re=!1,this.onreceive=null,this.onclose=null}async connect(t,n){if(m.isRequired(t,"url"),m.isRequired(n,"transferFormat"),m.isIn(n,B,"transferFormat"),this.oe=t,this.u.log(e.Trace,"(LongPolling transport) Connecting."),n===B.Binary&&"undefined"!=typeof XMLHttpRequest&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");const[o,r]=C(),s={[o]:r,...this.ne.headers},a={abortSignal:this.ie.signal,headers:s,timeout:1e5,withCredentials:this.ne.withCredentials};n===B.Binary&&(a.responseType="arraybuffer");const c=`${t}&_=${Date.now()}`;this.u.log(e.Trace,`(LongPolling transport) polling: ${c}.`);const l=await this.$.get(c,a);200!==l.statusCode?(this.u.log(e.Error,`(LongPolling transport) Unexpected response code: ${l.statusCode}.`),this.he=new i(l.statusText||"",l.statusCode),this.re=!1):this.re=!0,this.ce=this.ae(this.oe,a)}async ae(t,n){try{for(;this.re;)try{const o=`${t}&_=${Date.now()}`;this.u.log(e.Trace,`(LongPolling transport) polling: ${o}.`);const r=await this.$.get(o,n);204===r.statusCode?(this.u.log(e.Information,"(LongPolling transport) Poll terminated by server."),this.re=!1):200!==r.statusCode?(this.u.log(e.Error,`(LongPolling transport) Unexpected response code: ${r.statusCode}.`),this.he=new i(r.statusText||"",r.statusCode),this.re=!1):r.content?(this.u.log(e.Trace,`(LongPolling transport) data received. ${y(r.content,this.ne.logMessageContent)}.`),this.onreceive&&this.onreceive(r.content)):this.u.log(e.Trace,"(LongPolling transport) Poll timed out, reissuing.")}catch(t){this.re?t instanceof o?this.u.log(e.Trace,"(LongPolling transport) Poll timed out, reissuing."):(this.he=t,this.re=!1):this.u.log(e.Trace,`(LongPolling transport) Poll errored after shutdown: ${t.message}`)}}finally{this.u.log(e.Trace,"(LongPolling transport) Polling complete."),this.pollAborted||this.le()}}async send(t){return this.re?b(this.u,"LongPolling",this.$,this.oe,t,this.ne):Promise.reject(new Error("Cannot send until the transport is connected"))}async stop(){this.u.log(e.Trace,"(LongPolling transport) Stopping polling."),this.re=!1,this.ie.abort();try{await this.ce,this.u.log(e.Trace,`(LongPolling transport) sending DELETE request to ${this.oe}.`);const t={},[n,o]=C();t[n]=o;const r={headers:{...t,...this.ne.headers},timeout:this.ne.timeout,withCredentials:this.ne.withCredentials};let s;try{await this.$.delete(this.oe,r)}catch(t){s=t}s?s instanceof i&&(404===s.statusCode?this.u.log(e.Trace,"(LongPolling transport) A 404 response was returned from sending a DELETE request."):this.u.log(e.Trace,`(LongPolling transport) Error sending a DELETE request: ${s}`)):this.u.log(e.Trace,"(LongPolling transport) DELETE request accepted.")}finally{this.u.log(e.Trace,"(LongPolling transport) Stop finished."),this.le()}}le(){if(this.onclose){let t="(LongPolling transport) Firing onclose event.";this.he&&(t+=" Error: "+this.he),this.u.log(e.Trace,t),this.onclose(this.he)}}}class V{constructor(t,e,n,i){this.$=t,this.te=e,this.u=n,this.ne=i,this.onreceive=null,this.onclose=null}async connect(t,n){return m.isRequired(t,"url"),m.isRequired(n,"transferFormat"),m.isIn(n,B,"transferFormat"),this.u.log(e.Trace,"(SSE transport) Connecting."),this.oe=t,this.te&&(t+=(t.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(this.te)}`),new Promise(((i,o)=>{let r,s=!1;if(n===B.Text){if(g.isBrowser||g.isWebWorker)r=new this.ne.EventSource(t,{withCredentials:this.ne.withCredentials});else{const e=this.$.getCookieString(t),n={};n.Cookie=e;const[i,o]=C();n[i]=o,r=new this.ne.EventSource(t,{withCredentials:this.ne.withCredentials,headers:{...n,...this.ne.headers}})}try{r.onmessage=t=>{if(this.onreceive)try{this.u.log(e.Trace,`(SSE transport) data received. ${y(t.data,this.ne.logMessageContent)}.`),this.onreceive(t.data)}catch(t){return void this.ue(t)}},r.onerror=t=>{s?this.ue():o(new Error("EventSource failed to connect. The connection could not be found on the server, either the connection ID is not present on the server, or a proxy is refusing/buffering the connection. If you have multiple servers check that sticky sessions are enabled."))},r.onopen=()=>{this.u.log(e.Information,`SSE connected to ${this.oe}`),this.de=r,s=!0,i()}}catch(t){return void o(t)}}else o(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"))}))}async send(t){return this.de?b(this.u,"SSE",this.$,this.oe,t,this.ne):Promise.reject(new Error("Cannot send until the transport is connected"))}stop(){return this.ue(),Promise.resolve()}ue(t){this.de&&(this.de.close(),this.de=void 0,this.onclose&&this.onclose(t))}}class J{constructor(t,e,n,i,o,r){this.u=n,this.Zt=e,this.fe=i,this.pe=o,this.$=t,this.onreceive=null,this.onclose=null,this.we=r}async connect(t,n){let i;return m.isRequired(t,"url"),m.isRequired(n,"transferFormat"),m.isIn(n,B,"transferFormat"),this.u.log(e.Trace,"(WebSockets transport) Connecting."),this.Zt&&(i=await this.Zt()),new Promise(((o,r)=>{let s;t=t.replace(/^http/,"ws");const a=this.$.getCookieString(t);let c=!1;if(g.isNode||g.isReactNative){const e={},[n,o]=C();e[n]=o,i&&(e[L.Authorization]=`Bearer ${i}`),a&&(e[L.Cookie]=a),s=new this.pe(t,void 0,{headers:{...e,...this.we}})}else i&&(t+=(t.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(i)}`);s||(s=new this.pe(t)),n===B.Binary&&(s.binaryType="arraybuffer"),s.onopen=n=>{this.u.log(e.Information,`WebSocket connected to ${t}.`),this.ge=s,c=!0,o()},s.onerror=t=>{let n=null;n="undefined"!=typeof ErrorEvent&&t instanceof ErrorEvent?t.error:"There was an error with the transport",this.u.log(e.Information,`(WebSockets transport) ${n}.`)},s.onmessage=t=>{if(this.u.log(e.Trace,`(WebSockets transport) data received. ${y(t.data,this.fe)}.`),this.onreceive)try{this.onreceive(t.data)}catch(t){return void this.ue(t)}},s.onclose=t=>{if(c)this.ue(t);else{let e=null;e="undefined"!=typeof ErrorEvent&&t instanceof ErrorEvent?t.error:"WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.",r(new Error(e))}}}))}send(t){return this.ge&&this.ge.readyState===this.pe.OPEN?(this.u.log(e.Trace,`(WebSockets transport) sending data. ${y(t,this.fe)}.`),this.ge.send(t),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")}stop(){return this.ge&&this.ue(void 0),Promise.resolve()}ue(t){this.ge&&(this.ge.onclose=()=>{},this.ge.onmessage=()=>{},this.ge.onerror=()=>{},this.ge.close(),this.ge=void 0),this.u.log(e.Trace,"(WebSockets transport) socket closed."),this.onclose&&(!this.me(t)||!1!==t.wasClean&&1e3===t.code?t instanceof Error?this.onclose(t):this.onclose():this.onclose(new Error(`WebSocket closed with status code: ${t.code} (${t.reason||"no reason given"}).`)))}me(t){return t&&"boolean"==typeof t.wasClean&&"number"==typeof t.code}}class Z{constructor(t,n={}){var i;if(this.ye=()=>{},this.features={},this.ve=1,m.isRequired(t,"url"),this.u=void 0===(i=n.logger)?new w(e.Information):null===i?p.instance:void 0!==i.log?i:new w(i),this.baseUrl=this.be(t),(n=n||{}).logMessageContent=void 0!==n.logMessageContent&&n.logMessageContent,"boolean"!=typeof n.withCredentials&&void 0!==n.withCredentials)throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");n.withCredentials=void 0===n.withCredentials||n.withCredentials,n.timeout=void 0===n.timeout?1e5:n.timeout;let o=null,r=null;if(g.isNode){const t=require;o=t("ws"),r=t("eventsource")}g.isNode||"undefined"==typeof WebSocket||n.WebSocket?g.isNode&&!n.WebSocket&&o&&(n.WebSocket=o):n.WebSocket=WebSocket,g.isNode||"undefined"==typeof EventSource||n.EventSource?g.isNode&&!n.EventSource&&void 0!==r&&(n.EventSource=r):n.EventSource=EventSource,this.$=new F(n.httpClient||new D(this.u),n.accessTokenFactory),this.ut="Disconnected",this.dt=!1,this.ne=n,this.onreceive=null,this.onclose=null}async start(t){if(t=t||B.Binary,m.isIn(t,B,"transferFormat"),this.u.log(e.Debug,`Starting connection with transfer format '${B[t]}'.`),"Disconnected"!==this.ut)return Promise.reject(new Error("Cannot start an HttpConnection that is not in the 'Disconnected' state."));if(this.ut="Connecting",this.Ee=this.yt(t),await this.Ee,"Disconnecting"===this.ut){const t="Failed to start the HttpConnection before stop() was called.";return this.u.log(e.Error,t),await this.It,Promise.reject(new r(t))}if("Connected"!==this.ut){const t="HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!";return this.u.log(e.Error,t),Promise.reject(new r(t))}this.dt=!0}send(t){return"Connected"!==this.ut?Promise.reject(new Error("Cannot send data if the connection is not in the 'Connected' State.")):(this.$e||(this.$e=new Y(this.transport)),this.$e.send(t))}async stop(t){return"Disconnected"===this.ut?(this.u.log(e.Debug,`Call to HttpConnection.stop(${t}) ignored because the connection is already in the disconnected state.`),Promise.resolve()):"Disconnecting"===this.ut?(this.u.log(e.Debug,`Call to HttpConnection.stop(${t}) ignored because the connection is already in the disconnecting state.`),this.It):(this.ut="Disconnecting",this.It=new Promise((t=>{this.ye=t})),await this._t(t),void await this.It)}async _t(t){this.Ce=t;try{await this.Ee}catch(t){}if(this.transport){try{await this.transport.stop()}catch(t){this.u.log(e.Error,`HttpConnection.transport.stop() threw error '${t}'.`),this.Se()}this.transport=void 0}else this.u.log(e.Debug,"HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.")}async yt(t){let n=this.baseUrl;this.Zt=this.ne.accessTokenFactory,this.$.Zt=this.Zt;try{if(this.ne.skipNegotiation){if(this.ne.transport!==U.WebSockets)throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");this.transport=this.ke(U.WebSockets),await this.Pe(n,t)}else{let e=null,i=0;do{if(e=await this.Te(n),"Disconnecting"===this.ut||"Disconnected"===this.ut)throw new r("The connection was stopped during negotiation.");if(e.error)throw new Error(e.error);if(e.ProtocolVersion)throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");if(e.url&&(n=e.url),e.accessToken){const t=e.accessToken;this.Zt=()=>t,this.$.te=t,this.$.Zt=void 0}i++}while(e.url&&i<100);if(100===i&&e.url)throw new Error("Negotiate redirection limit exceeded.");await this.Ie(n,this.ne.transport,e,t)}this.transport instanceof X&&(this.features.inherentKeepAlive=!0),"Connecting"===this.ut&&(this.u.log(e.Debug,"The HttpConnection connected successfully."),this.ut="Connected")}catch(t){return this.u.log(e.Error,"Failed to start the connection: "+t),this.ut="Disconnected",this.transport=void 0,this.ye(),Promise.reject(t)}}async Te(t){const n={},[o,r]=C();n[o]=r;const s=this._e(t);this.u.log(e.Debug,`Sending negotiation request: ${s}.`);try{const t=await this.$.post(s,{content:"",headers:{...n,...this.ne.headers},timeout:this.ne.timeout,withCredentials:this.ne.withCredentials});if(200!==t.statusCode)return Promise.reject(new Error(`Unexpected status code returned from negotiate '${t.statusCode}'`));const e=JSON.parse(t.content);return(!e.negotiateVersion||e.negotiateVersion<1)&&(e.connectionToken=e.connectionId),e.useStatefulReconnect&&!0!==this.ne.He?Promise.reject(new l("Client didn't negotiate Stateful Reconnect but the server did.")):e}catch(t){let n="Failed to complete negotiation with the server: "+t;return t instanceof i&&404===t.statusCode&&(n+=" Either this is not a SignalR endpoint or there is a proxy blocking the connection."),this.u.log(e.Error,n),Promise.reject(new l(n))}}De(t,e){return e?t+(-1===t.indexOf("?")?"?":"&")+`id=${e}`:t}async Ie(t,n,i,o){let s=this.De(t,i.connectionToken);if(this.Re(n))return this.u.log(e.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=n,await this.Pe(s,o),void(this.connectionId=i.connectionId);const a=[],l=i.availableTransports||[];let d=i;for(const i of l){const l=this.xe(i,n,o,!0===(null==d?void 0:d.useStatefulReconnect));if(l instanceof Error)a.push(`${i.transport} failed:`),a.push(l);else if(this.Re(l)){if(this.transport=l,!d){try{d=await this.Te(t)}catch(t){return Promise.reject(t)}s=this.De(t,d.connectionToken)}try{return await this.Pe(s,o),void(this.connectionId=d.connectionId)}catch(t){if(this.u.log(e.Error,`Failed to start the transport '${i.transport}': ${t}`),d=void 0,a.push(new c(`${i.transport} failed: ${t}`,U[i.transport])),"Connecting"!==this.ut){const t="Failed to select transport before stop() was called.";return this.u.log(e.Debug,t),Promise.reject(new r(t))}}}}return a.length>0?Promise.reject(new u(`Unable to connect to the server with any of the available transports. ${a.join(" ")}`,a)):Promise.reject(new Error("None of the transports supported by the client are supported by the server."))}ke(t){switch(t){case U.WebSockets:if(!this.ne.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new J(this.$,this.Zt,this.u,this.ne.logMessageContent,this.ne.WebSocket,this.ne.headers||{});case U.ServerSentEvents:if(!this.ne.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new V(this.$,this.$.te,this.u,this.ne);case U.LongPolling:return new X(this.$,this.u,this.ne);default:throw new Error(`Unknown transport: ${t}.`)}}Pe(t,e){return this.transport.onreceive=this.onreceive,this.features.reconnect?this.transport.onclose=async n=>{let i=!1;if(this.features.reconnect){try{this.features.disconnected(),await this.transport.connect(t,e),await this.features.resend()}catch{i=!0}i&&this.Se(n)}else this.Se(n)}:this.transport.onclose=t=>this.Se(t),this.transport.connect(t,e)}xe(t,n,i,o){const r=U[t.transport];if(null==r)return this.u.log(e.Debug,`Skipping transport '${t.transport}' because it is not supported by this client.`),new Error(`Skipping transport '${t.transport}' because it is not supported by this client.`);if(!function(t,e){return!t||!!(e&t)}(n,r))return this.u.log(e.Debug,`Skipping transport '${U[r]}' because it was disabled by the client.`),new a(`'${U[r]}' is disabled by the client.`,r);if(!(t.transferFormats.map((t=>B[t])).indexOf(i)>=0))return this.u.log(e.Debug,`Skipping transport '${U[r]}' because it does not support the requested transfer format '${B[i]}'.`),new Error(`'${U[r]}' does not support ${B[i]}.`);if(r===U.WebSockets&&!this.ne.WebSocket||r===U.ServerSentEvents&&!this.ne.EventSource)return this.u.log(e.Debug,`Skipping transport '${U[r]}' because it is not supported in your environment.'`),new s(`'${U[r]}' is not supported in your environment.`,r);this.u.log(e.Debug,`Selecting transport '${U[r]}'.`);try{return this.features.reconnect=r===U.WebSockets?o:void 0,this.ke(r)}catch(t){return t}}Re(t){return t&&"object"==typeof t&&"connect"in t}Se(t){if(this.u.log(e.Debug,`HttpConnection.stopConnection(${t}) called while in state ${this.ut}.`),this.transport=void 0,t=this.Ce||t,this.Ce=void 0,"Disconnected"!==this.ut){if("Connecting"===this.ut)throw this.u.log(e.Warning,`Call to HttpConnection.stopConnection(${t}) was ignored because the connection is still in the connecting state.`),new Error(`HttpConnection.stopConnection(${t}) was called while the connection is still in the connecting state.`);if("Disconnecting"===this.ut&&this.ye(),t?this.u.log(e.Error,`Connection disconnected with error '${t}'.`):this.u.log(e.Information,"Connection disconnected."),this.$e&&(this.$e.stop().catch((t=>{this.u.log(e.Error,`TransportSendQueue.stop() threw error '${t}'.`)})),this.$e=void 0),this.connectionId=void 0,this.ut="Disconnected",this.dt){this.dt=!1;try{this.onclose&&this.onclose(t)}catch(n){this.u.log(e.Error,`HttpConnection.onclose(${t}) threw error '${n}'.`)}}}else this.u.log(e.Debug,`Call to HttpConnection.stopConnection(${t}) was ignored because the connection is already in the disconnected state.`)}be(t){if(0===t.lastIndexOf("https://",0)||0===t.lastIndexOf("http://",0))return t;if(!g.isBrowser)throw new Error(`Cannot resolve '${t}'.`);const n=window.document.createElement("a");return n.href=t,this.u.log(e.Information,`Normalizing '${t}' to '${n.href}'.`),n.href}_e(t){const e=new URL(t);e.pathname.endsWith("/")?e.pathname+="negotiate":e.pathname+="/negotiate";const n=new URLSearchParams(e.searchParams);return n.has("negotiateVersion")||n.append("negotiateVersion",this.ve.toString()),n.has("useStatefulReconnect")?"true"===n.get("useStatefulReconnect")&&(this.ne.He=!0):!0===this.ne.He&&n.append("useStatefulReconnect","true"),e.search=n.toString(),e.toString()}}class Y{constructor(t){this.Ae=t,this.Ue=[],this.Le=!0,this.Ne=new Q,this.qe=new Q,this.Me=this.je()}send(t){return this.We(t),this.qe||(this.qe=new Q),this.qe.promise}stop(){return this.Le=!1,this.Ne.resolve(),this.Me}We(t){if(this.Ue.length&&typeof this.Ue[0]!=typeof t)throw new Error(`Expected data to be of type ${typeof this.Ue} but was of type ${typeof t}`);this.Ue.push(t),this.Ne.resolve()}async je(){for(;;){if(await this.Ne.promise,!this.Le){this.qe&&this.qe.reject("Connection stopped.");break}this.Ne=new Q;const t=this.qe;this.qe=void 0;const e="string"==typeof this.Ue[0]?this.Ue.join(""):Y.Oe(this.Ue);this.Ue.length=0;try{await this.Ae.send(e),t.resolve()}catch(e){t.reject(e)}}}static Oe(t){const e=t.map((t=>t.byteLength)).reduce(((t,e)=>t+e)),n=new Uint8Array(e);let i=0;for(const e of t)n.set(new Uint8Array(e),i),i+=e.byteLength;return n.buffer}}class Q{constructor(){this.promise=new Promise(((t,e)=>[this.j,this.Fe]=[t,e]))}resolve(){this.j()}reject(t){this.Fe(t)}}class K{constructor(){this.name="json",this.version=2,this.transferFormat=B.Text}parseMessages(t,n){if("string"!=typeof t)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!t)return[];null===n&&(n=p.instance);const i=A.parse(t),o=[];for(const t of i){const i=JSON.parse(t);if("number"!=typeof i.type)throw new Error("Invalid payload.");switch(i.type){case N.Invocation:this.U(i);break;case N.StreamItem:this.Be(i);break;case N.Completion:this.Xe(i);break;case N.Ping:case N.Close:break;case N.Ack:this.Je(i);break;case N.Sequence:this.ze(i);break;default:n.log(e.Information,"Unknown message type '"+i.type+"' ignored.");continue}o.push(i)}return o}writeMessage(t){return A.write(JSON.stringify(t))}U(t){this.Ve(t.target,"Invalid payload for Invocation message."),void 0!==t.invocationId&&this.Ve(t.invocationId,"Invalid payload for Invocation message.")}Be(t){if(this.Ve(t.invocationId,"Invalid payload for StreamItem message."),void 0===t.item)throw new Error("Invalid payload for StreamItem message.")}Xe(t){if(t.result&&t.error)throw new Error("Invalid payload for Completion message.");!t.result&&t.error&&this.Ve(t.error,"Invalid payload for Completion message."),this.Ve(t.invocationId,"Invalid payload for Completion message.")}Je(t){if("number"!=typeof t.sequenceId)throw new Error("Invalid SequenceId for Ack message.")}ze(t){if("number"!=typeof t.sequenceId)throw new Error("Invalid SequenceId for Sequence message.")}Ve(t,e){if("string"!=typeof t||""===t)throw new Error(e)}}const G={trace:e.Trace,debug:e.Debug,info:e.Information,information:e.Information,warn:e.Warning,warning:e.Warning,error:e.Error,critical:e.Critical,none:e.None};class tt{configureLogging(t){if(m.isRequired(t,"logging"),void 0!==t.log)this.logger=t;else if("string"==typeof t){const e=function(t){const e=G[t.toLowerCase()];if(void 0!==e)return e;throw new Error(`Unknown log level: ${t}`)}(t);this.logger=new w(e)}else this.logger=new w(t);return this}withUrl(t,e){return m.isRequired(t,"url"),m.isNotEmpty(t,"url"),this.url=t,this.httpConnectionOptions="object"==typeof e?{...this.httpConnectionOptions,...e}:{...this.httpConnectionOptions,transport:e},this}withHubProtocol(t){return m.isRequired(t,"protocol"),this.protocol=t,this}withAutomaticReconnect(t){if(this.reconnectPolicy)throw new Error("A reconnectPolicy has already been set.");return t?Array.isArray(t)?this.reconnectPolicy=new z(t):this.reconnectPolicy=t:this.reconnectPolicy=new z,this}withServerTimeout(t){return m.isRequired(t,"milliseconds"),this.Ke=t,this}withKeepAliveInterval(t){return m.isRequired(t,"milliseconds"),this.Ge=t,this}withStatefulReconnect(t){return void 0===this.httpConnectionOptions&&(this.httpConnectionOptions={}),this.httpConnectionOptions.He=!0,this.Y=null==t?void 0:t.bufferSize,this}build(){const t=this.httpConnectionOptions||{};if(void 0===t.logger&&(t.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");const e=new Z(this.url,t);return O.create(e,this.logger||p.instance,this.protocol||new K,this.reconnectPolicy,this.Ke,this.Ge,this.Y)}}return Uint8Array.prototype.indexOf||Object.defineProperty(Uint8Array.prototype,"indexOf",{value:Array.prototype.indexOf,writable:!0}),Uint8Array.prototype.slice||Object.defineProperty(Uint8Array.prototype,"slice",{value:function(t,e){return new Uint8Array(Array.prototype.slice.call(this,t,e))},writable:!0}),Uint8Array.prototype.forEach||Object.defineProperty(Uint8Array.prototype,"forEach",{value:Array.prototype.forEach,writable:!0}),n})(),"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.signalR=e():t.signalR=e(),$.extend($t,{t1:"Eingabe erforderlich",t2:"Bitte überprüfen Sie Ihre Eingaben im Formular.",b0:"Erstellt",b1:"Zuletzt geändert",b2:"von",t12:"Der Server hat einen Fehler zurückgegeben. Bitte versuchen Sie es erneut.",t17:"Eine Email mit einem Aktivierungs-Link wurde an deine Adresse versandt.",t18:"Ein Account mit deinem Namen existiert bereits. Dennoch erstellen?",t19:"Einträge sind entweder unngültig oder zu kurz.",t20:"Der Server hat einen Fehler gemeldet. Bitte versuch es erneut.",t21:"Der Zugang wurde nicht gefunden.",t30a:"Als erledigt markieren.",t30b:"Als unerledigt markieren.",t55:"Ein Email mit einem Aktivierungs-Link wurde an Ihre Adresse versandt.",t56:"Ein Zugang für diesen Namen besteht bereits. Trotzdem erstellen?",t57:"Ein bestehender Zugang wurde für diese Serie registriert.",t60:"Bitte geben Sie Email-Adresse an, die Sie hier hinterlegt haben.",t61:"Ihr Passwort wurde erfolgreich versandt.",t62:"Die angegebene Email-Adresse stimmt nicht mit der hier hinterlegten überein.",ov:"Persönliche Übersicht"});var $v={}; /*! loadCSS. [c]2020 Filament Group, Inc. MIT License */ /*! onloadCSS. (onload callback for loadCSS) [c]2017 Filament Group, Inc. MIT License */ -function onloadCSS(t,e){e=e||{};let n=function(e){return new Promise(((n,i)=>{t.addEventListener?e.addEventListener("load",newcb):t.attachEvent&&e.attachEvent("onload",newcb),"isApplicationInstalled"in navigator&&"onloadcssdefined"in t&&e.onloadcssdefined(newcb)}))};if(Array.isArray(t)){let i=t.length;Promise.all(t.map(n)).then((function(t){var n=t.reduce(((t,e)=>t+(!0===e?1:0)));!async function(t){!0===t&&"function"==typeof e.success?e.success():!0===t&&"object"==typeof e.success&&e.success instanceof Promise&&await e.success(),e.complete()}(i===n)}))}else n(t)}!function(t){"use strict";var e=function(e,n,i,o){var r,s=t.document,a=s.createElement("link");if(n)r=n;else{var c=(s.body||s.getElementsByTagName("head")[0]).childNodes;r=c[c.length-1]}var l=s.styleSheets;if(o)for(var u in o)o.hasOwnProperty(u)&&a.setAttribute(u,o[u]);a.rel="stylesheet",a.href=e,a.media="only x",function t(e){if(s.body)return e();setTimeout((function(){t(e)}))}((function(){r.parentNode.insertBefore(a,n?r:r.nextSibling)}));var d=function(t){for(var e=a.href,n=l.length;n--;)if(l[n].href===e)return t();setTimeout((function(){d(t)}))};function h(){a.addEventListener&&a.removeEventListener("load",h),a.media=i||"all"}return a.addEventListener&&a.addEventListener("load",h),a.onloadcssdefined=d,d(h),a};"undefined"!=typeof exports?exports.loadCSS=e:t.loadCSS=e}("undefined"!=typeof global?global:this);const isIE=/MSIE\/|Trident/gi.test(window.navigator.userAgent)||void 0!==window.document.documentMode,isfileapi=!!(window.File&&window.FileReader&&window.FileList&&window.Blob);var $ocms={auth:{},no:function(t){t.stopPropagation()},vmin:function(t){var e=$(window).width*(t||1),n=$(window).height*(t||1);return e($ocms.baseurl+"/"+(t||"")).replace(/\/\//,"/"),cexi:null};function deepCopy(t){var e,n,i;if("object"!=typeof t||null===t)return t;for(i in e=Array.isArray(t)?[]:{},t)n=t[i],e[i]=deepCopy(n);return e}function fields_definition(t,e,n){this.label_sng=!0===Array.isArray(t)?"":t||"",this.label_pl=!0===Array.isArray(t)?"":e||"",this.fields=!0===Array.isArray(t)?t:n||[],this.itm=function(t){for(var e=0;e0)for(var n=0;nt||"")).filter(((t,e)=>""!==t)).join(e)}function parseDt(t,e,n){t=(t||"").substr(0,e.length);var i=e,o=t.length>0&&e.split(";").some((function(e){for(var n,o=/[^yMdhms0-9]/gi,r=!0;null!==(n=o.exec(e));)r=r&&e.substr(n.index,1)===t.substr(n.index,1);var s=t.length===e.length&&r;return!0===s&&(i=e),s}));if(!0===o){for(var r,s=[0,0,0,0,0,0,0],a=/(mm{1,2}|dd{1,2}|MM{1,2}|(yy){2,4}|ss{1,2}|hh{1,2})(?!\w)/g;null!==(r=a.exec(i));)s["yMdhms".indexOf(r[0].substr(0,1))]=parseInt(("yy"===r[0]?"20":"")+t.substr(r.index,r[0].length))-("M"===r[0].substr(0,1)?1:0);var c=new(Function.prototype.bind.apply(Date,[null].concat(s)));return"string"==typeof n?fdt(c,n):c}return!1}function bool(t,e){return"boolean"==typeof t?t:"boolean"==typeof e&&e}function booln(t,e){return"boolean"==typeof t?t:"number"==typeof t?1===t:"boolean"==typeof e&&e}Date.prototype.isValid=function(){return!isNaN(this)},Date.prototype.format=function(t){return fdt(this,t)},Date.prototype.addDays=function(t){return this.setDate(this.getDate()+t),this},Date.prototype.isBetween=function(t,e){return this>t&&this section");$(window).scroll((function(e){let n=$(window).scrollTop(),i=$("body");i.toggleClass("unfocus",n>vh()-1.2*t),i.toggleClass("btb",n>.5*vh()-t)}))},$ocms.cf_reset=function(){return $("#contentframe").empty()},function(t){t.fn.scrollTo=function(e){if(t(this).length>0){var n=t(this).offset().top||0;n>0&&t("html, body").animate({scrollTop:n-hh()},2e3)}},t.fn.ldng=function(e){var n=!0;return"boolean"==typeof e?n=e:"number"==typeof e&&(n=e>0),t(this).toggleClass("loading",n)},"function"!=typeof t.noop&&(t.noop=function(){}),t.fn.hasAttr=function(e){var n=t(this).attr(e);return void 0!==n&&!1!==n},t.fn.parseCssPx=function(e){try{return parseFloat(t(this).css(e).replace("px","")||0)}catch(t){return 0}},t.max=function(t,e){return isNaN(t)&&isNaN(e)?null:isNaN(t)&&!isNaN(e)?e:!isNaN(e)&&isNaN(e)||t>=e?t:e},t.min=function(t,e){return isNaN(t)&&isNaN(e)?null:isNaN(t)&&!isNaN(e)?e:!isNaN(e)&&isNaN(e)||t<=e?t:e},t.lim=function(t,e){return isNaN(t)?null:isNaN(e)?t:e<=t?e:t},t.fn.enterKey=function(e){return this.each((function(){t(this).keypress((function(t){"13"===(t.keyCode?t.keyCode:t.which).toString()&&e.call(this,t)}))}))}}(jQuery),$ocms.defaultTimeout=3e4,$ocms.AjaxEX=function(t){var e=this;e.responseText=e.responseText||"";var n=e.getResponseHeader("x-ocms-code")||"";e.internalCode=""!==n&&!1===isNaN(n)?parseInt(n):-1,e.isInternal=e.internalCode>-1,e.internalText=decodeURIComponent((e.getResponseHeader("x-ocms-desc")||"").replace(/\+/g,"%20")||"");var i=e.internalText||t,o=e.internalCode||e.status;e.logtext=i+" ("+o+")"},$ocms.postXTS=function(t){$ocms.postXT.call(this,$.extend(t,{sync:!0}))},$ocms.postXT=function(t){if((t=t||{}).trycount=t.trycount||0,""!==(t.url||"")){t.url=-1!==t.url.indexOf("&yy=")?t.url:t.url.indexOf("?")>-1?t.url+"&yy="+(new Date).getTime():t.url+"?yy="+(new Date).getTime();var e=t.context||this;switch(t.context=e,t.retryLimit=t.retryLimit||0,t.timeout=t.timeout||$ocms.defaultTimeout,t.timeout<100&&(t.timeout=1e3*t.timeout),t.data=t.data||{},t.contentType=t.contentType||"multipart/form-data; charset=UTF-8",t.islogin="boolean"==typeof t.islogin&&t.islogin,t.contentType){case"":case"json":t.contentType="application/json; charset=utf-8";break;case"form":t.contentType="application/x-www-form-urlencoded; charset=UTF-8";break;case"multi":t.contentType="multipart/form-data";break;case"text":t.contentType="text/plain; charset=UTF-8"}if(t.form instanceof jQuery?(t.data=t.form.serializeObject(),t.contentType="form-data"):t.lzw instanceof jQuery&&(t.data.lzw=$.ccLZW(t.lzw.serializeAnything(!0)).join(",")),"multipart/form-data"!==t.contentType.substr(0,19)&&"form-data"!==t.contentType.substr(0,9)||t.data instanceof FormData!=!1)t.data instanceof FormData&&(t.contentType=!1,t.processData=!1);else{t.contentType=!1;var n=new FormData;$.each(t.files||[],(function(t,e){n.append("upload_file",e)})),$.each(t.data||{},(function(t,e){n.append(t,e)})),t.data=n,t.processData=!1}var i={type:t.method||"post",url:t.url,data:t.data,processData:"boolean"!=typeof t.processData||t.processData,contentType:t.contentType,cache:t.cache||!1,timeout:t.timeout,beforeSend:function(n){$(t.loading).ldng(),$("body").addClass("ldng"),"function"==typeof t.beforesend&&t.beforesend.apply(e,[n])},success:function(n,i,o){"false"===n||"not authorized"===n?("function"==typeof t.error&&t.error.apply(e,[o,i,n]),"function"==typeof $.status&&$.status(i+" - "+n)):"function"==typeof t.success&&t.success.apply(e,[n,i,o])},error:function(n,i,o){if($ocms.AjaxEX.call(n,i),-1===t.url.indexOf("doc.ashx")||-1!==t.url.indexOf("ftest")){if(401===n.status&&111===n.internalCode&&!1===t.islogin&&"function"==typeof $ocms.login.dlg)$ocms.login.dlg({ajo:t});else if("timeout"===i||302===n.status)return t.tryCount++,t.tryCount<=t.retryLimit?void $ocms.postXT(t):void 0;"function"==typeof t.error?t.error.apply(e,[n,i,o]):"function"==typeof $ocms.failure?$ocms.failure.apply(e,[n]):"function"==typeof $.status&&$.status("Server error: "+i+" - "+o)}},dataType:t.datatype||"json",complete:function(n,i){"function"==typeof t.complete&&t.complete.apply(e,[n,i]),$(t.loading).ldng(0),$("body").removeClass("ldng");let o=$("body > .timer");if(o.length>0){let t=new Date(n.getResponseHeader("ocms_cec")||""),e=new Date(n.getResponseHeader("ocms_cex")||"");if(t.isValid()&&e.isValid()){let n=new Date,i=Math.abs(e-t);n.setMilliseconds(n.getMilliseconds()+i),o.data({cex:n,ctt:i}),$ocms.cex_timer()}}},context:e,async:!0};return"boolean"==typeof t.sync&&(i.async=!1===t.sync),!0==("boolean"==typeof t.contentType&&!1===t.contentType)&&(i.contentType=!1),$.ajax(i)}},$ocms.cex_timer=function(){$ocms.cexi||($ocms.cexi=setInterval($ocms.cex_timer,15e3));let t=$("body > .timer"),e=t.data("cex"),n=t.data("ctt"),i=new Date;if(e instanceof Date&&e.isValid()&&"number"==typeof n&&n>0&&e>i){let o=Math.abs(i-e)/n*100;t.css("width",o.toString()+"%"),o<98&&(!$ocms.cex_lp||Math.abs(i-$ocms.cex_lp)>6e5)&&$ocms.postXT({url:$ocms.url("ping"),success:()=>{$ocms.cex_lp=i},timeout:5e3,error:()=>{}})}},$ocms.vbl_send=function(t){var e=t.data||{};if(""!==(e.url||"")){var n=$("#contentframe form:first"),i={url:e.url,data:new FormData,success:function(t){"function"==typeof e.success?e.success(t):"string"==typeof e.success&&alert(e.success)},error:function(t,n,i){"function"==typeof e.error?e.error(i):"string"==typeof e.error&&alert(e.error)},complete:function(){n.ldng(0)}},o=!0;n.find("input").each((function(){var t=$(this),e=t.nza("name"),n=t.val(),r=$(this).prop("required")||!1;if(""!==e){var s=""!==n||!1===r;o=o&&s,!0===s?(i.data.append(e,n),t[0].setCustomValidity("")):""!==$(this).nza("ocms-nvnote")&&t[0].setCustomValidity($(this).nza("ocms-nvnote"))}})),!0===o&&(n.ldng(1),$ocms.postXT.call(this,i))}},function(t){t.fn.nza=function(e,n){var i=t(this).attr(e);return void 0!==i&&!1!==i?i:n||""},t.fn.serializeObject=function(e,n){var i=/\r?\n/g,o=/^(?:submit|button|image|reset|file)$/i,r=/^(?:input|select|textarea|keygen)/i,s=/^(?:checkbox|radio)$/i,a=bool((n=n||{}).typedvalues,!1),c={},l=t(this),u=l.find(':input:not([nosend],[type="file"])').addBack(":input"),d=!0;return t.each(u.not(".tinymce").get(),(function(n,l){var u=t(this),h=this,p=(this.type||"").toLowerCase(),f=u.prop("required")||!1;if(!0===(h.name&&!u.is(":disabled")&&r.test(h.nodeName)&&!o.test(p))){var m=u.val(),g=h.name,y=u.nza("data-format").split(":"),v=u.nza("pattern")||".*";if(!0===s.test(p)&&(m=h.checked?""!==m?m:"true":""),"date"===y[0].substr(0,4)&&y.length>1)"boolean"==typeof(m=parseDt(m,y.slice(1).join(":")))&&(m=null),null===m&&"date"===u.prop("type").substr(0,4)&&!1===isNaN(new Date(u.val()))&&(m=new Date(u.val())),m instanceof Date==!0&&"function"==typeof m.getMonth?!1===a&&(m=fdt(m,"date"===y[0]?"dts":"iso")):m=null;else if(("number"===p||"float"===y[0]||"integer"===y[0])&&!0===a){let t,e=String(m).replace(",",".");t="integer"===y[0]?parseInt(e):parseFloat(e),m=isNaN(t)?m:t}if(!0===f&&(""===m||null==m||"string"==typeof m&&null===m.match(v))?(!0===bool(e,!1)&&h.setCustomValidity(u.nza("ocms-nvnote",$ocms.t.inv||"Invalid field")),m=null):!0===bool(e,!1)&&h.setCustomValidity(""),null!=m&&"string"==typeof m){let t=c[g];null!=t?Array.isArray(t)?t.push(m.replace(i,"\r\n")):c[g]=[t,m.replace(i,"\r\n")]:c[g]=m.replace(i,"\r\n")}else if(null!=m){let t=c[g];null!=t?Array.isArray(t)?t.push(m):c[g]=[t,m]:c[g]=m}else d=!1}})),u.filter(".tinymce").each((function(e,n){var i=t(this),o=((this.type||"").toLowerCase(),i.prop("required")||!1);try{var r=tinymce.get(t(n).attr("id"));if(r){var s=t(n).attr("name"),a=r.getContent();!1===o||""!==(a||"")?c[s]=a:d=!1}}catch(e){t.noop()}})),l.toggleClass("invalid",!d),d?c:null},t.fn.sendForm=function(e,n,i){var o=t(this);i=i||{};var r={url:e,success:function(t){if(i.response=t,"function"==typeof n)n(t);o.closest("div.modal").remove()},error:function(t,e,n){"function"==typeof i.error?i.error.call(this,t):$ocms.failure.call(this,t)},complete:function(){o.ldng(0),"function"==typeof i.complete&&i.complete.call(this,jqXHR)}},s=o.find('input[type="file"]');r.data=new FormData,s.length>0&&t.each(s[0].files,(function(t,e){r.data.append(t,e),r.data.append("file_lastmodified",$ocms.isodt(e.lastModifiedDate))}));var a=o.serializeObject();t.each(a||{},(function(t,e){r.data.append(t,e)})),o.ldng(),$ocms.postXT.call(this,r)},t.fn.checkValidity=function(){var e=t(this),n=!0;return e.each((function(t,e){n=n&&e.checkValidity()})),n},t.fn.wrap=function(e,n){var i=t(this),o=$$.dc(e).attr(n||{}).insertAfter(i);return i.append(o),o}}(jQuery),$ocms.logout=function(){$ocms.postXT({url:$ocms.url("logout"),complete:function(){window.location.reload()}})},$ocms.login={send:function(t){t.preventDefault();var e=$(this);if(!0===e.find("#dbtn-confirm").hasClass("disabled"))return!1;var n=e.serializeObject();return n.loginaccount=ne(n.loginaccount,$ocms.auth.account||$ocms.auth.requestedaccount||""),n.loginaccount=ne(n.loginaccount,$ocms.auth.account||$ocms.auth.requestedaccount||""),""===ne(n.loginaccount)&&!0===bool($ocms.auth.accountrequired,!0)?(alert($t.l16),!1):($ocms.postXT({url:$ocms.url("login"),data:n,success:function(){window.location.reload()}}),!1)},uichange:function(){let t=$(this),e=t.closest("form"),n=bool($ocms.auth.accountrequired,!0),i=ne(e.find('[name="loginaccount"]').val(),$ocms.auth.account||$ocms.auth.requestedaccount||"");if(""!==i||!1===n){var o=e.find('[name="userlogin"]').empty().val(""),r=e.find('[name="username"]').empty().val(""),s=$("#dlg_userlogin_sel").empty().val(""),a=t.val()||"";if(!1===t.checkValidity()&&""===a)return;var c=t.closest("table").ldng();$ocms.postXT.call(this,{url:$ocms.url("auth"),data:{userinfo:a,account:i||""},success:function(t,e,n){if(1===t.length){var i=t[0];o.val(i.login).change().attr("required","").removeAttr("nosend"),r.val(i.name).change().attr("required","").show(),s.removeAttr("required").attr("nosend","").hide()}else t.length>0?(r.hide().removeAttr("required"),o.removeAttr("required").attr("nosend",""),0===s.length&&(s=$("").attr({name:"userlogin",size:t.length,id:"dlg_userlogin_sel",class:"form-control",required:""}).css({width:"100%","max-width":"100%",padding:"2px"}).insertAfter(r)),$.each(t,(function(t,e){var n=$("").attr({value:e.login,style:"padding-top: 2px; padding-bottom: 5px;","border-bottom":"1px solid #EEE;"}).text(e.name).appendTo(s);t%2==0&&n.css({"background-color":"#F9F9F9"})})),s.attr("required","").removeAttr("nosend")):(s.hide().attr("nosend",""),r.attr("required","").show(),o.attr("required","").removeAttr("nosend"),alert($t.l9))},error:function(t){$ocms.failure.call(this,t)},complete:function(){c.ldng(0)}})}else alert($t.l18)},sendpassword:function(t){var e=$(''),n=e.find(".form-body"),i=null;e.find("form").submit((function(t){t.preventDefault();var o=$(this).serializeObject(!0),r=null===i,s=r?"spwc":"spw";return $ocms.postXT.call(this,{url:$ocms.url(s),data:o,complete:function(){r?(n.append('
Ihnen wurde ein Code per SMS zugesandt.
Bitte tragen Sie den hier ein:
'),i=$('
').appendTo(n)):(alert($t.l13),e.remove())},error:()=>{}}),!1})),e.find(".modal-close").click((function(){e.remove()}));var o=[];$.each($t.l7a.split("\n"),((t,e)=>{Array.prototype.push.apply(o,[$("
"),$("").text(e)])})),e.find(".modal-note").append($('').text($t.alert)).append(o),e.appendTo("body"),setTimeout((function(){$(".modal").find('input[name="lastname"]').focus()}),600)}};var $$={s:function(t){return $("").text(t)},br:function(){return $("
")},sc:function(t,e){return $("").addClass(t).text(e)},td:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t?n.attr(t):"string"==typeof t&&n.text(t),"object"==typeof e?n.attr(e):"string"==typeof e&&n.text(e),n},th:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t?n.attr(t):"string"==typeof t&&n.text(t),"object"==typeof e?n.attr(e):"string"==typeof e&&n.text(e),n},tdc:function(t,e,n){return $$.td(e,n).addClass(t)},td2:function(t){var e=$('');return"string"===$.type(t)?e.text(t):t instanceof jQuery?e.append(t):"function"==typeof t?t.call(e):e.html(" "),e},td3:function(t){var e=$('');return"string"===$.type(t)?e.text(t):t instanceof jQuery?e.append(t):"function"==typeof t?t.call(e):e.html(" "),e},tdtr:function(t,e){var n=$$.tr().appendTo(e);return t instanceof jQuery==!0||"string"==typeof t?t.appendTo($$.td().appendTo(n)):!0===Array.isArray(t)&&$.each(t,(function(t,e){$(e).appendTo($$.td().appendTo(n))})),n},tr:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t&&n.attr(t),"object"==typeof e&&n.attr(e),n},trc:function(t,e){var n=$("").addClass(t);return e instanceof jQuery==!0?n.appendTo(e):"object"==typeof e&&n.attr(e),n},d:function(t){return $("
").attr(t||{})},dc:function(t,e,n,i){var o=$("
").addClass(t);return e instanceof jQuery==!0?o.appendTo(e):"object"==typeof e?o.attr(e):"function"==typeof e?o.click(e):"string"==typeof e&&o.text(e),"string"==typeof n?o.text(n):"object"==typeof n?o.attr(n):"function"==typeof n&&o.click(n),"string"==typeof i?o.text(i):"object"==typeof i?o.attr(i):"function"==typeof i&&o.click(i),o},df:function(t){return $("
 
").attr(t||{})},opt:function(t,e,n){var i=$("");return"string"==typeof t?i.attr("value",t):"object"==typeof t&&i.attr(t),"string"==typeof e?i.text(e):"object"==typeof e&&i.attr(e),"object"==typeof n&&i.attr(n),i},eOpt:function(t){var e=$('');return t&&e.attr("selected","selected"),e},tbl:function(t){return $("
").attr(t||{})},tblc:function(t){return $("
").addClass(t)},thead:function(t){let e=$("");return t instanceof jQuery&&e.prependTo(t),e},tbody:function(t){let e=$("");return t instanceof jQuery&&e.appendTo(t),e},tblset:function(t,e){let n=$$.tbl(t||{});return e instanceof jQuery&&e.append(n),{tbl:n,hd:$$.thead().appendTo(n),bdy:$$.tbody().appendTo(n)}},i:function(t){return $("").attr(t||{})},img:function(t,e){return $("").attr("src",t).attr(e||{})},sel:function(t){return $("").attr(t||{})},btn:function(t){return $("").attr(t||{})},a:function(t){return $("").attr(t||{})},li:function(t){return $("
  • ").attr(t||{})},ul:function(t){return $("
      ").attr(t||{})},nav:function(t){return $("").attr(t||{})},lbl:function(t,e){var n=$("");return"string"==typeof t&&n.text(t),"object"==typeof t?n.attr(t):"object"==typeof e&&n.attr(e),n},txt:function(t){return $("").attr(t||{})},0:function(t,e){return $("<"+t+">").attr(e||{})},bbtn:function(t,e){return $$.btn({type:"button",class:"btn"}).addClass(e).text(t)},svg:t=>$(document.createElementNS("http://www.w3.org/2000/svg",t))};function getMonday(t){var e=(t=new Date(t)).getDay(),n=t.getDate()-e+(0==e?-6:1);return new Date(t.setDate(n))}function $lf(t){var e=void 0===t?null:"number"==typeof t&&1!==t||"boolean"==typeof cl&&!1===t;return $("#listframe").tC("hd",e).is(".hd")}function $nuf(t){if(t&&t.stopPropagation(),!$(this).is(".disabled")){var e=function(t){t.removeClass("vis").find("li.dropdown").removeClass("open").removeClass("vis").attr("aria-expanded","false")},n=$(this).parent("li.dropdown");if(n.length>0){n.tC("open"),navs=!0===n.is(".open")?"true":"false",n.attr("aria-expanded",navs);var i=n.closest("nav");i.find("li.dropdown").not(n.parentsUntil("nav")).not(n).removeClass("open").attr("aria-expanded","false"),!1===n.is(".open")&&n.find("li.dropdown").removeClass("open").attr("aria-expanded","false"),e($("nav").not(i))}else e($("nav"))}}function $tbr(){return $lf(0),$("#topbar").ocmsmenu([])}function $lfr(){return $("#sidebar").empty(),$("#listframe").removeClass("fix").addClass("hd").empty()}function $cfr(){return $tbr(),$("#contentframe").empty()}function jObj(t,e){let n={};if("{"===(t||"").substr(0,1))try{n=JSON.parse(t)}catch(t){n={}}return n[e]||""}function string(t,e){var n,i=t||"";return $.each(e||[],(function(t,e){n=new RegExp("\\{"+t.toString()+"\\}","ig"),i=i.replace(n,e)})),i}function init_tooltip(t){var e=!0===("boolean"==typeof t&&t)&&"mouse";$("[title]").qtip({position:{target:e,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},effect:!1}),$("div.tooltiptext").each((function(){$(this).filter(":not(:empty)").parent().qtip({suppress:!1,content:{text:$(this).clone()},position:{target:e,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden}})}))}String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")},String.prototype.left=function(t){if("string"===$.type(t)){var e=this.indexOf(t);return e>0?this.slice(0,e):""}return this.substring(0,t)},String.prototype.right=function(t){if("string"===$.type(t)){var e=this.indexOf(t);return e>0?this.substring(this.length-e):""}return this.substring(this.length-t)},Array.prototype.move=function(t,e){if(e>=this.length)for(var n=e-this.length;1+n--;)this.push(void 0);return this.splice(e,0,this.splice(t,1)[0]),this},function(t){t.fn.appendToIf=function(e,n){var i=t(this),o="function"==typeof n?n(i):n;return!0===("boolean"!=typeof o||o)&&i.appendTo(e),i},t.fn.appendIf=function(e,n){var i=t(this),o="function"==typeof n?n(i):n;return!0===("boolean"!=typeof o||o)&&i.append(e),i},t.fn.rwText=function(e,n,i){var o=t(this).empty();i=t.extend({wrap:!0},i);var r=!0===Array.isArray(e)?e:(null==e?"":String(e)).split("\n");return t.each(r,(function(t,e){""!==(e||"")&&(t>0&&o.append($$.br()),o.append(!0===i.wrap?$$.s(e):e))})),n&&o.attr("title",n),o},t.fn.loadSel=function(e,n,i){if("SELECT"===t(this).prop("tagName").toUpperCase()){var o=t(this);$ocms.postXT.call(this,{url:e,data:n||{},success:function(e){t.each(e,(function(){o.append($$.opt(e.value,e.text))}))},complete:function(){o.ldng(0),"function"==typeof i&&i.call(o)}})}},t.fn.emptyWithEditors=function(e){var n=t(this);return n.find(":input.tinymce").each((function(e,n){try{var i=tinymce.get(t(n).attr("id"));i&&i.remove()}catch(e){t.noop()}})),n.empty()},t.fn.cssValue=function(t){if(this.length>0){var e=this.css(t)||"";if(""===e)return 0;var n=/(^[\d\.]*)(\D{1,3}$)/gi.exec(e);return null!==n?"rem"===n[2]?$ocms.rpx(parseFloat(n[1])):parseFloat(n[1]):!1===isNaN(e)?parseFloat(e):0}return 0},t.fn.veryInnerHeight=function(){let e=e=>t(this).cssValue(e);return t(this).innerHeight()-e("padding-top")-e("padding-bottom")},t.fn.veryInnerWidth=function(){let e=e=>t(this).cssValue(e);return t(this).innerWidth()-e("padding-left")-e("padding-right")},t.fn.marginWidth=function(){let e=e=>t(this).cssValue(e);return e("margin-left")+e("margin-right")},t.fn.marginHeight=function(){let e=e=>t(this).cssValue(e);return e("margin-top")+e("margin-bottom")},t.inArrayRegEx=function(e,n,i){var o="regexp"===t.type(e)?e:new RegExp(e);if(!n)return-1;for(var r=i=i||0;r7){i=e.split(","),o=(n||(t<0?"rgb(0,0,0)":"rgb(255,255,255)")).split(",");var c=a(i[0].slice(4)),l=a(i[1]),u=a(i[2]);return"rgb("+(s((a(o[0].slice(4))-c)*r)+c)+","+(s((a(o[1])-l)*r)+l)+","+(s((a(o[2])-u)*r)+u)+")"}var d=(i=a(e.slice(1),16))>>16,h=i>>8&255,p=255&i;return"#"+(16777216+65536*(s((((o=a((n||(t<0?"#000000":"#FFFFFF")).slice(1),16))>>16)-d)*r)+d)+256*(s(((o>>8&255)-h)*r)+h)+(s(((255&o)-p)*r)+p)).toString(16).slice(1)},t.fn.IN=function(e){return t(this).fadeIn(400,e),t(this)},t.fn.OUT=function(e){return t(this).fadeOut(400,e),t(this)},t.fn.tooltip=function(e,n){var i=!0===("boolean"==typeof e&&e)&&"mouse",o="boolean"==typeof n&&n,r=t(this);return r.each((function(){var e=o?t(this).find(".tooltiptext"):t(this).children(".tooltiptext");t(e).length>0?e.each((function(){var e=t(this);t(this).filter(":not(:empty)").parent().qtip({suppress:!1,content:{text:e.clone()},position:{target:i,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},show:{effect:!1},hide:{effect:!1}}),e.remove()})):t(this).qtip({position:{target:i,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},effect:!1})})),r},t.fn.rC=function(e){return t(this).removeClass(e)},t.fn.aC=function(e){return t(this).addClass(e)},t.fn.tC=function(e,n){return t(this).toggleClass(e,n)}}(jQuery),function(t){t.fn.ocmsmenu=function(e,n){var i=t(this);return $ocms.menu.call(i,e,n),i},t.fn.activatemenu=function(){var e=t(this).filter("nav");return e.find("a").not(".on").addClass("on").click($nuf),e.find(".nav-btn").not(".on").addClass("on").click((function(e){e.stopPropagation();var n=t(this);t(n.attr("data-target")).tC(n.attr("data-toggle"))})),e}}(jQuery);class ObjectArray extends Array{isEmpty(){return 0===this[0].length}static get[Symbol.species](){return Array}filter(t){return"function"==typeof t?new ObjectArray(this[0].filter(t)):this}remove(t){if("function"!=typeof t)return this;{let e=this[0].findIndex(t);for(;e>-1;)this[0].splice(e),e=this[0].findIndex(t)}}sortBy(t){return"function"==typeof t&&this[0].sort(t),this}sortString(t){return this[0].sort(((e,n)=>{let i=(e[t]||"").toString().toUpperCase(),o=(n[t]||"").toString().toUpperCase();return console.debug(i.localeCompare(o)),i.localeCompare(o)})),this}sortNum(t){return this[0].sort(((e,n)=>{let i=e[t],o=n[t];return!0===isNaN(o)&&!1===isNaN(i)||io?1:0})),this}sum(t){return this[0].reduce(((e,n)=>e+(!0===isNaN(n[t])?0:n[t])),0)}groupBy(t){return this[0].reduce((function(e,n){let i=n[t];return e[i]||(e[i]=[]),e[i].push(n),e}),{})}each(t){if("function"==typeof t){let e=!1;this[0].forEach(((n,i,o)=>{if(!1===e){let r=t(n,i,o);"boolean"==typeof r&&!1===r&&(e=!0)}}))}}get toArray(){return this[0]}}class NumArray extends Array{sum(){return this.reduce(((t,e)=>t+e))}first(){return this[0]}last(){return this[this.length-1]}average(){return this.sum()/this.length}range(){let t=this.map((t=>t)).sort();return{min:t[0],max:t[this.length-1]}}static get[Symbol.species](){return Array}}$ocms.ocmsmenu=[{lbl:"",id:"m_home",ico:"glyphicon glyphicon-home",fnc:"init:home"},{fnc:"separator"}],function(t){t.multline=function(t){let e=t.split("\n"),n=$$.d();return $.each(e,((t,e)=>{n.append($$.s(e))})),n.html()},t.tooltip_hidden=function(t,e){$(this).remove(),e.rendered=!1},t.isJSONDateString=function(t){return"string"==typeof t&&/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/.test(t)},t.failure=function(e){11110===(e.internalCode||-1)?t.login.dlg():alert($t.f1+"\n"+(e.internalText||""))},t.getScript=function(e,n){var i=[],o=[],r=function(t){return"string"==typeof t&&""!==(t||"")},s=function(t,e){!0===bool(e.condition,!0)&&(""!==(e.script||"")&&o.push({url:e.script,module:e.module||""}),!0===r(e.css||"")?i.push(e.css):!0===Array.isArray(e.css)&&Array.prototype.push.apply(i,e.css.filter(r)))};!0===r(e||"")?o.push(e):!0===Array.isArray(e)?$.each(e,s):"object"==typeof e&&""!==(e.script||"")&&s(0,e);let a=[];$.each(i,(function(t,e){""!==(e||"")&&a.push(loadCSS(e))}));let c=o.map((function(e,n){let i=e.url,r=e.module||"";if(""===r){let t=new Promise((function(t,e){try{!async function(){$.ajax({url:i,dataType:"script",success:function(){t(o)},error:function(){e(o)},timeout:3e4})}()}catch(t){console.debug(t.message+"%o",t)}}));return t}return t.loadmodule(r,i,e.alias)}));Promise.all(c).then(n)},t.loadmodule=function(e,n,i){let o=new Promise((function(o,r){!async function(){try{let s=(n.startsWith("/")||n.startsWith(".")?"":"/")+n;import(s).then((n=>{t[e]=n[i||"default"],o(e)})).catch((t=>{console.debug(t.message+"%o",t),r(e)}))}catch(t){console.debug(t.message+"%o",t)}}()}));return o},t.ocms_auth=function(e,n,i,o){!1===$.isPlainObject(t.auth.modules)&&(t.auth.modules={});var r=0;t.auth.modules[e+(i||"")]?((r=t.auth.modules[e+(i||"")])<2&&(i||"")===auth.guid&&(r=2),r>=(n||0)&&o(false)):t.postXT({url:t.url("auth"),data:{module:e,person_guid:i||""},success:function(s){r=s[e],t.auth.modules[e+(i||"")]=r,r<2&&(i||"")===t.auth.person_guid&&(r=2),r>=(n||0)&&o(false)},error:function(e){t.failure.call(this,e)}})},t.auth.locale="de",t.ocms_prepauth=function(e,n,i){t.postXT({url:t.url("auth"),data:{fn:"csv",modules:e,person_guid:n||""},success:function(e){t.ocms_regauth(e)},error:function(e){t.failure.call(this,e)},complete:function(){i()}})},t.ocms_regauth=function(t){$.each(t||{},(function(t,e){auth.modules[t]=parseInt(e)}))},t.init=function(e){var n="string"==typeof e?e:(e.data||{}).fn||"";""!==n&&("home"===n?($cfr(),$lfr(),$("#topbar").ocmsmenu([],!0),$("#activemodule").text($t.ov),t.ov.call($("#contentframe"))):($cfr(),$lfr(),$("#topbar").ocmsmenu([]),t.postXT({url:t.url(n+"/auth"),success:function(e){void 0===t[n]&&(t[n]={}),t[n].auth=e,e.manage>0&&t.getScript({module:n,script:["web/imdl",n,t.auth.locale||"de","js"].join("."),css:["web/imdl",n,"css"].join("."),condition:"function"!=typeof t[n].init2},(function(){t[n].init2()}))},error:function(){$("#contentframe").empty()}})))},t.menuarray=function(t){this.array=[],this.sep=function(){this.length>0&&"separator"!==this.array[array.length-1].fnc&&this.push({fnc:"separator"})},this.push=function(t){return void 0===t?null:(!0===Array.isArray(t)?Array.prototype.push.apply(this.array,t):"object"==typeof t&&this.array.push(t),t)},this.unshift=function(t){return void 0===t?null:(!0===Array.isArray(t)?Array.prototype.unshift.apply(this.array,t):"object"==typeof t&&this.array.unshift(t),t)},this.push(t)},t.menu=function(e,n){e=e||[];var i=$(this).removeClass("vis");if(!0===bool(n,!0)&&!1===i.is("#mainmenu")&&i.empty(),!1===bool(n,!1)&&i.is("#sidebar,#topbar")&&(e.unshift({id:"sbctrl",glyph:"glyphicon-th-list",aclass:"fbtn",fnc:function(){$lf()}}),$lf(0)),0===(e||[]).length)i.empty().addClass("hd");else{i.removeClass("hd");var o=!0===i.is("nav")?i:i.children("nav");1!==o.length&&(o=$("").tC("nv",i.is("#sidebar")).tC("ctxt",i.is("#topbar")).appendTo(i));var r,s=$$.ul().appendTo(o),a=function(t,e){var n=$(this).addClass("dropdown submenu");t.append($$.sc("caret dd")).addClass("dds dropdown-toggle").attr({"aria-expanded":"false"}),""!==(e.ico||"")&&t.prepend($$.sc("ico "+e.ico));var i=$$.ul({class:"dropdown-menu",role:"menu"}).appendTo(n);$.each(e.itm||[],(function(t,e){r.call(i,e)}))},c=function(t){$(this).tC("disabled","boolean"==typeof t.disabled?t.disabled:"string"==typeof t.disabled&&"subs"===t.disabled&&0===(t.itm||[]).length)};r=function(e){var n,i=$$.li({id:e.id}).attr(e.attr||{}).addClass(e.lclass).appendTo($(this)),o="string"==typeof e.fnc&&""!==e.fnc?e.fnc.split(":")[0]:"";""!==o&&"init"!==o?i.attr("role",o).appendIf($$.s(e.lbl),""!==ne(e.lbl)):(n=$$.a({class:"on",role:"button"}).addClass(e.aclass).appendTo(i).append($$.s(e.lbl)),c.call(n,e),(e.itm||[]).length>0&&a.call(i,n,e),n.click($nuf),"function"==typeof e.fnc?n.click(e.data||{},e.fnc):"init"===o&&n.click($.extend({},e.data||{},{fn:e.fnc.split(":")[1]}),t.init))},$.each(e,(function(e,n){var i,o=$$.li({id:n.id}).attr(n.attr||{}).addClass(n.lclass),a="string"==typeof n.fnc&&""!==n.fnc?n.fnc.split(":")[0]:"";if(""!==a&&"init"!==a)o.attr("role",a).appendIf($$.s(n.lbl),""!==ne(n.lbl));else{if(i=$$.a({class:"on",role:"button"}).addClass(n.aclass).appendTo(o),c.call(i,n),""!==(n.lbl||"")&&i.append($$.s(n.lbl)),""!==(n.ico||"")&&i.prepend($$.sc("ico "+n.ico)),""!==(n.glyph||"")&&i.prepend($$.sc("glyphicon "+n.glyph)),(n.itm||[]).length>0){o.addClass("dropdown"),i.append($$.sc("caret dd")).addClass("dds dropdown-toggle").attr({"aria-expanded":"false"});var l=$$.ul({class:"dropdown-menu",role:"menu"}).appendTo(o);$.each(n.itm||[],(function(t,e){r.call(l,e)}))}(n.sel||[]).length>0||(i.click($nuf),"function"==typeof n.fnc?i.click(n.data||{},n.fnc):"init"===a&&i.click($.extend({},n.data||{},{fn:n.fnc.split(":")[1]}),t.init))}o.appendTo(s)})),o.activatemenu()}},t.easytbl=(t,e)=>{e=e||{};let n=$$.tbl().addClass(e.class).css("border-collapse","collapse"),i=($$.tbody(n),!0===bool(e.frame,!1)?{padding:"5px",border:"1px solid #727272"}:{});if(!0===Array.isArray(e.header)){let t=$$.thead(n);$.each(e.header,((n,o)=>$$.th(t).css(e.cellcss||i).rwText(o)))}else if(!0===bool(e.header,!1)&&(t||[]).length>0){let o=$$.thead(n);$.each(Object.keys(t[0]),((t,n)=>$$.th(o).css(e.cellcss||i).rwText(n)))}return $.each(t||[],((t,o)=>{let r=$$.tr();$.each(o,((t,n)=>{n=n||"";let o=$$.td(r).css(e.cellcss||i);n instanceof jQuery?o.append(n):"string"==typeof n&&("<"===n.substring(0,1)?o.append(n):o.text(n))})),n.append(r)})),n},t.dlgtbl=(e,n,i)=>{i=i||{};let o=t.easytbl(e,i);t.dlg(o,$.extend({title:n},i))},t.dlg=function(t,n){n=n||{};let i=$("body > .modal").length>0,o=t=>typeof n[t],r=t=>"function"===o(t);if(!0===bool(n.exclusive,!0)&&!0===i)return void alert($t.dbldlg||"Es ist bereits ein Dialog geöffnet");let s=$$.dc("modal",$("body")),a=$$.dc("modal-dialog",s);!1===isNaN(n.zindex)?s.css("zIndex",n.zindex):!0===i&&s.css("zIndex",parseInt($("body > .modal:last").cssValue("zIndex"))+200),!1===isNaN(n.zindex_min)&&s.cssValue("zIndex")').appendTo(d)),""!==ne(n.title)&&(c=$$.dc("modal-header",d),$("

      ").text(n.title).appendTo(c));let p=$$.dc("modal-body",d),f=$$.dc("modal-footer",d);t instanceof jQuery==!0&&p.append(t);let m=function(t){t&&"function"==typeof t.stopPropagation&&t.stopPropagation(),a.removeClass("in"),!0===r("closing")&&n.closing.call(d),p.hide().emptyWithEditors(),s.remove(),!0===r("close")&&n.close.call(d)};if(d.find(":input[required]").length>0&&($$.dc("note_required",f).append($$.sc("ind_required","*")).append($$.s($t.t1||"Eingabe erforderlich")),$$.dc("note_invalid",f).append($$.s($t.t2||"Bitte überprüfen Sie Ihre Eingaben im Formular."))),!0===r("cancel")){$$.bbtn(n.cancelbutton||"Abbrechen","cancel").attr({type:"button",role:"cancel"}).appendTo(f).click((function(t){n.cancel.call(d,t);t.stopPropagation(),m()}))}if(!0===r("confirm")){let t=$$.bbtn(n.button||"OK","confirm").attr({type:!0===bool(n.form,!1)?"submit":"button",role:"confirm"}).appendTo(f);!0===h?(d.submit((function(t){try{n.confirm.call(d,t)}finally{t.preventDefault()}return!1})),d.on("modal_submit",(function(){n.confirm.call(d,e)}))):(t.click((function(t){n.confirm.call(d,t);t.stopPropagation()})),d.on("modal_submit",(function(){t.click()})))}else!0===h&&d.submit((function(t){return t.preventDefault(),!1}));return d.on("modal_close",(function(){m()})),l.click(m),!0===r("opening")&&n.opening.call(d),a.addClass("in"),ne(n.mode).indexOf("maxbody")>-1&&p.css("min-height",(u.height()-c.outerHeight()-f.outerHeight()).toString()+"px"),!0===r("open")&&n.open.call(d),{hd:c,bdy:p,ft:f,ct:u,dlg:a,c:d}},t.mform=function(e){let n=$$.dc("form-body"),i=Array.isArray(e)?e:e instanceof fields_definition?e.fields:[];return $.each(i||[],(function(e,i){let o=i.type||"";if("ignore"===o)return!0;let r=$$.dc("form-group",n),s=i.id||"dlg_"+(i.name||"")+("html"===i.type?"_"+(65536*(1+Math.random())||0).toString(16).substr(9):""),a=$$.lbl(i.label||i.name,{for:s}).appendTo($$.dc("form-itm",r)),c=$$.dc("form-itm",r),l=$$.i({id:s,name:i.name,placeholder:i.placeholder,type:i.type});switch(o){case"email":i.pattern=ne(i.pattern,"[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$");break;case"url":i.pattern=ne(i.pattern,"https?://.+");break;case"number":i.pattern=ne(i.pattern,"[-+]?[0-9]*[.,]?[0-9]*"),l.attr("type","text"),l.attr("inputmode","decimal"),l.attr("step",i.precision||"any"),l.attr("data-format","float");break;case"integer":case"int":i.pattern=ne(i.pattern,"[-+]?[0-9]*"),l.attr("type","number"),l.attr("data-format","integer");break;case"date":if(""!==ne(i.pattern,$t.datepattern)&&(i.pattern=ne(i.pattern,"("+$t.datepattern+")|([0-9]{4}.(0[1-9]|1[012]).(0[1-9]|1[0-9]|2[0-9]|3[01]))")),""!==ne(i.placeholder,$t.dateplaceholder)&&l.attr("placeholder",ne(i.placeholder,$t.dateplaceholder)),"string"==typeof i.value){var u=i.value.substr(0,10);i.value="date"!==l.prop("type")?fdt(u+"T00:00:00",ne(i.dateformat,$t.dateformat)):u}l.attr("data-format","date:"+ne(i.dateformat,$t.dateformat)+";yyyy-MM-dd");break;case"datetime":l.attr("type","datetime-local"),""!==ne(i.pattern,$t.datetimepattern)&&(i.pattern=ne(i.pattern,"("+$t.datetimepattern+")|([0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])\\s([0-5][0-9]):([0-5][0-9]))")),""!==ne(i.placeholder,$t.datetimeplaceholder)&&l.attr("placeholder",ne(i.placeholder,$t.datetimeplaceholder)),"string"==typeof i.value&&"T"===i.value.substr(10,1)&&(i.value="datetime"!==l.prop("type").substr(0,8)?fdt(i.value,ne(i.datetimeformat,$t.datetimeformat)):i.value),l.attr("data-format","datetime:"+ne(i.datetimeformat,$t.datetimeformat)+";yyyy-MM-dd HH:mm:ss");break;case"hidden":r.addClass("hd");break;case"html":case"text":l=$$.txt({id:s,name:i.name,placeholder:i.placeholder,type:i.type}),l.tC("tinymce","html"===i.type);break;case"bool":case"boolean":i.url=[{value:"true",label:($t||{}).true||"Yes"},{value:"false",label:($t||{}).false||"No"}],"boolean"==typeof i.value&&(i.value=i.value?"true":"false");case"select":l=$$.sel({id:s,name:i.name,type:i.type}),!1===bool(i.required,!1)&&$$.eOpt().appendTo(l);try{var d=function(t){!0===Array.isArray(t)&&$.each(t,(function(t,e){"string"==typeof e?$$.opt(e,e).appendTo(l):!0===Array.isArray(e)?$$.opt(e[0],e[1]).appendTo(l):"object"==typeof e&&$$.opt(e.value,e.label||e.text).appendTo(l)}))};!0===Array.isArray(i.url)?d(i.url):"function"==typeof i.url?i.url.call(l):"string"==typeof i.url&&t.postXT({url:i.url,success:d})}catch(t){$.noop()}break;default:""!==ne(i["max-length"])&&l.attr("max-length",i["max-length"])}""!==ne(i.pattern)&&l.attr("pattern",i.pattern),l.val(i.value).change(),l.change((function(){$(this)[0].setCustomValidity("")})),l.addClass("form-control").prop("required",bool(i.required,!1)).prop("readonly",bool(i.readonly,!1)).appendTo(c),!0===bool(i.required,!1)&&a.append($$.sc("ind_required","*")),"object"==typeof i.attr&&l.attr(i.attr),"object"==typeof i.prop&&l.prop(i.prop),"string"==typeof i.class&&l.addClass(i.class),"function"==typeof i.change&&(l.change(i.change),!0===bool(i.applychange,!1)&&void 0!==i.value&&l.change()),""!==(i.note||"")&&$$.dc("form-note",c).rwText(i.note),"function"==typeof i.complete&&i.complete.call(l)})),n},t.initMCE=function(t,e){t=$(t),e=e||{};try{let n={target:t[0],inline:!1,width:e.width||"100%",statusbar:!1,document_base_url:window.location.origin+"/",content_style:"ph:before {content: '«'; color: #BBB; font-style:italic; } ph:after {content: '»'; color: #BBB; font-style:italic; } ph { color: #AAA; font-style:italic; }",relative_urls:!1,remove_script_host:!1};!0===bool(e.hidemenu,!1)&&(n.menubar=!1,n.menu={}),!0===bool(e.hidetoolbar,!1)&&(n.toolbar=!1),$.extend(n,e||{}),tinymce.init(n)}catch(t){alert(t.message)}},t.dlgform=function(e,n){n=n||{};let i,o=$$.dc("frm").append(t.mform(e||[]).addClass("stacked"));n.addcontent instanceof jQuery&&o.append(n.addcontent),"function"==typeof n.submit?i=n.submit:"function"==typeof n.success&&(i=function(e){var i=$(this).ldng(1),o=$.extend({loginaccount:t.auth.account||""},i.serializeObject(bool(n.checkvalidity,!0),{typedvalues:bool(n.typedvalues,!1)}));""!==(n.url||"")?t.postXT({url:n.url,data:o,success:function(t){n.success.call(this,t),i.trigger("modal_close")},error:function(){alert($t.l17)},complete:function(){i.ldng(0)},timeout:6e4}):(n.success.call(this,o),i.trigger("modal_close"))});let r={form:!0,title:n.title||"",button:n.button||$t.submit,confirm:i,size:n.size||[500,600],open:function(){let e=$(this).find(".tinymce");e.length>0&&t.initMCE(e,n.tinymce||{})}};return t.dlg.call(this,o,r)},t.login.dlg=function(e){e=e||{};let n=[{name:"userinfo",label:$t.l1,type:"string",value:t.auth.login,change:t.login.uichange,required:!0},{name:"userlogin",type:"hidden",required:!0,value:t.auth.login},{name:"username",type:"string",label:$t.l4,required:!0,readonly:!0,placeholder:$t.l5,value:t.auth.fullname_rev},{name:"userpass",type:"password",label:$t.l3,required:!0,placeholder:$t.l3}];""===(t.auth.account||"")&&n.unshift({id:"dlg_loginaccount",name:"loginaccount",type:"string",required:!0,value:t.auth.account});let i=$$.dc("frm").append(t.mform(n).addClass("stacked")),o=t.dlg.call(this,i,{form:!0,title:$t.l0,button:$t.submit,confirm:function(n){var i=$(this).ldng(1),o=$.extend({loginaccount:t.auth.account||""},i.serializeObject());t.postXT({url:"/vt/login",data:o,success:function(n){""!==((n||{}).login||"")&&(i.trigger("modal_close"),t.auth=n,"object"==typeof e.ajo&&(e.ajo.islogin,$.ajax(e.ajo)))},error:function(){alert($t.l17)},complete:function(){i.ldng(0)},timeout:6e4})},size:[500,600]}),r=$$.dc("modal-content").css("height","auto").attr("novalidate","true").append($$.dc("modal-header").appendIf($("

      ").text(t.auth.accountname),""!==(t.auth.accountname||"")).append($("

      Vereinsmanager

      ")));o.dlg.prepend(r)},t.addNoEntryInfo=function(t){$(this).append($$.dc("noentryinfo").text(t||$t.t11))}}($ocms),Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),Element.prototype.closest||(Element.prototype.closest=function(t){var e=this;do{if(Element.prototype.matches.call(e,t))return e;e=e.parentElement||e.parentNode}while(null!==e&&1===e.nodeType);return null}),function(t,e){var n,i;"object"==typeof window&&(window[t]=(n=function(t){var e=window,n=document.body,i=document.documentElement,o=Math.max(0,e.pageXOffset||i.scrollLeft||n.scrollLeft||0)-(i.clientLeft||0),r=Math.max(0,e.pageYOffset||i.scrollTop||n.scrollTop||0)-(i.clientTop||0);return{x:t?Math.max(0,t.pageX||t.clientX||0)-o:0,y:t?Math.max(0,t.pageY||t.clientY||0)-r:0}},(i=function(t,e){t&&t instanceof Element&&(this._container=t,this._options=e||{},this._clickItem=null,this._dragItem=null,this._showDragItem="boolean"!=typeof this._options.dragItem||!1!==this._options.dragItem,this._hovItem=null,this._sortLists=[],this._click={},this._dragging=!1,this._dragHandleClass=this._options.dragHandleClass||"",this._parentident=this._options.parentident||"",this._swapdone="function"==typeof this._options.swapdone?this._options.swapdone:null,this._onend="function"==typeof this._options.onend?this._options.onend:null,this._container.setAttribute("data-is-sortable",1),this._container.classList.add("sortable"),this._container.style.position="static",window.addEventListener("mousedown",this._onPress.bind(this),!0),window.addEventListener("touchstart",this._onPress.bind(this),!0),window.addEventListener("mouseup",this._onRelease.bind(this),!0),window.addEventListener("touchend",this._onRelease.bind(this),!0),window.addEventListener("mousemove",this._onMove.bind(this),!0),window.addEventListener("touchmove",this._onMove.bind(this),!0))}).prototype={constructor:i,toArray:function(t){t=t||"id";for(var e=[],n="",i=0;ii.left&&ei.top&&n0?a.mousedown(c).addClass("dctrl"):s.mousedown(c).addClass("dctrl"),t(this)}}(jQuery),$(document).ready((function(){$("html").click((function(t){$nuf()})),$("#listframe").click((function(t){t.stopPropagation(),$nuf()})),$("#mainmenu").ocmsmenu($ocms.ocmsmenu),$("#mainmenu").activatemenu()})),$.extend($t,{m_inv:"Rechnungen",m_req:"Aufträge",m_rep:"Berichte",m_todo:"ToDos",m_bcd:"BankBuchungen",m_admin:"Administration",m_admin_status:"Systemüberwachung",m_usr:"Benutzerverwaltung",rsp:"Passwort ändern",pnm:"Die Passwörter stimmen nicht überein",cps:"Das neue Passwort wurde gespeichert.",pwr:"Bitte wählen Sie ein starkes Passwort (min 8 Zeichen, davon jeweils min 2 Zahlen, kleine und große Buchstaben, Sonderzeichen sind optional).",smsc:"Sie beötigen für diese Funktion einen SMS-Code.\nSoll dieser nun versandt werden?",wdc:"Doppelt klicken, um die Box zu aktualisieren.",wdg:{}}),$t.rspf={sms:"Der SMS-Code konnte nicht bestätigt werden",valid:"Das alte Passwort ist nicht korrekt",requirements:"Das Passwort entspricht nicht den Anforderungen.\n"+$t.pwr},$fd={rsp:new fields_definition("","",[{name:"opw",label:"aktuelles Passwort",type:"password",required:!0,attr:{"auto-complete":"current-password"}},{name:"npw",label:"neues Passwort",type:"password",required:!0,pattern:"(.{6,})",attr:{"auto-complete":"new-password"}},{name:"npwc",label:"neues Passwort (Bestätigung)",type:"password",required:!0,attr:{"auto-complete":"new-password"},note:$t.pwr},{name:"code",label:"SMS-Code",type:"string",required:!0,attr:{"auto-complete":"one-time-code"}}])},$ocms.init=function(t){var e="string"==typeof t?t:(t.data||{}).fn||"";""!==e&&("home"===e?($cfr(),$lfr(),$("#topbar").ocmsmenu([],!0),$("#activemodule").text($t.ov),$fis.ov()):($cfr(),$lfr(),$("#topbar").ocmsmenu([]),$ocms.postXT({url:$ocms.url(e+"/auth"),success:function(t){void 0===$ocms[e]&&($ocms[e]={}),$ocms[e].auth=t,t.manage>0&&$ocms.getScript({module:e,script:["/web/fis",e,$ocms.auth.locale||"de","js"].join("."),css:["/web/fis",e,"css"].join("."),condition:"function"!=typeof $ocms[e].init2},(function(){$ocms[e].init2()}))},error:function(){$("#contentframe").empty()}})))};var $fis={auth:{},db:function(){$("#mainmenu_activemodule").text($t.ov);let t=$(this).empty(),e=$$.d({id:"dashboard_frame"}).appendTo(t);$ocms.postXT({url:$ocms.url("wdg/my"),success:function(t){$.each(t,(function(t,n){var i=$$.dc("wdg_frame",e,{"data-wdg":n}).ldng(1);$ocms.wdg.call(i,{wdg:n})}))},loading:e})},ValidateEmail:function(t){return!!/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(t)},cf:t=>{let e=$("#contentframe");return!0===bool(t,!1)&&e.empty().rC("hd"),e},lf:t=>{let e=$("#listframe");return!0===bool(t,!1)&&e.empty().aC("hd").rC("fix"),e},frm_edit:function(t){let e=$fis.cf(!1),n=e.children(".cfrm"),i=e.children(".edit_frm");return n.length<1?n=$$.dc("cfrm hd").prependTo(e):!0===bool(t,!1)&&n.empty(),i.length<1&&(i=$$.dc("edit_frm").insertAfter(n)),i.empty()},frm_list:function(t,e){let n=$fis.cf(!1),i=n.children(".cfrm"),o=n.children(".list_frm");return i.length<1?i=$$.dc("cfrm hd").prependTo(n):!0===bool(t,!1)&&i.empty(),!0===bool(e,!1)&&n.children(".edit_frm").remove(),o.length<1&&(o=$$.dc("list_frm").appendTo(n)),o.empty()},lfm:()=>{let t=$fis.lf(!1),e=t.children(".lfrm");return e.length<1&&(e=$$.dc("lfrm").prependTo(t)),e},getAuth:(t,e)=>new Promise(((n,i)=>{$fis.auth[t]&&!1===bool(e,!1)?n($fis.auth[t]||-1):$ocms.postXT({url:$ocms.url("auth"),data:{module:t},success:e=>{$fis.auth[t]=e.auth||-1,n($fis.auth[t]||-1)},error:()=>{i()}})})),prepAuth:t=>new Promise(((e,n)=>{$ocms.postXT({url:$ocms.url("auth"),data:{module:t,array:1},success:t=>{$.extend($fis.auth,t||{})},complete:()=>{e()}})})),isAuth:(t,e)=>($fis.auth[t]||-1)>=(e||1),resetPass:function(t,e){confirm($t.smsc)&&($ocms.postXT({url:$ocms.url("account/sms"),data:{fn:"pwc"}}),$ocms.dlgform($fd.rsp.clone(),{title:$t.rsp||"",submit:function(t){var e=$(this).ldng(1),n=$.extend({loginaccount:$ocms.auth.account||""},e.serializeObject(!0,{typedvalues:!0}));(n.npw||"")!==(n.npwc||"")?e.find('input[name="npw"]:first')[0].setCustomValidity($t.pnm):$ocms.postXT({url:$ocms.url("account/changepassword"),data:n,success:function(t){alert($t.cps),e.trigger("modal_close")},error:function(t){alert($t.rspf[t.getResponseHeader("x-ocms-std")])},complete:function(){e.ldng(0)},timeout:6e4})}}))},wdg:function(t){let e=$(this).empty();$ocms.postXT({url:$ocms.url("wdg/one"),data:{short_name:t.wdg},timeout:9e4,success:function(n,i,o){let r=t.wdg,s=n[r];if(!s)return void e.ldng(0);let a=$.inArrayRegEx("dblwidth",s.rendering_options)>-1,c=$.inArrayRegEx("tiny",s.rendering_options)>-1;e.toggleClass("dbl",a&&!c).toggleClass("tny",c);$$.dc("wdg_hd",e,{title:ne(s.description,$t.wdc)}).toggleClass("dbl",a).text(ne(s.name,t.wdg)).dblclick((function(t){t.stopPropagation(),$fis.wdg.call(e,{wdg:r})}));let l=$$.dc("wdg_cnt",e).toggleClass("dbl",a).hide(),u=$.inArrayRegEx("bgcolor",s.rendering_options);switch(u>-1&&l.css("backgroundColor",s.rendering_options[u].toString().right(":")),s.type){case"table":var d=$$.tblset({},l),h=$$.tr().appendTo(d.hd),p=$t.wdg[r.indexOf("wdg_ev_")>=0?"wdg_ev_":r]||{};$.each(s.columns,(function(t,e){var n=p[e]?p[e].label:e;$$.th().text(n).appendTo(h)})),$.each(s.data,(function(t,e){var n=$$.tr().appendTo(d.bdy);$.each(s.columns,(function(t,i){var o=$$.td().appendTo(n);e[i]instanceof Date||!0===$ocms.isJSONDateString(e[i])?o.text(fdt(e[i],$t.dateformat)):o.rwText(e[i])}))})),$.inArray("firstrow_bold",s.rendering_options)>-1&&h.nextAll("tr:first").css("font-weight","bold");break;case"ind":$$.dc("ind",l).addClass("sts_"+(s.data.status||"")).append([$$.dc("ind").text(s.data.value),$$.lbl(s.data.label)]);break;case"image_url":l.css("background","url('"+s.url+"') no-repeat center center transparent");break;case"image_base64":l.css("background","url('data:image/png;base64,"+s.image+"') no-repeat center center transparent");break;case"html":if(l.html(s.html),$.inArray("reload_10min",s.rendering_options)>-1){var f=l.find("iframe");setTimeout((function(){f.attr("src",(function(t,e){return e}))}),6e5)}}$.inArray("reload_30min",s.rendering_options)>-1&&"html"!==s.type&&setTimeout((function(){$fis.wdg.call(e,{wdg:r})}),18e5),l.slideDown(150)},error:function(t){e.slideUp(150),$fis.failure.call(this,t)},complete:function(){e.ldng(0)}})},ov:function(){$fis.lf(!0);let t=$("#contentframe").empty(),e=$$.d({id:"dashboard_frame"}).appendTo(t);$ocms.postXT({url:$ocms.url("wdg/my"),success:function(t){$.each(t,(function(t,n){var i=$$.dc("wdg_frame",e,{"data-wdg":n}).ldng(1);$fis.wdg.call(i,{wdg:n})}))},loading:e})}};$fis.notifications={connection:null,init:function(){"undefined"!=typeof signalR&&null===this.connection&&$ocms.auth.useraccount_id&&(this.ensureFrame(),this.connection=(new signalR.HubConnectionBuilder).withUrl("/notifications").withAutomaticReconnect().build(),this.connection.on("notification",(t=>{this.push(t)})),this.connection.onclose((()=>{console.warn("Notification connection closed; retrying in 5s."),this.connection=null,setTimeout((()=>this.init()),5e3)})),this.start())},start:function(){this.connection.start().catch((t=>{console.warn("Notification connection failed to start; retrying in 5s.",t),this.connection=null,setTimeout((()=>this.init()),5e3)}))},ensureFrame:function(){$("#notification_frame").length<1&&$("
      ",{id:"notification_frame"}).appendTo($("footer:first").length?"footer:first":"body")},push:function(t){this.ensureFrame(),t=t||{};let e=$("
      ",{class:"notification_item"}).addClass((t.severity||"info").toLowerCase()).append($("
      '),n=e.find(".form-body"),i=null;e.find("form").submit((function(t){t.preventDefault();var o=$(this).serializeObject(!0),r=null===i,s=r?"spwc":"spw";return $ocms.postXT.call(this,{url:$ocms.url(s),data:o,complete:function(){r?(n.append('
      Ihnen wurde ein Code per SMS zugesandt.
      Bitte tragen Sie den hier ein:
      '),i=$('
      ').appendTo(n)):(alert($t.l13),e.remove())},error:()=>{}}),!1})),e.find(".modal-close").click((function(){e.remove()}));var o=[];$.each($t.l7a.split("\n"),((t,e)=>{Array.prototype.push.apply(o,[$("
      "),$("").text(e)])})),e.find(".modal-note").append($('').text($t.alert)).append(o),e.appendTo("body"),setTimeout((function(){$(".modal").find('input[name="lastname"]').focus()}),600)}};var $$={s:function(t){return $("").text(t)},br:function(){return $("
      ")},sc:function(t,e){return $("").addClass(t).text(e)},td:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t?n.attr(t):"string"==typeof t&&n.text(t),"object"==typeof e?n.attr(e):"string"==typeof e&&n.text(e),n},th:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t?n.attr(t):"string"==typeof t&&n.text(t),"object"==typeof e?n.attr(e):"string"==typeof e&&n.text(e),n},tdc:function(t,e,n){return $$.td(e,n).addClass(t)},td2:function(t){var e=$('');return"string"===$.type(t)?e.text(t):t instanceof jQuery?e.append(t):"function"==typeof t?t.call(e):e.html(" "),e},td3:function(t){var e=$('');return"string"===$.type(t)?e.text(t):t instanceof jQuery?e.append(t):"function"==typeof t?t.call(e):e.html(" "),e},tdtr:function(t,e){var n=$$.tr().appendTo(e);return t instanceof jQuery==!0||"string"==typeof t?t.appendTo($$.td().appendTo(n)):!0===Array.isArray(t)&&$.each(t,(function(t,e){$(e).appendTo($$.td().appendTo(n))})),n},tr:function(t,e){var n=$("");return t instanceof jQuery==!0?n.appendTo(t):"object"==typeof t&&n.attr(t),"object"==typeof e&&n.attr(e),n},trc:function(t,e){var n=$("").addClass(t);return e instanceof jQuery==!0?n.appendTo(e):"object"==typeof e&&n.attr(e),n},d:function(t){return $("
      ").attr(t||{})},dc:function(t,e,n,i){var o=$("
      ").addClass(t);return e instanceof jQuery==!0?o.appendTo(e):"object"==typeof e?o.attr(e):"function"==typeof e?o.click(e):"string"==typeof e&&o.text(e),"string"==typeof n?o.text(n):"object"==typeof n?o.attr(n):"function"==typeof n&&o.click(n),"string"==typeof i?o.text(i):"object"==typeof i?o.attr(i):"function"==typeof i&&o.click(i),o},df:function(t){return $("
       
      ").attr(t||{})},opt:function(t,e,n){var i=$("");return"string"==typeof t?i.attr("value",t):"object"==typeof t&&i.attr(t),"string"==typeof e?i.text(e):"object"==typeof e&&i.attr(e),"object"==typeof n&&i.attr(n),i},eOpt:function(t){var e=$('');return t&&e.attr("selected","selected"),e},tbl:function(t){return $("
      ").attr(t||{})},tblc:function(t){return $("
      ").addClass(t)},thead:function(t){let e=$("");return t instanceof jQuery&&e.prependTo(t),e},tbody:function(t){let e=$("");return t instanceof jQuery&&e.appendTo(t),e},tblset:function(t,e){let n=$$.tbl(t||{});return e instanceof jQuery&&e.append(n),{tbl:n,hd:$$.thead().appendTo(n),bdy:$$.tbody().appendTo(n)}},i:function(t){return $("").attr(t||{})},img:function(t,e){return $("").attr("src",t).attr(e||{})},sel:function(t){return $("").attr(t||{})},btn:function(t){return $("").attr(t||{})},a:function(t){return $("").attr(t||{})},li:function(t){return $("
    • ").attr(t||{})},ul:function(t){return $("
        ").attr(t||{})},nav:function(t){return $("").attr(t||{})},lbl:function(t,e){var n=$("");return"string"==typeof t&&n.text(t),"object"==typeof t?n.attr(t):"object"==typeof e&&n.attr(e),n},txt:function(t){return $("").attr(t||{})},0:function(t,e){return $("<"+t+">").attr(e||{})},bbtn:function(t,e){return $$.btn({type:"button",class:"btn"}).addClass(e).text(t)},svg:t=>$(document.createElementNS("http://www.w3.org/2000/svg",t))};function getMonday(t){var e=(t=new Date(t)).getDay(),n=t.getDate()-e+(0==e?-6:1);return new Date(t.setDate(n))}function $lf(t){var e=void 0===t?null:"number"==typeof t&&1!==t||"boolean"==typeof cl&&!1===t;return $("#listframe").tC("hd",e).is(".hd")}function $nuf(t){if(t&&t.stopPropagation(),!$(this).is(".disabled")){var e=function(t){t.removeClass("vis").find("li.dropdown").removeClass("open").removeClass("vis").attr("aria-expanded","false")},n=$(this).parent("li.dropdown");if(n.length>0){n.tC("open"),navs=!0===n.is(".open")?"true":"false",n.attr("aria-expanded",navs);var i=n.closest("nav");i.find("li.dropdown").not(n.parentsUntil("nav")).not(n).removeClass("open").attr("aria-expanded","false"),!1===n.is(".open")&&n.find("li.dropdown").removeClass("open").attr("aria-expanded","false"),e($("nav").not(i))}else e($("nav"))}}function $tbr(){return $lf(0),$("#topbar").ocmsmenu([])}function $lfr(){return $("#sidebar").empty(),$("#listframe").removeClass("fix").addClass("hd").empty()}function $cfr(){return $tbr(),$("#contentframe").empty()}function jObj(t,e){let n={};if("{"===(t||"").substr(0,1))try{n=JSON.parse(t)}catch(t){n={}}return n[e]||""}function string(t,e){var n,i=t||"";return $.each(e||[],(function(t,e){n=new RegExp("\\{"+t.toString()+"\\}","ig"),i=i.replace(n,e)})),i}function init_tooltip(t){var e=!0===("boolean"==typeof t&&t)&&"mouse";$("[title]").qtip({position:{target:e,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},effect:!1}),$("div.tooltiptext").each((function(){$(this).filter(":not(:empty)").parent().qtip({suppress:!1,content:{text:$(this).clone()},position:{target:e,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden}})}))}String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")},String.prototype.left=function(t){if("string"===$.type(t)){var e=this.indexOf(t);return e>0?this.slice(0,e):""}return this.substring(0,t)},String.prototype.right=function(t){if("string"===$.type(t)){var e=this.indexOf(t);return e>0?this.substring(this.length-e):""}return this.substring(this.length-t)},Array.prototype.move=function(t,e){if(e>=this.length)for(var n=e-this.length;1+n--;)this.push(void 0);return this.splice(e,0,this.splice(t,1)[0]),this},function(t){t.fn.appendToIf=function(e,n){var i=t(this),o="function"==typeof n?n(i):n;return!0===("boolean"!=typeof o||o)&&i.appendTo(e),i},t.fn.appendIf=function(e,n){var i=t(this),o="function"==typeof n?n(i):n;return!0===("boolean"!=typeof o||o)&&i.append(e),i},t.fn.rwText=function(e,n,i){var o=t(this).empty();i=t.extend({wrap:!0},i);var r=!0===Array.isArray(e)?e:(null==e?"":String(e)).split("\n");return t.each(r,(function(t,e){""!==(e||"")&&(t>0&&o.append($$.br()),o.append(!0===i.wrap?$$.s(e):e))})),n&&o.attr("title",n),o},t.fn.loadSel=function(e,n,i){if("SELECT"===t(this).prop("tagName").toUpperCase()){var o=t(this);$ocms.postXT.call(this,{url:e,data:n||{},success:function(e){t.each(e,(function(){o.append($$.opt(e.value,e.text))}))},complete:function(){o.ldng(0),"function"==typeof i&&i.call(o)}})}},t.fn.emptyWithEditors=function(e){var n=t(this);return n.find(":input.tinymce").each((function(e,n){try{var i=tinymce.get(t(n).attr("id"));i&&i.remove()}catch(e){t.noop()}})),n.empty()},t.fn.cssValue=function(t){if(this.length>0){var e=this.css(t)||"";if(""===e)return 0;var n=/(^[\d\.]*)(\D{1,3}$)/gi.exec(e);return null!==n?"rem"===n[2]?$ocms.rpx(parseFloat(n[1])):parseFloat(n[1]):!1===isNaN(e)?parseFloat(e):0}return 0},t.fn.veryInnerHeight=function(){let e=e=>t(this).cssValue(e);return t(this).innerHeight()-e("padding-top")-e("padding-bottom")},t.fn.veryInnerWidth=function(){let e=e=>t(this).cssValue(e);return t(this).innerWidth()-e("padding-left")-e("padding-right")},t.fn.marginWidth=function(){let e=e=>t(this).cssValue(e);return e("margin-left")+e("margin-right")},t.fn.marginHeight=function(){let e=e=>t(this).cssValue(e);return e("margin-top")+e("margin-bottom")},t.inArrayRegEx=function(e,n,i){var o="regexp"===t.type(e)?e:new RegExp(e);if(!n)return-1;for(var r=i=i||0;r7){i=e.split(","),o=(n||(t<0?"rgb(0,0,0)":"rgb(255,255,255)")).split(",");var c=a(i[0].slice(4)),l=a(i[1]),u=a(i[2]);return"rgb("+(s((a(o[0].slice(4))-c)*r)+c)+","+(s((a(o[1])-l)*r)+l)+","+(s((a(o[2])-u)*r)+u)+")"}var d=(i=a(e.slice(1),16))>>16,h=i>>8&255,p=255&i;return"#"+(16777216+65536*(s((((o=a((n||(t<0?"#000000":"#FFFFFF")).slice(1),16))>>16)-d)*r)+d)+256*(s(((o>>8&255)-h)*r)+h)+(s(((255&o)-p)*r)+p)).toString(16).slice(1)},t.fn.IN=function(e){return t(this).fadeIn(400,e),t(this)},t.fn.OUT=function(e){return t(this).fadeOut(400,e),t(this)},t.fn.tooltip=function(e,n){var i=!0===("boolean"==typeof e&&e)&&"mouse",o="boolean"==typeof n&&n,r=t(this);return r.each((function(){var e=o?t(this).find(".tooltiptext"):t(this).children(".tooltiptext");t(e).length>0?e.each((function(){var e=t(this);t(this).filter(":not(:empty)").parent().qtip({suppress:!1,content:{text:e.clone()},position:{target:i,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},show:{effect:!1},hide:{effect:!1}}),e.remove()})):t(this).qtip({position:{target:i,adjust:{x:2,y:2},viewport:!0},events:{hidden:$ocms.tooltip_hidden},effect:!1})})),r},t.fn.rC=function(e){return t(this).removeClass(e)},t.fn.aC=function(e){return t(this).addClass(e)},t.fn.tC=function(e,n){return t(this).toggleClass(e,n)}}(jQuery),function(t){t.fn.ocmsmenu=function(e,n){var i=t(this);return $ocms.menu.call(i,e,n),i},t.fn.activatemenu=function(){var e=t(this).filter("nav");return e.find("a").not(".on").addClass("on").click($nuf),e.find(".nav-btn").not(".on").addClass("on").click((function(e){e.stopPropagation();var n=t(this);t(n.attr("data-target")).tC(n.attr("data-toggle"))})),e}}(jQuery);class ObjectArray extends Array{isEmpty(){return 0===this[0].length}static get[Symbol.species](){return Array}filter(t){return"function"==typeof t?new ObjectArray(this[0].filter(t)):this}remove(t){if("function"!=typeof t)return this;{let e=this[0].findIndex(t);for(;e>-1;)this[0].splice(e),e=this[0].findIndex(t)}}sortBy(t){return"function"==typeof t&&this[0].sort(t),this}sortString(t){return this[0].sort(((e,n)=>{let i=(e[t]||"").toString().toUpperCase(),o=(n[t]||"").toString().toUpperCase();return console.debug(i.localeCompare(o)),i.localeCompare(o)})),this}sortNum(t){return this[0].sort(((e,n)=>{let i=e[t],o=n[t];return!0===isNaN(o)&&!1===isNaN(i)||io?1:0})),this}sum(t){return this[0].reduce(((e,n)=>e+(!0===isNaN(n[t])?0:n[t])),0)}groupBy(t){return this[0].reduce((function(e,n){let i=n[t];return e[i]||(e[i]=[]),e[i].push(n),e}),{})}each(t){if("function"==typeof t){let e=!1;this[0].forEach(((n,i,o)=>{if(!1===e){let r=t(n,i,o);"boolean"==typeof r&&!1===r&&(e=!0)}}))}}get toArray(){return this[0]}}class NumArray extends Array{sum(){return this.reduce(((t,e)=>t+e))}first(){return this[0]}last(){return this[this.length-1]}average(){return this.sum()/this.length}range(){let t=this.map((t=>t)).sort();return{min:t[0],max:t[this.length-1]}}static get[Symbol.species](){return Array}}$ocms.ocmsmenu=[{lbl:"",id:"m_home",ico:"glyphicon glyphicon-home",fnc:"init:home"},{fnc:"separator"}],function(t){t.multline=function(t){let e=t.split("\n"),n=$$.d();return $.each(e,((t,e)=>{n.append($$.s(e))})),n.html()},t.tooltip_hidden=function(t,e){$(this).remove(),e.rendered=!1},t.isJSONDateString=function(t){return"string"==typeof t&&/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/.test(t)},t.failure=function(e){11110===(e.internalCode||-1)?t.login.dlg():alert($t.f1+"\n"+(e.internalText||""))},t.getScript=function(e,n){var i=[],o=[],r=function(t){return"string"==typeof t&&""!==(t||"")},s=function(t,e){!0===bool(e.condition,!0)&&(""!==(e.script||"")&&o.push({url:e.script,module:e.module||""}),!0===r(e.css||"")?i.push(e.css):!0===Array.isArray(e.css)&&Array.prototype.push.apply(i,e.css.filter(r)))};!0===r(e||"")?o.push(e):!0===Array.isArray(e)?$.each(e,s):"object"==typeof e&&""!==(e.script||"")&&s(0,e);let a=[];$.each(i,(function(t,e){""!==(e||"")&&a.push(loadCSS(e))}));let c=o.map((function(e,n){let i=e.url,r=e.module||"";if(""===r){let t=new Promise((function(t,e){try{!async function(){$.ajax({url:i,dataType:"script",success:function(){t(o)},error:function(){e(o)},timeout:3e4})}()}catch(t){console.debug(t.message+"%o",t)}}));return t}return t.loadmodule(r,i,e.alias)}));Promise.all(c).then(n)},t.loadmodule=function(e,n,i){let o=new Promise((function(o,r){!async function(){try{let s=(n.startsWith("/")||n.startsWith(".")?"":"/")+n;import(s).then((n=>{t[e]=n[i||"default"],o(e)})).catch((t=>{console.debug(t.message+"%o",t),r(e)}))}catch(t){console.debug(t.message+"%o",t)}}()}));return o},t.ocms_auth=function(e,n,i,o){!1===$.isPlainObject(t.auth.modules)&&(t.auth.modules={});var r=0;t.auth.modules[e+(i||"")]?((r=t.auth.modules[e+(i||"")])<2&&(i||"")===auth.guid&&(r=2),r>=(n||0)&&o(false)):t.postXT({url:t.url("auth"),data:{module:e,person_guid:i||""},success:function(s){r=s[e],t.auth.modules[e+(i||"")]=r,r<2&&(i||"")===t.auth.person_guid&&(r=2),r>=(n||0)&&o(false)},error:function(e){t.failure.call(this,e)}})},t.auth.locale="de",t.ocms_prepauth=function(e,n,i){t.postXT({url:t.url("auth"),data:{fn:"csv",modules:e,person_guid:n||""},success:function(e){t.ocms_regauth(e)},error:function(e){t.failure.call(this,e)},complete:function(){i()}})},t.ocms_regauth=function(t){$.each(t||{},(function(t,e){auth.modules[t]=parseInt(e)}))},t.init=function(e){var n="string"==typeof e?e:(e.data||{}).fn||"";""!==n&&("home"===n?($cfr(),$lfr(),$("#topbar").ocmsmenu([],!0),$("#activemodule").text($t.ov),t.ov.call($("#contentframe"))):($cfr(),$lfr(),$("#topbar").ocmsmenu([]),t.postXT({url:t.url(n+"/auth"),success:function(e){void 0===t[n]&&(t[n]={}),t[n].auth=e,e.manage>0&&t.getScript({module:n,script:["web/imdl",n,t.auth.locale||"de","js"].join("."),css:["web/imdl",n,"css"].join("."),condition:"function"!=typeof t[n].init2},(function(){t[n].init2()}))},error:function(){$("#contentframe").empty()}})))},t.menuarray=function(t){this.array=[],this.sep=function(){this.length>0&&"separator"!==this.array[array.length-1].fnc&&this.push({fnc:"separator"})},this.push=function(t){return void 0===t?null:(!0===Array.isArray(t)?Array.prototype.push.apply(this.array,t):"object"==typeof t&&this.array.push(t),t)},this.unshift=function(t){return void 0===t?null:(!0===Array.isArray(t)?Array.prototype.unshift.apply(this.array,t):"object"==typeof t&&this.array.unshift(t),t)},this.push(t)},t.menu=function(e,n){e=e||[];var i=$(this).removeClass("vis");if(!0===bool(n,!0)&&!1===i.is("#mainmenu")&&i.empty(),!1===bool(n,!1)&&i.is("#sidebar,#topbar")&&(e.unshift({id:"sbctrl",glyph:"glyphicon-th-list",aclass:"fbtn",fnc:function(){$lf()}}),$lf(0)),0===(e||[]).length)i.empty().addClass("hd");else{i.removeClass("hd");var o=!0===i.is("nav")?i:i.children("nav");1!==o.length&&(o=$("").tC("nv",i.is("#sidebar")).tC("ctxt",i.is("#topbar")).appendTo(i));var r,s=$$.ul().appendTo(o),a=function(t,e){var n=$(this).addClass("dropdown submenu");t.append($$.sc("caret dd")).addClass("dds dropdown-toggle").attr({"aria-expanded":"false"}),""!==(e.ico||"")&&t.prepend($$.sc("ico "+e.ico));var i=$$.ul({class:"dropdown-menu",role:"menu"}).appendTo(n);$.each(e.itm||[],(function(t,e){r.call(i,e)}))},c=function(t){$(this).tC("disabled","boolean"==typeof t.disabled?t.disabled:"string"==typeof t.disabled&&"subs"===t.disabled&&0===(t.itm||[]).length)};r=function(e){var n,i=$$.li({id:e.id}).attr(e.attr||{}).addClass(e.lclass).appendTo($(this)),o="string"==typeof e.fnc&&""!==e.fnc?e.fnc.split(":")[0]:"";""!==o&&"init"!==o?i.attr("role",o).appendIf($$.s(e.lbl),""!==ne(e.lbl)):(n=$$.a({class:"on",role:"button"}).addClass(e.aclass).appendTo(i).append($$.s(e.lbl)),c.call(n,e),(e.itm||[]).length>0&&a.call(i,n,e),n.click($nuf),"function"==typeof e.fnc?n.click(e.data||{},e.fnc):"init"===o&&n.click($.extend({},e.data||{},{fn:e.fnc.split(":")[1]}),t.init))},$.each(e,(function(e,n){var i,o=$$.li({id:n.id}).attr(n.attr||{}).addClass(n.lclass),a="string"==typeof n.fnc&&""!==n.fnc?n.fnc.split(":")[0]:"";if(""!==a&&"init"!==a)o.attr("role",a).appendIf($$.s(n.lbl),""!==ne(n.lbl));else{if(i=$$.a({class:"on",role:"button"}).addClass(n.aclass).appendTo(o),c.call(i,n),""!==(n.lbl||"")&&i.append($$.s(n.lbl)),""!==(n.ico||"")&&i.prepend($$.sc("ico "+n.ico)),""!==(n.glyph||"")&&i.prepend($$.sc("glyphicon "+n.glyph)),(n.itm||[]).length>0){o.addClass("dropdown"),i.append($$.sc("caret dd")).addClass("dds dropdown-toggle").attr({"aria-expanded":"false"});var l=$$.ul({class:"dropdown-menu",role:"menu"}).appendTo(o);$.each(n.itm||[],(function(t,e){r.call(l,e)}))}(n.sel||[]).length>0||(i.click($nuf),"function"==typeof n.fnc?i.click(n.data||{},n.fnc):"init"===a&&i.click($.extend({},n.data||{},{fn:n.fnc.split(":")[1]}),t.init))}o.appendTo(s)})),o.activatemenu()}},t.easytbl=(t,e)=>{e=e||{};let n=$$.tbl().addClass(e.class).css("border-collapse","collapse"),i=($$.tbody(n),!0===bool(e.frame,!1)?{padding:"5px",border:"1px solid #727272"}:{});if(!0===Array.isArray(e.header)){let t=$$.thead(n);$.each(e.header,((n,o)=>$$.th(t).css(e.cellcss||i).rwText(o)))}else if(!0===bool(e.header,!1)&&(t||[]).length>0){let o=$$.thead(n);$.each(Object.keys(t[0]),((t,n)=>$$.th(o).css(e.cellcss||i).rwText(n)))}return $.each(t||[],((t,o)=>{let r=$$.tr();$.each(o,((t,n)=>{n=n||"";let o=$$.td(r).css(e.cellcss||i);n instanceof jQuery?o.append(n):"string"==typeof n&&("<"===n.substring(0,1)?o.append(n):o.text(n))})),n.append(r)})),n},t.dlgtbl=(e,n,i)=>{i=i||{};let o=t.easytbl(e,i);t.dlg(o,$.extend({title:n},i))},t.dlg=function(t,n){n=n||{};let i=$("body > .modal").length>0,o=t=>typeof n[t],r=t=>"function"===o(t);if(!0===bool(n.exclusive,!0)&&!0===i)return void alert($t.dbldlg||"Es ist bereits ein Dialog geöffnet");let s=$$.dc("modal",$("body")),a=$$.dc("modal-dialog",s);!1===isNaN(n.zindex)?s.css("zIndex",n.zindex):!0===i&&s.css("zIndex",parseInt($("body > .modal:last").cssValue("zIndex"))+200),!1===isNaN(n.zindex_min)&&s.cssValue("zIndex")').appendTo(d)),""!==ne(n.title)&&(c=$$.dc("modal-header",d),$("

        ").text(n.title).appendTo(c));let p=$$.dc("modal-body",d),f=$$.dc("modal-footer",d);t instanceof jQuery==!0&&p.append(t);let m=function(t){t&&"function"==typeof t.stopPropagation&&t.stopPropagation(),a.removeClass("in"),!0===r("closing")&&n.closing.call(d),p.hide().emptyWithEditors(),s.remove(),!0===r("close")&&n.close.call(d)};if(d.find(":input[required]").length>0&&($$.dc("note_required",f).append($$.sc("ind_required","*")).append($$.s($t.t1||"Eingabe erforderlich")),$$.dc("note_invalid",f).append($$.s($t.t2||"Bitte überprüfen Sie Ihre Eingaben im Formular."))),!0===r("cancel")){$$.bbtn(n.cancelbutton||"Abbrechen","cancel").attr({type:"button",role:"cancel"}).appendTo(f).click((function(t){n.cancel.call(d,t);t.stopPropagation(),m()}))}if(!0===r("confirm")){let t=$$.bbtn(n.button||"OK","confirm").attr({type:!0===bool(n.form,!1)?"submit":"button",role:"confirm"}).appendTo(f);!0===h?(d.submit((function(t){try{n.confirm.call(d,t)}finally{t.preventDefault()}return!1})),d.on("modal_submit",(function(){n.confirm.call(d,e)}))):(t.click((function(t){n.confirm.call(d,t);t.stopPropagation()})),d.on("modal_submit",(function(){t.click()})))}else!0===h&&d.submit((function(t){return t.preventDefault(),!1}));return d.on("modal_close",(function(){m()})),l.click(m),!0===r("opening")&&n.opening.call(d),a.addClass("in"),ne(n.mode).indexOf("maxbody")>-1&&p.css("min-height",(u.height()-c.outerHeight()-f.outerHeight()).toString()+"px"),!0===r("open")&&n.open.call(d),{hd:c,bdy:p,ft:f,ct:u,dlg:a,c:d}},t.mform=function(e){let n=$$.dc("form-body"),i=Array.isArray(e)?e:e instanceof fields_definition?e.fields:[];return $.each(i||[],(function(e,i){let o=i.type||"";if("ignore"===o)return!0;let r=$$.dc("form-group",n),s=i.id||"dlg_"+(i.name||"")+("html"===i.type?"_"+(65536*(1+Math.random())||0).toString(16).substr(9):""),a=$$.lbl(i.label||i.name,{for:s}).appendTo($$.dc("form-itm",r)),c=$$.dc("form-itm",r),l=$$.i({id:s,name:i.name,placeholder:i.placeholder,type:i.type});switch(o){case"email":i.pattern=ne(i.pattern,"[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$");break;case"url":i.pattern=ne(i.pattern,"https?://.+");break;case"number":i.pattern=ne(i.pattern,"[-+]?[0-9]*[.,]?[0-9]*"),l.attr("type","text"),l.attr("inputmode","decimal"),l.attr("step",i.precision||"any"),l.attr("data-format","float");break;case"integer":case"int":i.pattern=ne(i.pattern,"[-+]?[0-9]*"),l.attr("type","number"),l.attr("data-format","integer");break;case"date":if(""!==ne(i.pattern,$t.datepattern)&&(i.pattern=ne(i.pattern,"("+$t.datepattern+")|([0-9]{4}.(0[1-9]|1[012]).(0[1-9]|1[0-9]|2[0-9]|3[01]))")),""!==ne(i.placeholder,$t.dateplaceholder)&&l.attr("placeholder",ne(i.placeholder,$t.dateplaceholder)),"string"==typeof i.value){var u=i.value.substr(0,10);i.value="date"!==l.prop("type")?fdt(u+"T00:00:00",ne(i.dateformat,$t.dateformat)):u}l.attr("data-format","date:"+ne(i.dateformat,$t.dateformat)+";yyyy-MM-dd");break;case"datetime":l.attr("type","datetime-local"),""!==ne(i.pattern,$t.datetimepattern)&&(i.pattern=ne(i.pattern,"("+$t.datetimepattern+")|([0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])\\s([0-5][0-9]):([0-5][0-9]))")),""!==ne(i.placeholder,$t.datetimeplaceholder)&&l.attr("placeholder",ne(i.placeholder,$t.datetimeplaceholder)),"string"==typeof i.value&&"T"===i.value.substr(10,1)&&(i.value="datetime"!==l.prop("type").substr(0,8)?fdt(i.value,ne(i.datetimeformat,$t.datetimeformat)):i.value),l.attr("data-format","datetime:"+ne(i.datetimeformat,$t.datetimeformat)+";yyyy-MM-dd HH:mm:ss");break;case"hidden":r.addClass("hd");break;case"html":case"text":l=$$.txt({id:s,name:i.name,placeholder:i.placeholder,type:i.type}),l.tC("tinymce","html"===i.type);break;case"bool":case"boolean":i.url=[{value:"true",label:($t||{}).true||"Yes"},{value:"false",label:($t||{}).false||"No"}],"boolean"==typeof i.value&&(i.value=i.value?"true":"false");case"select":l=$$.sel({id:s,name:i.name,type:i.type}),!1===bool(i.required,!1)&&$$.eOpt().appendTo(l);try{var d=function(t){!0===Array.isArray(t)&&$.each(t,(function(t,e){"string"==typeof e?$$.opt(e,e).appendTo(l):!0===Array.isArray(e)?$$.opt(e[0],e[1]).appendTo(l):"object"==typeof e&&$$.opt(e.value,e.label||e.text).appendTo(l)}))};!0===Array.isArray(i.url)?d(i.url):"function"==typeof i.url?i.url.call(l):"string"==typeof i.url&&t.postXT({url:i.url,success:d})}catch(t){$.noop()}break;default:""!==ne(i["max-length"])&&l.attr("max-length",i["max-length"])}""!==ne(i.pattern)&&l.attr("pattern",i.pattern),l.val(i.value).change(),l.change((function(){$(this)[0].setCustomValidity("")})),l.addClass("form-control").prop("required",bool(i.required,!1)).prop("readonly",bool(i.readonly,!1)).appendTo(c),!0===bool(i.required,!1)&&a.append($$.sc("ind_required","*")),"object"==typeof i.attr&&l.attr(i.attr),"object"==typeof i.prop&&l.prop(i.prop),"string"==typeof i.class&&l.addClass(i.class),"function"==typeof i.change&&(l.change(i.change),!0===bool(i.applychange,!1)&&void 0!==i.value&&l.change()),""!==(i.note||"")&&$$.dc("form-note",c).rwText(i.note),"function"==typeof i.complete&&i.complete.call(l)})),n},t.initMCE=function(t,e){t=$(t),e=e||{};try{let n={target:t[0],inline:!1,width:e.width||"100%",statusbar:!1,document_base_url:window.location.origin+"/",content_style:"ph:before {content: '«'; color: #BBB; font-style:italic; } ph:after {content: '»'; color: #BBB; font-style:italic; } ph { color: #AAA; font-style:italic; }",relative_urls:!1,remove_script_host:!1};!0===bool(e.hidemenu,!1)&&(n.menubar=!1,n.menu={}),!0===bool(e.hidetoolbar,!1)&&(n.toolbar=!1),$.extend(n,e||{}),tinymce.init(n)}catch(t){alert(t.message)}},t.dlgform=function(e,n){n=n||{};let i,o=$$.dc("frm").append(t.mform(e||[]).addClass("stacked"));n.addcontent instanceof jQuery&&o.append(n.addcontent),"function"==typeof n.submit?i=n.submit:"function"==typeof n.success&&(i=function(e){var i=$(this).ldng(1),o=$.extend({loginaccount:t.auth.account||""},i.serializeObject(bool(n.checkvalidity,!0),{typedvalues:bool(n.typedvalues,!1)}));""!==(n.url||"")?t.postXT({url:n.url,data:o,success:function(t){n.success.call(this,t),i.trigger("modal_close")},error:function(){alert($t.l17)},complete:function(){i.ldng(0)},timeout:6e4}):(n.success.call(this,o),i.trigger("modal_close"))});let r={form:!0,title:n.title||"",button:n.button||$t.submit,confirm:i,size:n.size||[500,600],open:function(){let e=$(this).find(".tinymce");e.length>0&&t.initMCE(e,n.tinymce||{})}};return t.dlg.call(this,o,r)},t.login.dlg=function(e){e=e||{};let n=[{name:"userinfo",label:$t.l1,type:"string",value:t.auth.login,change:t.login.uichange,required:!0},{name:"userlogin",type:"hidden",required:!0,value:t.auth.login},{name:"username",type:"string",label:$t.l4,required:!0,readonly:!0,placeholder:$t.l5,value:t.auth.fullname_rev},{name:"userpass",type:"password",label:$t.l3,required:!0,placeholder:$t.l3}];""===(t.auth.account||"")&&n.unshift({id:"dlg_loginaccount",name:"loginaccount",type:"string",required:!0,value:t.auth.account});let i=$$.dc("frm").append(t.mform(n).addClass("stacked")),o=t.dlg.call(this,i,{form:!0,title:$t.l0,button:$t.submit,confirm:function(n){var i=$(this).ldng(1),o=$.extend({loginaccount:t.auth.account||""},i.serializeObject());t.postXT({url:"/vt/login",data:o,success:function(n){""!==((n||{}).login||"")&&(i.trigger("modal_close"),t.auth=n,"object"==typeof e.ajo&&(e.ajo.islogin,$.ajax(e.ajo)))},error:function(){alert($t.l17)},complete:function(){i.ldng(0)},timeout:6e4})},size:[500,600]}),r=$$.dc("modal-content").css("height","auto").attr("novalidate","true").append($$.dc("modal-header").appendIf($("

        ").text(t.auth.accountname),""!==(t.auth.accountname||"")).append($("

        Vereinsmanager

        ")));o.dlg.prepend(r)},t.addNoEntryInfo=function(t){$(this).append($$.dc("noentryinfo").text(t||$t.t11))}}($ocms),Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),Element.prototype.closest||(Element.prototype.closest=function(t){var e=this;do{if(Element.prototype.matches.call(e,t))return e;e=e.parentElement||e.parentNode}while(null!==e&&1===e.nodeType);return null}),function(t,e){var n,i;"object"==typeof window&&(window[t]=(n=function(t){var e=window,n=document.body,i=document.documentElement,o=Math.max(0,e.pageXOffset||i.scrollLeft||n.scrollLeft||0)-(i.clientLeft||0),r=Math.max(0,e.pageYOffset||i.scrollTop||n.scrollTop||0)-(i.clientTop||0);return{x:t?Math.max(0,t.pageX||t.clientX||0)-o:0,y:t?Math.max(0,t.pageY||t.clientY||0)-r:0}},(i=function(t,e){t&&t instanceof Element&&(this._container=t,this._options=e||{},this._clickItem=null,this._dragItem=null,this._showDragItem="boolean"!=typeof this._options.dragItem||!1!==this._options.dragItem,this._hovItem=null,this._sortLists=[],this._click={},this._dragging=!1,this._dragHandleClass=this._options.dragHandleClass||"",this._parentident=this._options.parentident||"",this._swapdone="function"==typeof this._options.swapdone?this._options.swapdone:null,this._onend="function"==typeof this._options.onend?this._options.onend:null,this._container.setAttribute("data-is-sortable",1),this._container.classList.add("sortable"),this._container.style.position="static",window.addEventListener("mousedown",this._onPress.bind(this),!0),window.addEventListener("touchstart",this._onPress.bind(this),!0),window.addEventListener("mouseup",this._onRelease.bind(this),!0),window.addEventListener("touchend",this._onRelease.bind(this),!0),window.addEventListener("mousemove",this._onMove.bind(this),!0),window.addEventListener("touchmove",this._onMove.bind(this),!0))}).prototype={constructor:i,toArray:function(t){t=t||"id";for(var e=[],n="",i=0;ii.left&&ei.top&&n0?a.mousedown(c).addClass("dctrl"):s.mousedown(c).addClass("dctrl"),t(this)}}(jQuery),$(document).ready((function(){$("html").click((function(t){$nuf()})),$("#listframe").click((function(t){t.stopPropagation(),$nuf()})),$("#mainmenu").ocmsmenu($ocms.ocmsmenu),$("#mainmenu").activatemenu()})),$.extend($t,{m_inv:"Rechnungen",m_req:"Aufträge",m_rep:"Berichte",m_todo:"ToDos",m_bcd:"BankBuchungen",m_admin:"Administration",m_admin_status:"Systemüberwachung",m_usr:"Benutzerverwaltung",rsp:"Passwort ändern",pnm:"Die Passwörter stimmen nicht überein",cps:"Das neue Passwort wurde gespeichert.",pwr:"Bitte wählen Sie ein starkes Passwort (min 8 Zeichen, davon jeweils min 2 Zahlen, kleine und große Buchstaben, Sonderzeichen sind optional).",smsc:"Sie beötigen für diese Funktion einen SMS-Code.\nSoll dieser nun versandt werden?",wdc:"Doppelt klicken, um die Box zu aktualisieren.",wdg:{}}),$t.rspf={sms:"Der SMS-Code konnte nicht bestätigt werden",valid:"Das alte Passwort ist nicht korrekt",requirements:"Das Passwort entspricht nicht den Anforderungen.\n"+$t.pwr},$fd={rsp:new fields_definition("","",[{name:"opw",label:"aktuelles Passwort",type:"password",required:!0,attr:{"auto-complete":"current-password"}},{name:"npw",label:"neues Passwort",type:"password",required:!0,pattern:"(.{6,})",attr:{"auto-complete":"new-password"}},{name:"npwc",label:"neues Passwort (Bestätigung)",type:"password",required:!0,attr:{"auto-complete":"new-password"},note:$t.pwr},{name:"code",label:"SMS-Code",type:"string",required:!0,attr:{"auto-complete":"one-time-code"}}])},$ocms.init=function(t){var e="string"==typeof t?t:(t.data||{}).fn||"";""!==e&&("home"===e?($cfr(),$lfr(),$("#topbar").ocmsmenu([],!0),$("#activemodule").text($t.ov),$fis.ov()):($cfr(),$lfr(),$("#topbar").ocmsmenu([]),$ocms.postXT({url:$ocms.url(e+"/auth"),success:function(t){if(void 0===$ocms[e]&&($ocms[e]={}),$ocms[e].auth=t,t.manage>0){let t="?v="+encodeURIComponent($ocms.auth.assetversion||"0");$ocms.getScript({module:e,script:["/web/fis",e,$ocms.auth.locale||"de","js"].join(".")+t,css:["/web/fis",e,"css"].join(".")+t,condition:"function"!=typeof $ocms[e].init2},(function(){$ocms[e].init2()}))}},error:function(){$("#contentframe").empty()}})))};var $fis={auth:{},db:function(){$("#mainmenu_activemodule").text($t.ov);let t=$(this).empty(),e=$$.d({id:"dashboard_frame"}).appendTo(t);$ocms.postXT({url:$ocms.url("wdg/my"),success:function(t){$.each(t,(function(t,n){var i=$$.dc("wdg_frame",e,{"data-wdg":n}).ldng(1);$ocms.wdg.call(i,{wdg:n})}))},loading:e})},ValidateEmail:function(t){return!!/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(t)},cf:t=>{let e=$("#contentframe");return!0===bool(t,!1)&&e.empty().rC("hd"),e},lf:t=>{let e=$("#listframe");return!0===bool(t,!1)&&e.empty().aC("hd").rC("fix"),e},frm_edit:function(t){let e=$fis.cf(!1),n=e.children(".cfrm"),i=e.children(".edit_frm");return n.length<1?n=$$.dc("cfrm hd").prependTo(e):!0===bool(t,!1)&&n.empty(),i.length<1&&(i=$$.dc("edit_frm").insertAfter(n)),i.empty()},frm_list:function(t,e){let n=$fis.cf(!1),i=n.children(".cfrm"),o=n.children(".list_frm");return i.length<1?i=$$.dc("cfrm hd").prependTo(n):!0===bool(t,!1)&&i.empty(),!0===bool(e,!1)&&n.children(".edit_frm").remove(),o.length<1&&(o=$$.dc("list_frm").appendTo(n)),o.empty()},lfm:()=>{let t=$fis.lf(!1),e=t.children(".lfrm");return e.length<1&&(e=$$.dc("lfrm").prependTo(t)),e},getAuth:(t,e)=>new Promise(((n,i)=>{$fis.auth[t]&&!1===bool(e,!1)?n($fis.auth[t]||-1):$ocms.postXT({url:$ocms.url("auth"),data:{module:t},success:e=>{$fis.auth[t]=e.auth||-1,n($fis.auth[t]||-1)},error:()=>{i()}})})),prepAuth:t=>new Promise(((e,n)=>{$ocms.postXT({url:$ocms.url("auth"),data:{module:t,array:1},success:t=>{$.extend($fis.auth,t||{})},complete:()=>{e()}})})),isAuth:(t,e)=>($fis.auth[t]||-1)>=(e||1),resetPass:function(t,e){confirm($t.smsc)&&($ocms.postXT({url:$ocms.url("account/sms"),data:{fn:"pwc"}}),$ocms.dlgform($fd.rsp.clone(),{title:$t.rsp||"",submit:function(t){var e=$(this).ldng(1),n=$.extend({loginaccount:$ocms.auth.account||""},e.serializeObject(!0,{typedvalues:!0}));(n.npw||"")!==(n.npwc||"")?e.find('input[name="npw"]:first')[0].setCustomValidity($t.pnm):$ocms.postXT({url:$ocms.url("account/changepassword"),data:n,success:function(t){alert($t.cps),e.trigger("modal_close")},error:function(t){alert($t.rspf[t.getResponseHeader("x-ocms-std")])},complete:function(){e.ldng(0)},timeout:6e4})}}))},wdg:function(t){let e=$(this).empty();$ocms.postXT({url:$ocms.url("wdg/one"),data:{short_name:t.wdg},timeout:9e4,success:function(n,i,o){let r=t.wdg,s=n[r];if(!s)return void e.ldng(0);let a=$.inArrayRegEx("dblwidth",s.rendering_options)>-1,c=$.inArrayRegEx("tiny",s.rendering_options)>-1;e.toggleClass("dbl",a&&!c).toggleClass("tny",c);$$.dc("wdg_hd",e,{title:ne(s.description,$t.wdc)}).toggleClass("dbl",a).text(ne(s.name,t.wdg)).dblclick((function(t){t.stopPropagation(),$fis.wdg.call(e,{wdg:r})}));let l=$$.dc("wdg_cnt",e).toggleClass("dbl",a).hide(),u=$.inArrayRegEx("bgcolor",s.rendering_options);switch(u>-1&&l.css("backgroundColor",s.rendering_options[u].toString().right(":")),s.type){case"table":var d=$$.tblset({},l),h=$$.tr().appendTo(d.hd),p=$t.wdg[r.indexOf("wdg_ev_")>=0?"wdg_ev_":r]||{};$.each(s.columns,(function(t,e){var n=p[e]?p[e].label:e;$$.th().text(n).appendTo(h)})),$.each(s.data,(function(t,e){var n=$$.tr().appendTo(d.bdy);$.each(s.columns,(function(t,i){var o=$$.td().appendTo(n);e[i]instanceof Date||!0===$ocms.isJSONDateString(e[i])?o.text(fdt(e[i],$t.dateformat)):o.rwText(e[i])}))})),$.inArray("firstrow_bold",s.rendering_options)>-1&&h.nextAll("tr:first").css("font-weight","bold");break;case"ind":$$.dc("ind",l).addClass("sts_"+(s.data.status||"")).append([$$.dc("ind").text(s.data.value),$$.lbl(s.data.label)]);break;case"image_url":l.css("background","url('"+s.url+"') no-repeat center center transparent");break;case"image_base64":l.css("background","url('data:image/png;base64,"+s.image+"') no-repeat center center transparent");break;case"html":if(l.html(s.html),$.inArray("reload_10min",s.rendering_options)>-1){var f=l.find("iframe");setTimeout((function(){f.attr("src",(function(t,e){return e}))}),6e5)}}$.inArray("reload_30min",s.rendering_options)>-1&&"html"!==s.type&&setTimeout((function(){$fis.wdg.call(e,{wdg:r})}),18e5),l.slideDown(150)},error:function(t){e.slideUp(150),$fis.failure.call(this,t)},complete:function(){e.ldng(0)}})},ov:function(){$fis.lf(!0);let t=$("#contentframe").empty(),e=$$.d({id:"dashboard_frame"}).appendTo(t);$ocms.postXT({url:$ocms.url("wdg/my"),success:function(t){$.each(t,(function(t,n){var i=$$.dc("wdg_frame",e,{"data-wdg":n}).ldng(1);$fis.wdg.call(i,{wdg:n})}))},loading:e})}};$fis.notifications={connection:null,init:function(){"undefined"!=typeof signalR&&null===this.connection&&$ocms.auth.useraccount_id&&(this.ensureFrame(),this.connection=(new signalR.HubConnectionBuilder).withUrl("/notifications").withAutomaticReconnect().build(),this.connection.on("notification",(t=>{this.push(t)})),this.connection.onclose((()=>{console.warn("Notification connection closed; retrying in 5s."),this.connection=null,setTimeout((()=>this.init()),5e3)})),this.start())},start:function(){this.connection.start().catch((t=>{console.warn("Notification connection failed to start; retrying in 5s.",t),this.connection=null,setTimeout((()=>this.init()),5e3)}))},ensureFrame:function(){$("#notification_frame").length<1&&$("
        ",{id:"notification_frame"}).appendTo($("footer:first").length?"footer:first":"body")},push:function(t){this.ensureFrame(),t=t||{};let e=$("
        ",{class:"notification_item"}).addClass((t.severity||"info").toLowerCase()).append($("