Compare commits

...

6 Commits

31 changed files with 628 additions and 320 deletions

View File

@@ -149,7 +149,7 @@ namespace Elwig {
} catch (Exception e) { } catch (Exception e) {
list.Add(new InvalidScale(s.Id)); list.Add(new InvalidScale(s.Id));
if (s.Required) if (s.Required)
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagen-Fehler", MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
@@ -249,14 +249,18 @@ namespace Elwig {
private void OnSerialPortConnected(object? sender, string name) { private void OnSerialPortConnected(object? sender, string name) {
for (var i = 0; i < Config.Scales.Count; i++) { for (var i = 0; i < Config.Scales.Count; i++) {
var s = Config.Scales[i]; var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is InvalidScale) { if (s.Connection?.StartsWith($"serial://{name}:") ?? false) {
try { if (Scales[i] is InvalidScale) {
Scales[i] = Scale.FromConfig(s); try {
Scales[i] = Scale.FromConfig(s);
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception e) {
Scales[i] = new InvalidScale(s.Id);
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error);
}
} else if (Scales[i] is IEventScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show($"Verbindung zu Waage {s.Id} wieder hergestellt!", $"Waage {s.Id}", MessageBoxButton.OK, MessageBoxImage.Information);
} catch (Exception e) {
Scales[i] = new InvalidScale(s.Id);
MessageBox.Show($"Verbindung zu Waage {s.Id} konnte nicht hergestellt werden:\n\n{e.Message}", "Waagen-Fehler",
MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
} }
@@ -268,12 +272,14 @@ namespace Elwig {
var s = Config.Scales[i]; var s = Config.Scales[i];
if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) { if ((s.Connection?.StartsWith($"serial://{name}:") ?? false) && Scales[i] is not InvalidScale) {
MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning); MessageBox.Show($"Verbindung zu Waage {s.Id} unterbrochen!", $"Waagen {s.Id}", MessageBoxButton.OK, MessageBoxImage.Warning);
try { if (Scales[i] is ICommandScale) {
Scales[i].Dispose(); try {
} catch { Scales[i].Dispose();
// ignore } catch {
// ignore
}
Scales[i] = new InvalidScale(s.Id);
} }
Scales[i] = new InvalidScale(s.Id);
} }
} }
UpdateScales(); UpdateScales();

View File

@@ -10,7 +10,6 @@ using Microsoft.Data.Sqlite;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Collections.Generic; using System.Collections.Generic;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using System.Reflection;
using System.Data; using System.Data;
namespace Elwig.Helpers { namespace Elwig.Helpers {
@@ -117,40 +116,6 @@ namespace Elwig.Helpers {
return cnx; return cnx;
} }
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.NextResultAsync());
}
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
using var reader = new StreamReader(stream);
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
}
public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
return await cmd.ExecuteScalarAsync();
}
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(SqliteConnection cnx) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA foreign_key_check";
using var reader = await cmd.ExecuteReaderAsync();
var list = new List<(string, long, string, long)>();
while (await reader.ReadAsync()) {
var table = reader.GetString(0);
var rowid = reader.GetInt64(1);
var parent = reader.GetString(2);
var fkid = reader.GetInt64(3);
list.Add((table, rowid, parent, fkid));
}
return [.. list];
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder.UseSqlite(ConnectionString); optionsBuilder.UseSqlite(ConnectionString);
optionsBuilder.UseLazyLoadingProxies(); optionsBuilder.UseLazyLoadingProxies();

View File

@@ -16,10 +16,10 @@ namespace Elwig.Helpers {
public static async Task<Version> CheckDb() { public static async Task<Version> CheckDb() {
using var cnx = AppDbContext.Connect(); using var cnx = AppDbContext.Connect();
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0; var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})"); if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0; var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
VersionOffset = (int)(schemaVers % 100); VersionOffset = (int)(schemaVers % 100);
if (VersionOffset != 0) { if (VersionOffset != 0) {
// schema was modified manually/externally // schema was modified manually/externally
@@ -27,12 +27,12 @@ namespace Elwig.Helpers {
} }
await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion); await UpdateDbSchema(cnx, (int)(schemaVers / 100), RequiredSchemaVersion);
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0; var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF)); var v = new Version((int)(userVers >> 24), (int)((userVers >> 16) & 0xFF), (int)((userVers >> 8) & 0xFF), (int)(userVers & 0xFF));
if (App.Version > v) { if (App.Version > v) {
long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision; long vers = (App.Version.Major << 24) | (App.Version.Minor << 16) | (App.Version.Build << 8) | App.Version.Revision;
await AppDbContext.ExecuteBatch(cnx, $"PRAGMA user_version = {vers}"); await cnx.ExecuteBatch($"PRAGMA user_version = {vers}");
} }
return v; return v;
@@ -67,20 +67,20 @@ namespace Elwig.Helpers {
if (toExecute.Count == 0) if (toExecute.Count == 0)
return; return;
await AppDbContext.ExecuteBatch(cnx, """ await cnx.ExecuteBatch("""
PRAGMA locking_mode = EXCLUSIVE; PRAGMA locking_mode = EXCLUSIVE;
BEGIN EXCLUSIVE; BEGIN EXCLUSIVE;
"""); """);
foreach (var script in toExecute) { foreach (var script in toExecute) {
await AppDbContext.ExecuteEmbeddedScript(cnx, asm, script); await cnx.ExecuteEmbeddedScript(asm, script);
} }
var violations = await AppDbContext.ForeignKeyCheck(cnx); var violations = await cnx.ForeignKeyCheck();
if (violations.Length > 0) { if (violations.Length > 0) {
throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations throw new Exception($"Foreign key violations ({violations.Length}):\n" + string.Join("\n", violations
.Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}"))); .Select(v => $"{v.Table} - {v.RowId} - {v.Parent} - {v.FkId}")));
} }
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
COMMIT; COMMIT;
VACUUM; VACUUM;
PRAGMA schema_version = {toVersion * 100 + VersionOffset}; PRAGMA schema_version = {toVersion * 100 + VersionOffset};

View File

@@ -27,7 +27,7 @@ namespace Elwig.Helpers.Billing {
public async Task FinishSeason() { public async Task FinishSeason() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
UPDATE season UPDATE season
SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year}) SET (start_date, end_date) = (SELECT MIN(date), MAX(date) FROM delivery WHERE year = {Year})
WHERE year = {Year}; WHERE year = {Year};
@@ -37,7 +37,7 @@ namespace Elwig.Helpers.Billing {
public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) { public async Task AutoAdjustBusinessShares(DateOnly date, int allowanceKg = 0, double allowanceBs = 0, int allowanceKgPerBs = 0, double allowanceRel = 0, int addMinBs = 1) {
if (addMinBs < 1) addMinBs = 1; if (addMinBs < 1) addMinBs = 1;
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
UPDATE member UPDATE member
SET business_shares = member.business_shares - h.business_shares SET business_shares = member.business_shares - h.business_shares
FROM member_history h FROM member_history h
@@ -66,7 +66,7 @@ namespace Elwig.Helpers.Billing {
public async Task UnAdjustBusinessShares() { public async Task UnAdjustBusinessShares() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
UPDATE member UPDATE member
SET business_shares = member.business_shares - h.business_shares SET business_shares = member.business_shares - h.business_shares
FROM member_history h FROM member_history h
@@ -157,9 +157,9 @@ namespace Elwig.Helpers.Billing {
lastMgNr = mgnr; lastMgNr = mgnr;
} }
await AppDbContext.ExecuteBatch(cnx, $"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}"); await cnx.ExecuteBatch($"UPDATE delivery_part_bucket SET value = 0 WHERE year = {Year}");
if (inserts.Count > 0) { if (inserts.Count > 0) {
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value) INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))} VALUES {string.Join(",\n ", inserts.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '{i.Item4}', {i.Item5})"))}
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
@@ -237,7 +237,7 @@ namespace Elwig.Helpers.Billing {
if (needed == 0) break; if (needed == 0) break;
} }
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value) INSERT INTO delivery_part_bucket (year, did, dpnr, bktnr, discr, value)
VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))} VALUES {string.Join(",\n ", posChanges.Select(i => $"({Year}, {i.Item1}, {i.Item2}, {i.Item3}, '', {i.Item4})"))}
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE

