From 11357bdf988bbcbcd2337328f435d0e18ad97c9a Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 7 Aug 2025 16:06:26 +0200 Subject: [PATCH] [WIP] ElwigData: Sync WbKg --- Elwig/Helpers/Export/ElwigData.cs | 167 +++++++++++++++++++++++------- Elwig/Services/DeliveryService.cs | 21 +++- Elwig/Services/MemberService.cs | 18 +++- Elwig/Windows/MainWindow.xaml.cs | 11 +- 4 files changed, 174 insertions(+), 43 deletions(-) diff --git a/Elwig/Helpers/Export/ElwigData.cs b/Elwig/Helpers/Export/ElwigData.cs index 3868be4..54809e5 100644 --- a/Elwig/Helpers/Export/ElwigData.cs +++ b/Elwig/Helpers/Export/ElwigData.cs @@ -40,6 +40,7 @@ namespace Elwig.Helpers.Export { Dictionary currentDids; Dictionary currentLsNrs; Dictionary> currentWbRde; + Dictionary currentWbGls; Dictionary kgs; using (var ctx = new AppDbContext()) { @@ -52,6 +53,11 @@ namespace Elwig.Helpers.Export { currentWbRde = await ctx.WbRde .GroupBy(r => r.KgNr) .ToDictionaryAsync(g => g.Key, g => g.ToList()); + currentWbRde = await ctx.WbRde + .GroupBy(r => r.KgNr) + .ToDictionaryAsync(g => g.Key, g => g.ToList()); + currentWbGls = await ctx.WbGls + .ToDictionaryAsync(g => g.GlNr, g => g); kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr); } @@ -62,6 +68,8 @@ namespace Elwig.Helpers.Export { List EmailAddresses, List AreaCommitments, List Riede, + List WbKgs, + List WbGls, List Deliveries, List DeliveryParts, List Modifiers, @@ -97,13 +105,28 @@ namespace Elwig.Helpers.Export { areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null, deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null)); - data.Add(new([], [], [], [], [], [], [], new([], [], new() { + data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() { ["member"] = [], ["area_commitment"] = [], ["delivery"] = [], }))); var r = data[^1]; + var wbKgsJson = zip.GetEntry("wb_kgs.json"); + if (wbKgsJson != null) { + using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8); + string? line; + while ((line = await reader.ReadLineAsync()) != null) { + var obj = JsonNode.Parse(line)!.AsObject(); + var (k, g) = obj.ToWbKg(currentWbGls); + r.WbKgs.Add(k); + if (g != null) { + currentWbGls[g.GlNr] = g; + r.WbGls.Add(g); + } + } + } + var membersJson = zip.GetEntry("members.json"); if (membersJson != null) { using var reader = new StreamReader(membersJson.Open(), Utils.UTF8); @@ -129,7 +152,6 @@ namespace Elwig.Helpers.Export { var (areaCom, wbrd, timestamps) = obj.ToAreaCom(kgs, currentWbRde); r.AreaCommitments.Add(areaCom); if (wbrd != null) { - currentWbRde[wbrd.KgNr].Add(wbrd); r.Riede.Add(wbrd); } if (timestamps.HasValue) @@ -143,10 +165,11 @@ namespace Elwig.Helpers.Export { string? line; while ((line = await reader.ReadLineAsync()) != null) { var obj = JsonNode.Parse(line)!.AsObject(); - var (d, parts, mods, timestamps) = obj.ToDelivery(currentLsNrs, currentDids); + var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde); r.Deliveries.Add(d); r.DeliveryParts.AddRange(parts); r.Modifiers.AddRange(mods); + r.Riede.AddRange(rde); if (timestamps.HasValue) r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt)); } @@ -157,12 +180,18 @@ namespace Elwig.Helpers.Export { var importedAreaComs = new List<(string FileName, string ZwstId, string Device, int Imported, int NotImported, string Filters)>(); var importedDeliveries = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>(); - foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) { + foreach (var ((members, billingAddresses, telephoneNumbers, emailAddresses, areaCommitments, riede, wbKgs, wbGls, deliveries, deliveryParts, modifiers, timestamps), meta) in data.Zip(metaData)) { var branch = branches[meta.ZwstId]; var device = meta.Device; using var ctx = new AppDbContext(); + var kgnrs = wbKgs.Select(k => k.KgNr).ToList(); + var duplicateKgNrs = await ctx.WbKgs + .Where(k => kgnrs.Contains(k.KgNr)) + .Select(k => k.KgNr) + .ToListAsync(); + var mgnrs = members.Select(m => m.MgNr).ToList(); var duplicateMgNrs = await ctx.Members .Where(m => mgnrs.Contains(m.MgNr)) @@ -216,6 +245,12 @@ namespace Elwig.Helpers.Export { importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count); } + if (importDuplicateMembers || importNewMembers || importDuplicateDeliveries || importNewDeliveries) { + ctx.AddRange(wbGls); + ctx.UpdateRange(wbKgs.Where(k => duplicateKgNrs.Contains(k.KgNr))); + ctx.AddRange(wbKgs.Where(k => !duplicateKgNrs.Contains(k.KgNr))); + } + if (importDuplicateMembers) { ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr))); ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr))); @@ -352,22 +387,25 @@ namespace Elwig.Helpers.Export { ) == MessageBoxResult.Yes; } - public static Task Export(string filename, IEnumerable members, IEnumerable filters) { + public static Task Export(string filename, IEnumerable members, IEnumerable wbKgs, IEnumerable filters) { return new ElwigExport { - Members = (members, filters) + Members = (members, filters), + WbKgs = (wbKgs, ["von exportierten Mitgliedern"]), }.Export(filename); } - public static Task Export(string filename, IEnumerable members, IEnumerable areaComs, IEnumerable filters) { + public static Task Export(string filename, IEnumerable members, IEnumerable areaComs, IEnumerable wbKgs, IEnumerable filters) { return new ElwigExport { Members = (members, filters), AreaComs = (areaComs, ["von exportierten Mitgliedern"]), + WbKgs = (wbKgs, ["von exportierten Mitgliedern und Flächenbindungen"]), }.Export(filename); } - public static Task Export(string filename, IEnumerable deliveries, IEnumerable filters) { + public static Task Export(string filename, IEnumerable deliveries, IEnumerable wbKgs, IEnumerable filters) { return new ElwigExport { - Deliveries = (deliveries, filters) + Deliveries = (deliveries, filters), + WbKgs = (wbKgs, ["von exportierten Lieferungen"]), }.Export(filename); } @@ -395,6 +433,7 @@ namespace Elwig.Helpers.Export { } public class ElwigExport { + public (IEnumerable WbKgs, IEnumerable Filters)? WbKgs { get; set; } public (IEnumerable Members, IEnumerable Filters)? Members { get; set; } public (IEnumerable AreaComs, IEnumerable Filters)? AreaComs { get; set; } public (IEnumerable Deliveries, IEnumerable Filters)? Deliveries { get; set; } @@ -415,6 +454,12 @@ namespace Elwig.Helpers.Export { ["zwstid"] = App.ZwstId, ["device"] = Environment.MachineName, }; + if (WbKgs != null) { + obj["wb_kgs"] = new JsonObject { + ["count"] = WbKgs.Value.WbKgs.Count(), + ["filters"] = new JsonArray(WbKgs.Value.Filters.Select(f => (JsonNode)f).ToArray()), + }; + } if (Members != null) obj["members"] = new JsonObject { ["count"] = Members.Value.Members.Count(), @@ -435,6 +480,13 @@ namespace Elwig.Helpers.Export { } // TODO encrypt files + if (WbKgs != null) { + var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize); + using var writer = new StreamWriter(json.Open(), Utils.UTF8); + foreach (var k in WbKgs.Value.WbKgs) { + await writer.WriteLineAsync(k.ToJson().ToJsonString(JsonOpts)); + } + } if (Members != null) { var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize); using var writer = new StreamWriter(json.Open(), Utils.UTF8); @@ -459,6 +511,26 @@ namespace Elwig.Helpers.Export { } } + public static JsonObject ToJson(this WbKg k) { + return new JsonObject { + ["kgnr"] = k.KgNr, + ["großlage"] = k.Gl.Name, + }; + } + + public static (WbKg, WbGl?) ToWbKg(this JsonNode json, Dictionary gls) { + var grosslage = json["großlage"]?.AsValue().GetValue(); + WbGl? gl = null; + bool newGl = false; + if (grosslage != null) { + // TODO + } + return (new WbKg { + KgNr = json["kgnr"]!.AsValue().GetValue(), + GlNr = gl?.GlNr, + }, newGl ? gl : null); + } + public static JsonObject ToJson(this Member m) { return new JsonObject { ["mgnr"] = m.MgNr, @@ -633,7 +705,7 @@ namespace Elwig.Helpers.Export { Area = json["area"]!.AsValue().GetValue(), KgNr = kgnr, GstNr = json["gstnr"]?.AsValue().GetValue() ?? "-", - RdNr = rd?.RdNr, + RdNr = rd?.RdNr ?? json["rdnr"]?.AsValue().GetValue(), YearFrom = json["year_from"]?.AsValue().GetValue(), YearTo = json["year_to"]?.AsValue().GetValue(), Comment = json["comment"]?.AsValue().GetValue(), @@ -664,7 +736,7 @@ namespace Elwig.Helpers.Export { ["qualid"] = p.QualId, ["hkid"] = p.HkId, ["kgnr"] = p.KgNr, - ["rdnr"] = p.RdNr, + ["ried"] = p.Rd?.Name, ["net_weight"] = p.IsNetWeight, ["manual_weighing"] = p.IsManualWeighing, ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()), @@ -689,7 +761,7 @@ namespace Elwig.Helpers.Export { }; } - public static (Delivery, List, List, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary currentLsNrs, Dictionary currentDids) { + public static (Delivery, List, List, List, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary currentLsNrs, Dictionary currentDids, Dictionary kgs, Dictionary> riede) { var year = json["year"]!.AsValue().GetValue(); var lsnr = json["lsnr"]!.AsValue().GetValue(); var did = currentLsNrs.GetValueOrDefault(lsnr, -1); @@ -700,6 +772,7 @@ namespace Elwig.Helpers.Export { currentLsNrs[lsnr] = did; var createdAt = json["created_at"]?.AsValue().GetValue(); var modifiedAt = json["modified_at"]?.AsValue().GetValue(); + var wbRde = new List(); return (new Delivery { Year = year, DId = did, @@ -711,37 +784,57 @@ namespace Elwig.Helpers.Export { MgNr = json["mgnr"]!.AsValue().GetValue(), Comment = json["comment"]?.AsValue().GetValue(), ImportedAt = DateTime.Now, - }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => new DeliveryPart { - Year = year, - DId = did, - DPNr = p["dpnr"]!.AsValue().GetValue(), - SortId = p["sortid"]!.AsValue().GetValue(), - AttrId = p["attrid"]?.AsValue().GetValue(), - CultId = p["cultid"]?.AsValue().GetValue(), - Weight = p["weight"]!.AsValue().GetValue(), - Kmw = p["kmw"]!.AsValue().GetValue(), - QualId = p["qualid"]!.AsValue().GetValue(), - HkId = p["hkid"]!.AsValue().GetValue(), - KgNr = p["kgnr"]?.AsValue().GetValue(), - RdNr = p["rdnr"]?.AsValue().GetValue(), - IsNetWeight = p["net_weight"]!.AsValue().GetValue(), - IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue(), - Comment = p["comment"]?.AsValue().GetValue(), - IsSplCheck = p["spl_check"]?.AsValue().GetValue() ?? false, - IsHandPicked = p["hand_picked"]?.AsValue().GetValue(), - IsLesewagen = p["lesewagen"]?.AsValue().GetValue(), - IsGebunden = p["gebunden"]?.AsValue().GetValue(), - Temperature = p["temperature"]?.AsValue().GetValue(), - Acid = p["acid"]?.AsValue().GetValue(), - ScaleId = p["scale_id"]?.AsValue().GetValue(), - WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts), - WeighingReason = p["weighing_reason"]?.AsValue().GetValue(), + }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => { + var kgnr = json["kgnr"]!.AsValue().GetValue(); + var ried = json["ried"]?.AsValue().GetValue(); + WbRd? rd = null; + if (ried != null) { + if (!riede.TryGetValue(kgnr, out var rde)) + throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)"); + rd = rde.FirstOrDefault(r => r.Name == ried); + if (rd == null) { + rd = new WbRd { + KgNr = kgnr, + RdNr = (rde.Count == 0 ? 0 : rde.Max(r => r.RdNr)) + 1, + Name = ried, + }; + rde.Add(rd); + wbRde.Add(rd); + } + } + return new DeliveryPart { + Year = year, + DId = did, + DPNr = p["dpnr"]!.AsValue().GetValue(), + SortId = p["sortid"]!.AsValue().GetValue(), + AttrId = p["attrid"]?.AsValue().GetValue(), + CultId = p["cultid"]?.AsValue().GetValue(), + Weight = p["weight"]!.AsValue().GetValue(), + Kmw = p["kmw"]!.AsValue().GetValue(), + QualId = p["qualid"]!.AsValue().GetValue(), + HkId = p["hkid"]!.AsValue().GetValue(), + KgNr = p["kgnr"]?.AsValue().GetValue(), + RdNr = rd?.RdNr ?? p["rdnr"]?.AsValue().GetValue(), + IsNetWeight = p["net_weight"]!.AsValue().GetValue(), + IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue(), + Comment = p["comment"]?.AsValue().GetValue(), + IsSplCheck = p["spl_check"]?.AsValue().GetValue() ?? false, + IsHandPicked = p["hand_picked"]?.AsValue().GetValue(), + IsLesewagen = p["lesewagen"]?.AsValue().GetValue(), + IsGebunden = p["gebunden"]?.AsValue().GetValue(), + Temperature = p["temperature"]?.AsValue().GetValue(), + Acid = p["acid"]?.AsValue().GetValue(), + ScaleId = p["scale_id"]?.AsValue().GetValue(), + WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts), + WeighingReason = p["weighing_reason"]?.AsValue().GetValue(), + }; }).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier { Year = year, DId = did, DPNr = p["dpnr"]!.AsValue().GetValue(), ModId = m!.AsValue().GetValue(), })).ToList(), + wbRde, createdAt == null || modifiedAt == null ? null : (DateTime.ParseExact(createdAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None), DateTime.ParseExact(modifiedAt, "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None))); diff --git a/Elwig/Services/DeliveryService.cs b/Elwig/Services/DeliveryService.cs index 5e397aa..936efaf 100644 --- a/Elwig/Services/DeliveryService.cs +++ b/Elwig/Services/DeliveryService.cs @@ -727,13 +727,21 @@ namespace Elwig.Services { Mouse.OverrideCursor = Cursors.Wait; await Task.Run(async () => { try { - await ElwigData.Export(d.FileName, await query + var list = await query .Select(p => p.Delivery) .Distinct() .Include(d => d.Parts) .ThenInclude(p => p.PartModifiers) .AsSplitQuery() - .ToListAsync(), filterNames); + .ToListAsync(); + var wbKgs = list + .SelectMany(d => d.Parts) + .Where(p => p.Kg != null) + .Select(p => p.Kg!) + .DistinctBy(k => k.KgNr) + .OrderBy(k => k.KgNr) + .ToList(); + await ElwigData.Export(d.FileName, list, wbKgs, filterNames); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } @@ -753,11 +761,18 @@ namespace Elwig.Services { .ThenInclude(p => p.PartModifiers) .AsSplitQuery() .ToListAsync(); + var wbKgs = list + .SelectMany(d => d.Parts) + .Where(p => p.Kg != null) + .Select(p => p.Kg!) + .DistinctBy(k => k.KgNr) + .OrderBy(k => k.KgNr) + .ToList(); if (list.Count == 0) { MessageBox.Show("Es wurden keine Lieferungen zum Hochladen ausgewählt!", "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } else { - await ElwigData.Export(path, list, filterNames); + await ElwigData.Export(path, list, wbKgs, filterNames); await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen", MessageBoxButton.OK, MessageBoxImage.Information); diff --git a/Elwig/Services/MemberService.cs b/Elwig/Services/MemberService.cs index 557385f..8a4bc98 100644 --- a/Elwig/Services/MemberService.cs +++ b/Elwig/Services/MemberService.cs @@ -515,7 +515,14 @@ namespace Elwig.Services { .SelectMany(m => m.AreaCommitments) .Include(c => c.Rd) .ToListAsync(); - await ElwigData.Export(d.FileName, members, areaComs, filterNames); + var wbKgs = members + .Where(m => m.DefaultWbKg != null) + .Select(m => m.DefaultWbKg!) + .Union(areaComs.Select(c => c.Kg)) + .Distinct() + .OrderBy(k => k.KgNr) + .ToList(); + await ElwigData.Export(d.FileName, members, areaComs, wbKgs, filterNames); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } @@ -539,11 +546,18 @@ namespace Elwig.Services { .SelectMany(m => m.AreaCommitments) .Include(c => c.Rd) .ToListAsync(); + var wbKgs = members + .Where(m => m.DefaultWbKg != null) + .Select(m => m.DefaultWbKg!) + .Union(areaComs.Select(c => c.Kg)) + .Distinct() + .OrderBy(k => k.KgNr) + .ToList(); if (members.Count == 0) { MessageBox.Show("Es wurden keine Mitglieder zum Hochladen ausgewählt!", "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } else { - await ElwigData.Export(path, members, areaComs, filterNames); + await ElwigData.Export(path, members, areaComs, wbKgs, filterNames); await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen", MessageBoxButton.OK, MessageBoxImage.Information); diff --git a/Elwig/Windows/MainWindow.xaml.cs b/Elwig/Windows/MainWindow.xaml.cs index a7fef8e..d048abb 100644 --- a/Elwig/Windows/MainWindow.xaml.cs +++ b/Elwig/Windows/MainWindow.xaml.cs @@ -202,16 +202,25 @@ namespace Elwig.Windows { .Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId) .Include(d => d.Parts) .ThenInclude(p => p.PartModifiers) + .Include(d => d.Parts) + .ThenInclude(p => p.Kg) + .ThenInclude(k => k!.Gl) .OrderBy(d => d.DateString) .ThenBy(d => d.TimeString) .ThenBy(d => d.LsNr) .AsSplitQuery() .ToListAsync(); + var wbKgs = deliveries + .SelectMany(d => d.Parts) + .Where(p => p.Kg != null) + .Select(p => p.Kg!) + .DistinctBy(k => k.KgNr) + .ToList(); if (deliveries.Count == 0) { MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } else { - await ElwigData.Export(path, deliveries, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]); + await ElwigData.Export(path, deliveries, wbKgs, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]); await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Information);