842 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			842 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Elwig.Models.Entities;
 | |
| using Microsoft.EntityFrameworkCore;
 | |
| using System;
 | |
| using System.Collections.Generic;
 | |
| using System.Globalization;
 | |
| using System.IO;
 | |
| using System.IO.Compression;
 | |
| using System.Linq;
 | |
| using System.Text.Json.Nodes;
 | |
| using System.Threading.Tasks;
 | |
| using System.Windows;
 | |
| 
 | |
| 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");
 | |
| 
 | |
|         public static async Task<string[]> 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<string> filenames, ImportMode mode) {
 | |
|             try {
 | |
|                 Dictionary<string, Branch> branches;
 | |
|                 Dictionary<int, int> currentDids;
 | |
|                 Dictionary<string, int> currentLsNrs;
 | |
|                 Dictionary<int, List<WbRd>> currentWbRde;
 | |
|                 Dictionary<int, AT_Kg> kgs;
 | |
|                 List<WbGl> currentWbGls;
 | |
| 
 | |
|                 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());
 | |
|                     currentWbGls = await ctx.WbGls.ToListAsync();
 | |
|                     kgs = await ctx.Katastralgemeinden.Include(k => k.WbKg).ToDictionaryAsync(k => k.KgNr);
 | |
|                 }
 | |
| 
 | |
|                 var data = new List<(
 | |
|                     List<Member> Members,
 | |
|                     List<BillingAddr> BillingAddresses,
 | |
|                     List<MemberTelNr> TelephoneNumbers,
 | |
|                     List<MemberEmailAddr> EmailAddresses,
 | |
|                     List<AreaCom> AreaCommitments,
 | |
|                     List<WbRd> Riede,
 | |
|                     List<WbKg> WbKgs,
 | |
|                     List<WbGl> WbGls,
 | |
|                     List<Delivery> Deliveries,
 | |
|                     List<DeliveryPart> DeliveryParts,
 | |
|                     List<DeliveryPartModifier> Modifiers,
 | |
|                     Dictionary<string, List<(int Id1, int Id2, DateTime CreatedAt, DateTime ModifiedAt)>> Timestamps)>();
 | |
| 
 | |
|                 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) {
 | |
|                     try {
 | |
|                         data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
 | |
|                             ["member"] = [],
 | |
|                             ["area_commitment"] = [],
 | |
|                             ["delivery"] = [],
 | |
|                         })));
 | |
|                         var r = data[^1];
 | |
| 
 | |
|                         // TODO read encrypted files
 | |
|                         using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
 | |
|                         await zip.CheckIntegrity();
 | |
| 
 | |
|                         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 Elwig-Export-Datei ({filename})");
 | |
|                         }
 | |
| 
 | |
|                         var metaJson = zip.GetEntry("meta.json");
 | |
|                         var meta = await JsonNode.ParseAsync(metaJson!.Open());
 | |
|                         var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
 | |
|                         var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | |
|                         var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
 | |
|                         var areaComFilters = meta!["area_commitments"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | |
|                         var deliveryCount = meta!["deliveries"]?["count"]?.AsValue().GetValue<int>();
 | |
|                         var deliveryFilters = meta!["deliveries"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
 | |
|                         metaData.Add((Path.GetFileName(filename),
 | |
|                             meta["zwstid"]!.AsValue().GetValue<string>(), meta["device"]!.AsValue().GetValue<string>(),
 | |
|                             memberCount, memberFilters != null ? string.Join(" / ", memberFilters) : null,
 | |
|                             areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
 | |
|                             deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
 | |
| 
 | |
|                         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);
 | |
|                             string? line;
 | |
|                             while ((line = await reader.ReadLineAsync()) != null) {
 | |
|                                 var obj = JsonNode.Parse(line)!.AsObject();
 | |
|                                 var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
 | |
|                                 r.Members.Add(m);
 | |
|                                 if (b != null) r.BillingAddresses.Add(b);
 | |
|                                 r.TelephoneNumbers.AddRange(telNrs);
 | |
|                                 r.EmailAddresses.AddRange(emailAddrs);
 | |
|                                 if (timestamps.HasValue)
 | |
|                                     r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         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, timestamps) = obj.ToAreaCom(currentWbRde);
 | |
|                                 r.AreaCommitments.Add(areaCom);
 | |
|                                 if (wbrd != null) {
 | |
|                                     r.Riede.Add(wbrd);
 | |
|                                 }
 | |
|                                 if (timestamps.HasValue)
 | |
|                                     r.Timestamps["area_commitment"].Add((areaCom.FbNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
 | |
|                             }
 | |
|                         }
 | |
| 
 | |
|                         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, 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));
 | |
|                             }
 | |