View File

@@ -47,7 +47,7 @@ namespace Elwig.Helpers.Billing {
public async Task Commit() { public async Task Commit() {
await Revert(); await Revert();
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers) INSERT INTO credit (year, tgnr, mgnr, avnr, net_amount, prev_net_amount, vat, modifiers, prev_modifiers)
SELECT s.year, SELECT s.year,
COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr, COALESCE(t.tgnr, 0) + ROW_NUMBER() OVER(ORDER BY m.mgnr) AS tgnr,
@@ -82,27 +82,27 @@ namespace Elwig.Helpers.Billing {
LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr) LEFT JOIN payment_custom x ON (x.year, x.mgnr) = (s.year, m.mgnr)
WHERE s.year = {Year} AND v.avnr = {AvNr}; WHERE s.year = {Year} AND v.avnr = {AvNr};
"""); """);
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET test_variant = FALSE WHERE (year, avnr) = ({Year}, {AvNr});
"""); """);
} }
public async Task Revert() { public async Task Revert() {
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM credit WHERE (year, avnr) = ({Year}, {AvNr});
UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr}); UPDATE payment_variant SET test_variant = TRUE WHERE (year, avnr) = ({Year}, {AvNr});
"""); """);
} }
protected async Task SetCalcTime(SqliteConnection cnx) { protected async Task SetCalcTime(SqliteConnection cnx) {
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr}) UPDATE payment_variant SET calc_time = UNIXEPOCH() WHERE (year, avnr) = ({Year}, {AvNr})
"""); """);
} }
protected async Task DeleteInDb(SqliteConnection cnx) { protected async Task DeleteInDb(SqliteConnection cnx) {
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_delivery_part_bucket WHERE (year, avnr) = ({Year}, {AvNr});
DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_delivery_part WHERE (year, avnr) = ({Year}, {AvNr});
DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr}); DELETE FROM payment_member WHERE (year, avnr) = ({Year}, {AvNr});
@@ -116,7 +116,7 @@ namespace Elwig.Helpers.Billing {
var multiplier = 0.50; var multiplier = 0.50;
var includePredecessor = true; var includePredecessor = true;
var modName = "Treue%"; var modName = "Treue%";
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT c.year, {AvNr}, s.mgnr, 0, SELECT c.year, {AvNr}, s.mgnr, 0,
ROUND(s.sum * COALESCE(m.abs, 0)), ROUND(s.sum * COALESCE(m.abs, 0)),
@@ -138,7 +138,7 @@ namespace Elwig.Helpers.Billing {
mod_rel = mod_rel + excluded.mod_rel mod_rel = mod_rel + excluded.mod_rel
"""); """);
} }
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_member (year, avnr, mgnr, net_amount, mod_abs, mod_rel)
SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0) SELECT x.year, {AvNr}, x.mgnr, 0, COALESCE(x.mod_abs * POW(10, s.precision - 2), 0), COALESCE(x.mod_rel, 0)
FROM payment_custom x FROM payment_custom x
@@ -194,7 +194,7 @@ namespace Elwig.Helpers.Billing {
var msg = invalid.Count == 0 ? null : "Für folgende Sorten wurde noch keine Preiskurve festgelegt: " + string.Join(", ", invalid); var msg = invalid.Count == 0 ? null : "Für folgende Sorten wurde noch keine Preiskurve festgelegt: " + string.Join(", ", invalid);
if (msg != null && strict) if (msg != null && strict)
throw new KeyNotFoundException(msg); throw new KeyNotFoundException(msg);
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount) INSERT INTO payment_delivery_part_bucket (year, did, dpnr, bktnr, avnr, price, amount)
VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))}; VALUES {string.Join(",\n ", inserts.Select(i => $"({i.Year}, {i.DId}, {i.DPNr}, {i.BktNr}, {AvNr}, {i.Price}, {i.Amount})"))};
"""); """);
@@ -205,7 +205,7 @@ namespace Elwig.Helpers.Billing {
protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) { protected async Task CalculateDeliveryModifiers(SqliteConnection cnx) {
var netMod = Data.NetWeightModifier.ToString().Replace(',', '.'); var netMod = Data.NetWeightModifier.ToString().Replace(',', '.');
var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.'); var grossMod = Data.GrossWeightModifier.ToString().Replace(',', '.');
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel) INSERT INTO payment_delivery_part (year, did, dpnr, avnr, net_amount, mod_abs, mod_rel)
SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod}) SELECT d.year, d.did, d.dpnr, {AvNr}, 0, 0, IIF(d.net_weight, {netMod}, {grossMod})
FROM delivery_part d FROM delivery_part d

View File

