using System.IO.Compression; using System.IO; using System.Threading.Tasks; using Elwig.Models.Entities; using System.Collections.Generic; using System; using System.Text.Json.Nodes; using System.Linq; using System.Windows; using Microsoft.EntityFrameworkCore; using System.Text.Json; namespace Elwig.Helpers.Export { public static class ElwigData { public enum ImportMode { Auto, Interactively, FromBranches } public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt"); private static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; public static async Task GetImportedFiles() { try { return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8); } catch { return []; } } public static async Task AddImportedFiles(params string[] filenames) { await File.AppendAllLinesAsync(ImportedTxt, filenames, Utils.UTF8); } public static Task Import(string filename, ImportMode mode) => Import([filename], mode); public static async Task Import(IEnumerable filenames, ImportMode mode) { try { Dictionary branches; Dictionary currentDids; Dictionary currentLsNrs; Dictionary> currentWbRde; using (var ctx = new AppDbContext()) { branches = await ctx.Branches.ToDictionaryAsync(b => b.ZwstId); currentDids = await ctx.Deliveries .GroupBy(d => d.Year) .ToDictionaryAsync(g => g.Key, g => g.Max(d => d.DId)); currentLsNrs = await ctx.Deliveries .ToDictionaryAsync(d => d.LsNr, d => d.DId); currentWbRde = await ctx.WbRde .GroupBy(r => r.KgNr) .ToDictionaryAsync(g => g.Key, g => g.ToList()); } var data = new List<( List Members, List BillingAddresses, List TelephoneNumbers, List EmailAddresses, List AreaCommitments, List Riede, List Deliveries, List DeliveryParts, List Modifiers)>(); var metaData = new List<(string FileName, string ZwstId, string Device, int? MemberNum, string? MemberFilters, int? AreaComNum, string? AreaComFilters, int? DeliveryNum, string? DeliveryFilters)>(); foreach (var filename in filenames) { // TODO read encrypted files using var zip = ZipFile.Open(filename, ZipArchiveMode.Read); var version = zip.GetEntry("version"); using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) { if (await reader.ReadToEndAsync() != "elwig:1") throw new FileFormatException($"Ungültige Export-Datei ({filename})"); } var metaJson = zip.GetEntry("meta.json"); var meta = await JsonNode.ParseAsync(metaJson!.Open()); var memberCount = meta!["members"]?["count"]?.AsValue().GetValue(); var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue()).ToArray(); var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue(); var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue()).ToArray(); var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue(); var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue()).ToArray(); metaData.Add((Path.GetFileName(filename), meta["zwstid"]!.AsValue().GetValue(), meta["device"]!.AsValue().GetValue(), memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null, areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null, deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null)); data.Add(new([], [], [], [], [], [], [], new([], []))); var r = data[^1]; var membersJson = zip.GetEntry("members.json"); if (membersJson != null) { using var reader = new StreamReader(membersJson.Open(), Utils.UTF8); string? line; while ((line = await reader.ReadLineAsync()) != null) { var obj = JsonNode.Parse(line)!.AsObject(); var (m, b, telNrs, emailAddrs) = obj.ToMember(); r.Members.Add(m); if (b != null) data[^1].BillingAddresses.Add(b); r.TelephoneNumbers.AddRange(telNrs); r.EmailAddresses.AddRange(emailAddrs); } } var areaComsJson = zip.GetEntry("area_commitments.json"); if (areaComsJson != null) { using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8); string? line; while ((line = await reader.ReadLineAsync()) != null) { var obj = JsonNode.Parse(line)!.AsObject(); var (areaCom, wbrd) = obj.ToAreaCom(currentWbRde); r.AreaCommitments.Add(areaCom); if (wbrd != null) r.Riede.Add(wbrd); } } var deliveriesJson = zip.GetEntry("deliveries.json"); if (deliveriesJson != null) { using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8); string? line; while ((line = await reader.ReadLineAsync()) != null) { var obj = JsonNode.Parse(line)!.AsObject(); var (d, parts, mods) = obj.ToDelivery(currentLsNrs, currentDids); r.Deliveries.Add(d); r.DeliveryParts.AddRange(parts); r.Modifiers.AddRange(mods); } } } var importedMembers = new List<(string FileName, string ZwstId, string Device, int New, int Overwritten, int NotImported, string Filters)>(); 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), meta) in data.Zip(metaData)) { var branch = branches[meta.ZwstId]; var device = meta.Device; using var ctx = new AppDbContext(); var mgnrs = members.Select(m => m.MgNr).ToList(); var duplicateMgNrs = await ctx.Members .Where(m => mgnrs.Contains(m.MgNr)) .Select(m => m.MgNr) .ToListAsync(); bool importNewMembers = false, importDuplicateMembers = false; if (mode == ImportMode.Interactively) { if (mgnrs.Count - duplicateMgNrs.Count > 0) importNewMembers = ImportQuestion(branch.Name, device, "Mitglieder", false, mgnrs.Count - duplicateMgNrs.Count); } else { importNewMembers = true; } if (duplicateMgNrs.Count > 0) importDuplicateMembers = ImportQuestion(branch.Name, device, "Mitglieder", true, duplicateMgNrs.Count); var fbnrs = areaCommitments.Select(c => c.FbNr).ToList(); var duplicateFbNrs = await ctx.AreaCommitments .Where(c => fbnrs.Contains(c.FbNr)) .Select(c => c.FbNr) .ToListAsync(); var lsnrs = deliveries.Select(d => d.LsNr).ToList(); var duplicateLsNrs = await ctx.Deliveries .Where(d => lsnrs.Contains(d.LsNr)) .Select(d => d.LsNr) .ToListAsync(); var duplicateDIds = deliveries .Where(d => duplicateLsNrs.Contains(d.LsNr)) .Select(d => (d.Year, d.DId)) .ToList(); var allowedDuplicateLsNrs = new List(); bool importNewDeliveries = false, importDuplicateDeliveries = false; if (mode == ImportMode.Interactively) { if (lsnrs.Count - duplicateLsNrs.Count > 0) importNewDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", false, lsnrs.Count - duplicateLsNrs.Count); if (duplicateLsNrs.Count > 0) importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count); } else if (mode == ImportMode.FromBranches) { importNewDeliveries = true; if (duplicateLsNrs.Count > 0) { allowedDuplicateLsNrs = await ctx.Deliveries .Where(d => lsnrs.Contains(d.LsNr) && d.ZwstId == branch.ZwstId) .Select(d => d.LsNr) .ToListAsync(); if (duplicateLsNrs.Count - allowedDuplicateLsNrs.Count > 0) importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count - allowedDuplicateLsNrs.Count); } } else { importNewDeliveries = true; if (duplicateLsNrs.Count > 0) importDuplicateDeliveries = ImportQuestion(branch.Name, device, "Lieferungen", true, duplicateLsNrs.Count); } if (importDuplicateMembers) { ctx.RemoveRange(ctx.BillingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr))); ctx.RemoveRange(ctx.MemberTelephoneNrs.Where(n => duplicateMgNrs.Contains(n.MgNr))); ctx.RemoveRange(ctx.MemberEmailAddrs.Where(a => duplicateMgNrs.Contains(a.MgNr))); ctx.UpdateRange(members.Where(m => duplicateMgNrs.Contains(m.MgNr))); ctx.AddRange(billingAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr))); ctx.AddRange(telephoneNumbers.Where(n => duplicateMgNrs.Contains(n.MgNr))); ctx.AddRange(emailAddresses.Where(a => duplicateMgNrs.Contains(a.MgNr))); ctx.UpdateRange(areaCommitments.Where(c => duplicateMgNrs.Contains(c.MgNr) && duplicateFbNrs.Contains(c.FbNr))); ctx.AddRange(areaCommitments.Where(c => duplicateMgNrs.Contains(c.MgNr) && !duplicateFbNrs.Contains(c.FbNr))); } if (importNewMembers) { ctx.AddRange(members.Where(m => !duplicateMgNrs.Contains(m.MgNr))); ctx.AddRange(billingAddresses.Where(a => !duplicateMgNrs.Contains(a.MgNr))); ctx.AddRange(telephoneNumbers.Where(n => !duplicateMgNrs.Contains(n.MgNr))); ctx.AddRange(emailAddresses.Where(a => !duplicateMgNrs.Contains(a.MgNr))); ctx.UpdateRange(areaCommitments.Where(c => !duplicateMgNrs.Contains(c.MgNr) && duplicateFbNrs.Contains(c.FbNr))); ctx.AddRange(areaCommitments.Where(c => !duplicateMgNrs.Contains(c.MgNr) && !duplicateFbNrs.Contains(c.FbNr))); } if (members.Count > 0) { var n = importNewMembers ? members.Count - duplicateMgNrs.Count : 0; var o = importDuplicateMembers ? duplicateMgNrs.Count : 0; importedMembers.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, members.Count - n - o, meta.MemberFilters)); } if (areaCommitments.Count > 0) { var imported = areaCommitments.Where(c => (importNewMembers && !duplicateMgNrs.Contains(c.MgNr)) || (importDuplicateMembers && duplicateMgNrs.Contains(c.MgNr))).ToList(); importedAreaComs.Add((meta.FileName, meta.ZwstId, meta.Device, imported.Count, areaCommitments.Count - imported.Count, meta.AreaComFilters)); } if (allowedDuplicateLsNrs.Count > 0) { var dids = deliveries .Where(d => allowedDuplicateLsNrs.Contains(d.LsNr)) .Select(d => (d.Year, d.DId)) .ToList(); ctx.RemoveRange(ctx.DeliveryParts .Where(p => allowedDuplicateLsNrs.Contains(p.Delivery.LsNr)) .SelectMany(p => p.PartModifiers)); ctx.RemoveRange(ctx.DeliveryParts.Where(p => allowedDuplicateLsNrs.Contains(p.Delivery.LsNr))); ctx.UpdateRange(deliveries.Where(d => dids.Contains((d.Year, d.DId)))); ctx.AddRange(deliveryParts.Where(p => dids.Contains((p.Year, p.DId)))); ctx.AddRange(modifiers.Where(m => dids.Contains((m.Year, m.DId)))); } if (importDuplicateDeliveries) { var l = duplicateLsNrs.Except(allowedDuplicateLsNrs).ToList(); var dids = deliveries .Where(d => l.Contains(d.LsNr)) .Select(d => (d.Year, d.DId)) .ToList(); ctx.RemoveRange(ctx.DeliveryParts .Where(p => l.Contains(p.Delivery.LsNr)) .SelectMany(p => p.PartModifiers)); ctx.RemoveRange(ctx.DeliveryParts.Where(p => l.Contains(p.Delivery.LsNr))); ctx.UpdateRange(deliveries.Where(d => dids.Contains((d.Year, d.DId)))); ctx.AddRange(deliveryParts.Where(p => dids.Contains((p.Year, p.DId)))); ctx.AddRange(modifiers.Where(m => dids.Contains((m.Year, m.DId)))); } if (importNewDeliveries) { ctx.AddRange(deliveries.Where(d => !duplicateDIds.Contains((d.Year, d.DId)))); ctx.AddRange(deliveryParts.Where(p => !duplicateDIds.Contains((p.Year, p.DId)))); ctx.AddRange(modifiers.Where(m => !duplicateDIds.Contains((m.Year, m.DId)))); } if (deliveries.Count > 0) { var n = importNewDeliveries ? deliveries.Count - duplicateDIds.Count : 0; var o = allowedDuplicateLsNrs.Count + (importDuplicateDeliveries ? duplicateDIds.Count - allowedDuplicateLsNrs.Count : 0); importedDeliveries.Add((meta.FileName, meta.ZwstId, meta.Device, n, o, deliveries.Count - n - o, meta.DeliveryFilters)); } await ctx.SaveChangesAsync(); await AddImportedFiles(Path.GetFileName(meta.FileName)); } await App.HintContextChange(); MessageBox.Show( $"Das importieren der Daten war erfolgreich!\n" + $"Folgendes wurde importiert:\n" + string.Join("\n", [ $"Mitglieder: {importedMembers.Sum(d => d.New + d.Overwritten)}", ..importedMembers.Select(d => $" {d.FileName} ({d.New + d.Overwritten})\n" + $" ({d.New} neu, {d.Overwritten} überschrieben, {d.NotImported} nicht importiert)\n" + $" Zweigstelle: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" + $" Filter: {d.Filters}"), $"Flächenbindungen: {importedAreaComs.Sum(d => d.Imported)}", ..importedAreaComs.Select(d => $" {d.FileName} ({d.Imported})\n" + $" ({d.Imported} importiert, {d.NotImported} nicht importiert)\n" + $" Zweigstelle: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" + $" Filter: {d.Filters}"), $"Lieferungen: {importedDeliveries.Sum(d => d.New + d.Overwritten)}", ..importedDeliveries.Select(d => $" {d.FileName} ({d.New + d.Overwritten})\n" + $" ({d.New} neu, {d.Overwritten} überschr., {d.NotImported} nicht importiert)\n" + $" Zwst.: {branches[d.ZwstId].Name} (Gerät {d.Device})\n" + $" Filter: {d.Filters}") ]), "Importieren erfolgreich", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception exc) { var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message; if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message; MessageBox.Show(str, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } GC.Collect(); GC.WaitForPendingFinalizers(); } private static bool ImportQuestion(string branch, string device, string subject, bool duplicate, int number) { return MessageBox.Show( $"Sollen {number} {(duplicate ? "" : "neue ")}{subject} durch die Zweigstelle\n" + $"{branch} (Gerät {device}) {(duplicate ? "überschrieben" : "importiert")} werden?", $"{subject} importieren", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes ) == MessageBoxResult.Yes; } public static Task Export(string filename, IEnumerable members, IEnumerable filters) { return new ElwigExport { Members = (members, filters) }.Export(filename); } public static Task Export(string filename, IEnumerable members, IEnumerable areaComs, IEnumerable filters) { return new ElwigExport { Members = (members, filters), AreaComs = (areaComs, ["von exportierten Mitgliedern"]), }.Export(filename); } public static Task Export(string filename, IEnumerable deliveries, IEnumerable filters) { return new ElwigExport { Deliveries = (deliveries, filters) }.Export(filename); } public class ElwigExport { public (IEnumerable Members, IEnumerable Filters)? Members { get; set; } public (IEnumerable AreaComs, IEnumerable Filters)? AreaComs { get; set; } public (IEnumerable Deliveries, IEnumerable Filters)? Deliveries { get; set; } public async Task Export(string filename) { File.Delete(filename); using var zip = ZipFile.Open(filename, ZipArchiveMode.Create); var version = zip.CreateEntry("version", CompressionLevel.NoCompression); using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) { await writer.WriteAsync("elwig:1"); } var meta = zip.CreateEntry("meta.json", CompressionLevel.NoCompression); using (var writer = new StreamWriter(meta.Open(), Utils.UTF8)) { var obj = new JsonObject { ["timestamp"] = $"{DateTime.UtcNow:yyyy-MM-ddTHH:mm:ssZ}", ["zwstid"] = App.ZwstId, ["device"] = Environment.MachineName, }; if (Members != null) obj["members"] = new JsonObject { ["count"] = Members.Value.Members.Count(), ["filters"] = new JsonArray(Members.Value.Filters.Select(f => (JsonNode)f).ToArray()), }; if (AreaComs != null) obj["area_commitments"] = new JsonObject { ["count"] = AreaComs.Value.AreaComs.Count(), ["filters"] = new JsonArray(AreaComs.Value.Filters.Select(f => (JsonNode)f).ToArray()), }; if (Deliveries != null) obj["deliveries"] = new JsonObject { ["count"] = Deliveries.Value.Deliveries.Count(), ["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count), ["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()), }; await writer.WriteAsync(obj.ToJsonString(JsonOpts)); } // TODO encrypt files if (Members != null) { var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize); using var writer = new StreamWriter(json.Open(), Utils.UTF8); foreach (var m in Members.Value.Members) { await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts)); } } if (AreaComs != null) { var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize); using var writer = new StreamWriter(json.Open(), Utils.UTF8); foreach (var c in AreaComs.Value.AreaComs) { await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts)); } } if (Deliveries != null) { var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize); using var writer = new StreamWriter(json.Open(), Utils.UTF8); foreach (var d in Deliveries.Value.Deliveries) { await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts)); } } } } public static JsonObject ToJson(this Member m) { return new JsonObject { ["mgnr"] = m.MgNr, ["predecessor_mgnr"] = m.PredecessorMgNr, ["prefix"] = m.Prefix, ["given_name"] = m.GivenName, ["middle_names"] = m.MiddleName, ["family_name"] = m.FamilyName, ["suffix"] = m.Suffix, ["birthday"] = m.Birthday, ["entry_date"] = m.EntryDate != null ? $"{m.EntryDate:yyyy-MM-dd}" : null, ["exit_date"] = m.ExitDate != null ? $"{m.ExitDate:yyyy-MM-dd}" : null, ["business_shares"] = m.BusinessShares, ["accounting_nr"] = m.AccountingNr, ["zwstid"] = m.ZwstId, ["lfbis_nr"] = m.LfbisNr, ["ustid_nr"] = m.UstIdNr, ["volllieferant"] = m.IsVollLieferant, ["buchführend"] = m.IsBuchführend, ["organic"] = m.IsOrganic, ["funktionär"] = m.IsFunktionär, ["active"] = m.IsActive, ["deceased"] = m.IsDeceased, ["iban"] = m.Iban, ["bic"] = m.Bic, ["default_kgnr"] = m.DefaultKgNr, ["contact_postal"] = m.ContactViaPost, ["contact_email"] = m.ContactViaEmail, ["address"] = new JsonObject { ["address"] = m.Address, ["postal_dest"] = m.PostalDestId, ["country"] = m.CountryNum, }, ["billing_address"] = m.BillingAddress != null ? new JsonObject { ["name"] = m.BillingAddress.Name, ["address"] = m.BillingAddress.Address, ["postal_dest"] = m.BillingAddress.PostalDestId, ["country"] = m.BillingAddress.CountryNum, } : null, ["telephone_numbers"] = new JsonArray(m.TelephoneNumbers.OrderBy(n => n.Nr).Select(n => { var obj = new JsonObject { ["number"] = n.Number, ["type"] = n.Type, }; if (n.Comment != null) obj["comment"] = n.Comment; return obj; }).ToArray()), ["email_addresses"] = new JsonArray(m.EmailAddresses.OrderBy(a => a.Nr).Select(a => { var obj = new JsonObject { ["address"] = a.Address, }; if (a.Comment != null) obj["comment"] = a.Comment; return obj; }).ToArray()), ["comment"] = m.Comment, }; } public static (Member, BillingAddr?, List, List) ToMember(this JsonNode json) { var mgnr = json["mgnr"]!.AsValue().GetValue(); return (new Member { MgNr = mgnr, PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue(), Prefix = json["prefix"]?.AsValue().GetValue(), GivenName = json["given_name"]!.AsValue().GetValue(), MiddleName = json["middle_names"]?.AsValue().GetValue(), FamilyName = json["family_name"]!.AsValue().GetValue(), Suffix = json["suffix"]?.AsValue().GetValue(), Birthday = json["birthday"]?.AsValue().GetValue(), EntryDateString = json["entry_date"]?.AsValue().GetValue(), ExitDateString = json["exit_date"]?.AsValue().GetValue(), BusinessShares = json["business_shares"]?.AsValue().GetValue() ?? 0, AccountingNr = json["accounting_nr"]?.AsValue().GetValue(), ZwstId = json["zwstid"]?.AsValue().GetValue(), LfbisNr = json["lfbis_nr"]?.AsValue().GetValue(), UstIdNr = json["ustid_nr"]?.AsValue().GetValue(), IsVollLieferant = json["volllieferant"]?.AsValue().GetValue() ?? false, IsBuchführend = json["buchführend"]?.AsValue().GetValue() ?? false, IsOrganic = json["organic"]?.AsValue().GetValue() ?? false, IsFunktionär = json["funktionär"]?.AsValue().GetValue() ?? false, IsActive = json["active"]?.AsValue().GetValue() ?? false, IsDeceased = json["deceased"]?.AsValue().GetValue() ?? false, Iban = json["iban"]?.AsValue().GetValue(), Bic = json["bic"]?.AsValue().GetValue(), CountryNum = json["address"]!["country"]!.AsValue().GetValue(), PostalDestId = json["address"]!["postal_dest"]!.AsValue().GetValue(), Address = json["address"]!["address"]!.AsValue().GetValue(), DefaultKgNr = json["default_kgnr"]?.AsValue().GetValue(), ContactViaPost = json["contact_postal"]?.AsValue().GetValue() ?? false, ContactViaEmail = json["contact_email"]?.AsValue().GetValue() ?? false, Comment = json["comment"]?.AsValue().GetValue(), }, json["billing_address"] is JsonObject a ? new BillingAddr { MgNr = mgnr, Name = a["name"]!.AsValue().GetValue(), CountryNum = a["country"]!.AsValue().GetValue(), PostalDestId = a["postal_dest"]!.AsValue().GetValue(), Address = a["address"]!.AsValue().GetValue(), } : null, json["telephone_numbers"]!.AsArray().Select(n => n!.AsObject()).Select((n, i) => new MemberTelNr { MgNr = mgnr, Nr = i + 1, Type = n["type"]!.AsValue().GetValue(), Number = n["number"]!.AsValue().GetValue(), Comment = n["comment"]?.AsValue().GetValue(), }).ToList(), json["email_addresses"]!.AsArray().Select(a => a!.AsObject()).Select((a, i) => new MemberEmailAddr { MgNr = mgnr, Nr = i + 1, Address = a["address"]!.AsValue().GetValue(), Comment = a["comment"]?.AsValue().GetValue(), }).ToList()); } public static JsonObject ToJson(this AreaCom c) { return new JsonObject { ["fbnr"] = c.FbNr, ["mgnr"] = c.MgNr, ["vtrgid"] = c.VtrgId, ["cultid"] = c.CultId, ["area"] = c.Area, ["kgnr"] = c.KgNr, ["gstnr"] = c.GstNr, ["ried"] = c.Rd?.Name, ["year_from"] = c.YearFrom, ["year_to"] = c.YearTo, ["comment"] = c.Comment, }; } public static (AreaCom, WbRd?) ToAreaCom(this JsonNode json, Dictionary> riede) { var kgnr = json["kgnr"]!.AsValue().GetValue(); var ried = json["ried"]?.AsValue().GetValue(); WbRd? rd = null; bool newRd = false; if (ried != null) { var rde = riede[kgnr] ?? throw new ArgumentException($"KG {kgnr:00000} is no WbKg"); rd = rde.FirstOrDefault(r => r.Name == ried); if (rd == null) { newRd = true; rd = new WbRd { KgNr = kgnr, RdNr = (rde.Count == 0 ? 0 : rde.Max(r => r.RdNr)) + 1, Name = ried, }; rde.Add(rd); } } return (new AreaCom { FbNr = json["fbnr"]!.AsValue().GetValue(), MgNr = json["mgnr"]!.AsValue().GetValue(), VtrgId = json["vtrgid"]!.AsValue().GetValue(), CultId = json["cultid"]?.AsValue().GetValue(), Area = json["area"]!.AsValue().GetValue(), KgNr = kgnr, GstNr = json["gstnr"]?.AsValue().GetValue() ?? "-", RdNr = rd?.RdNr, YearFrom = json["year_from"]!.AsValue().GetValue(), YearTo = json["year_to"]?.AsValue().GetValue(), Comment = json["comment"]?.AsValue().GetValue(), }, newRd ? rd : null); } public static JsonObject ToJson(this Delivery d) { return new JsonObject { ["lsnr"] = d.LsNr, ["year"] = d.Year, ["date"] = $"{d.Date:yyyy-MM-dd}", ["zwstid"] = d.ZwstId, ["lnr"] = d.LNr, ["time"] = d.Time != null ? $"{d.Time:HH:mm:ss}" : null, ["mgnr"] = d.MgNr, ["parts"] = new JsonArray(d.Parts.OrderBy(p => p.DPNr).Select(p => { var obj = new JsonObject { ["dpnr"] = p.DPNr, ["sortid"] = p.SortId, ["attrid"] = p.AttrId, ["cultid"] = p.CultId, ["weight"] = p.Weight, ["kmw"] = p.Kmw, ["qualid"] = p.QualId, ["hkid"] = p.HkId, ["kgnr"] = p.KgNr, ["rdnr"] = p.RdNr, ["net_weight"] = p.IsNetWeight, ["manual_weighing"] = p.IsManualWeighing, ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()), ["comment"] = p.Comment, }; if (p.IsSplCheck) obj["spl_check"] = p.IsSplCheck; if (p.IsHandPicked != null) obj["hand_picked"] = p.IsHandPicked; if (p.IsLesewagen != null) obj["lesewagen"] = p.IsLesewagen; if (p.IsGebunden != null) obj["gebunden"] = p.IsGebunden; if (p.Temperature != null) obj["temperature"] = p.Temperature; if (p.Acid != null) obj["acid"] = p.Acid; if (p.ScaleId != null) obj["scale_id"] = p.ScaleId; if (p.WeighingData != null) obj["weighing_data"] = JsonNode.Parse(p.WeighingData); if (p.WeighingReason != null) obj["weighing_reason"] = p.WeighingReason; return obj; }).ToArray()), ["comment"] = d.Comment, }; } public static (Delivery, List, List) ToDelivery(this JsonNode json, Dictionary currentLsNrs, Dictionary currentDids) { var year = json["year"]!.AsValue().GetValue(); var lsnr = json["lsnr"]!.AsValue().GetValue(); var did = currentLsNrs.GetValueOrDefault(lsnr, -1); if (did == -1) { if (!currentDids.ContainsKey(year)) currentDids[year] = 0; did = ++currentDids[year]; } currentLsNrs[lsnr] = did; return (new Delivery { Year = year, DId = did, DateString = json["date"]!.AsValue().GetValue(), TimeString = json["time"]?.AsValue().GetValue(), ZwstId = json["zwstid"]!.AsValue().GetValue(), LNr = json["lnr"]!.AsValue().GetValue(), LsNr = lsnr, MgNr = json["mgnr"]!.AsValue().GetValue(), Comment = json["comment"]?.AsValue().GetValue(), }, 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(), }).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()); } } }