Compare commits

...

20 Commits

Author SHA1 Message Date
34d95eab9d Bump version to 1.0.1.3
All checks were successful
Test / Run tests (push) Successful in 1m47s
Deploy / Build and Deploy (push) Successful in 1m43s
2025-10-13 20:40:55 +02:00
548aeb2ce9 DeliveryAdminWindow: Show delivery (part) comments in list 2025-10-13 20:39:09 +02:00
7edd888aa2 DeliveryAncmtAdminWindow: Increase performace by aggregating AnnouncedWeight
All checks were successful
Test / Run tests (push) Successful in 2m23s
2025-10-07 20:41:15 +02:00
a0d4f19f30 DeliveryAdminWindow: Include attribute and cultivation in wine variety column
All checks were successful
Test / Run tests (push) Successful in 2m24s
2025-10-05 21:21:03 +02:00
67ba342c28 App: Delay auto update check even more
All checks were successful
Test / Run tests (push) Successful in 2m15s
2025-09-29 16:14:16 +02:00
1b69fcb16a Bump version to 1.0.1.2
All checks were successful
Test / Run tests (push) Successful in 1m48s
Deploy / Build and Deploy (push) Successful in 1m40s
2025-09-25 22:51:35 +02:00
c8a95422af Export/Database: Add version and meta.json entry to zip export
All checks were successful
Test / Run tests (push) Successful in 2m13s
2025-09-25 21:08:11 +02:00
9d02f18bac ElwigData: Fail more gracefully when single files may not be processed
All checks were successful
Test / Run tests (push) Successful in 1m47s
2025-09-25 12:08:43 +02:00
f9ee2cb120 Bump version to 1.0.1.1
All checks were successful
Test / Run tests (push) Successful in 1m43s
Deploy / Build and Deploy (push) Successful in 1m38s
2025-09-21 22:15:47 +02:00
b27b89f599 DeliveryAdminWindow: Improve responsiveness by caching wine quality levels
All checks were successful
Test / Run tests (push) Successful in 1m54s
2025-09-21 22:09:10 +02:00
bfbd0a6a22 README: Add about sections 2025-09-21 15:15:57 +02:00
e2de7a1f1c DeliveryAdminWindow: Unselect wine quality level on invalid gradation 2025-09-19 10:33:19 +02:00
3f769eb7d7 Bump version to 1.0.1.0
All checks were successful
Test / Run tests (push) Successful in 1m58s
Deploy / Build and Deploy (push) Successful in 1m53s
2025-09-18 23:44:15 +02:00
8bc053053c App: Add auto update check on network change to active
All checks were successful
Test / Run tests (push) Successful in 1m55s
2025-09-18 23:37:10 +02:00
a0dcaf7b4f Elwig: Update database to include new BKI wine varieties
All checks were successful
Test / Run tests (push) Successful in 2m43s
2025-09-18 23:29:38 +02:00
844fc5217a Utils: Add User-Agent to default http client
All checks were successful
Test / Run tests (push) Successful in 1m53s
2025-09-17 15:09:28 +02:00
542afa5892 Bump version to 1.0.0.6
All checks were successful
Test / Run tests (push) Successful in 1m38s
Deploy / Build and Deploy (push) Successful in 1m37s
2025-09-17 09:58:20 +02:00
9e02b15ff1 Elwig: Fix extensions in SaveFileDialog
All checks were successful
Test / Run tests (push) Successful in 1m49s
2025-09-17 09:55:19 +02:00
463769b549 App: Fix auto selection of weighing mode
All checks were successful
Test / Run tests (push) Successful in 2m17s
2025-09-17 09:33:34 +02:00
2c383d0c55 AboutWindow: Fix typo in description 2025-09-17 09:33:11 +02:00
22 changed files with 443 additions and 167 deletions

View File