@@ -11,9 +11,9 @@ namespace Elwig.Helpers.Export {
private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() { private static async Task<(long? ApplicationId, string? UserVersion, long? SchemaVersion, long FileSize)> GetMeta() {
long size = new FileInfo(App.Config.DatabaseFile).Length; long size = new FileInfo(App.Config.DatabaseFile).Length;
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id"); var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id");
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version"); var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version");
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version"); var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version");
return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size); return (applId, userVers != null ? $"{userVers >> 24}.{(userVers >> 16) & 0xFF}.{(userVers >> 8) & 0xFF}.{userVers & 0xFF}" : null, schemaVers, size);
} }
@@ -100,9 +100,9 @@ namespace Elwig.Helpers.Export {
} }
} }
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0; var applId = (long?)await cnx.ExecuteScalar("PRAGMA application_id") ?? 0;
var userVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA user_version") ?? 0; var userVers = (long?)await cnx.ExecuteScalar("PRAGMA user_version") ?? 0;
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0; var schemaVers = (long?)await cnx.ExecuteScalar("PRAGMA schema_version") ?? 0;
await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}"); await writer.WriteLineAsync($"-- Elwig database dump, {DateTime.Now:yyyy-MM-dd, HH:mm:ss}");
await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}"); await writer.WriteLineAsync($"-- {Environment.MachineName}, Zwst. {App.BranchName}, {App.Client.Name}");
@@ -224,7 +224,7 @@ namespace Elwig.Helpers.Export {
File.Move(filename, App.Config.DatabaseFile, false); File.Move(filename, App.Config.DatabaseFile, false);
using var cnx = await AppDbContext.ConnectAsync(); using var cnx = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteBatch(cnx, "VACUUM"); await cnx.ExecuteBatch("VACUUM");
} }
public static async Task ImportSql(StreamReader reader) { public static async Task ImportSql(StreamReader reader) {
@@ -232,7 +232,7 @@ namespace Elwig.Helpers.Export {
File.Delete(newName); File.Delete(newName);
try { try {
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) { using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
await AppDbContext.ExecuteBatch(cnx, await reader.ReadToEndAsync()); await cnx.ExecuteBatch(await reader.ReadToEndAsync());
} }
await ImportSqlite(newName); await ImportSqlite(newName);
} finally { } finally {

View File

@@ -345,7 +345,7 @@ namespace Elwig.Helpers.Export {
$"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " + $"mtime = {((DateTimeOffset)m.ModifiedAt.ToUniversalTime()).ToUnixTimeSeconds()} " +
$"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});")); $"WHERE ({primaryKeys[e.Key]}) = ({m.Id1}, {m.Id2});"));
using var cnx = AppDbContext.Connect(); using var cnx = AppDbContext.Connect();
await AppDbContext.ExecuteBatch(cnx, $""" await cnx.ExecuteBatch($"""
BEGIN; BEGIN;
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS'; UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
{string.Join("\n", updateStmts)} {string.Join("\n", updateStmts)}

View File

@@ -1,14 +1,17 @@
using Microsoft.Data.Sqlite;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Hashing; using System.IO.Hashing;
using System.Net.Http; using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Elwig.Helpers { namespace Elwig.Helpers {
static partial class Extensions { public static partial class Extensions {
[LibraryImport("msvcrt.dll")] [LibraryImport("msvcrt.dll")]
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])] [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
@@ -108,5 +111,39 @@ namespace Elwig.Helpers {
throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'"); throw new InvalidDataException($"CRC-32 mismatch in '{entry.FullName}'");
} }
} }
public static async Task ExecuteBatch(this SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
using var reader = await cmd.ExecuteReaderAsync();
while (await reader.NextResultAsync()) ;
}
public static async Task ExecuteEmbeddedScript(this SqliteConnection cnx, Assembly asm, string name) {
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
using var reader = new StreamReader(stream);
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
}
public static async Task<object?> ExecuteScalar(this SqliteConnection cnx, string sql) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = sql;
return await cmd.ExecuteScalarAsync();
}
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(this SqliteConnection cnx) {
using var cmd = cnx.CreateCommand();
cmd.CommandText = "PRAGMA foreign_key_check";
using var reader = await cmd.ExecuteReaderAsync();
var list = new List<(string, long, string, long)>();
while (await reader.ReadAsync()) {
var table = reader.GetString(0);
var rowid = reader.GetInt64(1);
var parent = reader.GetString(2);
var fkid = reader.GetInt64(3);
list.Add((table, rowid, parent, fkid));
}
return [.. list];
}
} }
} }

View File

