Compare commits

...
6 Commits
Author SHA1 Message Date
lorenz.stechauner 59891d084f [WIP] certs
Test / Run tests (push) Successful in 1m52s
2026-09-16 23:02:54 +02:00
lorenz.stechauner 9c3d02a037 Entities/Member: Add OrganicAuthorityCode 2026-09-16 23:02:54 +02:00
lorenz.stechauner 8d1ef87590 DeliveryNote: Remove right padding for weight cells
Test / Run tests (push) Successful in 2m29s
2026-09-16 23:02:41 +02:00
lorenz.stechauner 7fef895491 DeliveryService: Add red/white distinction to tool tips
Test / Run tests (push) Successful in 1m53s
2026-09-16 17:28:38 +02:00
lorenz.stechauner 0e9121c462 TareCorrectionDialog: Allow actual tare to be null 2026-09-16 17:23:12 +02:00
lorenz.stechauner 15e191d74e Export/Database: Do not delete import source files 2026-09-16 17:12:52 +02:00
21 changed files with 427 additions and 118 deletions

No files matched your search

+6 -4
View File
@@ -8,10 +8,11 @@ namespace Elwig.Dialogs {
public int ActualTare { get; private set; }
public int TargetTare { get; private set; }
public TareCorrectionDialog(int actualTare, int? targetTare = null) {
public TareCorrectionDialog(int? actualTare, int? targetTare = null) {
InitializeComponent();
ActualTare = actualTare;
TargetTare = targetTare ?? actualTare;
ActualTare = actualTare ?? 0;
TargetTare = targetTare ?? actualTare ?? 0;
ActualTareInput.IsReadOnly = actualTare != null;
ActualTareInput.Text = ActualTare.ToString();
TargetTareInput.Text = TargetTare.ToString();
TargetTareInput.SelectAll();
@@ -19,12 +20,13 @@ namespace Elwig.Dialogs {
private void ConfirmButton_Click(object sender, RoutedEventArgs evt) {
DialogResult = true;
ActualTare = int.Parse(ActualTareInput.Text);
TargetTare = int.Parse(TargetTareInput.Text);
Close();
}
private void UpdateButtons() {
ConfirmButton.IsEnabled = TargetTareInput.Text.Length > 0;
ConfirmButton.IsEnabled = ActualTareInput.Text.Length > 0 && TargetTareInput.Text.Length > 0;
}
private void TareInput_TextChanged(object sender, TextChangedEventArgs evt) {
+1 -1
View File
@@ -68,7 +68,7 @@ namespace Elwig.Documents {
.SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness))
.AddCell(NewAsideCell("Mitglied", 2))
.AddCell(NewAsideCell("Mitglieds-Nr.:", isName: true)).AddCell(NewAsideCell($"{Member.MgNr}"))
.AddCell(NewAsideCell("Betriebs-Nr.:", isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? ""))
.AddCell(NewAsideCell("Betriebs-Nr.:", isName: true)).AddCell(NewAsideCell((Member.LfbisNr ?? "-") + (Member.OrganicAuthorityCode != null ? $" ({Member.OrganicAuthorityCode})" : "")))
.AddCell(NewAsideCell("UID:", isName: true)).AddCell(NewAsideCell(uid));
}
+6 -6
View File
@@ -130,13 +130,13 @@ namespace Elwig.Documents {
.AddHeaderCell(NewTh("Attribut", colspan: 2, rowspan: 2, left: true))
.AddHeaderCell(NewTh("Qualitätsstufe", rowspan: 2, left: true))
.AddHeaderCell(NewTh("Gradation", colspan: 2))
.AddHeaderCell(NewTh("Menge"))
.AddHeaderCell(NewTh("Menge").SetPaddingRight(0))
.AddHeaderCell(NewTh("[°Oe]", 8))
.AddHeaderCell(NewTh("[°KMW]", 8))
.AddHeaderCell(NewTh("[kg]", 8));
.AddHeaderCell(NewTh("[kg]", 8).SetPaddingRight(0));
foreach (var part in Delivery.Parts.OrderBy(p => p.DPNr)) {
var sub = new Table(ColsMM(10, 21, 25, 19.5, 19.5, 30, 12.5, 12.5, 15), true)
var sub = new Table(ColsMM(10, 21, 25, 20, 20, 30, 12.5, 12.5, 14), true)
.SetWidth(UnitValue.CreatePercentValue(100)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetKeepTogether(true);
@@ -148,7 +148,7 @@ namespace Elwig.Documents {
.AddCell(NewDeliveryMainTd(part.Quality.Name))
.AddCell(NewDeliveryMainTd($"{part.Oe:N0}", center: true))
.AddCell(NewDeliveryMainTd($"{part.Kmw:N1}", center: true))
.AddCell(NewDeliveryMainTd($"{part.Weight:N0}", right: true));
.AddCell(NewDeliveryMainTd($"{part.Weight:N0}", right: true).SetPaddingRight(0));
if (part.Cultivation != null) {
var cult = new KernedParagraph(8);
@@ -238,10 +238,10 @@ namespace Elwig.Documents {
}
if (Delivery.Parts.Count > 1) {
tbl.AddCell(NewTd("Gesamt:", 12, bold: true, borderTop: true, colspan: 6).SetPaddingsMM(1, 1, 1, 1))
tbl.AddCell(NewTd("Gesamt:", 12, bold: true, borderTop: true, colspan: 6).SetPaddingsMM(1, 1, 1, 0))
.AddCell(NewTd($"{Delivery.Oe:N0}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1))
.AddCell(NewTd($"{Delivery.Kmw:N1}", 12, bold: true, center: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1))
.AddCell(NewTd($"{Delivery.Weight:N0}", 12, bold: true, right: true, borderTop: true).SetPaddingsMM(1, 1, 1, 1));
.AddCell(NewTd($"{Delivery.Weight:N0}", 12, bold: true, right: true, borderTop: true).SetPaddingsMM(1, 0, 1, 1));
}
return tbl;
+1 -1
View File
@@ -161,7 +161,7 @@ namespace Elwig.Documents {
.AddCell(NewDataTh("Buchführend:", colspan: 2)).AddCell(NewTd(new KernedParagraph(Member.IsBuchführend ? "Ja " : "Nein ", 10)
.Add(Normal($"({(Member.IsBuchführend ? season.VatNormal : season.VatFlatrate) * 100:N0}% USt.)", 8)), colspan: 2))
.AddCell(NewDataTh("(Katastralgemeinde mit dem größten Anteil an Weinbauflächen)", 8, colspan: 2))
.AddCell(NewDataTh("Bio:", colspan: 2)).AddCell(NewTd(Member.IsOrganic ? "Ja" : "Nein", colspan: 2))
.AddCell(NewDataTh("Bio:", colspan: 2)).AddCell(NewTd((Member.IsOrganic ? "Ja" : "Nein") + (Member.OrganicAuthorityCode != null ? $" ({Member.OrganicAuthorityCode})" : ""), colspan: 2))
.AddCell(NewDataHdr("Genossenschaft", colspan: 6))
.AddCell(NewDataTh("Status:")).AddCell(NewTd(new KernedParagraph(Member.IsActive ? "Aktiv " : "Nicht aktiv ", 10)
.Add(Normal("(" + (Member.ExitDate != null ? $"{Member.EntryDate:dd.MM.yyyy}\u2013{Member.ExitDate:dd.MM.yyyy}" : $"seit {Member.EntryDate:dd.MM.yyyy}") + ")", 8))))
+39 -4
View File
@@ -1,3 +1,4 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
@@ -9,7 +10,7 @@ namespace Elwig.Helpers {
public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat!
public static readonly int RequiredSchemaVersion = 41;
public static readonly int RequiredSchemaVersion = 42;
private static int VersionOffset = 0;
@@ -68,11 +69,11 @@ namespace Elwig.Helpers {
})
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)];
List<string> toExecute = [];
List<(int ToVersion, string File)> toExecute = [];
var vers = fromVersion;
while (vers < toVersion) {
var (_, to, name) = scripts.Last(s => s.From == vers);
toExecute.Add(name);
toExecute.Add((to, name));
vers = to;
}
if (toExecute.Count == 0)
@@ -87,8 +88,11 @@ namespace Elwig.Helpers {
await cnx.IntegrityCheck();
await cnx.ForeignKeyCheck();
foreach (var script in toExecute) {
foreach (var (to, script) in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script);
if (to == 42) {
await UpdateDbSchema_41_To_42(cnx);
}
}
await cnx.IntegrityCheck();
@@ -103,5 +107,36 @@ namespace Elwig.Helpers {
File.Delete(backup);
}
}
private static async Task UpdateDbSchema_41_To_42(SqliteConnection cnx) {
List<(int MgNr, int LfbisNr, string Name, string Address, int Plz, string Locality)> members = [];
using (var cmd = cnx.CreateCommand()) {
// TODO
cmd.CommandText = """
SELECT m.mgnr, m.lfbis_nr, '', '', 0, ''
FROM member m
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
WHERE organic
""";
using var reader = await cmd.ExecuteReaderAsync();
var header = await reader.GetColumnSchemaAsync();
while (await reader.ReadAsync()) {
members.Add((reader.GetInt32(0), reader.GetInt32(1), reader.GetString(2), reader.GetString(3), reader.GetInt32(4), reader.GetString(5)));
}
}
foreach (var m in members) {
string? oocId = null;
string? authCode = null;
// TODO fetch traces certs
if (oocId != null || authCode != null) {
oocId = oocId == null ? "NULL" : $"'{oocId}'";
authCode = authCode == null ? "NULL" : $"'{authCode}'";
await cnx.ExecuteBatch($"UPDATE member SET ooc_id = {oocId}, org_auth_code = {authCode} WHERE mgnr = {m.MgNr}");
}
}
}
}
}
+8 -4
View File
@@ -212,7 +212,7 @@ namespace Elwig.Helpers.Export {
}
}
public static async Task ImportSqlite(string filename, bool zipFile = false) {
public static async Task ImportSqlite(string filename, bool zipFile = false, bool keepSource = true) {
if (zipFile) {
var newName = Path.ChangeExtension(App.Config.DatabaseFile, ".new.sqlite3");
try {
@@ -221,7 +221,7 @@ namespace Elwig.Helpers.Export {
foreach (var entry in zip.Entries) {
if (entry.Name.EndsWith(".sqlite3")) {
entry.ExtractToFile(newName);
await ImportSqlite(newName);
await ImportSqlite(newName, keepSource: false);
return;
}
}
@@ -233,7 +233,11 @@ namespace Elwig.Helpers.Export {
var oldName = Path.ChangeExtension(App.Config.DatabaseFile, ".old.sqlite3");
File.Move(App.Config.DatabaseFile, oldName, true);
File.Move(filename, App.Config.DatabaseFile, false);
if (keepSource) {
File.Copy(filename, App.Config.DatabaseFile, false);
} else {
File.Move(filename, App.Config.DatabaseFile, false);
}
using var cnx = await AppDbContext.ConnectAsync();
await cnx.ExecuteBatch("VACUUM");
@@ -246,7 +250,7 @@ namespace Elwig.Helpers.Export {
using (var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{newName}\"; Mode=ReadWriteCreate; Foreign Keys=False; Cache=Default; Pooling=False")) {
await cnx.ExecuteBatch(await reader.ReadToEndAsync());
}
await ImportSqlite(newName);
await ImportSqlite(newName, keepSource: false);
} finally {
if (File.Exists(newName)) File.Delete(newName);
}
+4
View File
@@ -638,6 +638,8 @@ namespace Elwig.Helpers.Export {
["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr,
["ustid_nr"] = m.UstIdNr,
["ooc_id"] = m.OrganicOperatorId,
["org_auth_code"] = m.OrganicAuthorityCode,
["juridical_pers"] = m.IsJuridicalPerson,
["volllieferant"] = m.IsVollLieferant,
["buchführend"] = m.IsBuchführend,
@@ -710,6 +712,8 @@ namespace Elwig.Helpers.Export {
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
UstIdNr = json["ustid_nr"]?.AsValue().GetValue<string>(),
OrganicOperatorId = json["ooc_id"]?.AsValue().GetValue<string>(),
OrganicAuthorityCode = json["org_auth_code"]?.AsValue().GetValue<string>(),
IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false,
IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
+3 -1
View File
@@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.Helpers {
public static partial class Extensions {
@@ -192,13 +193,14 @@ namespace Elwig.Helpers {
}
}
public static void AddToolTipCell(this Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false) {
public static void AddToolTipCell(this Grid grid, string text, int row, int col, int colSpan = 1, bool bold = false, bool alignRight = false, bool alignCenter = false, Brush? color = null) {
var tb = new TextBlock() {
Text = text,
TextAlignment = alignRight ? TextAlignment.Right : alignCenter ? TextAlignment.Center : TextAlignment.Left,
Margin = new(0, 12 * row, 0, 0),
FontWeight = bold ? FontWeights.Bold : FontWeights.Normal,
};
if (color != null) tb.Foreground = color;
tb.SetValue(Grid.ColumnProperty, col);
tb.SetValue(Grid.ColumnSpanProperty, colSpan);
grid.Children.Add(tb);
+1 -1
View File
@@ -303,7 +303,7 @@ namespace Elwig.Helpers {
return d.ShowDialog() == true ? (d.Weight, d.Reason) : null;
}
public static int? ShowTareCorrectionDialog(int actualTare, int? targetTare = null) {
public static int? ShowTareCorrectionDialog(int? actualTare, int? targetTare = null) {
var d = new TareCorrectionDialog(actualTare, targetTare);
return d.ShowDialog() == true ? d.TargetTare - d.ActualTare : null;
}
+15 -7
View File
@@ -631,15 +631,18 @@ namespace Elwig.Helpers {
if (v < 2 && char.IsAsciiLetter(ch)) {
v++;
text += ch;
} else if ((v == 2 || v == 6) && ch == '-') {
v++;
} else if ((v == 2 || v == 5) && ch == '-' && text[^1] != '-') {
text += ch;
} else if (v >= 2 && char.IsLetterOrDigit(ch)) {
} else if (v >= 2 && v <= 4 && char.IsLetter(ch)) {
if (v == 2 && text[^1] != '-')
text += "-";
if (text.StartsWith("AT")) {
if (v == 3 && ch == 'B' || v == 4 && ch == 'I' || v == 5 && ch == 'O') {
if (v == 2 && ch == 'B' || v == 3 && ch == 'I' || v == 4 && ch == 'O') {
v++;
text += ch;
} else if (v > 6 && char.IsAsciiDigit(ch)) {
}
} else if (text.StartsWith("DE")) {
if (v == 2 && ch == 'Ö' || v == 3 && ch == 'K' || v == 4 && ch == 'O') {
v++;
text += ch;
}
@@ -647,11 +650,16 @@ namespace Elwig.Helpers {
v++;
text += ch;
}
} else if (v >= 5 && v <= 7 && char.IsAsciiDigit(ch)) {
if (v == 5 && text[^1] != '-')
text += "-";
v++;
text += ch;
}
if (i == input.CaretIndex - 1) {
pos = text.Length;
} else if (text.StartsWith("AT") && v >= 10) {
} else if ((text.StartsWith("AT") || text.StartsWith("DE")) && v >= 10) {
break;
}
}
@@ -662,7 +670,7 @@ namespace Elwig.Helpers {
if (text.Length == 0)
return required ? new(false, "Bio-Kontrollstellen-Code ist nicht optional") : new(true, null);
if (text.StartsWith("AT")) {
if (text.StartsWith("AT") || text.StartsWith("DE")) {
if (text.Length != 10) {
return new(false, "Bio-Kontrollstellen-Code ist ungültig");
}
+6
View File
@@ -96,6 +96,12 @@ namespace Elwig.Models.Entities {
[Column("ustid_nr")]
public string? UstIdNr { get; set; }
[Column("ooc_id")]
public string? OrganicOperatorId { get; set; }
[Column("org_auth_code")]
public string? OrganicAuthorityCode { get; set; }
[Column("juridical_pers")]
public bool IsJuridicalPerson { get; set; }
+4
View File
@@ -0,0 +1,4 @@
-- schema version 41 to 42
ALTER TABLE member ADD COLUMN ooc_id TEXT CHECK (ooc_id REGEXP '^[0-9]{3}-[0-9]{7}$') DEFAULT NULL;
ALTER TABLE member ADD COLUMN org_auth_code TEXT CHECK (org_auth_code REGEXP '^[A-Z]{2}-[A-ZÖØ]{3}-[0-9]{2,3}(-[A-Z]{2})?$') DEFAULT NULL;
+106 -53
View File
@@ -1,19 +1,21 @@
using Elwig.Documents;
using Elwig.Helpers.Export;
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Models.Dtos;
using Elwig.Models.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using Elwig.ViewModels;
using iText.Commons.Internal.Runtime;
using LinqKit;
using System.Globalization;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.Services {
public static class DeliveryService {
@@ -37,7 +39,7 @@ namespace Elwig.Services {
vm.GradationKmwString = "";
vm.Weight = null;
vm.ActualTare = 0;
vm.ActualTare = null;
vm.TargetTare = null;
vm.Tare = null;
vm.IsManualWeighing = false;
@@ -58,9 +60,9 @@ namespace Elwig.Services {
vm.WineOrigin = ControlUtils.GetItemFromSourceWithPk(vm.WineOriginSource, p.HkId) as WineOrigin;
vm.Weight = p.Weight;
vm.ActualTare = p.WeighingInfo.Tare ?? 0;
vm.ActualTare = p.WeighingInfo.Tare;
vm.TargetTare = vm.ActualTare + p.WeighingInfo.TareCorrection;
vm.Tare = vm.TargetTare ?? (vm.ActualTare != 0 ? vm.ActualTare : null);
vm.Tare = vm.TargetTare ?? (vm.ActualTare is int t && t != 0 ? t : null);
vm.IsManualWeighing = p.IsManualWeighing;
vm.IsNetWeight = p.IsNetWeight;
@@ -975,30 +977,40 @@ namespace Elwig.Services {
}
}
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, int weight, int? total1, int total2) {
var bold = h2 == null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, 1, bold);
grid.AddToolTipCell($"{weight:N0} kg", row, 2, bold: bold, alignRight: true);
private static void AddWeightToolTipRow(Grid grid, int row, string? h1, string? h2, string? h3, int weight, int? total1, int? total2, int total3, string? type) {
var bold = h3 == null;
var color = type == "R" ? Brushes.Firebrick : type == "W" ? Brushes.ForestGreen : null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? (h3 == null) ? 3 : 2 : 1, bold: bold, color: color);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, (h3 == null) ? 2 : 1, bold: bold, color: color);
if (h3 != null) grid.AddToolTipCell(h3 + ":", row, 2, 1, bold: bold, color: color);
grid.AddToolTipCell($"{weight:N0} kg", row, 3, bold: bold, alignRight: true, color: color);
if (total1 != null && total1 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 3, bold: bold, alignRight: true);
if (total2 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 4, bold: bold, alignRight: true);
grid.AddToolTipCell($"{weight * 100.0 / total1:N1} %", row, 4, bold: bold, alignRight: true, color: color);
if (total2 != null && total2 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total2:N1} %", row, 5, bold: bold, alignRight: true, color: color);
if (total3 != 0)
grid.AddToolTipCell($"{weight * 100.0 / total3:N1} %", row, 6, bold: bold, alignRight: true, color: color);
}
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, double min, double avg, double max) {
var bold = h2 == null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? 2 : 1, bold);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, bold: bold);
grid.AddToolTipCell($"{min:N1}°", row, 2, bold: bold, alignRight: true);
grid.AddToolTipCell($"{avg:N1}°", row, 3, bold: bold, alignRight: true);
grid.AddToolTipCell($"{max:N1}°", row, 4, bold: bold, alignRight: true);
private static void AddGradationToolTipRow(Grid grid, int row, string? h1, string? h2, string? h3, double min, double avg, double max, string? type) {
var bold = h3 == null;
var color = type == "R" ? Brushes.Firebrick : type == "W" ? Brushes.ForestGreen : null;
if (h1 != null) grid.AddToolTipCell(h1 + ":", row, 0, (h2 == null) ? (h3 == null) ? 3 : 2 : 1, bold: bold, color: color);
if (h2 != null) grid.AddToolTipCell(h2 + ":", row, 1, (h3 == null) ? 2 : 1, bold: bold, color: color);
if (h3 != null) grid.AddToolTipCell(h3 + ":", row, 2, bold: bold, color: color);
grid.AddToolTipCell($"{min:N1}°", row, 3, bold: bold, alignRight: true, color: color);
grid.AddToolTipCell($"{avg:N1}°", row, 4, bold: bold, alignRight: true, color: color);
grid.AddToolTipCell($"{max:N1}°", row, 5, bold: bold, alignRight: true, color: color);
}
public static async Task<(string WeightText, (string?, string?, int, int?, int)[] WeightGrid, string GradationText, (string?, string?, double, double, double)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) {
var wGrid = new List<(string?, string?, int, int?, int)>();
private static string? GetGroupName(params string?[] parts) {
return parts.All(p => p == null) ? null : string.Join(" / ", parts.Where(p => p != null));
}
public static async Task<(string WeightText, (string?, string?, string?, int, int?, int?, int, string?)[] WeightGrid, string GradationText, (string?, string?, string?, double, double, double, string?)[] GradationGrid)> GenerateToolTipData(IQueryable<DeliveryPart> deliveryParts) {
var wGrid = new List<(string?, string?, string?, int, int?, int?, int, string?)>();
var wText = "-";
var gGrid = new List<(string?, string?, double, double, double)>();
var gGrid = new List<(string?, string?, string?, double, double, double, string?)>();
var gText = "-";
var stat = (await deliveryParts.GroupBy(p => 0)
@@ -1013,11 +1025,11 @@ namespace Elwig.Services {
.Single();
wText = $"{stat.Weight:N0} kg";
wGrid.Add(("Menge", null, stat.Weight, null, stat.Weight));
wGrid.Add(("Menge", null, null, stat.Weight, null, null, stat.Weight, null));
if (stat.Min != null && stat.Max != null) {
gText = $"{stat.Min:N1}° / {stat.Avg:N1}° / {stat.Max:N1}°";
gGrid.Add(("Gradation", null, stat.Min.Value, stat.Avg, stat.Max.Value));
gGrid.Add(("Gradation", null, null, stat.Min.Value, stat.Avg, stat.Max.Value, null));
var attrGroups = await deliveryParts
.GroupBy(p => new { Attr = p.Attribute!.Name, Cult = p.Cultivation!.Name })
@@ -1034,9 +1046,10 @@ namespace Elwig.Services {
.ThenBy(g => g.Cult)
.ToListAsync();
var sortGroups = await deliveryParts
.GroupBy(p => p.SortId)
.GroupBy(p => new { p.SortId, p.Variety.Type })
.Select(g => new {
SortId = g.Key,
g.Key.SortId,
g.Key.Type,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw),
Avg = g.Sum(p => p.Kmw * p.Weight) / g.Sum(p => p.Weight),
@@ -1049,11 +1062,13 @@ namespace Elwig.Services {
.GroupBy(p => new {
Attr = p.Attribute!.Name,
Cult = p.Cultivation!.Name,
p.Variety.Type,
p.SortId,
})
.Select(g => new {
g.Key.Attr,
g.Key.Cult,
g.Key.Type,
g.Key.SortId,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Kmw),
@@ -1063,27 +1078,62 @@ namespace Elwig.Services {
.OrderByDescending(g => g.Weight)
.ThenBy(g => g.Attr)
.ThenBy(g => g.Cult)
.ThenBy(g => g.Type)
.ThenBy(g => g.SortId)
.ToListAsync();
foreach (var attrG in attrGroups) {
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
wGrid.Add((name, null, attrG.Weight, attrG.Weight, stat.Weight));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
wGrid.Add((null, g.SortId, g.Weight, attrG.Weight, stat.Weight));
var name = GetGroupName(attrG.Attr, attrG.Cult);
var typeGroups = groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult)
.GroupBy(g => g.Type)
.Select(g => new { Type = g.Key, Weight = g.Sum(p => p.Weight) })
.OrderByDescending(g => g.Weight).ThenBy(g => g.Type)
.ToList();
wGrid.Add((name, null, null, attrG.Weight, null, attrG.Weight, stat.Weight, typeGroups.Count == 1 ? typeGroups.First().Type : null));
if (typeGroups.Count == 1) {
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
wGrid.Add((null, null, g.SortId, g.Weight, null, attrG.Weight, stat.Weight, null));
}
} else {
foreach (var t in typeGroups) {
wGrid.Add((null, t.Type == "R" ? "Rot" : "Weiß", null, t.Weight, t.Weight, attrG.Weight, stat.Weight, t.Type));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult && g.Type == t.Type).OrderByDescending(g => g.Weight).ThenBy(g => g.SortId)) {
wGrid.Add((null, null, g.SortId, g.Weight, t.Weight, attrG.Weight, stat.Weight, null));
}
}
}
}
foreach (var attrG in attrGroups) {
var name = attrG.Attr == null && attrG.Cult == null ? null : attrG.Attr + (attrG.Attr != null && attrG.Cult != null ? " / " : "") + attrG.Cult;
gGrid.Add((name, null, attrG.Min, attrG.Avg, attrG.Max));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
gGrid.Add((null, g.SortId, g.Min, g.Avg, g.Max));
var name = GetGroupName(attrG.Attr, attrG.Cult);
var typeGroups = groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult)
.GroupBy(g => g.Type)
.Select(g => new {
Type = g.Key,
Weight = g.Sum(p => p.Weight),
Min = g.Min(p => p.Min),
Avg = g.Sum(p => p.Avg * p.Weight) / g.Sum(p => p.Weight),
Max = g.Max(p => p.Max),
})
.OrderByDescending(g => g.Weight).ThenBy(g => g.Type)
.ToList();
gGrid.Add((name, null, null, attrG.Min, attrG.Avg, attrG.Max, typeGroups.Count == 1 ? typeGroups.First().Type : null));
if (typeGroups.Count == 1) {
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
gGrid.Add((null, null, g.SortId, g.Min, g.Avg, g.Max, null));
}
} else {
foreach (var t in typeGroups) {
gGrid.Add((null, t.Type == "R" ? "Rot" : "Weiß", null, t.Min, t.Avg, t.Max, t.Type));
foreach (var g in groups.Where(g => g.Attr == attrG.Attr && g.Cult == attrG.Cult && g.Type == t.Type).OrderByDescending(g => g.Avg).ThenBy(g => g.SortId)) {
gGrid.Add((null, null, g.SortId, g.Min, g.Avg, g.Max, null));
}
}
}
}
if (attrGroups.Count == 1) {
var g = attrGroups.First();
var name = g.Attr == null && g.Cult == null ? null : g.Attr + (g.Attr != null && g.Cult != null ? " / " : "") + g.Cult;
var name = GetGroupName(g.Attr, g.Cult);
if (name != null) {
wText += $" [{name}]";
gText += $" [{name}]";
@@ -1094,40 +1144,43 @@ namespace Elwig.Services {
}
} else if (attrGroups.Count <= 4) {
wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / stat.Weight:0%})" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (g.Attr == null && g.Cult == null ? "" : $" [{g.Attr}{(g.Attr != null && g.Cult != null ? " / " : "")}{g.Cult}]")))}";
wText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Weight:N0} kg ({(double)g.Weight / stat.Weight:0%})" + (GetGroupName(g.Attr, g.Cult) is string s ? $" [{s}]" : "")))}";
gText += $" = {string.Join(" + ", attrGroups.Select(g => $"{g.Min:N1}/{g.Avg:N1}/{g.Max:N1}" + (GetGroupName(g.Attr, g.Cult) is string s ? $" [{s}]" : "")))}";
}
}
return (wText, wGrid.ToArray(), gText, gGrid.ToArray());
}
public static (Grid WeightGrid, Grid GradationGrid) GenerateToolTip((string?, string?, int, int?, int)[] weightData, (string?, string?, double, double, double)[] gradationData) {
public static (Grid WeightGrid, Grid GradationGrid) GenerateToolTip((string?, string?, string?, int, int?, int?, int, string?)[] weightData, (string?, string?, string?, double, double, double, string?)[] gradationData) {
var wGrid = new Grid();
wGrid.ColumnDefinitions.Add(new() { Width = new(10) });
wGrid.ColumnDefinitions.Add(new() { Width = new(60) });
wGrid.ColumnDefinitions.Add(new() { Width = new(10) });
wGrid.ColumnDefinitions.Add(new() { Width = new(80) });
wGrid.ColumnDefinitions.Add(new() { Width = new(80) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
wGrid.ColumnDefinitions.Add(new() { Width = new(50) });
int rowNum = 0;
foreach (var row in weightData) {
if (rowNum != 0 && row.Item2 == null) rowNum++;
AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
if (rowNum != 0 && row.Item2 == null && row.Item3 == null) rowNum++;
AddWeightToolTipRow(wGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5, row.Item6, row.Item7, row.Item8);
}
var gGrid = new Grid();
gGrid.ColumnDefinitions.Add(new() { Width = new(10) });
gGrid.ColumnDefinitions.Add(new() { Width = new(60) });
gGrid.ColumnDefinitions.Add(new() { Width = new(10) });
gGrid.ColumnDefinitions.Add(new() { Width = new(80) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.ColumnDefinitions.Add(new() { Width = new(35) });
gGrid.AddToolTipCell("Min.", 0, 2, alignCenter: true);
gGrid.AddToolTipCell("⌀", 0, 3, alignCenter: true);
gGrid.AddToolTipCell("Max.", 0, 4, alignCenter: true);
gGrid.AddToolTipCell("Min.", 0, 3, alignCenter: true);
gGrid.AddToolTipCell("⌀", 0, 4, alignCenter: true);
gGrid.AddToolTipCell("Max.", 0, 5, alignCenter: true);
rowNum = 1;
foreach (var row in gradationData) {
if (rowNum != 1 && row.Item2 == null) rowNum++;
AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5);
if (rowNum != 1 && row.Item2 == null && row.Item3 == null) rowNum++;
AddGradationToolTipRow(gGrid, rowNum++, row.Item1, row.Item2, row.Item3, row.Item4, row.Item5, row.Item6, row.Item7);
}
return (wGrid, gGrid);
+57 -3
View File
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
namespace Elwig.Services {
public static class MemberService {
@@ -106,6 +107,7 @@ namespace Elwig.Services {
vm.UstIdNr = m.UstIdNr;
vm.LfbisNr = m.LfbisNr;
vm.OrganicAuthorityCode = m.OrganicAuthorityCode;
vm.IsBuchführend = m.IsBuchführend;
vm.IsOrganic = m.IsOrganic;
@@ -187,10 +189,9 @@ namespace Elwig.Services {
.GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Any());
if (m.MgNr != vm.MgNr)
return;
await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr)
return;
var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []);
var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []);
var grid = AreaComService.GenerateToolTip(gridData);
@@ -207,6 +208,58 @@ namespace Elwig.Services {
});
});
vm.OrganicTest1 = null;
vm.OrganicTest2 = "...";
vm.OrganicTest3 = Brushes.Black;
vm.OrganicTest4 = null;
Utils.RunBackground("Bio-Zertifikatsdaten laden", async () => {
if (App.MainDispatcher == null)
return;
var certs = await OrganicService.FetchTracesCertificatesOfMember(m);
// TODO button for TRACES search url
// TODO button for authority search url
await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr)
return;
vm.OrganicTest2 = null;
if (certs.Length == 0) {
vm.OrganicTest1 = "Nicht gefunden";
vm.OrganicTest3 = Brushes.DarkRed;
} else {
var cert = certs[0];
vm.OrganicTest4 = $"Zertifikat-Nr.: {cert.Id}\nBio-KSt.: {cert.AuthorityCode}\nEU Operator-ID: {cert.OperatorId}\nOperator: {cert.OperatorName}";
if (cert.IsValidForWineProduction) {
vm.OrganicTest1 = "Gültig";
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
vm.OrganicTest3 = Brushes.DarkGreen;
} else if (cert.IsValid) {
vm.OrganicTest1 = "Teilw. gültig";
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
vm.OrganicTest3 = Brushes.OrangeRed;
} else {
vm.OrganicTest3 = Brushes.DarkRed;
switch (cert.Status) {
case OrganicService.TracesStatus.ISSUED:
vm.OrganicTest1 = "Ausgestellt";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.SUSPENDED:
vm.OrganicTest1 = "Gesperrt";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.EXPIRED:
vm.OrganicTest1 = "Ausgelaufen";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
case OrganicService.TracesStatus.WITHDRAWN:
vm.OrganicTest1 = "Widerrufen";
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
}
}
}
});
});
vm.MemberHasEmail = m.EmailAddresses.Count > 0;
vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => false).ToList();
@@ -555,6 +608,7 @@ namespace Elwig.Services {
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
OrganicAuthorityCode = string.IsNullOrWhiteSpace(vm.OrganicAuthorityCode) ? null : vm.OrganicAuthorityCode,
IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic,
+132
View File
@@ -0,0 +1,132 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using System.Web;
namespace Elwig.Services {
public static class OrganicService {
public const string TRACES_API_URL = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/for/query";
public const string TRACES_PDF_URL_PREFIX = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/digitally-signed/";
public enum TracesStatus {
ISSUED, SUSPENDED, WITHDRAWN, EXPIRED
}
public enum TracesActivity {
PREPARATION, EXPORT, IMPORT, STORING, PRODUCTION, DISTRIBUTION, DISTRIBUTION_PLACING_ON_THE_MARKET
}
public enum TracesCategoryOfProduct {
UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS, LIVESTOCK_AND_UNPROCESSED_LIVESTOCK_PRODUCTS, ALGAE_AND_UNPROCESSED_AQUACULTURE_PRODUCTS,
PROCESSED_AGRICULTURAL_PRODUCTS_INCLUDING_AQUACULTURE_FOR_USE_AS_FOOD, FEED, WINE, OTHER_PRODUCTS_NOT_COVERED_BY_PREVIOUS_CATEGORIES
}
public record TracesCertificate {
public required string Id { get; init; }
public required string AuthorityCode { get; init; }
public required string OperatorId { get; init; }
public required string OperatorName { get; init; }
public required TracesActivity[] Activities { get; init; }
public required TracesCategoryOfProduct[] CategoriesOfProduct { get; init; }
public required DateOnly IssuedOn { get; init; }
public required DateOnly ExpiresOn { get; init; }
public required DateOnly LastStatusUpdateOn { get; init; }
public required TracesStatus Status { get; init; }
public string PdfUrl => TRACES_PDF_URL_PREFIX + Id + ".pdf";
public bool IsValid => Status == TracesStatus.ISSUED && IssuedOn.ToDateTime(new()) <= DateTime.Today && ExpiresOn.ToDateTime(new()) >= DateTime.Today;
public bool IsValidForWineProduction => IsValid && Activities.Contains(TracesActivity.PRODUCTION) &&
(CategoriesOfProduct.Contains(TracesCategoryOfProduct.UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS) || CategoriesOfProduct.Contains(TracesCategoryOfProduct.WINE));
}
private async static Task<JsonArray> TryFetchTracesCertificates(Dictionary<string, string> query) {
var q = HttpUtility.ParseQueryString("");
q.Add("sort", "-issuedOn");
q.Add("countryCode", "AT");
foreach (var (k, v) in query) {
q.Add(k, v);
}
using var client = Utils.GetHttpClient();
using var res = await client.GetAsync($"{TRACES_API_URL}?{q}");
res.EnsureSuccessStatusCode();
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
var certs = resJson?.AsArray() ?? throw new Exception();
if (certs.Count == 0)
throw new Exception();
return certs;
}
public async static Task<TracesCertificate[]> FetchTracesCertificatesOfMember(Member m, bool? tryNameAndAddress = null) {
bool searchedByName = false;
JsonArray? jsonCerts = null;
if (m.OrganicOperatorId != null) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "ooc_identifier" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", m.OrganicOperatorId },
});
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null && m.LfbisNr != null) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "comp_reg" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", m.LfbisNr.TrimStart('0') },
});
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null && (tryNameAndAddress ?? m.IsOrganic)) {
try {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorPostalCode", $"{(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}" },
{ "query", m.BillingAddress is BillingAddr a ? $"{a.FullName} {a.Address}" : $"{m.FullName} {m.Address}" },
});
searchedByName = true;
} catch {
jsonCerts = null;
}
}
if (jsonCerts == null)
return [];
TracesCertificate[] certs = [];
try {
certs = [.. jsonCerts.Select(j => new TracesCertificate {
Id = j!["reference"]?.GetValue<string>() ?? throw new Exception(),
AuthorityCode = j["issuingBody"]?["code"]?.GetValue<string>() ?? throw new Exception(),
OperatorId = j["operatorIdentifier"]?.GetValue<string>() ?? throw new Exception(),
OperatorName = j["operator"]?["name"]?.GetValue<string>() ?? throw new Exception(),
Activities = [.. j["activities"]?.AsArray().Select(a => Enum.Parse<TracesActivity>(a?["id"]?.GetValue<string>().ToUpper().Replace("_IMPORT", "IMPORT") ?? throw new Exception())) ?? []],
CategoriesOfProduct = [.. j["categoriesOfProduct"]?.AsArray().Select(c => Enum.Parse<TracesCategoryOfProduct>(c?["id"]?.GetValue<string>() ?? throw new Exception())) ?? []],
IssuedOn = DateOnly.FromDateTime(DateTime.ParseExact(j["issuedOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
ExpiresOn = DateOnly.ParseExact(j["expiresOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-dd"),
LastStatusUpdateOn = DateOnly.FromDateTime(DateTime.ParseExact(j["lastStatusUpdateDateTime"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
Status = Enum.Parse<TracesStatus>(j["status"]?["id"]?.GetValue<string>() ?? throw new Exception()),
})];
} catch {
return [];
}
return (!searchedByName || certs.Where(c => c.Status == TracesStatus.ISSUED).Select(c => c.OperatorId).ToHashSet().Count == 1) ? certs : [];
}
}
}
+1 -1
View File
@@ -125,7 +125,7 @@ namespace Elwig.ViewModels {
get => int.TryParse(TareString?.Replace(Utils.GroupSeparator, "").Replace(" kg", ""), out var w) ? w : null;
set => TareString = value == null ? "-" : $"{value:N0} kg{(TargetTare != null ? "*" : "")}";
}
public int ActualTare;
public int? ActualTare;
public int? TargetTare;
[ObservableProperty]
private bool _isManualWeighing;
+12
View File
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject {
@@ -118,10 +119,21 @@ namespace Elwig.ViewModels {
[ObservableProperty]
private string? _lfbisNr;
[ObservableProperty]
private string? _organicAuthorityCode;
[ObservableProperty]
private bool _isBuchführend;
[ObservableProperty]
private bool _isOrganic;
[ObservableProperty]
private string? _organicTest1;
[ObservableProperty]
private string? _organicTest2;
[ObservableProperty]
private Brush? _organicTest3;
[ObservableProperty]
private string? _organicTest4;
[ObservableProperty]
private string? _entryDate;
[ObservableProperty]
+8 -8
View File
@@ -614,7 +614,7 @@ namespace Elwig.Windows {
ClearOriginalValues();
ClearDefaultValues();
ViewModel.FillInputs(p);
CorrectTareButton.Visibility = !ViewModel.IsReceipt && (ViewModel.ActualTare != 0 || ViewModel.IsUnloadingBox) ? Visibility.Visible : Visibility.Hidden;
CorrectTareButton.Visibility = !ViewModel.IsReceipt && ((ViewModel.ActualTare is int t && t != 0) || ViewModel.IsUnloadingBox) ? Visibility.Visible : Visibility.Hidden;
FinishInputFilling();
}
@@ -625,13 +625,13 @@ namespace Elwig.Windows {
}
private void CorrectTareButton_Click(object sender, RoutedEventArgs evt) {
var lastCorrection = (ViewModel.TargetTare ?? ViewModel.ActualTare) - ViewModel.ActualTare;
var lastCorrection = (ViewModel.TargetTare ?? ViewModel.ActualTare ?? 0) - (ViewModel.ActualTare ?? 0);
var correction = Utils.ShowTareCorrectionDialog(ViewModel.ActualTare, ViewModel.TargetTare);
if (correction == null) return;
ViewModel.Weight += lastCorrection - correction;
ViewModel.TargetTare = correction == 0 ? null : ViewModel.ActualTare + correction;
ViewModel.Tare = ViewModel.TargetTare ?? ViewModel.ActualTare;
if (ViewModel.WeighingData is string s) {
ViewModel.TargetTare = correction == 0 ? null : (ViewModel.ActualTare ?? 0) + correction;
ViewModel.Tare = ViewModel.TargetTare ?? ViewModel.ActualTare;
var obj = JsonNode.Parse(s)!.AsObject();
if (correction == 0) {
obj.Remove("tare_correction");
@@ -673,16 +673,16 @@ namespace Elwig.Windows {
private void OnWeighingResult(IScale scale, WeighingResult res) {
if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) {
ViewModel.Weight = res.NetWeight;
ViewModel.ActualTare = res.TareWeight ?? 0;
ViewModel.ActualTare = res.TareWeight;
ViewModel.TargetTare = null;
ViewModel.Tare = ViewModel.ActualTare != 0 ? ViewModel.ActualTare : null;
ViewModel.Tare = ViewModel.ActualTare is int t && t != 0 ? t : null;
ViewModel.ScaleTerminalId = scale.ScaleTerminalId;
ViewModel.WeighingData = res.ToJson().ToJsonString();
ViewModel.ManualWeighingReason = null;
ManualWeighingInput.IsChecked = false;
} else {
ViewModel.Weight = null;
ViewModel.ActualTare = 0;
ViewModel.ActualTare = null;
ViewModel.TargetTare = null;
ViewModel.Tare = null;
ViewModel.ScaleTerminalId = null;
@@ -1410,7 +1410,7 @@ namespace Elwig.Windows {
private void UnloadingInput_Checked(object sender, RoutedEventArgs evt) {
if (!IsEditing && !IsCreating) return;
if (!ViewModel.IsReceipt)
CorrectTareButton.Visibility = ViewModel.ActualTare != 0 || ViewModel.IsUnloadingBox ? Visibility.Visible : Visibility.Hidden;
CorrectTareButton.Visibility = (ViewModel.ActualTare is int t && t != 0) || ViewModel.IsUnloadingBox ? Visibility.Visible : Visibility.Hidden;
var mod = ViewModel.Modifiers.ToList();
var source = ViewModel.ModifiersSource.ToList();
if (App.Client.IsMatzen) {
+16 -11
View File
@@ -548,36 +548,41 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="110"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0" ToolTip="USt-IdNr. / Umsatzsteuer-Identifikationsnummer"/>
<TextBox x:Name="UstIdNrInput" Text="{Binding UstIdNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
<TextBox x:Name="UstIdNrInput" Text="{Binding UstIdNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,10,0" Grid.Column="1" Width="96" HorizontalAlignment="Left"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
<Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/>
<Button x:Name="OrganicButton" Content="easy-cert.com" IsEnabled="{Binding IsMemberSelected}"
Height="25" FontSize="12"
Click="OrganicButton_Click"
Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,40,0,0"/>
<Label Content="Bio-Zert.:" Margin="10,10,150,0" Grid.Column="2" HorizontalAlignment="Right"/>
<TextBlock FontSize="14" Margin="10,12,10,0" Grid.Column="2" HorizontalAlignment="Right" ToolTip="{Binding OrganicTest4}">
<Run Text="{Binding OrganicTest1}" FontWeight="Bold" Foreground="{Binding OrganicTest3}"/><Run FontSize="12" Text="{Binding OrganicTest2}"/>
</TextBlock>
<Label Content="Bio-KSt.:" Margin="10,40,95,0" Grid.Column="2" HorizontalAlignment="Right" ToolTip="Bio-Kontrollstelle"/>
<TextBox x:Name="OrganicAuthorityCodeInput" Text="{Binding OrganicAuthorityCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="2" Width="82" HorizontalAlignment="Right"
TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/>
</Grid>
</GroupBox>
<GroupBox Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
<GroupBox.Header>
<TextBlock>Rechnungsadresse (optional) <Run FontSize="10">für Lieferschein, Anlfrg.-Bstng., Tr.-Gutschr.</Run></TextBlock>
<TextBlock>Rechnungsadresse (optional)
<Run FontSize="10">für Lieferschein, Anlfrg.-Bstng., Tr.-Gutschr.</Run>
</TextBlock>
</GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
-12
View File
@@ -7,7 +7,6 @@ using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
@@ -966,17 +965,6 @@ namespace Elwig.Windows {
}
}
private void OrganicButton_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m) return;
var url = "https://www.easy-cert.com/htm/suchergebnis.htm?" +
//$"CustomerNumber={m.LfbisNr}&" +
$"Name={(m.BillingAddress?.FullName ?? m.FullName).Replace(' ', '+')}&" +
$"PostalCode={(m.BillingAddress?.PostalDest ?? m.PostalDest).AtPlz?.Plz}";
Process.Start(new ProcessStartInfo(url) {
UseShellExecute = true,
});
}
private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return;
FocusMember((int)m.PredecessorMgNr);
+1 -1
View File
@@ -1 +1 @@
curl --fail -s -L "https://elwig.at/files/create.sql?v=41" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
curl --fail -s -L "https://elwig.at/files/create.sql?v=42" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"