Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
34d95eab9d | |||
548aeb2ce9 | |||
7edd888aa2 | |||
a0d4f19f30 | |||
67ba342c28 | |||
1b69fcb16a | |||
c8a95422af | |||
9d02f18bac |
35
CHANGELOG.md
35
CHANGELOG.md
@@ -2,6 +2,41 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
[v1.0.1.3][v1.0.1.3] (2025-10-13) {#v1.0.1.3}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Neue Funktionen {#v1.0.1.3-features}
|
||||||
|
|
||||||
|
* In der Liste des Lieferungen-Fenster (`DeliveryAdminWindow`) werden
|
||||||
|
* statt ausschließlich der Sorte auch Attribut und Bewirtschaftungsart angezeigt. (a0d4f19f30)
|
||||||
|
* Kommentare der Lieferungen (und Teillieferungen) angezeigt. (548aeb2ce9)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.3-misc}
|
||||||
|
|
||||||
|
* Verzögerung der Überprüfung auf automatische Updates auf einige Sekunden verlängert. (67ba342c28)
|
||||||
|
* Verbesserung der Ladezeiten im Anmeldungen-Fenster (`DeliveryAncmtAdminWindow`). (7edd888aa2)
|
||||||
|
|
||||||
|
[v1.0.1.3]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[v1.0.1.2][v1.0.1.2] (2025-09-25) {#v1.0.1.2}
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
### Behobene Fehler {#v1.0.1.2-bugfixes}
|
||||||
|
|
||||||
|
* Beim automatischen Importieren/Synchronisieren wird bei einem Fehlerfall der Benutzer verständigt, aber der Vorgang nicht abgebrochen. (9d02f18bac)
|
||||||
|
|
||||||
|
### Sonstiges {#v1.0.1.2-misc}
|
||||||
|
|
||||||
|
* Beim Sichern der Datenbank werden Meta-Informationen in der ZIP-Datei gespeichert. (c8a95422af)
|
||||||
|
|
||||||
|
[v1.0.1.2]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[v1.0.1.1][v1.0.1.1] (2025-09-21) {#v1.0.1.1}
|
[v1.0.1.1][v1.0.1.1] (2025-09-21) {#v1.0.1.1}
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
|
|
||||||
|
@@ -128,7 +128,7 @@ namespace Elwig {
|
|||||||
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", async () => {
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
await Task.Delay(500);
|
await Task.Delay(1000);
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -234,7 +234,7 @@ namespace Elwig {
|
|||||||
if (!evt.IsAvailable) return;
|
if (!evt.IsAvailable) return;
|
||||||
if (Utils.HasInternetConnectivity()) {
|
if (Utils.HasInternetConnectivity()) {
|
||||||
Utils.RunBackground("Auto Updater", async () => {
|
Utils.RunBackground("Auto Updater", async () => {
|
||||||
await Task.Delay(500);
|
await Task.Delay(2000);
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||||
<Version>1.0.1.1</Version>
|
<Version>1.0.1.3</Version>
|
||||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
@@ -2,16 +2,48 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Elwig.Helpers.Export {
|
namespace Elwig.Helpers.Export {
|
||||||
public static class Database {
|
public static class Database {
|
||||||
|
|
||||||
|
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
|
||||||
|
long size = new FileInfo(App.Config.DatabaseFile).Length;
|
||||||
|
using var cnx = await AppDbContext.ConnectAsync();
|
||||||
|
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id");
|
||||||
|
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version");
|
||||||
|
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version");
|
||||||
|
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task ExportSqlite(string filename, bool zipFile) {
|
public static async Task ExportSqlite(string filename, bool zipFile) {
|
||||||
if (zipFile) {
|
if (zipFile) {
|
||||||
File.Delete(filename);
|
File.Delete(filename);
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
await zip.CheckIntegrity();
|
|
||||||
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
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,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
var db = zip.CreateEntryFromFile(App.Config.DatabaseFile, "database.sqlite3", CompressionLevel.SmallestSize);
|
||||||
} else {
|
} else {
|
||||||
File.Copy(App.Config.DatabaseFile, filename, true);
|
File.Copy(App.Config.DatabaseFile, filename, true);
|
||||||
@@ -22,10 +54,33 @@ namespace Elwig.Helpers.Export {
|
|||||||
if (zipFile) {
|
if (zipFile) {
|
||||||
File.Delete(filename);
|
File.Delete(filename);
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
|
||||||
var entry = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
|
||||||
using var stream = entry.Open();
|
var version = zip.CreateEntry("version", CompressionLevel.NoCompression);
|
||||||
using var writer = new StreamWriter(stream, Utils.UTF8);
|
using (var writer = new StreamWriter(version.Open(), Utils.UTF8)) {
|
||||||
await ExportSql(writer);
|
await writer.WriteAsync("elwig:1");
|
||||||
|
}
|
||||||
|
|
||||||
|
var (applId, userVers, schemaVers, size) = await GetMeta();
|
||||||
|
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,
|
||||||
|
["database"] = new JsonObject {
|
||||||
|
["application_id"] = applId,
|
||||||
|
["user_version"] = userVers,
|
||||||
|
["schema_version"] = schemaVers,
|
||||||
|
["file_size"] = size,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
|
}
|
||||||
|
|
||||||
|
var sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
|
||||||
|
using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
|
||||||
|
await ExportSql(writer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
using var stream = File.OpenWrite(filename);
|
using var stream = File.OpenWrite(filename);
|
||||||
using var writer = new StreamWriter(stream, Utils.UTF8);
|
using var writer = new StreamWriter(stream, Utils.UTF8);
|
||||||
|
@@ -6,7 +6,6 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
@@ -18,8 +17,6 @@ namespace Elwig.Helpers.Export {
|
|||||||
|
|
||||||
public static readonly string ImportedTxt = Path.Combine(App.DataPath, "imported.txt");
|
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<string[]> GetImportedFiles() {
|
public static async Task<string[]> GetImportedFiles() {
|
||||||
try {
|
try {
|
||||||
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
|
||||||
@@ -77,97 +74,117 @@ namespace Elwig.Helpers.Export {
|
|||||||
int? DeliveryNum, string? DeliveryFilters)>();
|
int? DeliveryNum, string? DeliveryFilters)>();
|
||||||
|
|
||||||
foreach (var filename in filenames) {
|
foreach (var filename in filenames) {
|
||||||
// TODO read encrypted files
|
try {
|
||||||
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
|
||||||
await zip.CheckIntegrity();
|
["member"] = [],
|
||||||
|
["area_commitment"] = [],
|
||||||
|
["delivery"] = [],
|
||||||
|
})));
|
||||||
|
var r = data[^1];
|
||||||
|
|
||||||
var version = zip.GetEntry("version");
|
// TODO read encrypted files
|
||||||
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
using var zip = ZipFile.Open(filename, ZipArchiveMode.Read);
|
||||||
if (await reader.ReadToEndAsync() != "elwig:1")
|
await zip.CheckIntegrity();
|
||||||
throw new FileFormatException($"Ungültige Export-Datei ({filename})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var metaJson = zip.GetEntry("meta.json");
|
var version = zip.GetEntry("version");
|
||||||
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
|
||||||
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
if (await reader.ReadToEndAsync() != "elwig:1")
|
||||||
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
throw new FileFormatException($"Ungültige Elwig-Export-Datei ({filename})");
|
||||||
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));
|
|
||||||
|
|
||||||
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
|
var metaJson = zip.GetEntry("meta.json");
|
||||||
["member"] = [],
|
var meta = await JsonNode.ParseAsync(metaJson!.Open());
|
||||||
["area_commitment"] = [],
|
var memberCount = meta!["members"]?["count"]?.AsValue().GetValue<int>();
|
||||||
["delivery"] = [],
|
var memberFilters = meta!["members"]?["filters"]?.AsArray().Select(f => f!.AsValue().GetValue<string>()).ToArray();
|
||||||
})));
|
var areaComCount = meta!["area_commitments"]?["count"]?.AsValue().GetValue<int>();
|
||||||
var r = data[^1];
|
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");
|
var wbKgsJson = zip.GetEntry("wb_kgs.json");
|
||||||
if (wbKgsJson != null) {
|
if (wbKgsJson != null) {
|
||||||
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (k, g) = obj.ToWbKg(currentWbGls);
|
var (k, g) = obj.ToWbKg(currentWbGls);
|
||||||
r.WbKgs.Add(k);
|
r.WbKgs.Add(k);
|
||||||
if (g != null) {
|
if (g != null) {
|
||||||
currentWbGls[g.GlNr] = g;
|
currentWbGls[g.GlNr] = g;
|
||||||
r.WbGls.Add(g);
|
r.WbGls.Add(g);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var membersJson = zip.GetEntry("members.json");
|
var membersJson = zip.GetEntry("members.json");
|
||||||
if (membersJson != null) {
|
if (membersJson != null) {
|
||||||
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(membersJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
|
var (m, b, telNrs, emailAddrs, timestamps) = obj.ToMember(kgs);
|
||||||
r.Members.Add(m);
|
r.Members.Add(m);
|
||||||
if (b != null) r.BillingAddresses.Add(b);
|
if (b != null) r.BillingAddresses.Add(b);
|
||||||
r.TelephoneNumbers.AddRange(telNrs);
|
r.TelephoneNumbers.AddRange(telNrs);
|
||||||
r.EmailAddresses.AddRange(emailAddrs);
|
r.EmailAddresses.AddRange(emailAddrs);
|
||||||
if (timestamps.HasValue)
|
if (timestamps.HasValue)
|
||||||
r.Timestamps["member"].Add((m.MgNr, 0, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
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");
|
var areaComsJson = zip.GetEntry("area_commitments.json");
|
||||||
if (deliveriesJson != null) {
|
if (areaComsJson != null) {
|
||||||
using var reader = new StreamReader(deliveriesJson.Open(), Utils.UTF8);
|
using var reader = new StreamReader(areaComsJson.Open(), Utils.UTF8);
|
||||||
string? line;
|
string? line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null) {
|
while ((line = await reader.ReadLineAsync()) != null) {
|
||||||
var obj = JsonNode.Parse(line)!.AsObject();
|
var obj = JsonNode.Parse(line)!.AsObject();
|
||||||
var (d, parts, mods, rde, timestamps) = obj.ToDelivery(currentLsNrs, currentDids, kgs, currentWbRde);
|
var (areaCom, wbrd, timestamps) = obj.ToAreaCom(currentWbRde);
|
||||||
r.Deliveries.Add(d);
|
r.AreaCommitments.Add(areaCom);
|
||||||
r.DeliveryParts.AddRange(parts);
|
if (wbrd != null) {
|
||||||
r.Modifiers.AddRange(mods);
|
r.Riede.Add(wbrd);
|
||||||
r.Riede.AddRange(rde);
|
}
|
||||||
if (timestamps.HasValue)
|
if (timestamps.HasValue)
|
||||||
r.Timestamps["delivery"].Add((d.Year, d.DId, timestamps.Value.CreatedAt, timestamps.Value.ModifiedAt));
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +466,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
|
||||||
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
|
||||||
};
|
};
|
||||||
await writer.WriteAsync(obj.ToJsonString(JsonOpts));
|
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO encrypt files
|
// TODO encrypt files
|
||||||
@@ -457,28 +474,28 @@ namespace Elwig.Helpers.Export {
|
|||||||
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var k in WbKgs.Value.WbKgs) {
|
foreach (var k in WbKgs.Value.WbKgs) {
|
||||||
await writer.WriteLineAsync(k.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(k.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Members != null) {
|
if (Members != null) {
|
||||||
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("members.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var m in Members.Value.Members) {
|
foreach (var m in Members.Value.Members) {
|
||||||
await writer.WriteLineAsync(m.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(m.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (AreaComs != null) {
|
if (AreaComs != null) {
|
||||||
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("area_commitments.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var c in AreaComs.Value.AreaComs) {
|
foreach (var c in AreaComs.Value.AreaComs) {
|
||||||
await writer.WriteLineAsync(c.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(c.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Deliveries != null) {
|
if (Deliveries != null) {
|
||||||
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
var json = zip.CreateEntry("deliveries.json", CompressionLevel.SmallestSize);
|
||||||
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
|
||||||
foreach (var d in Deliveries.Value.Deliveries) {
|
foreach (var d in Deliveries.Value.Deliveries) {
|
||||||
await writer.WriteLineAsync(d.ToJson().ToJsonString(JsonOpts));
|
await writer.WriteLineAsync(d.ToJson().ToJsonString(Utils.JsonOpts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -806,7 +823,7 @@ namespace Elwig.Helpers.Export {
|
|||||||
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
Temperature = p["temperature"]?.AsValue().GetValue<double>(),
|
||||||
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
Acid = p["acid"]?.AsValue().GetValue<double>(),
|
||||||
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
ScaleId = p["scale_id"]?.AsValue().GetValue<string>(),
|
||||||
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(JsonOpts),
|
WeighingData = p["weighing_data"]?.AsObject().ToJsonString(Utils.JsonOpts),
|
||||||
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
WeighingReason = p["weighing_reason"]?.AsValue().GetValue<string>(),
|
||||||
};
|
};
|
||||||
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
}).ToList(), json["parts"]!.AsArray().SelectMany(p => p!["modids"]!.AsArray().Select(m => new DeliveryPartModifier {
|
||||||
|
@@ -1,40 +1,42 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using Elwig.Dialogs;
|
using Elwig.Dialogs;
|
||||||
using System.Text;
|
using Elwig.Documents;
|
||||||
using System.Numerics;
|
|
||||||
using Elwig.Models.Entities;
|
|
||||||
using Elwig.Helpers.Billing;
|
using Elwig.Helpers.Billing;
|
||||||
using System.Runtime.InteropServices;
|
using Elwig.Models;
|
||||||
using System.Net.Http;
|
using Elwig.Models.Entities;
|
||||||
using System.Text.Json.Nodes;
|
using LinqKit;
|
||||||
using System.IO;
|
|
||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MailKit.Security;
|
using MailKit.Security;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections;
|
|
||||||
using Elwig.Documents;
|
|
||||||
using MimeKit;
|
|
||||||
using LinqKit;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Elwig.Models;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using MimeKit;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Ports;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Markup;
|
using System.Windows.Markup;
|
||||||
|
|
||||||
namespace Elwig.Helpers {
|
namespace Elwig.Helpers {
|
||||||
public static partial class Utils {
|
public static partial class Utils {
|
||||||
|
|
||||||
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
public static readonly Encoding UTF8 = new UTF8Encoding(false, true);
|
||||||
|
public static readonly JsonSerializerOptions JsonOpts = new() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
|
||||||
|
|
||||||
public static int CurrentYear => DateTime.Now.Year;
|
public static int CurrentYear => DateTime.Now.Year;
|
||||||
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
public static int CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Elwig.Helpers;
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Helpers.Billing;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -62,6 +63,11 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("comment")]
|
[Column("comment")]
|
||||||
public string? Comment { get; set; }
|
public string? Comment { get; set; }
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string[] Comments => [.. Parts.Select(p => p.Comment).Prepend(Comment).Where(c => c != null).Cast<string>()];
|
||||||
|
[NotMapped]
|
||||||
|
public string CommentsString => string.Join(" / ", Comments);
|
||||||
|
|
||||||
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
[Column("ctime"), DatabaseGenerated(DatabaseGeneratedOption.Computed)]
|
||||||
public long CTime { get; set; }
|
public long CTime { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
@@ -108,16 +114,16 @@ namespace Elwig.Models.Entities {
|
|||||||
public int Weight => Parts.Select(p => p.Weight).Sum();
|
public int Weight => Parts.Select(p => p.Weight).Sum();
|
||||||
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
|
||||||
|
|
||||||
public IEnumerable<string> SortIds => Parts
|
public IEnumerable<RawVaribute> Vaributes => Parts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public IEnumerable<string> FilteredSortIds => FilteredParts
|
public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
|
||||||
.GroupBy(p => p.SortId)
|
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
|
||||||
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
|
||||||
.Select(g => g.Key);
|
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
|
||||||
public string SortIdString => string.Join(", ", SortIds);
|
public string VaributeString => string.Join(", ", Vaributes);
|
||||||
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
|
public string FilteredVaributeString => string.Join(", ", FilteredVaributes);
|
||||||
|
|
||||||
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
|
||||||
|
@@ -42,7 +42,9 @@ namespace Elwig.Models.Entities {
|
|||||||
[Column("max_weight")]
|
[Column("max_weight")]
|
||||||
public int? MaxWeight { get; set; }
|
public int? MaxWeight { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int AnnouncedWeight => Announcements.Sum(a => a.Weight);
|
public int AnnouncedWeight => AnnouncedWeightOverride ?? Announcements.Sum(a => a.Weight);
|
||||||
|
[NotMapped]
|
||||||
|
public int? AnnouncedWeightOverride { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;
|
||||||
|
|
||||||
|
@@ -265,13 +265,18 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</DataGridTextColumn.CellStyle>
|
</DataGridTextColumn.CellStyle>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
|
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredVaributeString}" Width="60">
|
||||||
<DataGridTextColumn.CellStyle>
|
<DataGridTextColumn.CellStyle>
|
||||||
<Style>
|
<Style>
|
||||||
<Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
|
<Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
|
||||||
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
|
||||||
</Style>
|
</Style>
|
||||||
</DataGridTextColumn.CellStyle>
|
</DataGridTextColumn.CellStyle>
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
|
<DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
|
||||||
<DataGridTextColumn.CellStyle>
|
<DataGridTextColumn.CellStyle>
|
||||||
@@ -290,6 +295,7 @@
|
|||||||
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
|
||||||
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
|
||||||
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
|
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
|
||||||
|
<DataGridTextColumn Header="Kommentar" Binding="{Binding CommentsString}" Width="150"/>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|
||||||
|
@@ -83,14 +83,19 @@ namespace Elwig.Windows {
|
|||||||
|
|
||||||
private async Task RefreshDeliveryScheduleList() {
|
private async Task RefreshDeliveryScheduleList() {
|
||||||
using var ctx = new AppDbContext();
|
using var ctx = new AppDbContext();
|
||||||
var deliverySchedules = await ctx.DeliverySchedules
|
var list = await ctx.DeliverySchedules
|
||||||
.Where(s => s.Year == ViewModel.FilterSeason)
|
.Where(s => s.Year == ViewModel.FilterSeason)
|
||||||
.Include(s => s.Branch)
|
.Include(s => s.Branch)
|
||||||
.Include(s => s.Announcements)
|
|
||||||
.OrderBy(s => s.DateString)
|
.OrderBy(s => s.DateString)
|
||||||
.ThenBy(s => s.Branch.Name)
|
.ThenBy(s => s.Branch.Name)
|
||||||
.ThenBy(s => s.Description)
|
.ThenBy(s => s.Description)
|
||||||
|
.Select(s => new {
|
||||||
|
Schedule = s,
|
||||||
|
AnnouncedWeight = s.Announcements.Sum(a => a.Weight)
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
|
||||||
|
var deliverySchedules = list.Select(v => v.Schedule).ToList();
|
||||||
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
|
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
|
||||||
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
|
.Where(s => !ViewModel.FilterOnlyUpcoming || s.DateString.CompareTo(Utils.Today.ToString("yyyy-MM-dd")) >= 0)
|
||||||
.ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
|
.ToList(), DeliveryScheduleList_SelectionChanged, ViewModel.FilterFromAllSchedules ? ControlUtils.RenewSourceDefault.None : ControlUtils.RenewSourceDefault.First);
|
||||||
|
@@ -13,7 +13,7 @@ About
|
|||||||
**Product:** Elwig
|
**Product:** Elwig
|
||||||
**Description:** Electronic Management for Vintners' Cooperatives
|
**Description:** Electronic Management for Vintners' Cooperatives
|
||||||
**Type:** ERP system
|
**Type:** ERP system
|
||||||
**Version:** 1.0.1.1 ([Changelog](./CHANGELOG.md))
|
**Version:** 1.0.1.3 ([Changelog](./CHANGELOG.md))
|
||||||
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
**Website:** https://elwig.at/
|
**Website:** https://elwig.at/
|
||||||
**Source code:** https://git.necronda.net/winzer/elwig
|
**Source code:** https://git.necronda.net/winzer/elwig
|
||||||
@@ -33,7 +33,7 @@ Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
|
|||||||
**Produkt:** Elwig
|
**Produkt:** Elwig
|
||||||
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
|
||||||
**Typ:** Warenwirtschaftssystem (ERP-System)
|
**Typ:** Warenwirtschaftssystem (ERP-System)
|
||||||
**Version:** 1.0.1.1 ([Änderungsprotokoll](./CHANGELOG.md))
|
**Version:** 1.0.1.2 ([Änderungsprotokoll](./CHANGELOG.md))
|
||||||
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
|
||||||
**Website:** https://elwig.at/
|
**Website:** https://elwig.at/
|
||||||
**Quellcode:** https://git.necronda.net/winzer/elwig
|
**Quellcode:** https://git.necronda.net/winzer/elwig
|
||||||
|
Reference in New Issue
Block a user