@@ -17,28 +17,34 @@ namespace Elwig.Helpers {
public SerialPortWatcher() { public SerialPortWatcher() {
_knownPorts = SerialPort.GetPortNames(); _knownPorts = SerialPort.GetPortNames();
_deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2"); _deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
_deviceArrivalWatcher.EventArrived += (s, e) => OnDeviceArrived(); _deviceArrivalWatcher.EventArrived += OnDeviceArrived;
_deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3"); _deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
_deviceRemovalWatcher.EventArrived += (s, e) => OnDeviceRemoved(); _deviceRemovalWatcher.EventArrived += OnDeviceRemoved;
_deviceArrivalWatcher.Start(); _deviceArrivalWatcher.Start();
_deviceRemovalWatcher.Start(); _deviceRemovalWatcher.Start();
} }
private void OnDeviceArrived() { private void OnDeviceArrived(object sender, EventArrivedEventArgs evt) {
string[] currentPorts = SerialPort.GetPortNames(); App.MainDispatcher.Invoke(() => {
var newPorts = currentPorts.Except(_knownPorts).ToArray(); // "synchronized"
foreach (var port in newPorts) string[] currentPorts = SerialPort.GetPortNames();
SerialPortConnected?.Invoke(this, port); var newPorts = currentPorts.Except(_knownPorts).ToArray();
_knownPorts = currentPorts; foreach (var port in newPorts)
SerialPortConnected?.Invoke(this, port);
_knownPorts = currentPorts;
});
} }
private void OnDeviceRemoved() { private void OnDeviceRemoved(object sender, EventArrivedEventArgs evt) {
string[] currentPorts = SerialPort.GetPortNames(); App.MainDispatcher.Invoke(() => {
var removedPorts = _knownPorts.Except(currentPorts).ToArray(); // "synchronized"
foreach (var port in removedPorts) string[] currentPorts = SerialPort.GetPortNames();
SerialPortDisconnected?.Invoke(this, port); var removedPorts = _knownPorts.Except(currentPorts).ToArray();
_knownPorts = currentPorts; foreach (var port in removedPorts)
SerialPortDisconnected?.Invoke(this, port);
_knownPorts = currentPorts;
});
} }
public void Dispose() { public void Dispose() {

View File

@@ -15,15 +15,17 @@ namespace Elwig.Helpers.Weighing {
public bool IsReady { get; private set; } public bool IsReady { get; private set; }
public bool HasFillingClearance { get; private set; } public bool HasFillingClearance { get; private set; }
public event IEventScale.EventHandler<WeighingEventArgs> WeighingEvent; public event IEventScale.EventHandler<WeighingEventArgs>? WeighingEvent;
private bool IsRunning = true; private bool IsRunning = true;
private readonly Thread BackgroundThread; private readonly Thread BackgroundThread;
private readonly string Connection;
public AveryEventScale(string id, string model, string cnx, string? empty = null, string? filling = null, int? limit = null, string? log = null) : public AveryEventScale(string id, string model, string cnx, string? log = null, bool required = true) :
base(cnx, empty, filling, limit, log) { base(cnx, null, null, null, log, true, !required) {
ScaleId = id; ScaleId = id;
Model = model; Model = model;
Connection = cnx;
IsReady = true; IsReady = true;
HasFillingClearance = false; HasFillingClearance = false;
Stream.WriteTimeout = -1; Stream.WriteTimeout = -1;
@@ -50,19 +52,49 @@ namespace Elwig.Helpers.Weighing {
var data = await Receive(); var data = await Receive();
if (data != null) if (data != null)
RaiseWeighingEvent(new WeighingEventArgs(data.Value)); RaiseWeighingEvent(new WeighingEventArgs(data.Value));
} catch (ThreadInterruptedException) {
// ignore
} catch (IOException) {
await Task.Delay(500);
await Reconnect();
} catch (TimeoutException) {
await Task.Delay(500);
await Reconnect();
} catch (Exception ex) { } catch (Exception ex) {
MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message}", "Waagenfehler", MessageBox.Show($"Beim Wiegen ist ein Fehler Aufgetreten:\n\n{ex.Message} ({ex.GetType().Name})", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
} }
} }
protected async Task Reconnect() {
try { Reader.Close(); } catch { }
try { Stream.Close(); } catch { }
try { Serial?.Close(); } catch { }
while (IsRunning) {
try {
if (Connection.StartsWith("serial:")) {
Serial = Utils.OpenSerialConnection(Connection);
Stream = Serial.BaseStream;
} else if (Connection.StartsWith("tcp:")) {
Tcp = Utils.OpenTcpConnection(Connection);
Stream = Tcp.GetStream();
}
Reader = new(Stream, Encoding.ASCII, false, 512);
break;
} catch {
// ignore
}
await Task.Delay(1000);
}
}
protected async Task<WeighingResult?> Receive() { protected async Task<WeighingResult?> Receive() {
var line = ""; var line = "";
while (line.Length < 33) { while (line.Length < 33) {
var ch = Reader.Read(); var ch = Reader.Read();
if (ch == -1) { if (ch == -1) {
return null; throw new IOException("Connection closed");
} else if (line.Length > 0 || ch == ' ') { } else if (line.Length > 0 || ch == ' ') {
line += char.ToString((char)ch); line += char.ToString((char)ch);
} }
@@ -71,7 +103,7 @@ namespace Elwig.Helpers.Weighing {
if (line == null || line == "") { if (line == null || line == "") {
return null; return null;
} else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') { } else if (line.Length != 33 || line[0] != ' ' || line[9] != ' ' || line[15] != ' ' || line[20] != ' ' || line[32] != ' ') {
throw new IOException($"Invalid event from scale: '{line}'"); throw new FormatException($"Invalid event from scale: '{line}'");
} }
var date = line[ 1.. 9]; var date = line[ 1.. 9];
@@ -81,7 +113,7 @@ namespace Elwig.Helpers.Weighing {
var unit = line[30..32]; var unit = line[30..32];
if (unit != "kg") { if (unit != "kg") {
throw new IOException($"Unsupported unit in weighing event: '{unit}'"); throw new WeighingException($"Unsupported unit in weighing event: '{unit}'");
} }
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null; identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;

View File

@@ -36,7 +36,7 @@ namespace Elwig.Helpers.Weighing {
var line = await Reader.ReadUntilAsync('\x03'); var line = await Reader.ReadUntilAsync('\x03');
if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n"); if (LogPath != null) await File.AppendAllTextAsync(LogPath, $"{line}\r\n");
if (line == null || line.Length < 4 || !line.StartsWith('\x02')) { if (line == null || line.Length < 4 || !line.StartsWith('\x02')) {
throw new IOException("Invalid response from scale"); throw new FormatException("Invalid response from scale");
} }
var status = line[1..3]; var status = line[1..3];
@@ -45,9 +45,9 @@ namespace Elwig.Helpers.Weighing {
switch (status[1]) { switch (status[1]) {
case 'M': msg = "Waage in Bewegung"; break; case 'M': msg = "Waage in Bewegung"; break;
} }
throw new IOException($"Waagenfehler {status}: {msg}"); throw new WeighingException($"Waagenfehler {status}: {msg}");
} else if (status[0] != ' ') { } else if (status[0] != ' ') {
throw new IOException($"Invalid response from scale (error code {status})"); throw new WeighingException($"Invalid response from scale (error code {status})");
} }
return line[1..^1]; return line[1..^1];
@@ -57,7 +57,7 @@ namespace Elwig.Helpers.Weighing {
await SendCommand(incIdentNr ? '\x05' : '?'); await SendCommand(incIdentNr ? '\x05' : '?');
string record = await ReceiveResponse(); string record = await ReceiveResponse();
if (record.Length != 45) if (record.Length != 45)
throw new IOException("Invalid response from scale: Received record has invalid size"); throw new FormatException("Invalid response from scale: Received record has invalid size");
var line = record[2..]; var line = record[2..];
var brutto = line[ 0.. 7].Trim(); var brutto = line[ 0.. 7].Trim();

View File

@@ -1,8 +1,9 @@
using System.IO.Ports;
using System.IO;
using System.Net.Sockets;
using System; using System;
using System.IO;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text; using System.Text;
using System.Windows;
namespace Elwig.Helpers.Weighing { namespace Elwig.Helpers.Weighing {
public abstract class Scale : IDisposable { public abstract class Scale : IDisposable {
@@ -27,7 +28,7 @@ namespace Elwig.Helpers.Weighing {
if (config.Type == "SysTec-IT") { if (config.Type == "SysTec-IT") {
return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); return new SysTecITScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else if (config.Type == "Avery-Async") { } else if (config.Type == "Avery-Async") {
return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); return new AveryEventScale(config.Id, config.Model!, config.Connection!, config.Log, config.Required);
} else if (config.Type == "Gassner") { } else if (config.Type == "Gassner") {
return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log); return new GassnerScale(config.Id, config.Model!, config.Connection!, config.Empty, config.Filling, limit, config.Log);
} else { } else {
@@ -35,10 +36,17 @@ namespace Elwig.Helpers.Weighing {
} }
} }
protected Scale(string cnx, string? empty, string? filling, int? limit, string? log) { protected Scale(string cnx, string? empty, string? filling, int? limit, string? log, bool softFail = false, bool failSilent = false) {
if (cnx.StartsWith("serial:")) { if (cnx.StartsWith("serial:")) {
Serial = Utils.OpenSerialConnection(cnx); try {
Stream = Serial.BaseStream; Serial = Utils.OpenSerialConnection(cnx);
} catch (Exception e) {
if (!softFail) throw;
if (!failSilent)
MessageBox.Show($"Verbindung zu Waage konnte nicht hergestellt werden:\n\n{e.Message}", "Waagenfehler",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
Stream = Serial?.BaseStream ?? Stream.Null;
} else if (cnx.StartsWith("tcp:")) { } else if (cnx.StartsWith("tcp:")) {
Tcp = Utils.OpenTcpConnection(cnx); Tcp = Utils.OpenTcpConnection(cnx);
Stream = Tcp.GetStream(); Stream = Tcp.GetStream();

View File

@@ -34,14 +34,14 @@ namespace Elwig.Helpers.Weighing {
var line = await Reader.ReadUntilAsync("\r\n"); var line = await Reader.ReadUntilAsync("\r\n");
if (LogPath != null) await File.AppendAllTextAsync(LogPath, line); if (LogPath != null) await File.AppendAllTextAsync(LogPath, line);
if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) { if (line == null || line.Length < 4 || !line.StartsWith('<') || !line.EndsWith(">\r\n")) {
throw new IOException("Invalid response from scale"); throw new FormatException("Invalid response from scale");
} }
var error = line[1..3]; var error = line[1..3];
string msg = $"Unbekannter Fehler (Fehler code {error})"; string msg = $"Unbekannter Fehler (Fehler code {error})";
if (error[0] == '0') { if (error[0] == '0') {
if (error[1] != '0') { if (error[1] != '0') {
throw new IOException($"Invalid response from scale (error code {error})"); throw new WeighingException($"Invalid response from scale (error code {error})");
} }
} else if (error[0] == '1') { } else if (error[0] == '1') {
switch (error[1]) { switch (error[1]) {
@@ -52,21 +52,21 @@ namespace Elwig.Helpers.Weighing {
case '6': msg = "Drucker nicht bereit"; break; case '6': msg = "Drucker nicht bereit"; break;
case '7': msg = "Druckmuster enthält ungültiges Kommando"; break; case '7': msg = "Druckmuster enthält ungültiges Kommando"; break;
} }
throw new IOException($"Waagenfehler {error}: {msg}"); throw new WeighingException($"Waagenfehler {error}: {msg}");
} else if (error[0] == '2') { } else if (error[0] == '2') {
switch (error[1]) { switch (error[1]) {
case '0': msg = "Brutto negativ"; break; case '0': msg = "Brutto negativ"; break;
} }
throw new IOException($"Fehler {error}: {msg}"); throw new WeighingException($"Fehler {error}: {msg}");
} else if (error[0] == '3') { } else if (error[0] == '3') {
switch (error[1]) { switch (error[1]) {
case '1': msg = "Übertragunsfehler"; break; case '1': msg = "Übertragunsfehler"; break;
case '2': msg = "Ungültiger Befehl"; break; case '2': msg = "Ungültiger Befehl"; break;
case '3': msg = "Ungültiger Parameter"; break; case '3': msg = "Ungültiger Parameter"; break;
} }
throw new IOException($"Kommunikationsfehler {error}: {msg}"); throw new WeighingException($"Kommunikationsfehler {error}: {msg}");
} else { } else {
throw new IOException($"Invalid response from scale (error code {error})"); throw new WeighingException($"Invalid response from scale (error code {error})");
} }
return line[1..^3]; return line[1..^3];
@@ -76,7 +76,7 @@ namespace Elwig.Helpers.Weighing {
await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}"); await SendCommand(incIdentNr ? $"RN{InternalScaleNr}" : $"RM{InternalScaleNr}");
string record = await ReceiveResponse(); string record = await ReceiveResponse();
if (record.Length != 62) if (record.Length != 62)
throw new IOException("Invalid response from scale: Received record has invalid size"); throw new FormatException("Invalid response from scale: Received record has invalid size");
var line = record[2..]; var line = record[2..];
var status = line[ 0.. 2]; var status = line[ 0.. 2];
@@ -94,9 +94,9 @@ namespace Elwig.Helpers.Weighing {
var crc16 = line[52..60].Trim(); var crc16 = line[52..60].Trim();
if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) { if (Utils.CalcCrc16Modbus(record[..54]) != ushort.Parse(crc16)) {
throw new IOException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})"); throw new WeighingException($"Invalid response from scale: Invalid CRC16 checksum ({crc16} != {Utils.CalcCrc16Modbus(record[..54])})");
} else if (unit != "kg") { } else if (unit != "kg") {
throw new IOException($"Unsupported unit in weighing response: '{unit}'"); throw new WeighingException($"Unsupported unit in weighing response: '{unit}'");
} }
identNr = identNr.Length > 0 && identNr != "0" ? identNr : null; identNr = identNr.Length > 0 && identNr != "0" ? identNr : null;

View File

@@ -0,0 +1,6 @@
using System;
namespace Elwig.Helpers.Weighing {
public class WeighingException(string? message = null) : Exception(message) {
}
}

View File

@@ -15,10 +15,8 @@ using LinqKit;
using System.Globalization; using System.Globalization;
using System.Linq.Expressions; using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.IO;
using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking;
using System.Windows.Controls; using System.Windows.Controls;
using System.Net.Http;
namespace Elwig.Services { namespace Elwig.Services {
public static class DeliveryService { public static class DeliveryService {
@@ -220,6 +218,22 @@ namespace Elwig.Services {
prd = prd.And(p => p.Unloading != DeliveryPart.Box); prd = prd.And(p => p.Unloading != DeliveryPart.Box);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add("keine Kisten"); filterNames.Add("keine Kisten");
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => (p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime));
filter.RemoveAt(i--);
filterNames.Add("geändert seit letztem Export");
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => !((p.Delivery.XTime == null || p.Delivery.MTime > p.Delivery.XTime) && (p.Delivery.ITime == null || p.Delivery.MTime > p.Delivery.ITime)));
filter.RemoveAt(i--);
filterNames.Add("unverändert seit letztem Export");
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.ITime != null && p.Delivery.MTime > p.Delivery.ITime);
filter.RemoveAt(i--);
filterNames.Add("geändert seit letztem Import");
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
prd = prd.And(p => p.Delivery.MTime <= p.Delivery.ITime);
filter.RemoveAt(i--);
filterNames.Add("unverändert seit letztem Import");
} else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) { } else if (e.Length == 2 && var.ContainsKey(e.ToUpper())) {
filterVar.Add(e.ToUpper()); filterVar.Add(e.ToUpper());
filter.RemoveAt(i--); filter.RemoveAt(i--);
@@ -799,40 +813,7 @@ namespace Elwig.Services {
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) { } else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
try { await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
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)
.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 {
await ElwigData.Export(path, list, wbKgs, filterNames);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
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);
}
}); });
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} else { } else {

View File

@@ -9,9 +9,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@@ -315,6 +313,22 @@ namespace Elwig.Services {
memberQuery = memberQuery.Where(m => !m.ContactViaPost); memberQuery = memberQuery.Where(m => !m.ContactViaPost);
filter.RemoveAt(i--); filter.RemoveAt(i--);
filterNames.Add("nicht Kontaktart Post"); filterNames.Add("nicht Kontaktart Post");
} else if ("upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(p => (p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime));
filter.RemoveAt(i--);
filterNames.Add("geändert seit letztem Export");
} else if ("!upload".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(p => !((p.XTime == null || p.MTime > p.XTime) && (p.ITime == null || p.MTime > p.ITime)));
filter.RemoveAt(i--);
filterNames.Add("unverändert seit letztem Export");
} else if (">import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(p => p.MTime > p.ITime);
filter.RemoveAt(i--);
filterNames.Add("geändert seit letztem Import");
} else if ("<import".StartsWith(e, StringComparison.CurrentCultureIgnoreCase)) {
memberQuery = memberQuery.Where(p => p.MTime <= p.ITime);
filter.RemoveAt(i--);
filterNames.Add("unverändert seit letztem Import");
} else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) { } else if (e.All(char.IsAsciiDigit) && mgnr.ContainsKey(e)) {
filterMgNr.Add(int.Parse(e)); filterMgNr.Add(int.Parse(e));
filter.RemoveAt(i--); filter.RemoveAt(i--);
@@ -562,46 +576,7 @@ namespace Elwig.Services {
} else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) { } else if (mode == ExportMode.Upload && App.Config.SyncUrl != null) {
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
try { await SyncService.Upload(App.Config.SyncUrl, App.Config.SyncUrl, App.Config.SyncPassword, query, filterNames);
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 {
await ElwigData.Export(path, members, areaComs, wbKgs, filterNames);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
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);
}
}); });
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} else { } else {

View File

@@ -0,0 +1,277 @@
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<Func<Member, bool>> ChangedMembers = (m) => ((m.XTime == null && m.MTime > 1751328000) || m.MTime > m.XTime) && (m.ITime == null || m.MTime > m.ITime);
public static readonly Expression<Func<Delivery, bool>> 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<Member> query, IEnumerable<string> 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<DeliveryPart> query, IEnumerable<string> 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<Member> members;
List<AreaCom> areaComs;
List<Delivery> 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<List<(string Name, string Url)>> 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<string>(),
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(),
Url = f!["url"]!.AsValue().GetValue<string>(),
Size = f!["size"]!.AsValue().GetValue<long>(),
})
.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<string>();
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> member, IEnumerable<Delivery> 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<bool> 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);
}
}
}

View File

@@ -226,6 +226,7 @@
<Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/> <Bold>Uhrzeit</Bold>: z.B. 06:00-08:00, 18:00-, ...<LineBreak/>
<Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/> <Bold>Handwiegung</Bold>: handw[iegung], !Handw[iegung] (alle ohne Handwiegung)<LineBreak/>
<Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/> <Bold>Handlese</Bold>: Handl[ese], !handl[ese] (alle ohne Handlese)<LineBreak/>
<Bold>Anlieferung</Bold>: Plane[nwagen]/Kipp[er], !plane[nwagen]/!kipp[er], Lesew[agen], !lesew[agen], kiste[n], !kiste[n]<LineBreak/>
<Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/> <Bold>Gebunden</Bold>: geb[unden], ungeb[unden], !geb[unden], !ungeb[unden]<LineBreak/>
<Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/> <Bold>Gerebelt</Bold>: gerebelt, !Gerebelt (nicht gerebelt gewogen)<LineBreak/>
<Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw") <Bold>Freitext</Bold>: z.B. Lieferscheinnummern, Anmerkung, "quw" (sucht nach dem Text "quw")

View File

@@ -135,7 +135,7 @@ namespace Elwig.Windows {
NewDeliveryButton_Click(null, null); NewDeliveryButton_Click(null, null);
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
if (ctx.Seasons.Find(Utils.CurrentYear) == null) { if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.", MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n(Stammdaten -> Saisons -> Neu anlegen...)",
"Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning); "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
} }
} }