|                         }
 | |
|                     } catch (Exception exc) when (
 | |
|                             exc is InvalidDataException ||
 | |
|                             exc is FileFormatException ||
 | |
|                             exc is FileNotFoundException ||
 | |
|                             exc is IOException) {
 | |
|                         data.RemoveAt(data.Count - 1);
 | |
|                         var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden und wird übersprungen.\n\n" + exc.Message;
 | |
|                         if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
 | |
|                         MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.OK, MessageBoxImage.Error);
 | |
|                         await AddImportedFiles(Path.GetFileName(filename));
 | |
|                     } catch (Exception exc) {
 | |
|                         data.RemoveAt(data.Count - 1);
 | |
|                         var str = $"Die Elwig-Export-Datei '{Path.GetFileName(filename)}' konnte nicht verarbeitet werden. Soll sie in Zukunft übersprungen werden?\n\n" + exc.Message;
 | |
|                         if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
 | |
|                         var r = MessageBox.Show(str, "Fehler beim Importieren", MessageBoxButton.YesNo, MessageBoxImage.Error, MessageBoxResult.No);
 | |
|                         if (r == MessageBoxResult.Yes) {
 | |
|                             await AddImportedFiles(Path.GetFileName(filename));
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 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, 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))
 | |
|                         .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<string>();
 | |
|                     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 || 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)));
 | |
