using Elwig.Helpers; using Elwig.Helpers.Export; using Elwig.Models.Entities; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net.Http; using System.Threading.Tasks; using System.Windows; namespace Elwig.Services { public static class SyncService { public static readonly Expression> ChangedMembers = (m) => ((m.XTime == null && m.MTime > 1751328000) || m.MTime > m.XTime) && (m.ITime == null || m.MTime > m.ITime); public static readonly Expression> ChangedDeliveries = (d) => ((d.XTime == null && d.MTime > 1751328000) || d.MTime > d.XTime) && (d.ITime == null || d.MTime > d.ITime); public static async Task Upload(string url, string username, string password, IQueryable query, IEnumerable filterNames) { try { var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip"; var path = Path.Combine(App.TempPath, filename); var members = await query .OrderBy(m => m.MgNr) .Include(m => m.BillingAddress) .Include(m => m.TelephoneNumbers) .Include(m => m.EmailAddresses) .Include(m => m.DefaultWbKg!.Gl) .AsSplitQuery() .ToListAsync(); var areaComs = await query .SelectMany(m => m.AreaCommitments) .OrderBy(c => c.MgNr).ThenBy(c => c.FbNr) .Include(c => c.Rd) .Include(c => c.Kg.Gl) .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 { var exportedAt = DateTime.Now; await ElwigData.Export(path, members, areaComs, wbKgs, filterNames); await Utils.UploadExportData(path, url, username, password); await UpdateExportedAt(members, [], exportedAt); MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern erfolgreich!", "Mitglieder hochgeladen", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } public static async Task Upload(string url, string username, string password, IQueryable query, IEnumerable filterNames) { try { var filename = $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip"; var path = Path.Combine(App.TempPath, filename); var list = await query .Select(p => p.Delivery) .Distinct() .Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier) .Include(d => d.Parts).ThenInclude(p => p.Rd) .Include(d => d.Parts).ThenInclude(p => p.Kg!.Gl) .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 { var exportedAt = DateTime.Now; await ElwigData.Export(path, list, wbKgs, filterNames); await Utils.UploadExportData(path, url, username, password); await UpdateExportedAt([], list, exportedAt); MessageBox.Show($"Hochladen von {list.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochgeladen", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error); } } public static async Task UploadModified(string url, string username, string password) { try { var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip"); List members; List areaComs; List deliveries; using (var ctx = new AppDbContext()) { members = await ctx.Members .Where(ChangedMembers) .Include(m => m.BillingAddress) .Include(m => m.TelephoneNumbers) .Include(m => m.EmailAddresses) .Include(m => m.DefaultWbKg!.Gl) .OrderBy(m => m.MgNr) .AsSplitQuery() .ToListAsync(); areaComs = await ctx.Members .Where(ChangedMembers) .SelectMany(m => m.AreaCommitments) .Include(c => c.Rd) .Include(c => c.Kg.Gl) .OrderBy(c => c.MgNr).ThenBy(c => c.FbNr) .ToListAsync(); deliveries = await ctx.Deliveries .Where(ChangedDeliveries) .Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier) .Include(d => d.Parts).ThenInclude(p => p.Rd) .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 = members .Where(m => m.DefaultWbKg != null) .Select(m => m.DefaultWbKg!) .Union(areaComs.Select(c => c.Kg)) .Union(deliveries.SelectMany(d => d.Parts) .Where(p => p.Kg != null) .Select(p => p.Kg!)) .DistinctBy(k => k.KgNr) .OrderBy(k => k.KgNr) .ToList(); if (members.Count == 0 && deliveries.Count == 0) { MessageBox.Show("Es gibt keine geänderten Mitglieder oder Lieferungen, die hochgeladen werden könnten!", "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Information); } else { var exportedAt = DateTime.Now; await (new ElwigData.ElwigExport { Members = (members, ["geändert seit letztem Export"]), AreaComs = (areaComs, ["von exportierten Mitgliedern"]), Deliveries = (deliveries, ["geändert seit letzem Export"]), WbKgs = (wbKgs, ["von exportierten Mitgliedern, Flächenbindungen und Lieferungen"]), }).Export(path); await Utils.UploadExportData(path, url, username, password); await UpdateExportedAt(members, deliveries, exportedAt); MessageBox.Show($"Hochladen von {members.Count:N0} Mitgliedern und {deliveries.Count:N0} Lieferungen erfolgreich!", "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Mitglieder und Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } } public static async Task UploadBranchDeliveries(string url, string username, string password, int? year = null) { try { year ??= Utils.CurrentLastSeason; var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip"); using var ctx = new AppDbContext(); var deliveries = await ctx.Deliveries .Where(d => d.Year == year && d.ZwstId == App.ZwstId) .Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier) .Include(d => d.Parts).ThenInclude(p => p.Rd) .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 { var exportedAt = DateTime.Now; await ElwigData.Export(path, deliveries, wbKgs, [$"{year}", $"Zweigstelle {App.BranchName}"]); await Utils.UploadExportData(path, url, username, password); await UpdateExportedAt([], deliveries, exportedAt); MessageBox.Show($"Hochladen von {deliveries.Count:N0} Lieferungen erfolgreich!", "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Lieferungen hochladen", MessageBoxButton.OK, MessageBoxImage.Error); } } public static async Task> GetFilesToImport(string url, string username, string password) { var data = await Utils.GetExportMetaData(url, username, password); var files = data .Select(f => new { Name = f!["name"]!.AsValue().GetValue(), Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null, ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue() ?? f!["zwstid"]?.AsValue().GetValue(), Device = f!["meta"]?["device"]?.AsValue().GetValue(), Url = f!["url"]!.AsValue().GetValue(), Size = f!["size"]!.AsValue().GetValue(), }) .Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1)) .ToList(); var imported = await ElwigData.GetImportedFiles(); return [.. files .Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name)) .Select(f => (f.Name, f.Url)) ]; } public static async Task Download(string url, string username, string password) { try { var import = await GetFilesToImport(url, username, password); var paths = new List(); using (var client = Utils.GetHttpClient(username, password)) { foreach (var f in import) { var filename = Path.Combine(App.TempPath, f.Name); using var stream = new FileStream(filename, FileMode.Create); await client.DownloadAsync(f.Url, stream); paths.Add(filename); } } await ElwigData.Import(paths, ElwigData.ImportMode.FromBranches); } catch (HttpRequestException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (TaskCanceledException exc) { MessageBox.Show("Eventuell Internetverbindung prüfen!\n\n" + exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception exc) { MessageBox.Show(exc.Message, "Daten herunterladen", MessageBoxButton.OK, MessageBoxImage.Error); } } private static async Task UpdateExportedAt(IEnumerable member, IEnumerable deliveries, DateTime dateTime) { var timestamp = ((DateTimeOffset)dateTime.ToUniversalTime()).ToUnixTimeSeconds(); var mgnrs = string.Join(",", member.Select(m => $"{m.MgNr}").Append("0")); var dids = string.Join(",", deliveries.Select(d => $"({d.Year},{d.DId})").Append("(0,0)")); using (var cnx = await AppDbContext.ConnectAsync()) { await cnx.ExecuteBatch($""" BEGIN; UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS'; UPDATE member SET xtime = {timestamp} WHERE mgnr IN ({mgnrs}); UPDATE area_commitment SET xtime = {timestamp} WHERE mgnr IN ({mgnrs}); UPDATE delivery SET xtime = {timestamp} WHERE (year, did) IN ({dids}); UPDATE delivery_part SET xtime = {timestamp} WHERE (year, did) IN ({dids}); UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS'; COMMIT; """); } App.HintContextChange(); } public static async Task ChangesAvailable(AppDbContext ctx, string url, string username, string password) { return await ctx.Members.AnyAsync(ChangedMembers) || await ctx.Deliveries.AnyAsync(ChangedDeliveries) || (Utils.HasInternetConnectivity() && (await GetFilesToImport(url, username, password)).Count > 0); } } }