View File

@@ -64,6 +64,23 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Synchronisieren" x:Name="Menu_Sync" IsEnabled="false">
<MenuItem x:Name="Menu_Sync_Download" Header="Mitgliederdaten und Lieferungen herunterladen" Click="Menu_Sync_Download_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE896;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Sync_UploadBranchDeliveries" Header="Lieferungen dieser Saison/Zweigstelle hochladen" Click="Menu_Sync_UploadBranchDeliveries_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE898;"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="Menu_Sync_UploadModified" Header="Geänderte Mitglieder und Lieferungen hochladen" Click="Menu_Sync_UploadModified_Click">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xECC5;"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="Waage"> <MenuItem Header="Waage">
<MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click"> <MenuItem Header="Datum und Uhrzeit setzen" Click="Menu_Scale_SetDateTime_Click">
<MenuItem.Icon> <MenuItem.Icon>
@@ -174,16 +191,18 @@
</Grid> </Grid>
</Button> </Button>
<Button x:Name="DownloadButton" Click="DownloadButton_Click" <Button x:Name="SyncButton" Click="SyncButton_Click"
Margin="310,135,0,0" Padding="0.375,0.5,0,0" Height="30" Width="30"
Content="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center"
ToolTip="Lieferungen und Mitgliederdaten anderer Zweigstellen herunterladen"/>
<Button x:Name="UploadButton" Click="UploadButton_Click"
Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30" Margin="375,135,0,0" Padding="1.0,0.5,0,0" Height="30" Width="30"
Content="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="16" FontFamily="Segoe MDL2 Assets" FontSize="16"
HorizontalContentAlignment="Center" HorizontalContentAlignment="Center"
ToolTip="Lieferungen dieser Zweigstelle hochladen"/> ToolTip="Geänderte Mitgliederdaten und Lieferungen synchronisieren">
<Button.Content>
<Grid TextElement.FontFamily="Segoe MDL2 Assets">
<TextBlock x:Name="SyncButton_1" Text="&#xE895;"/>
<TextBlock x:Name="SyncButton_2" Text="" Foreground="DarkOrange"/>
</Grid>
</Button.Content>
</Button>
<Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True" <Expander x:Name="SeasonFinish" Header="Leseabschluss" SnapsToDevicePixels="True"
Expanded="SeasonFinish_Expanded" Collapsed="SeasonFinish_Collapsed" Expanded="SeasonFinish_Expanded" Collapsed="SeasonFinish_Collapsed"