|                         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) {
 | |
|                         ctx.AddRange(riede);
 | |
|                         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();
 | |
| 
 | |
|                     var primaryKeys = new Dictionary<string, string>() {
 | |
|                         ["member"] = "mgnr, 0",
 | |
|                         ["area_commitment"] = "fbnr, 0",
 | |
|                         ["delivery"] = "year, did",
 | |
|                     };
 | |
|                     var updateStmts = timestamps
 | |
|                         .SelectMany(e => e.Value.Select(m => $"UPDATE {e.Key} " +
 | |
|                                                              $"SET ctime = {((DateTimeOffset)m.CreatedAt.ToUniversalTime()).ToUnixTimeSeconds()}, " +
 | |
|                                                                  $"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
 | |
|                                                              $"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
 | |
|                     using var cnx = AppDbContext.Connect();
 | |
|                     await AppDbContext.ExecuteBatch(cnx, $"""
 | |
|                         BEGIN;
 | |
|                         UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
 | |
|                         {string.Join("\n", updateStmts)}
 | |
|                         UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
 | |
|                         COMMIT;
 | |
|                         """);
 | |
| 
 | |
|                     await AddImportedFiles(Path.GetFileName(meta.FileName));
 | |
|                 }
 | |
|                 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\nEvtl. muss die Datenbank manuell auf dieses Gerät kopieren werden.\n\n" + exc.Message;
 | |
|                 if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
 | |
|                 MessageBox.Show(str, "Fehler beim Importieren", 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<Member> members, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
 | |
|             return new ElwigExport {
 | |
|                 Members = (members, filters),
 | |
|                 WbKgs = (wbKgs, ["von exportierten Mitgliedern"]),
 | |
|             }.Export(filename);
 | |
|         }
 | |
| 
 | |
|         public static Task Export(string filename, IEnumerable<Member> members, IEnumerable<AreaCom> areaComs, IEnumerable<WbKg> wbKgs, IEnumerable<string> 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<Delivery> deliveries, IEnumerable<WbKg> wbKgs, IEnumerable<string> filters) {
 | |
|             return new ElwigExport {
 | |
|                 Deliveries = (deliveries, filters),
 | |
|                 WbKgs = (wbKgs, ["von exportierten Lieferungen"]),
 | |
|             }.Export(filename);
 | |
|         }
 | |
| 
 | |
|         public class ElwigExport {
 | |
|             public (IEnumerable<WbKg> WbKgs, IEnumerable<string> Filters)? WbKgs { get; set; }
 | |
|             public (IEnumerable<Member> Members, IEnumerable<string> Filters)? Members { get; set; }
 | |
|             public (IEnumerable<AreaCom> AreaComs, IEnumerable<string> Filters)? AreaComs { get; set; }
 | |
|             public (IEnumerable<Delivery> Deliveries, IEnumerable<string> 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 (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(),
 | |
|                             ["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(Utils.JsonOpts));
 | |
|                 }
 | |
| 
 | |
|                 // 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(Utils.JsonOpts));
 | |
|                     }
 | |
|                 }
 | |
|                 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(Utils.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(Utils.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(Utils.JsonOpts));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         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, List<WbGl> gls) {
 | |
|             var grosslage = json["großlage"]?.AsValue().GetValue<string>();
 | |
|             WbGl? gl = null;
 | |
|             bool newGl = false;
 | |
|             if (grosslage != null) {
 | |
|                 gl = gls.FirstOrDefault(g => g.Name == grosslage);
 | |
|                 if (gl == null) {
 | |
|                     newGl = true;
 | |
|                     gl = new WbGl {
 | |
|                         GlNr = (gls.Count == 0 ? 1 : gls.Max(g => g.GlNr)) + 1,
 | |
|                         Name = grosslage,
 | |
|                     };
 | |
|                     gls[gl.GlNr] = gl;
 | |
|                 }
 | |
|             }
 | |
|             return (new WbKg {
 | |
|                 KgNr = json["kgnr"]!.AsValue().GetValue<int>(),
 | |
|                 GlNr = gl?.GlNr,
 | |
|             }, newGl ? gl : null);
 | |
|         }
 | |
| 
 | |
|         public static JsonObject ToJson(this Member m) {
 | |
|             return new JsonObject {
 | |
|                 ["mgnr"] = m.MgNr,
 | |
|                 ["predecessor_mgnr"] = m.PredecessorMgNr,
 | |
|                 ["name"] = m.Name,
 | |
|                 ["prefix"] = m.Prefix,
 | |
|                 ["given_name"] = m.GivenName,
 | |
|                 ["middle_names"] = m.MiddleName,
 | |
|                 ["suffix"] = m.Suffix,
 | |
|                 ["attn"] = m.ForTheAttentionOf,
 | |
|                 ["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,
 | |
|                 ["juridical_pers"] = m.IsJuridicalPerson,
 | |
|                 ["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.FullName,
 | |
|                     ["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,
 | |
|                 ["created_at"] = $"{m.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|                 ["modified_at"] = $"{m.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static (Member, BillingAddr?, List<MemberTelNr>, List<MemberEmailAddr>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToMember(this JsonNode json, Dictionary<int, AT_Kg> kgs) {
 | |
|             var mgnr = json["mgnr"]!.AsValue().GetValue<int>();
 | |
|             var kgnr = json["default_kgnr"]?.AsValue().GetValue<int>();
 | |
|             if (kgnr != null && !kgs.Values.Any(k => k.WbKg?.KgNr == kgnr)) {
 | |
|                 throw new ArgumentException($"Für KG {(kgs.TryGetValue(kgnr.Value, out var k) ? k.Name : "?")} ({kgnr:00000}) ist noch keine Großlage festgelegt!\n(Stammdaten → Herkunftshierarchie)");
 | |
|             }
 | |
|             var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | |
|             var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
 | |
|             return (new Member {
 | |
|                 MgNr = mgnr,
 | |
|                 PredecessorMgNr = json["predecessor_mgnr"]?.AsValue().GetValue<int>(),
 | |
|                 Name = json["name"]?.AsValue().GetValue<string>() ?? json["family_name"]!.AsValue().GetValue<string>(),
 | |
|                 Prefix = json["prefix"]?.AsValue().GetValue<string>(),
 | |
|                 GivenName = json["given_name"]?.AsValue().GetValue<string>(),
 | |
|                 MiddleName = json["middle_names"]?.AsValue().GetValue<string>(),
 | |
|                 Suffix = json["suffix"]?.AsValue().GetValue<string>(),
 | |
|                 ForTheAttentionOf = json["attn"]?.AsValue().GetValue<string>(),
 | |
|                 Birthday = json["birthday"]?.AsValue().GetValue<string>(),
 | |
|                 EntryDateString = json["entry_date"]?.AsValue().GetValue<string>(),
 | |
|                 ExitDateString = json["exit_date"]?.AsValue().GetValue<string>(),
 | |
|                 BusinessShares = json["business_shares"]?.AsValue().GetValue<int>() ?? 0,
 | |
|                 AccountingNr = json["accounting_nr"]?.AsValue().GetValue<string>(),
 | |
|                 ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
 | |
|                 LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
 | |
|                 UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(),
 | |
|                 IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsOrganic = json["organic"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsFunktionär = json["funktionär"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsActive = json["active"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 IsDeceased = json["deceased"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 Iban = json["iban"]?.AsValue().GetValue<string>(),
 | |
|                 Bic = json["bic"]?.AsValue().GetValue<string>(),
 | |
|                 CountryNum = json["address"]!["country"]!.AsValue().GetValue<int>(),
 | |
|                 PostalDestId = json["address"]!["postal_dest"]!.AsValue().GetValue<string>(),
 | |
|                 Address = json["address"]!["address"]!.AsValue().GetValue<string>(),
 | |
|                 DefaultKgNr = kgnr,
 | |
|                 ContactViaPost = json["contact_postal"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 ContactViaEmail = json["contact_email"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                 Comment = json["comment"]?.AsValue().GetValue<string>(),
 | |
|                 ImportedAt = DateTime.Now,
 | |
|             }, json["billing_address"] is JsonObject a ? new BillingAddr {
 | |
|                 MgNr = mgnr,
 | |
|                 FullName = a["name"]!.AsValue().GetValue<string>(),
 | |
|                 CountryNum = a["country"]!.AsValue().GetValue<int>(),
 | |
|                 PostalDestId = a["postal_dest"]!.AsValue().GetValue<string>(),
 | |
|                 Address = a["address"]!.AsValue().GetValue<string>(),
 | |
|             } : null, json["telephone_numbers"]!.AsArray().Select(n => n!.AsObject()).Select((n, i) => new MemberTelNr {
 | |
|                 MgNr = mgnr,
 | |
|                 Nr = i + 1,
 | |
|                 Type = n["type"]!.AsValue().GetValue<string>(),
 | |
|                 Number = n["number"]!.AsValue().GetValue<string>(),
 | |
|                 Comment = n["comment"]?.AsValue().GetValue<string>(),
 | |
|             }).ToList(), json["email_addresses"]!.AsArray().Select(a => a!.AsObject()).Select((a, i) => new MemberEmailAddr {
 | |
|                 MgNr = mgnr,
 | |
|                 Nr = i + 1,
 | |
|                 Address = a["address"]!.AsValue().GetValue<string>(),
 | |
|                 Comment = a["comment"]?.AsValue().GetValue<string>(),
 | |
|             }).ToList(),
 | |
|             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)));
 | |
|         }
 | |
| 
 | |
|         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,
 | |
|                 ["created_at"] = $"{c.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|                 ["modified_at"] = $"{c.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static (AreaCom, WbRd?, (DateTime CreatedAt, DateTime ModifiedAt)?) ToAreaCom(this JsonNode json, Dictionary<int, List<WbRd>> riede) {
 | |
|             var kgnr = json["kgnr"]!.AsValue().GetValue<int>();
 | |
|             var ried = json["ried"]?.AsValue().GetValue<string>();
 | |
|             WbRd? rd = null;
 | |
|             bool newRd = false;
 | |
|             if (ried != null) {
 | |
|                 var rde = riede.GetValueOrDefault(kgnr, []);
 | |
|                 rd = rde.FirstOrDefault(r => r.Name == ried);
 | |
|                 if (rd == null) {
 | |
|                     newRd = true;
 | |
|                     rd = new WbRd {
 | |
|                         KgNr = kgnr,
 | |
|                         RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
 | |
|                         Name = ried,
 | |
|                     };
 | |
|                     rde.Add(rd);
 | |
|                     riede[rd.KgNr] = rde;
 | |
|                 }
 | |
|             }
 | |
|             var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | |
|             var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
 | |
|             return (new AreaCom {
 | |
|                 FbNr = json["fbnr"]!.AsValue().GetValue<int>(),
 | |
|                 MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
 | |
|                 VtrgId = json["vtrgid"]!.AsValue().GetValue<string>(),
 | |
|                 CultId = json["cultid"]?.AsValue().GetValue<string>(),
 | |
|                 Area = json["area"]!.AsValue().GetValue<int>(),
 | |
|                 KgNr = kgnr,
 | |
|                 GstNr = json["gstnr"]?.AsValue().GetValue<string>() ?? "-",
 | |
|                 RdNr = rd?.RdNr ?? json["rdnr"]?.AsValue().GetValue<int>(),
 | |
|                 YearFrom = json["year_from"]?.AsValue().GetValue<int>(),
 | |
|                 YearTo = json["year_to"]?.AsValue().GetValue<int>(),
 | |
|                 Comment = json["comment"]?.AsValue().GetValue<string>(),
 | |
|                 ImportedAt = DateTime.Now,
 | |
|             }, newRd ? rd : null,
 | |
|             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)));
 | |
|         }
 | |
| 
 | |
|         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,
 | |
|                         ["ried"] = p.Rd?.Name,
 | |
|                         ["net_weight"] = p.IsNetWeight,
 | |
|                         ["manual_weighing"] = p.IsManualWeighing,
 | |
|                         ["modids"] = new JsonArray(p.Modifiers.Select(m => (JsonNode)m.ModId).ToArray()),
 | |
|                         ["comment"] = p.Comment,
 | |
|                         ["created_at"] = $"{p.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|                         ["modified_at"] = $"{p.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|                     };
 | |
|                     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,
 | |
|                 ["created_at"] = $"{d.CreatedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|                 ["modified_at"] = $"{d.ModifiedAt:yyyy-MM-ddTHH:mm:ssK}",
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         public static (Delivery, List<DeliveryPart>, List<DeliveryPartModifier>, List<WbRd>, (DateTime CreatedAt, DateTime ModifiedAt)?) ToDelivery(this JsonNode json, Dictionary<string, int> currentLsNrs, Dictionary<int, int> currentDids, Dictionary<int, AT_Kg> kgs, Dictionary<int, List<WbRd>> riede) {
 | |
|             var year = json["year"]!.AsValue().GetValue<int>();
 | |
|             var lsnr = json["lsnr"]!.AsValue().GetValue<string>();
 | |
|             var did = currentLsNrs.GetValueOrDefault(lsnr, -1);
 | |
|             if (did == -1) {
 | |
|                 if (!currentDids.ContainsKey(year)) currentDids[year] = 0;
 | |
|                 did = ++currentDids[year];
 | |
|             }
 | |
|             currentLsNrs[lsnr] = did;
 | |
|             var createdAt = json["created_at"]?.AsValue().GetValue<string>();
 | |
|             var modifiedAt = json["modified_at"]?.AsValue().GetValue<string>();
 | |
|             var wbRde = new List<WbRd>();
 | |
|             return (new Delivery {
 | |
|                 Year = year,
 | |
|                 DId = did,
 | |
|                 DateString = json["date"]!.AsValue().GetValue<string>(),
 | |
|                 TimeString = json["time"]?.AsValue().GetValue<string>(),
 | |
|                 ZwstId = json["zwstid"]!.AsValue().GetValue<string>(),
 | |
|                 LNr = json["lnr"]!.AsValue().GetValue<int>(),
 | |
|                 LsNr = lsnr,
 | |
|                 MgNr = json["mgnr"]!.AsValue().GetValue<int>(),
 | |
|                 Comment = json["comment"]?.AsValue().GetValue<string>(),
 | |
|                 ImportedAt = DateTime.Now,
 | |
|             }, json["parts"]!.AsArray().Select(p => p!.AsObject()).Select(p => {
 | |
|                 var kgnr = p["kgnr"]!.AsValue().GetValue<int>();
 | |
|                 var ried = p["ried"]?.AsValue().GetValue<string>();
 | |
|                 WbRd? rd = null;
 | |
|                 if (ried != null) {
 | |
|                     var rde = riede.GetValueOrDefault(kgnr, []);
 | |
|                     rd = rde.FirstOrDefault(r => r.Name == ried);
 | |
|                     if (rd == null) {
 | |
|                         rd = new WbRd {
 | |
|                             KgNr = kgnr,
 | |
|                             RdNr = (rde.Count == 0 ? 1 : rde.Max(r => r.RdNr)) + 1,
 | |
|                             Name = ried,
 | |
|                         };
 | |
|                         rde.Add(rd);
 | |
|                         riede[rd.KgNr] = rde;
 | |
|                         wbRde.Add(rd);
 | |
|                     }
 | |
|                 }
 | |
|                 return new DeliveryPart {
 | |
|                     Year = year,
 | |
|                     DId = did,
 | |
|                     DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | |
|                     SortId = p["sortid"]!.AsValue().GetValue<string>(),
 | |
|                     AttrId = p["attrid"]?.AsValue().GetValue<string>(),
 | |
|                     CultId = p["cultid"]?.AsValue().GetValue<string>(),
 | |
|                     Weight = p["weight"]!.AsValue().GetValue<int>(),
 | |
|                     Kmw = p["kmw"]!.AsValue().GetValue<double>(),
 | |
|                     QualId = p["qualid"]!.AsValue().GetValue<string>(),
 | |
|                     HkId = p["hkid"]!.AsValue().GetValue<string>(),
 | |
|                     KgNr = p["kgnr"]?.AsValue().GetValue<int>(),
 | |
|                     RdNr = rd?.RdNr ?? p["rdnr"]?.AsValue().GetValue<int>(),
 | |
|                     IsNetWeight = p["net_weight"]!.AsValue().GetValue<bool>(),
 | |
|                     IsManualWeighing = p["manual_weighing"]!.AsValue().GetValue<bool>(),
 | |
|                     Comment = p["comment"]?.AsValue().GetValue<string>(),
 | |
|                     IsSplCheck = p["spl_check"]?.AsValue().GetValue<bool>() ?? false,
 | |
|                     IsHandPicked = p["hand_picked"]?.AsValue().GetValue<bool>(),
 | |
|                     IsLesewagen = p["lesewagen"]?.AsValue().GetValue<bool>(),
 | |
|                     IsGebunden = p["gebunden"]?.AsValue().GetValue<bool>(),
 | |
|                     Temperature = p["temperature"]?.AsValue().GetValue<double>(),
 | |
|                     Acid = p["acid"]?.AsValue().GetValue<double>(),
 | |
|                     ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
 | |
|                     WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
 | |
|                     WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
 | |
|                 };
 | |
|             }).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
 | |
|                 Year = year,
 | |
|                 DId = did,
 | |
|                 DPNr = p["dpnr"]!.AsValue().GetValue<int>(),
 | |
|                 ModId = m!.AsValue().GetValue<string>(),
 | |
|             })).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)));
 | |
|         }
 | |
|     }
 | |
| }
 |