@@ -2,6 +2,87 @@
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}
---------------------------------------------
### Sonstiges {#v1.0.1.1-misc}
* Eingabe von Sorten und Qualitätsstufen im Übernahme-Fenster (`DeliveryAdminWindows`) verbessert. (e2de7a1f1c, b27b89f599)
[v1.0.1.1]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.1
[v1.0.1.0][v1.0.1.0] (2025-09-18) {#v1.0.1.0}
---------------------------------------------
### Neue Funktionen {#v1.0.1.0-features}
* Neue Weinsorten gemäß Kürzelliste der Bundeskellereiinspektion hinzugefügt. (a0dcaf7b4f)
### Sonstiges {#v1.0.1.0-misc}
* HTTP-Anfragen haben jetzt das Feld `User-Agent` gesetzt. (844fc5217a)
* Auto-Update-Funktion wird auch beim Erlangen von Netzwerkverbindung ausgeführt. (8bc053053c)
[v1.0.1.0]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.1.0
[v1.0.0.6][v1.0.0.6] (2025-09-17) {#v1.0.0.6}
---------------------------------------------
### Behobene Fehler {#v1.0.0.6-bugfixes}
* Die automatische Erkennung des Wiege-Modus hat im WKW nicht funktioniert. (463769b549)
### Sonstiges {#v1.0.0.6-misc}
* Tippfehler im Über-Fenster (`AboutWindow`) behoben. (2c383d0c55)
* `SaveFileDialog` mit Multi-File-Extensions verbessert. (9e02b15ff1)
[v1.0.0.6]: https://git.necronda.net/winzer/elwig/releases/tag/v1.0.0.6
[v1.0.0.5][v1.0.0.5] (2025-09-15) {#v1.0.0.5}
---------------------------------------------

View File

@@ -35,7 +35,7 @@
</DataTemplate>
<DataTemplate x:Key="WineVarietyTemplateExpanded">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SortId}" Foreground="{Binding Color}" MinWidth="36" Margin="0,0,10,0"/>
<TextBlock Text="{Binding SortIdFormat}" Foreground="{Binding Color}" MinWidth="36" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Name}" Foreground="{Binding Color}"/>
<TextBlock Text="{Binding CommentFormat}" FontSize="10" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
</StackPanel>

View File

@@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
@@ -115,16 +116,6 @@ namespace Elwig {
BranchNum = branches.Count;
}
if (Config.WeighingMode == null) {
if (App.Client.IsMatzen || App.Client.IsWolkersdorf) {
Config.WeighingMode = WeighingMode.Net;
} else if (App.Client.IsHaugsdorf || App.Client.IsSitzendorf) {
Config.WeighingMode = WeighingMode.Box;
} else if (App.Client.IsBaden || App.Client.IsGrInzersdorf) {
Config.WeighingMode = WeighingMode.Gross;
}
}
Utils.RunBackground("Temp File Cleanup", () => {
Utils.CleanupTempFiles();
return Task.CompletedTask;
@@ -137,10 +128,11 @@ namespace Elwig {
if (Config.UpdateAuto && Config.UpdateUrl != null) {
if (Utils.HasInternetConnectivity()) {
Utils.RunBackground("Auto Updater", async () => {
await Task.Delay(500);
await Task.Delay(1000);
await CheckForUpdates();
});
}
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
_autoUpdateTimer.Tick += new EventHandler(OnAutoUpdateTimer);
_autoUpdateTimer.Start();
}
@@ -172,6 +164,16 @@ namespace Elwig {
Shutdown();
}
if (Config.WeighingMode == null) {
if (Client.IsMatzen || Client.IsWolkersdorf) {
Config.WeighingMode = WeighingMode.Net;
} else if (Client.IsHaugsdorf || Client.IsSitzendorf) {
Config.WeighingMode = WeighingMode.Box;
} else if (Client.IsBaden || Client.IsGrInzersdorf) {
Config.WeighingMode = WeighingMode.Gross;
}
}
base.OnStartup(evt);
var window = new MainWindow();
@@ -228,6 +230,16 @@ namespace Elwig {
}
}
private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) {
if (!evt.IsAvailable) return;
if (Utils.HasInternetConnectivity()) {
Utils.RunBackground("Auto Updater", async () => {
await Task.Delay(2000);
await CheckForUpdates();
});
}
}
public static async Task CheckForUpdates(bool showAlert = false) {
if (Config.UpdateUrl == null) return;
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);

View File

@@ -7,7 +7,7 @@
<UseWPF>true</UseWPF>
<PreserveCompilationContext>true</PreserveCompilationContext>
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
<Version>1.0.0.5</Version>
<Version>1.0.1.3</Version>
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ApplicationManifest>app.manifest</ApplicationManifest>

View File

@@ -248,13 +248,6 @@ namespace Elwig.Helpers {
return c + 1;
}
public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
return await WineQualityLevels
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
.OrderBy(q => q.MinKmw)
.LastAsync();
}
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
var mod = new DeliveryPartModifier {

View File

@@ -9,7 +9,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 32;
public static readonly int RequiredSchemaVersion = 33;
private static int VersionOffset = 0;

View File

@@ -2,16 +2,48 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
namespace Elwig.Helpers.Export {
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) {
if (zipFile) {
File.Delete(filename);
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);
} else {
File.Copy(App.Config.DatabaseFile, filename, true);
@@ -22,10 +54,33 @@ namespace Elwig.Helpers.Export {
if (zipFile) {
File.Delete(filename);
using var zip = ZipFile.Open(filename, ZipArchiveMode.Create);
var entry = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
using var stream = entry.Open();
using var writer = new StreamWriter(stream, Utils.UTF8);
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 sql = zip.CreateEntry("database.sql", CompressionLevel.SmallestSize);
using (var writer = new StreamWriter(sql.Open(), Utils.UTF8)) {
await ExportSql(writer);
}
} else {
using var stream = File.OpenWrite(filename);
using var writer = new StreamWriter(stream, Utils.UTF8);

View File

@@ -6,7 +6,6 @@ using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Windows;
@@ -18,8 +17,6 @@ namespace Elwig.Helpers.Export {
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() {
try {
return await File.ReadAllLinesAsync(ImportedTxt, Utils.UTF8);
@@ -77,6 +74,14 @@ namespace Elwig.Helpers.Export {
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();
@@ -84,7 +89,7 @@ namespace Elwig.Helpers.Export {
var version = zip.GetEntry("version");
using (var reader = new StreamReader(version!.Open(), Utils.UTF8)) {
if (await reader.ReadToEndAsync() != "elwig:1")
throw new FileFormatException($"Ungültige Export-Datei ({filename})");
throw new FileFormatException($"Ungültige Elwig-Export-Datei ({filename})");
}
var metaJson = zip.GetEntry("meta.json");
@@ -101,13 +106,6 @@ namespace Elwig.Helpers.Export {
areaComCount, areaComFilters != null ? string.Join(" / ", areaComFilters) : null,
deliveryCount, deliveryFilters != null ? string.Join(" / ", deliveryFilters) : null));
data.Add(new([], [], [], [], [], [], [], new([], [], [], [], new() {
["member"] = [],
["area_commitment"] = [],
["delivery"] = [],
})));
var r = data[^1];
var wbKgsJson = zip.GetEntry("wb_kgs.json");
if (wbKgsJson != null) {
using var reader = new StreamReader(wbKgsJson.Open(), Utils.UTF8);
@@ -170,6 +168,25 @@ namespace Elwig.Helpers.Export {
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)>();
@@ -449,7 +466,7 @@ namespace Elwig.Helpers.Export {
["parts"] = Deliveries.Value.Deliveries.Sum(d => d.Parts.Count),
["filters"] = new JsonArray(Deliveries.Value.Filters.Select(f => (JsonNode)f).ToArray()),
};
await writer.WriteAsync(obj.ToJsonString(JsonOpts));
await writer.WriteAsync(obj.ToJsonString(Utils.JsonOpts));
}
// TODO encrypt files
@@ -457,28 +474,28 @@ namespace Elwig.Helpers.Export {
var json = zip.CreateEntry("wb_kgs.json", CompressionLevel.SmallestSize);
using var writer = new StreamWriter(json.Open(), Utils.UTF8);
foreach (var k in WbKgs.Value.WbKgs) {
await writer.WriteLineAsync(k.ToJson().ToJsonString(JsonOpts));
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(JsonOpts));
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(JsonOpts));
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(JsonOpts));
await writer.WriteLineAsync(d.ToJson().ToJsonString(Utils.JsonOpts));
}
}
}
@@ -806,7 +823,7 @@ namespace Elwig.Helpers.Export {
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(JsonOpts),
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 {

View File

@@ -1,41 +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 System.Text;
using System.Numerics;
using Elwig.Models.Entities;
using Elwig.Documents;
using Elwig.Helpers.Billing;
using System.Runtime.InteropServices;
using System.Net.Http;
using System.Text.Json.Nodes;
using System.IO;
using Elwig.Models;
using Elwig.Models.Entities;
using LinqKit;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using System.Collections;
using Elwig.Documents;
using MimeKit;
using System.Windows.Input;
using LinqKit;
using System.Linq.Expressions;
using Elwig.Models;
using Microsoft.Win32;
using MimeKit;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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.Tasks;
using System.Windows;
using System.Windows.Markup;
namespace Elwig.Helpers {
public static partial class Utils {
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 CurrentNextSeason => DateTime.Now.Year - (DateTime.Now.Month <= 3 ? 1 : 0);
@@ -430,6 +431,8 @@ namespace Elwig.Helpers {
var client = new HttpClient() {
Timeout = TimeSpan.FromSeconds(5),
};
client.DefaultRequestHeaders.UserAgent.Clear();
client.DefaultRequestHeaders.UserAgent.ParseAdd($"Elwig/{App.Version} ({App.Client.NameToken}, {App.BranchName}, {Environment.MachineName}, {Environment.OSVersion})");
client.DefaultRequestHeaders.Accept.Clear();
if (accept != null)
client.DefaultRequestHeaders.Accept.Add(new(accept));

View File

@@ -1,4 +1,5 @@
using Elwig.Helpers;
using Elwig.Helpers.Billing;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
@@ -62,6 +63,11 @@ namespace Elwig.Models.Entities {
[Column("comment")]
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)]
public long CTime { get; set; }
[NotMapped]
@@ -108,16 +114,16 @@ namespace Elwig.Models.Entities {
public int Weight => Parts.Select(p => p.Weight).Sum();
public int FilteredWeight => FilteredParts.Select(p => p.Weight).Sum();
public IEnumerable<string> SortIds => Parts
.GroupBy(p => p.SortId)
public IEnumerable<RawVaribute> Vaributes => Parts
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
.Select(g => g.Key);
public IEnumerable<string> FilteredSortIds => FilteredParts
.GroupBy(p => p.SortId)
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
public IEnumerable<RawVaribute> FilteredVaributes => FilteredParts
.GroupBy(p => (p.SortId, p.AttrId, p.CultId))
.OrderByDescending(g => g.Select(p => p.Weight).Sum())
.Select(g => g.Key);
public string SortIdString => string.Join(", ", SortIds);
public string FilteredSortIdString => string.Join(", ", FilteredSortIds);
.Select(g => new RawVaribute(g.Key.SortId, g.Key.AttrId, g.Key.CultId));
public string VaributeString => string.Join(", ", Vaributes);
public string FilteredVaributeString => string.Join(", ", FilteredVaributes);
public Brush? Color => Parts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();
public Brush? FilteredColor => FilteredParts.Select(p => p.Variety.Color).Distinct().SingleOrDefault();

View File

@@ -42,7 +42,9 @@ namespace Elwig.Models.Entities {
[Column("max_weight")]
public int? MaxWeight { get; set; }
[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]
public double? Percent => (double)AnnouncedWeight / MaxWeight * 100;

View File

@@ -11,16 +11,24 @@ namespace Elwig.Models.Entities {
[Column("type")]
public string Type { get; private set; } = null!;
[Column("max_qualid")]
public string MaxQualId { get; private set; } = null!;
[ForeignKey("MaxQualId")]
public virtual WineQualLevel MaxQualityLevel { get; private set; } = null!;
[Column("name")]
public string Name { get; private set; } = null!;
[Column("comment")]
public string? Comment { get; private set; }
public string SortIdFormat => IsQuw ? SortId : $"({SortId})";
public string CommentFormat => (Comment != null) ? $" ({Comment})" : "";
public bool IsRed => Type == "R";
public bool IsWhite => Type == "W";
public bool IsQuw => MaxQualId == "QUW";
public Brush? Color => IsWhite ? Brushes.DarkGreen : IsRed ? Brushes.DarkRed : null;
public WineVar() { }
@@ -28,6 +36,7 @@ namespace Elwig.Models.Entities {
public WineVar(string sortId, string name) {
SortId = sortId;
Name = name;
MaxQualId = "QUW";
}
public override string ToString() {

View File

@@ -0,0 +1,17 @@
-- schema version 32 to 33
ALTER TABLE wine_variety ADD COLUMN max_qualid TEXT NOT NULL DEFAULT 'QUW';
UPDATE wine_quality_level SET qualid = 'ALW' WHERE qualid = 'AUL';
UPDATE wine_variety SET comment = 'Muscato' WHERE sortid = 'MO';
INSERT INTO wine_variety (sortid, type, max_qualid, name, comment) VALUES
('DR', 'W', 'QUW', 'Donauriesling', NULL),
('DV', 'W', 'QUW', 'Donauveltliner', NULL),
('BN', 'W', 'RSW', 'Bronner', NULL),
('CB', 'W', 'RSW', 'Cabernet Blanc', NULL),
('CJ', 'R', 'RSW', 'Cabernet Jura', NULL),
('JO', 'W', 'RSW', 'Johanniter', NULL),
('OR', 'W', 'RSW', 'Orangetraube', NULL),
('PI', 'R', 'RSW', 'Pinot Nova', NULL),
('RE', 'R', 'RSW', 'Regent', NULL),
('SI', 'W', 'RSW', 'Solaris', NULL);

View File

@@ -721,9 +721,11 @@ namespace Elwig.Services {
FileName = subject == ExportSubject.Selected ? $"Lieferung_{vm.SelectedDelivery?.LsNr}.elwig.zip" : $"Lieferungen_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
DefaultExt = "elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{DeliveryJournal.Name} speichern unter - Elwig"
Title = $"{DeliveryJournal.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {

View File

@@ -496,11 +496,13 @@ namespace Elwig.Services {
} else if (mode == ExportMode.Export) {
var d = new SaveFileDialog() {
FileName = subject == ExportSubject.Selected ? $"Mitglied_{vm.SelectedMember?.MgNr}.elwig.zip" : $"Mitglieder_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip",
DefaultExt = ".elwig.zip",
DefaultExt = "elwig.zip",
Filter = "Elwig-Export-Datei (*.elwig.zip)|*.elwig.zip",
Title = $"{MemberList.Name} speichern unter - Elwig"
Title = $"{MemberList.Name} speichern unter - Elwig",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".elwig.zip")) d.FileName += ".elwig.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
try {

View File

@@ -4,12 +4,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Elwig.Windows"
Title="Über - Elwig" Height="340" Width="480" ResizeMode="NoResize">
Title="Über - Elwig" Height="340" Width="460" ResizeMode="NoResize">
<Grid>
<TextBlock Margin="20,10" FontSize="12">
<Bold>Produkt:</Bold> Elwig<LineBreak/>
<Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsveraltung<LineBreak/>
<Bold>Typ:</Bold> Warenwirschaftssystem<LineBreak/>
<Bold>Beschreibung:</Bold> Elektronische Winzergenossenschaftsverwaltung<LineBreak/>
<Bold>Typ:</Bold> Warenwirtschaftssystem (ERP-System)<LineBreak/>
<Bold>Version:</Bold> <Run x:Name="Version">0.0.0.0</Run> (<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungsprotokoll</Hyperlink>)<LineBreak/>
<Bold>Lizenz:</Bold> <Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0.html" RequestNavigate="Hyperlink_RequestNavigate">GNU General Public License 3.0 (GPLv3)</Hyperlink><LineBreak/>
<Bold>Website:</Bold> <Hyperlink NavigateUri="https://elwig.at/" RequestNavigate="Hyperlink_RequestNavigate">https://elwig.at/</Hyperlink><LineBreak/>
@@ -26,7 +26,7 @@
Paketierung: <Hyperlink NavigateUri="https://www.firegiant.com/wixtoolset/" RequestNavigate="Hyperlink_RequestNavigate">WiX Toolset</Hyperlink>
</TextBlock>
<Image Source="\Resources\Images\Elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Height="96"
<Image Source="\Resources\Images\Elwig.png" RenderOptions.BitmapScalingMode="HighQuality" Height="64"
HorizontalAlignment="Right" Margin="10" VerticalAlignment="Top"/>
</Grid>
</Window>

View File

@@ -265,13 +265,18 @@
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredSortIdString}" Width="50">
<DataGridTextColumn Header="Sorte" Binding="{Binding FilteredVaributeString}" Width="60">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.Foreground" Value="{Binding FilteredColor}"/>
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.CellStyle>
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Menge" Binding="{Binding FilteredWeight, StringFormat='{}{0:N0} kg '}" Width="75">
<DataGridTextColumn.CellStyle>
@@ -290,6 +295,7 @@
<DataGridTextColumn Header="LsNr." Binding="{Binding LsNr}" Width="120"/>
<DataGridTextColumn Header="Mitglied" Binding="{Binding Member.AdministrativeName}" Width="180"/>
<DataGridTextColumn Header="Zu-/Abschläge" Binding="{Binding FilteredModifiersString}" Width="150"/>
<DataGridTextColumn Header="Kommentar" Binding="{Binding CommentsString}" Width="150"/>
</DataGrid.Columns>
</DataGrid>

View File

@@ -8,6 +8,7 @@ using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
@@ -34,6 +35,8 @@ namespace Elwig.Windows {
private readonly Button[] WeighingButtons;
private List<WineQualLevel> WineQualityLevels = [];
public DeliveryAdminWindow(bool receipt = false) {
InitializeComponent();
CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
@@ -483,7 +486,8 @@ namespace Elwig.Windows {
var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
cultList.Insert(0, new NullItem(""));
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
WineQualityLevels = await ctx.WineQualityLevels.ToListAsync();
ControlUtils.RenewItemsSource(WineQualityLevelInput, WineQualityLevels);
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
.Where(m => m.Year == y && (!IsCreating || m.IsActive))
.OrderBy(m => m.Ordering)
@@ -1207,6 +1211,7 @@ namespace Elwig.Windows {
AttributeInput.SelectedIndex = 0;
CultivationInput.SelectedIndex = 0;
}
UpdateWineQualityLevels();
}
private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
@@ -1220,19 +1225,39 @@ namespace Elwig.Windows {
private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
if (WineVarietyInput.SelectedItem is WineVar s)
ViewModel.SortId = s.SortId;
UpdateWineQualityLevels();
if (!ViewModel.WineVar?.IsQuw ?? false) {
App.MainDispatcher.BeginInvoke(() => {
MessageBox.Show("Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!", "Kein Qualitätswein",
MessageBoxButton.OK, MessageBoxImage.Warning);
});
}
}
private WineQualLevel GetWineQualityLevel(double kmw, string? maxQualId = null) {
return WineQualityLevels
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
.Where(q => maxQualId == null || q.QualId == "WEI" || q.QualId == maxQualId)
.OrderBy(q => q.MinKmw)
.Last();
}
private void UpdateWineQualityLevels() {
using var ctx = new AppDbContext();
if (!GetInputValid(GradationKmwInput)) {
UnsetDefaultValue(WineQualityLevelInput);
ComboBox_SelectionChanged(WineQualityLevelInput, null);
WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.QualId == "WEI").ToList();
WineQualityLevelInput.ItemsSource = WineQualityLevels;
WineQualityLevelInput.SelectedItem = null;
return;
}
var kmw = (double)ViewModel.GradationKmw!;
WineQualityLevelInput.ItemsSource = ctx.WineQualityLevels.Where(q => q.MinKmw == null || q.MinKmw <= kmw).ToList();
var qual = ctx.GetWineQualityLevel(kmw).GetAwaiter().GetResult();
var max = ViewModel.WineVar?.MaxQualId;
var quw = ViewModel.WineVar?.IsQuw ?? true;
WineQualityLevelInput.ItemsSource = WineQualityLevels
.Where(q => q.MinKmw == null || q.MinKmw <= kmw)
.Where(q => quw || q.QualId == "WEI" || q.QualId == max)
.ToList();
var qual = GetWineQualityLevel(kmw, !quw ? max : null);
SetDefaultValue(WineQualityLevelInput, qual);
if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
ControlUtils.SelectItem(WineQualityLevelInput, qual);
@@ -1372,8 +1397,7 @@ namespace Elwig.Windows {
AbgewertetInput.IsChecked = false;
return;
}
using var ctx = new AppDbContext();
var defQual = ctx.GetWineQualityLevel(double.Parse(GradationKmwInput.Text)).GetAwaiter().GetResult();
var defQual = GetWineQualityLevel(ViewModel.GradationKmw!.Value, !(ViewModel.WineVar?.IsQuw ?? true) ? ViewModel.WineVar?.MaxQualId : null);
AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
}

View File

@@ -83,14 +83,19 @@ namespace Elwig.Windows {
private async Task RefreshDeliveryScheduleList() {
using var ctx = new AppDbContext();
var deliverySchedules = await ctx.DeliverySchedules
var list = await ctx.DeliverySchedules
.Where(s => s.Year == ViewModel.FilterSeason)
.Include(s => s.Branch)
.Include(s => s.Announcements)
.OrderBy(s => s.DateString)
.ThenBy(s => s.Branch.Name)
.ThenBy(s => s.Description)
.Select(s => new {
Schedule = s,
AnnouncedWeight = s.Announcements.Sum(a => a.Weight)
})
.ToListAsync();
list.ForEach(v => v.Schedule.AnnouncedWeightOverride = v.AnnouncedWeight);
var deliverySchedules = list.Select(v => v.Schedule).ToList();
ControlUtils.RenewItemsSource(DeliveryScheduleList, deliverySchedules
.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);

View File

@@ -158,8 +158,10 @@ namespace Elwig.Windows {
FileName = $"database_{Utils.Today:yyyy-MM-dd}.sql.zip",
DefaultExt = "sql.zip",
Filter = "Komprimierte SQL-Datei (*.sql.zip)|*.sql.zip",
AddExtension = false,
};
if (d.ShowDialog() == true) {
if (!d.FileName.EndsWith(".sql.zip")) d.FileName += ".sql.zip";
Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => {
await Database.ExportSql(d.FileName, true);

View File

@@ -5,3 +5,43 @@ Elwig
**El**ektronische **Wi**nzer**g**enossenschaftsverwaltung (Electronic Management for Vintners' Cooperatives).
Further information may be found on [the website](https://elwig.at).
About
=====
**Product:** Elwig
**Description:** Electronic Management for Vintners' Cooperatives
**Type:** ERP system
**Version:** 1.0.1.3 ([Changelog](./CHANGELOG.md))
**License:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Source code:** https://git.necronda.net/winzer/elwig
**Developement period:** 20222025
**Technology Stack:**
Language: C#
Framework: Windows Presentation Framework (WPF)
Database: [SQLite](https://sqlite.org/)
PDF creation: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)
Packaging: [WiX Toolset](https://www.firegiant.com/wixtoolset/)
Über
====
**Produkt:** Elwig
**Beschreibung:** Elektronische Winzergenossenschaftsverwaltung
**Typ:** Warenwirtschaftssystem (ERP-System)
**Version:** 1.0.1.2 ([Änderungsprotokoll](./CHANGELOG.md))
**Lizenz:** [GNU General Public License 3.0 (GPLv3)](./LICENSE)
**Website:** https://elwig.at/
**Quellcode:** https://git.necronda.net/winzer/elwig
**Entwicklungszeitraum:** 20222025
**Verwendete Technologien:**
Programmiersprache: C#
Framework: Windows Presentation Framework (WPF)
Datenbank: [SQLite](https://sqlite.org/)
PDF-Erstellung: [WeasyPrint](https://weasyprint.org/), [RazorLight](https://github.com/toddams/RazorLight), [PdfiumViewer](https://github.com/pvginkel/PdfiumViewer)
Paketierung: [WiX Toolset](https://www.firegiant.com/wixtoolset/)

View File

@@ -1 +1 @@
curl --fail -s -L "https://elwig.at/files/create.sql?v=32" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
curl --fail -s -L "https://elwig.at/files/create.sql?v=33" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"