View File

@@ -2,26 +2,30 @@ using Elwig.Helpers;
using Elwig.Helpers.Billing; using Elwig.Helpers.Billing;
using Elwig.Helpers.Export; using Elwig.Helpers.Export;
using Elwig.Models.Dtos; using Elwig.Models.Dtos;
using Elwig.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Win32; using Microsoft.Win32;
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.NetworkInformation;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
namespace Elwig.Windows { namespace Elwig.Windows {
public partial class MainWindow : ContextWindow { public partial class MainWindow : ContextWindow {
private readonly DispatcherTimer _syncTimer = new() { Interval = TimeSpan.FromHours(1) };
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
var v = Assembly.GetExecutingAssembly().GetName().Version; var v = Assembly.GetExecutingAssembly().GetName().Version;
@@ -29,14 +33,21 @@ namespace Elwig.Windows {
if (App.Client.Client == null) VersionField.Text += " (Unbekannt)"; if (App.Client.Client == null) VersionField.Text += " (Unbekannt)";
Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null; Menu_Help_Update.IsEnabled = App.Config.UpdateUrl != null;
Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null; Menu_Help_Smtp.IsEnabled = App.Config.Smtp != null;
DownloadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; Menu_Sync.IsEnabled = App.Config.SyncUrl != null;
UploadButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden; SyncButton.Visibility = App.Config.SyncUrl != null ? Visibility.Visible : Visibility.Hidden;
Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null; Menu_Database_Upload.IsEnabled = App.Config.SyncUrl != null;
Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null; Menu_Database_Download.IsEnabled = App.Config.SyncUrl != null;
} }
private void Window_Loaded(object sender, RoutedEventArgs evt) { private void Window_Loaded(object sender, RoutedEventArgs evt) {
SeasonInput.Value = Utils.CurrentLastSeason; SeasonInput.Value = Utils.CurrentLastSeason;
if (Utils.HasInternetConnectivity()) {
CheckSync(200);
}
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
_syncTimer.Tick += new EventHandler(OnSyncTimer);
_syncTimer.Start();
} }
private void Window_Closing(object sender, CancelEventArgs evt) { private void Window_Closing(object sender, CancelEventArgs evt) {
@@ -195,92 +206,43 @@ namespace Elwig.Windows {
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private async void DownloadButton_Click(object sender, RoutedEventArgs evt) { private async void SyncButton_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null) if (App.Config.SyncUrl == null)
return; return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
try { await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
var data = await Utils.GetExportMetaData(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword); await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
var files = data
.Select(f => new {
Name = f!["name"]!.AsValue().GetValue<string>(),
Timestamp = f!["timestamp"] != null && DateTime.TryParseExact(f!["timestamp"]!.AsValue().GetValue<string>(), "yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) ? dt : (DateTime?)null,
ZwstId = f!["meta"]?["zwstid"]?.AsValue().GetValue<string>() ?? f!["zwstid"]?.AsValue().GetValue<string>(),
Device = f!["meta"]?["device"]?.AsValue().GetValue<string>(),
Url = f!["url"]!.AsValue().GetValue<string>(),
Size = f!["size"]!.AsValue().GetValue<long>(),
})
.Where(f => f.Timestamp >= new DateTime(Utils.CurrentLastSeason, 7, 1))
.ToList();
var imported = await ElwigData.GetImportedFiles();
var import = files
.Where(f => f.Device != Environment.MachineName && !imported.Contains(f.Name))
.ToList();
var paths = new List<string>();
using (var client = Utils.GetHttpClient(App.Config.SyncUsername, App.Config.SyncPassword)) {
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);
}
}); });
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
private async void UploadButton_Click(object sender, RoutedEventArgs evt) { private async void Menu_Sync_Download_Click(object sender, RoutedEventArgs evt) {
if (App.Config.SyncUrl == null) if (App.Config.SyncUrl == null)
return; return;
Mouse.OverrideCursor = Cursors.Wait; Mouse.OverrideCursor = Cursors.Wait;
await Task.Run(async () => { await Task.Run(async () => {
try { await SyncService.Download(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
var path = Path.Combine(App.TempPath, $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{App.ZwstId}.elwig.zip"); });
using var ctx = new AppDbContext(); Mouse.OverrideCursor = null;
var deliveries = await ctx.Deliveries }
.Where(d => d.Year == Utils.CurrentLastSeason && d.ZwstId == App.ZwstId)
.Include(d => d.Parts) private async void Menu_Sync_UploadBranchDeliveries_Click(object sender, RoutedEventArgs evt) {
.ThenInclude(p => p.PartModifiers) if (App.Config.SyncUrl == null)
.Include(d => d.Parts) return;
.ThenInclude(p => p.Kg) Mouse.OverrideCursor = Cursors.Wait;
.ThenInclude(k => k!.Gl) await Task.Run(async () => {
.OrderBy(d => d.DateString) await SyncService.UploadBranchDeliveries(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
.ThenBy(d => d.TimeString) });
.ThenBy(d => d.LsNr) Mouse.OverrideCursor = null;
.AsSplitQuery() }
.ToListAsync();
var wbKgs = deliveries private async void Menu_Sync_UploadModified_Click(object sender, RoutedEventArgs evt) {
.SelectMany(d => d.Parts) if (App.Config.SyncUrl == null)
.Where(p => p.Kg != null) return;
.Select(p => p.Kg!) Mouse.OverrideCursor = Cursors.Wait;
.DistinctBy(k => k.KgNr) await Task.Run(async () => {
.ToList(); await SyncService.UploadModified(App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
if (deliveries.Count == 0) {
MessageBox.Show("Es gibt keine Lieferungen, die hochgeladen werden können!", "Lieferungen hochladen",
MessageBoxButton.OK, MessageBoxImage.Error);
} else {
await ElwigData.Export(path, deliveries, wbKgs, [$"{Utils.CurrentLastSeason}", $"Zweigstelle {App.BranchName}"]);
await Utils.UploadExportData(path, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
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);
}
}); });
Mouse.OverrideCursor = null; Mouse.OverrideCursor = null;
} }
@@ -394,9 +356,42 @@ namespace Elwig.Windows {
App.FocusMailWindow(); App.FocusMailWindow();
} }
protected override Task OnRenewContext(AppDbContext ctx) { protected async override Task OnRenewContext(AppDbContext ctx) {
SeasonInput_TextChanged(null, null); SeasonInput_TextChanged(null, null);
return Task.CompletedTask; CheckSync();
}
private void OnSyncTimer(object? sender, EventArgs? evt) {
if (Utils.HasInternetConnectivity()) {
CheckSync();
}
}
private void OnNetworkAvailabilityChanged(object? sender, NetworkAvailabilityEventArgs evt) {
if (!evt.IsAvailable) return;
if (Utils.HasInternetConnectivity()) {
CheckSync(1000);
}
}
private async void CheckSync(int delay = 0) {
if (App.Config.SyncUrl == null) return;
Utils.RunBackground("Daten Synchronisieren", async () => {
await Task.Delay(delay);
var ch = false;
using (var ctx = new AppDbContext()) {
ch = await SyncService.ChangesAvailable(ctx, App.Config.SyncUrl, App.Config.SyncUsername, App.Config.SyncPassword);
}
await App.MainDispatcher.BeginInvoke(() => {
if (ch) {
SyncButton_1.Text = "\uEA6A";
SyncButton_2.Text = "\uEA81";
} else {
SyncButton_1.Text = "\uE895";
SyncButton_2.Text = "";
}
});
});
} }
private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) { private void SeasonFinish_Expanded(object sender, RoutedEventArgs evt) {

View File

@@ -14,8 +14,8 @@ namespace Tests {
public async Task Setup_1_Database() { public async Task Setup_1_Database() {
AppDbContext.ConnectionStringOverride = $"Data Source=ElwigTestDB; Mode=Memory; Foreign Keys=True; Cache=Shared"; AppDbContext.ConnectionStringOverride = $"Data Source=ElwigTestDB; Mode=Memory; Foreign Keys=True; Cache=Shared";
Connection = await AppDbContext.ConnectAsync(); Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
} }
[OneTimeSetUp] [OneTimeSetUp]

View File

@@ -16,9 +16,9 @@ namespace Tests.E2ETests {
public static async Task SetupDatabase() { public static async Task SetupDatabase() {
if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath); if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath);
using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default; Pooling=False"); using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default; Pooling=False");
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql"); await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql"); await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql"); await cnx.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql");
} }
[OneTimeTearDown] [OneTimeTearDown]

View File

@@ -12,13 +12,13 @@ namespace Tests.UnitTests.DocumentTests {
[OneTimeSetUp] [OneTimeSetUp]
public async Task SetupDatabase() { public async Task SetupDatabase() {
Connection = await AppDbContext.ConnectAsync(); Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentInsert.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentInsert.sql");
} }
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownDatabase() { public async Task TeardownDatabase() {
if (Connection == null) return; if (Connection == null) return;
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentDelete.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.DocumentDelete.sql");
await Connection.DisposeAsync(); await Connection.DisposeAsync();
Connection = null; Connection = null;
} }

View File

@@ -24,13 +24,13 @@ namespace Tests.UnitTests.HelperTests {
[OneTimeSetUp] [OneTimeSetUp]
public async Task SetupDatabase() { public async Task SetupDatabase() {
Connection = await AppDbContext.ConnectAsync(); Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingInsert.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingInsert.sql");
} }
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownDatabase() { public async Task TeardownDatabase() {
if (Connection == null) return; if (Connection == null) return;
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingDelete.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.BillingDelete.sql");
await Connection.DisposeAsync(); await Connection.DisposeAsync();
Connection = null; Connection = null;
} }
@@ -72,7 +72,7 @@ namespace Tests.UnitTests.HelperTests {
[TearDown] [TearDown]
public async Task CleanupDatabasePayment() { public async Task CleanupDatabasePayment() {
if (Connection == null) return; if (Connection == null) return;
await AppDbContext.ExecuteBatch(Connection, """ await Connection.ExecuteBatch("""
DELETE FROM credit; DELETE FROM credit;
DELETE FROM payment_variant; DELETE FROM payment_variant;
DELETE FROM delivery_part_bucket; DELETE FROM delivery_part_bucket;
@@ -115,7 +115,7 @@ namespace Tests.UnitTests.HelperTests {
} }
private Task InsertPaymentVariant(int year, int avnr, string data) { private Task InsertPaymentVariant(int year, int avnr, string data) {
return AppDbContext.ExecuteBatch(Connection!, $""" return Connection!.ExecuteBatch($"""
INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, data) INSERT INTO payment_variant (year, avnr, name, date, transfer_date, test_variant, calc_time, data)
VALUES ({year}, {avnr}, 'Test', '2021-01-15', NULL, TRUE, NULL, '{data}'); VALUES ({year}, {avnr}, 'Test', '2021-01-15', NULL, TRUE, NULL, '{data}');
"""); """);

View File

@@ -11,13 +11,13 @@ namespace Tests.UnitTests.ServiceTests {
[OneTimeSetUp] [OneTimeSetUp]
public async Task SetupDatabase() { public async Task SetupDatabase() {
Connection = await AppDbContext.ConnectAsync(); Connection = await AppDbContext.ConnectAsync();
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceInsert.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceInsert.sql");
} }
[OneTimeTearDown] [OneTimeTearDown]
public async Task TeardownDatabase() { public async Task TeardownDatabase() {
if (Connection == null) return; if (Connection == null) return;
await AppDbContext.ExecuteEmbeddedScript(Connection, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceDelete.sql"); await Connection.ExecuteEmbeddedScript(Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.ServiceDelete.sql");
await Connection.DisposeAsync(); await Connection.DisposeAsync();
Connection = null; Connection = null;
} }

View File

@@ -71,7 +71,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_03_Moving() { public void Test_03_Moving() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "moving"; Mock.Error = "moving";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung")); Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
} }
@@ -79,7 +79,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_04_Overloaded() { public void Test_04_Overloaded() {
Mock.Weight = 10_000; Mock.Weight = 10_000;
Mock.Error = "overloaded"; Mock.Error = "overloaded";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Überlast")); Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
} }
@@ -87,14 +87,14 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_05_InvalidResponse() { public void Test_05_InvalidResponse() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "invalid"; Mock.Error = "invalid";
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
} }
[Test] [Test]
public void Test_06_InvalidCrc() { public void Test_06_InvalidCrc() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "crc"; Mock.Error = "crc";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum")); Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
} }
@@ -102,7 +102,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_07_InvalidUnit() { public void Test_07_InvalidUnit() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "unit"; Mock.Error = "unit";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
} }
} }
} }

View File

@@ -100,7 +100,7 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Tare = 41; Mock.Tare = 41;
Mock.Error = "moving"; Mock.Error = "moving";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung")); Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
} }
@@ -109,7 +109,7 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Tare = 41; Mock.Tare = 41;
Mock.Error = "invalid"; Mock.Error = "invalid";
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
} }
} }
} }

View File

@@ -71,7 +71,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_03_Moving() { public void Test_03_Moving() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "moving"; Mock.Error = "moving";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung")); Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
} }
@@ -79,7 +79,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_04_Overloaded() { public void Test_04_Overloaded() {
Mock.Weight = 10_000; Mock.Weight = 10_000;
Mock.Error = "overloaded"; Mock.Error = "overloaded";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Überlast")); Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
} }
@@ -87,14 +87,14 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_05_InvalidResponse() { public void Test_05_InvalidResponse() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "invalid"; Mock.Error = "invalid";
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
} }
[Test] [Test]
public void Test_06_InvalidCrc() { public void Test_06_InvalidCrc() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "crc"; Mock.Error = "crc";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum")); Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
} }
@@ -102,7 +102,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_07_InvalidUnit() { public void Test_07_InvalidUnit() {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Error = "unit"; Mock.Error = "unit";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
} }
} }
} }

View File

@@ -100,7 +100,7 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Tare = 41; Mock.Tare = 41;
Mock.Error = "moving"; Mock.Error = "moving";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await Scale!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung")); Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
} }
@@ -109,7 +109,7 @@ namespace Tests.UnitTests.WeighingTests {
Mock.Weight = 1_000; Mock.Weight = 1_000;
Mock.Tare = 41; Mock.Tare = 41;
Mock.Error = "invalid"; Mock.Error = "invalid";
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh()); Assert.ThrowsAsync<FormatException>(async () => await Scale!.Weigh());
} }
} }
} }

View File

@@ -91,7 +91,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_03_Moving() { public void Test_03_Moving() {
MockA.Weight = 1_000; MockA.Weight = 1_000;
MockA.Error = "moving"; MockA.Error = "moving";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung")); Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
} }
@@ -99,7 +99,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_04_Overloaded() { public void Test_04_Overloaded() {
MockA.Weight = 10_000; MockA.Weight = 10_000;
MockA.Error = "overloaded"; MockA.Error = "overloaded";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
Assert.That(ex.Message, Contains.Substring("Waage in Überlast")); Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
} }
@@ -107,14 +107,14 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_05_InvalidResponse() { public void Test_05_InvalidResponse() {
MockA.Weight = 1_000; MockA.Weight = 1_000;
MockA.Error = "invalid"; MockA.Error = "invalid";
Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh()); Assert.ThrowsAsync<FormatException>(async () => await ScaleA!.Weigh());
} }
[Test] [Test]
public void Test_06_InvalidCrc() { public void Test_06_InvalidCrc() {
MockA.Weight = 1_000; MockA.Weight = 1_000;
MockA.Error = "crc"; MockA.Error = "crc";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum")); Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
} }
@@ -122,7 +122,7 @@ namespace Tests.UnitTests.WeighingTests {
public void Test_07_InvalidUnit() { public void Test_07_InvalidUnit() {
MockA.Weight = 1_000; MockA.Weight = 1_000;
MockA.Error = "unit"; MockA.Error = "unit";
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh()); var ex = Assert.ThrowsAsync<WeighingException>(async () => await ScaleA!.Weigh());
} }
} }
} }