Compare commits

...
10 Commits
36 changed files with 3903 additions and 148 deletions

No files matched your search

+33 -15
View File
@@ -30,23 +30,27 @@ namespace Elwig.Documents {
get { get {
IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member; IAddress addr = (Member.BillingAddress != null && UseBillingAddress) ? Member.BillingAddress : Member;
var plz = addr.PostalDest.AtPlz; var plz = addr.PostalDest.AtPlz;
return string.Join("\n", ((string?[])[Member.BillingAddress?.FullName, Member.AdministrativeName, Member.ForTheAttentionOf, addr.Address, $"{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}", addr.PostalDest.Country.Name]).Where(s => !string.IsNullOrWhiteSpace(s))); return string.Join("\n", ((string?[])[addr is BillingAddr ? addr.FullName : null, Member.AdministrativeName, Member.ForTheAttentionOf, addr.Address, $"{plz?.Plz} {plz?.Ort.Name.Split(",")[0]}", addr.PostalDest.Country.Name]).Where(s => !string.IsNullOrWhiteSpace(s)));
} }
} }
protected Cell NewAsideCell(Paragraph text, int colspan = 1, bool isName = false) { protected Cell NewAsideCell(Paragraph text, int colspan = 1, bool isName = false, bool right = false, bool center = false) {
var cell = NewCell(text, colspan: colspan).SetPaddingsMM(0.25f, 0.5f, 0.25f, isName ? 1 : 0); var cell = NewCell(text, colspan: colspan).SetPaddingsMM(0.25f, center ? 0 : right ? 1 : 0.5f, 0.25f, isName ? 1 : 0);
if (colspan == 26) { if (colspan == 26) {
cell.SetTextAlignment(TextAlignment.CENTER).SetFont(BF) cell.SetTextAlignment(TextAlignment.CENTER).SetFont(BF)
.SetBackgroundColor(new DeviceRgb(0xe0, 0xe0, 0xe0)) .SetBackgroundColor(new DeviceRgb(0xe0, 0xe0, 0xe0))
.SetBorderTop(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .SetBorderTop(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness))
.SetPaddingsMM(0.5f, 1, 0.5f, 1); .SetPaddingsMM(0.5f, 1, 0.5f, 1);
} else if (isName) {
cell.SetBorderLeft(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness));
} }
if (right) cell.SetTextAlignment(TextAlignment.RIGHT);
if (center) cell.SetTextAlignment(TextAlignment.CENTER);
return cell; return cell;
} }
protected Cell NewAsideCell(string text, int colspan = 1, bool isName = false) { protected Cell NewAsideCell(string text, int colspan = 1, bool isName = false, bool right = false, bool center = false) {
return NewAsideCell(new KernedParagraph(text, 10), colspan, isName); return NewAsideCell(new KernedParagraph(text, 10), colspan, isName, right, center);
} }
public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) : public BusinessDocument(string title, Member m, DateOnly? dateFrom, bool includeSender = false) :
@@ -60,17 +64,27 @@ namespace Elwig.Documents {
protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void BeforeRenderBody(iText.Layout.Document doc, PdfDocument pdf) {
base.BeforeRenderBody(doc, pdf); base.BeforeRenderBody(doc, pdf);
var uid = new KernedParagraph(Member.UstIdNr ?? "-", 10);
if (!Member.IsBuchführend) uid.Add(Normal(" ")).Add(Italic("(pauschaliert)"));
Aside = new Table(ColsMM(2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5)) Aside = new Table(ColsMM(2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5))
.SetWidth(UnitValue.CreatePointValue(65 * PtInMM)).SetFixedLayout() .SetWidth(UnitValue.CreatePointValue(65 * PtInMM)).SetFixedLayout()
.SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE) .SetBorderCollapse(BorderCollapsePropertyValue.COLLAPSE)
.SetFont(NF).SetFontSize(10) .SetFont(NF).SetFontSize(10)
.SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness)) .SetBorder(new SolidBorder(new DeviceRgb(0x80, 0x80, 0x80), BorderThickness))
.AddCell(NewAsideCell("Mitglied", 26)) .AddCell(NewAsideCell("Mitglied", 26));
.AddCell(NewAsideCell("Mitglieds-Nr.:", 9, isName: true)).AddCell(NewAsideCell($"{Member.MgNr}", 17)) if (Member.IsOrganic) {
.AddCell(NewAsideCell("Betriebs-Nr.:", 9, isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "", 17)) Aside
.AddCell(NewAsideCell("UID:", 9, isName: true)).AddCell(NewAsideCell(uid, 17)); .AddCell(NewAsideCell("Mitgl.-Nr.:", 8, isName: true)).AddCell(NewAsideCell($"{Member.MgNr}", 4, right: true))
.AddCell(NewAsideCell("Bio-KSt.:", 6, isName: true)).AddCell(NewAsideCell(Member.OrganicAuthorityCode ?? "-", 8, right: true, center: Member.OrganicAuthorityCode == null))
.AddCell(NewAsideCell("Betr.-Nr.:", 6, isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "-", 6, right: true, center: Member.LfbisNr == null))
.AddCell(NewAsideCell("OOC-ID:", 6, isName: true)).AddCell(NewAsideCell(Member.OrganicOperatorId ?? "-", 8, right: true, center: Member.OrganicOperatorId == null))
.AddCell(NewAsideCell("UID:", 6, isName: true)).AddCell(NewAsideCell(Member.UstIdNr ?? "-", 10, right: true, center: Member.UstIdNr == null))
.AddCell(NewAsideCell(new KernedParagraph("", 10).Add(Italic(Member.IsBuchführend ? "(buchführend)" : "(pauschaliert)")), 10, center: true));
} else {
Aside
.AddCell(NewAsideCell("Mitglieds-Nr.:", 12, isName: true)).AddCell(NewAsideCell($"{Member.MgNr}", 4, right: true)).AddCell(NewAsideCell("", 10))
.AddCell(NewAsideCell("Betriebs-Nr.:", 10, isName: true)).AddCell(NewAsideCell(Member.LfbisNr ?? "-", 6, right: true, center: Member.LfbisNr == null)).AddCell(NewAsideCell("", 10))
.AddCell(NewAsideCell("UID:", 6, isName: true)).AddCell(NewAsideCell(Member.UstIdNr ?? "-", 10, right: true, center: Member.UstIdNr == null))
.AddCell(NewAsideCell(new KernedParagraph("", 10).Add(Italic(Member.IsBuchführend ? "(buchführend)" : "(pauschaliert)")), 10, center: true));
}
} }
protected void RenderAddress(Canvas canvas, Rectangle pageSize) { protected void RenderAddress(Canvas canvas, Rectangle pageSize) {
@@ -123,11 +137,15 @@ namespace Elwig.Documents {
return new KernedParagraph(10) return new KernedParagraph(10)
.AddAll(Utils.GenerateFooter("\n", " \u00b7 ") .AddAll(Utils.GenerateFooter("\n", " \u00b7 ")
.Item(c.NameFull).NextLine() .Item(c.NameFull).NextLine()
.Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextLine() .Item(c.Address).Item($"{c.Plz} {c.Ort}").Item("Österreich").NextGroup()
.Item(c.EmailAddress != null ? link1 : null) .Item("Tel.", c.PhoneNr).Item("Fax", c.FaxNr).NextGroup()
.Item(c.Website != null ? link2 : null) .Item(c.EmailAddress != null ? link1 : null).NextLine()
.Item("Betriebs-Nr.", c.LfbisNr).Item("Bio-KSt.", c.OrganicAuthority).NextLine() .Item(c.Website != null ? link2 : null).NextGroup()
.Item("Betriebs-Nr.", c.LfbisNr).NextGroup()
.Item("Bio-KSt.", c.OrganicAuthorityCode).NextGroup()
.Item("OOC-ID", c.OrganicOperatorId).NextLine()
.Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban) .Item("UID", c.UstIdNr).Item("BIC", c.Bic).Item("IBAN", c.Iban)
.RebalanceLines()
.ToLeafElements()); .ToLeafElements());
} }
+3 -3
View File
@@ -56,9 +56,9 @@ namespace Elwig.Documents {
var firstDay = Data.Rows.MinBy(r => r.Date)?.Date; var firstDay = Data.Rows.MinBy(r => r.Date)?.Date;
var lastDay = Data.Rows.MaxBy(r => r.Date)?.Date; var lastDay = Data.Rows.MaxBy(r => r.Date)?.Date;
Aside?.AddCell(NewAsideCell("Saison", 26)) Aside?.AddCell(NewAsideCell("Saison", 26))
.AddCell(NewAsideCell("Lieferungen:", 9, isName: true)).AddCell(NewAsideCell($"{Data.Rows.DistinctBy(r => r.LsNr).Count():N0} (Teil-Lfrg.: {Data.RowNum:N0})", 17)) .AddCell(NewAsideCell("Lieferungen:", 12, isName: true)).AddCell(NewAsideCell($"{Data.Rows.DistinctBy(r => r.LsNr).Count():N0} (Teil-Lfrg.: {Data.RowNum:N0})", 14))
.AddCell(NewAsideCell("Zeitraum:", 9, isName: true)).AddCell(NewAsideCell(firstDay == null || lastDay == null ? "-" : firstDay == lastDay ? $"{firstDay:dd.MM.} (1 Tag)" : $"{firstDay:dd.MM.}\u2013{lastDay:dd.MM.} ({lastDay?.DayNumber - firstDay?.DayNumber + 1:N0} Tage)", 17)) .AddCell(NewAsideCell("Zeitraum:", 8, isName: true)).AddCell(NewAsideCell(firstDay == null || lastDay == null ? "-" : firstDay == lastDay ? $"{firstDay:dd.MM.} (1 Tag)" : $"{firstDay:dd.MM.}\u2013{lastDay:dd.MM.} ({lastDay?.DayNumber - firstDay?.DayNumber + 1:N0} Tage)", 18, center: true))
.AddCell(NewAsideCell("Menge:", 9, isName: true)).AddCell(NewAsideCell($"{MemberStats.Sum(s => s.Weight):N0} kg", 17)); .AddCell(NewAsideCell("Menge:", 12, isName: true)).AddCell(NewAsideCell($"{MemberStats.Sum(s => s.Weight):N0} kg", 14));
} }
protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) { protected override void RenderBody(iText.Layout.Document doc, PdfDocument pdf) {
+3
View File
@@ -153,6 +153,9 @@ namespace Elwig.Documents {
if (part.Cultivation != null) { if (part.Cultivation != null) {
var cult = new KernedParagraph(8); var cult = new KernedParagraph(8);
cult.Add(Italic("Bewirtschaftung:")).Add(Normal(" " + part.Cultivation.Name + (part.Cultivation.Description != null ? $" ({part.Cultivation.Description})" : ""))); cult.Add(Italic("Bewirtschaftung:")).Add(Normal(" " + part.Cultivation.Name + (part.Cultivation.Description != null ? $" ({part.Cultivation.Description})" : "")));
if (part.Cultivation.IsOrganic) {
cult.Add(" \u2013 ").Add(Italic("Kontrollstelle:")).Add(Normal($" {Member.OrganicAuthorityCode ?? "?"}"));
}
sub.AddCell(NewTd()) sub.AddCell(NewTd())
.AddCell(NewTd(cult, colspan: 5)) .AddCell(NewTd(cult, colspan: 5))
.AddCell(NewTd(colspan: 3)); .AddCell(NewTd(colspan: 3));
+2 -1
View File
@@ -161,7 +161,8 @@ namespace Elwig.Documents {
.AddCell(NewDataTh("Buchführend:", colspan: 2)).AddCell(NewTd(new KernedParagraph(Member.IsBuchführend ? "Ja " : "Nein ", 10) .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)) .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("(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(new KernedParagraph(Member.IsOrganic ? "Ja" : "Nein", 10)
.Add(Normal(Member.OrganicAuthorityCode != null || Member.OrganicOperatorId != null ? $" ({Member.OrganicAuthorityCode}{(Member.OrganicAuthorityCode != null && Member.OrganicOperatorId != null ? ", " : "")}{Member.OrganicOperatorId})" : "", 8)), colspan: 2))
.AddCell(NewDataHdr("Genossenschaft", colspan: 6)) .AddCell(NewDataHdr("Genossenschaft", colspan: 6))
.AddCell(NewDataTh("Status:")).AddCell(NewTd(new KernedParagraph(Member.IsActive ? "Aktiv " : "Nicht aktiv ", 10) .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)))) .Add(Normal("(" + (Member.ExitDate != null ? $"{Member.EntryDate:dd.MM.yyyy}\u2013{Member.ExitDate:dd.MM.yyyy}" : $"seit {Member.EntryDate:dd.MM.yyyy}") + ")", 8))))
+11 -2
View File
@@ -170,16 +170,25 @@ namespace Elwig.Helpers {
SavedChanges += OnSavedChanges; SavedChanges += OnSavedChanges;
} }
private static void CreateFunctions(SqliteConnection cnx) {
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => {
return value == null ? null : Regex.IsMatch(value, pattern);
}, true);
cnx.CreateFunction<string?, string?, string?>("NORMALIZE_ADDRESS", (address, locality) => {
return string.IsNullOrWhiteSpace(address) ? null : Normalizer.NormalizeAddress(address, locality);
}, true);
}
public static SqliteConnection Connect(string? connectionString = null) { public static SqliteConnection Connect(string? connectionString = null) {
var cnx = new SqliteConnection(connectionString ?? ConnectionString); var cnx = new SqliteConnection(connectionString ?? ConnectionString);
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true); CreateFunctions(cnx);
cnx.Open(); cnx.Open();
return cnx; return cnx;
} }
public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) { public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) {
var cnx = new SqliteConnection(connectionString ?? ConnectionString); var cnx = new SqliteConnection(connectionString ?? ConnectionString);
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true); CreateFunctions(cnx);
await cnx.OpenAsync(); await cnx.OpenAsync();
return cnx; return cnx;
} }
+62 -4
View File
@@ -1,15 +1,18 @@
using Elwig.Services;
using Microsoft.Data.Sqlite;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
namespace Elwig.Helpers { namespace Elwig.Helpers {
public static class AppDbUpdater { public static class AppDbUpdater {
// Don't forget to update value in Tests/fetch-resources.bat! // 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; private static int VersionOffset = 0;
@@ -68,11 +71,11 @@ namespace Elwig.Helpers {
}) })
.OrderBy(s => s.Item1).ThenBy(s => s.Item2)]; .OrderBy(s => s.Item1).ThenBy(s => s.Item2)];
List<string> toExecute = []; List<(int ToVersion, string File)> toExecute = [];
var vers = fromVersion; var vers = fromVersion;
while (vers < toVersion) { while (vers < toVersion) {
var (_, to, name) = scripts.Last(s => s.From == vers); var (_, to, name) = scripts.Last(s => s.From == vers);
toExecute.Add(name); toExecute.Add((to, name));
vers = to; vers = to;
} }
if (toExecute.Count == 0) if (toExecute.Count == 0)
@@ -87,8 +90,11 @@ namespace Elwig.Helpers {
await cnx.IntegrityCheck(); await cnx.IntegrityCheck();
await cnx.ForeignKeyCheck(); await cnx.ForeignKeyCheck();
foreach (var script in toExecute) { foreach (var (to, script) in toExecute) {
await cnx.ExecuteEmbeddedScript(asm, script); await cnx.ExecuteEmbeddedScript(asm, script);
if (to == 42) {
await UpdateDbSchema_41_To_42(cnx);
}
} }
await cnx.IntegrityCheck(); await cnx.IntegrityCheck();
@@ -103,5 +109,57 @@ namespace Elwig.Helpers {
File.Delete(backup); File.Delete(backup);
} }
} }
private static async Task UpdateDbSchema_41_To_42(SqliteConnection cnx) {
List<(int MgNr, string LfbisNr, string Name, string Address, int Plz)> members = [];
using (var cmd = cnx.CreateCommand()) {
cmd.CommandText = """
SELECT m.mgnr, m.lfbis_nr,
COALESCE(a.name, (COALESCE(m.prefix || ' ', '') || COALESCE(m.given_name || ' ', '') || m.name || COALESCE(' ' || m.middle_names, '') || COALESCE(' ' || m.suffix, ''))),
COALESCE(a.address, m.address),
COALESCE(aplz.plz, mplz.plz)
FROM member m
JOIN postal_dest mp ON (mp.country, mp.id) = (m.country, m.postal_dest)
JOIN AT_plz_dest ma ON (ma.country, ma.id) = (mp.country, mp.id)
JOIN AT_plz mplz ON mplz.plz = ma.plz
LEFT JOIN member_billing_address a ON a.mgnr = m.mgnr
LEFT JOIN postal_dest ap ON (ap.country, ap.id) = (a.country, a.postal_dest)
LEFT JOIN AT_plz_dest aa ON (aa.country, aa.id) = (ap.country, mp.id)
LEFT JOIN AT_plz aplz ON aplz.plz = aa.plz
WHERE organic
""";
using var reader = await cmd.ExecuteReaderAsync();
var header = await reader.GetColumnSchemaAsync();
while (await reader.ReadAsync()) {
members.Add((reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), reader.GetInt32(4)));
}
}
if (members.Count > 0)
InteractionService.ShowInformation("Datenbank-Update", "Es wird versucht, die Bio-Kontrollstellen-Informationen der Mitglieder automatisch abzurufen. Dies könnte einen kurzen Moment dauern.");
try {
Mouse.OverrideCursor = Cursors.Wait;
foreach (var m in members) {
string? oocId = null;
string? authCode = null;
var certs = await OrganicService.FetchTracesCertificates(lfbisNr: m.LfbisNr, address: (m.Name, m.Address, m.Plz.ToString()));
if (certs.Length > 0) {
oocId = certs[0].OperatorId;
authCode = certs[0].AuthorityCode;
}
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}");
}
}
} finally {
Mouse.OverrideCursor = null;
}
}
} }
} }
+6 -3
View File
@@ -49,7 +49,8 @@ namespace Elwig.Helpers {
public string? Bic; public string? Bic;
public string? UstIdNr; public string? UstIdNr;
public string? LfbisNr; public string? LfbisNr;
public string? OrganicAuthority; public string? OrganicOperatorId;
public string? OrganicAuthorityCode;
public string? PhoneNr; public string? PhoneNr;
public string? FaxNr; public string? FaxNr;
@@ -114,7 +115,8 @@ namespace Elwig.Helpers {
UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR"); UstIdNr = parameters.GetValueOrDefault("CLIENT_USTIDNR");
Bic = parameters.GetValueOrDefault("CLIENT_BIC"); Bic = parameters.GetValueOrDefault("CLIENT_BIC");
Iban = parameters.GetValueOrDefault("CLIENT_IBAN"); Iban = parameters.GetValueOrDefault("CLIENT_IBAN");
OrganicAuthority = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITY"); OrganicOperatorId = parameters.GetValueOrDefault("CLIENT_ORGANIC_OPERATORID");
OrganicAuthorityCode = parameters.GetValueOrDefault("CLIENT_ORGANIC_AUTHORITYCODE");
EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch { EnableMemberHistory = (parameters.GetValueOrDefault("ENABLE_MEMBERHISTORY")?.ToUpper()) switch {
"1" or "TRUE" or "YES" or "JA" => true, "1" or "TRUE" or "YES" or "JA" => true,
@@ -273,7 +275,8 @@ namespace Elwig.Helpers {
("CLIENT_USTIDNR", UstIdNr), ("CLIENT_USTIDNR", UstIdNr),
("CLIENT_BIC", Bic), ("CLIENT_BIC", Bic),
("CLIENT_IBAN", Iban), ("CLIENT_IBAN", Iban),
("CLIENT_ORGANIC_AUTHORITY", OrganicAuthority), ("CLIENT_ORGANIC_OPERATORID", OrganicOperatorId),
("CLIENT_ORGANIC_AUTHORITYCODE", OrganicAuthorityCode),
("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"), ("ENABLE_MEMBERHISTORY", EnableMemberHistory ? "YES" : "NO"),
("MODE_BUSINESSSHARES", businessShares), ("MODE_BUSINESSSHARES", businessShares),
("MODE_DELIVERYNOTE_STATS", deliveryNoteStats), ("MODE_DELIVERYNOTE_STATS", deliveryNoteStats),
+8 -2
View File
@@ -638,6 +638,8 @@ namespace Elwig.Helpers.Export {
["zwstid"] = m.ZwstId, ["zwstid"] = m.ZwstId,
["lfbis_nr"] = m.LfbisNr, ["lfbis_nr"] = m.LfbisNr,
["ustid_nr"] = m.UstIdNr, ["ustid_nr"] = m.UstIdNr,
["ooc_id"] = m.OrganicOperatorId,
["org_auth_code"] = m.OrganicAuthorityCode,
["juridical_pers"] = m.IsJuridicalPerson, ["juridical_pers"] = m.IsJuridicalPerson,
["volllieferant"] = m.IsVollLieferant, ["volllieferant"] = m.IsVollLieferant,
["buchführend"] = m.IsBuchführend, ["buchführend"] = m.IsBuchführend,
@@ -710,6 +712,8 @@ namespace Elwig.Helpers.Export {
ZwstId = json["zwstid"]?.AsValue().GetValue<string>(), ZwstId = json["zwstid"]?.AsValue().GetValue<string>(),
LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(), LfbisNr = json["lfbis_nr"]?.AsValue().GetValue<string>(),
UstIdNr = json["ustid_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, IsJuridicalPerson = json["juridical_pers"]?.AsValue().GetValue<bool>() ?? false,
IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false, IsVollLieferant = json["volllieferant"]?.AsValue().GetValue<bool>() ?? false,
IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false, IsBuchführend = json["buchführend"]?.AsValue().GetValue<bool>() ?? false,
@@ -762,7 +766,8 @@ namespace Elwig.Helpers.Export {
["reason"] = h.Reason, ["reason"] = h.Reason,
["source"] = h.Source, ["source"] = h.Source,
["shares"] = h.Shares, ["shares"] = h.Shares,
["signed"] = h.MemberHasSigned, ["signed_company"] = h.CompanyHasSigned,
["signed_member"] = h.MemberHasSigned,
["value_per_share"] = h.ValuePerShare, ["value_per_share"] = h.ValuePerShare,
["currency"] = h.CurrencyCode, ["currency"] = h.CurrencyCode,
["deduct_year"] = h.DeductYear, ["deduct_year"] = h.DeductYear,
@@ -782,7 +787,8 @@ namespace Elwig.Helpers.Export {
Reason = json["reason"]!.AsValue().GetValue<string>(), Reason = json["reason"]!.AsValue().GetValue<string>(),
Source = json["source"]!.AsValue().GetValue<string>(), Source = json["source"]!.AsValue().GetValue<string>(),
Shares = json["shares"]!.AsValue().GetValue<int>(), Shares = json["shares"]!.AsValue().GetValue<int>(),
MemberHasSigned = json["signed"]?.AsValue().GetValue<bool>() ?? false, CompanyHasSigned = json["signed_company"]?.AsValue().GetValue<bool>() ?? false,
MemberHasSigned = json["signed_member"]?.AsValue().GetValue<bool>() ?? false,
ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(), ValuePerShare = json["value_per_share"]?.AsValue().GetValue<decimal>(),
CurrencyCode = json["currency"]?.AsValue().GetValue<string>(), CurrencyCode = json["currency"]?.AsValue().GetValue<string>(),
DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(), DeductYear = json["deduct_year"]?.AsValue().GetValue<int>(),
+303
View File
@@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Elwig.Helpers {
public static partial class Normalizer {
[GeneratedRegex(@"(?=(?:(?:bundes)?straße|gasse|weg|platz|allee|ring|zeile?|steig|hof|markt|kai|ufer|brücke|tor|stiege|promenade|gang|siedlung)\b)|[\s-]+", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
private static partial Regex GeneratedStreetNameWordsRegex();
private static readonly Regex StreetNameWordsRegex = GeneratedStreetNameWordsRegex();
private static readonly HashSet<string> StreetTypes = [
"Straße", "Gasse", "Weg", "Platz", "Allee", "Ring", "Zeile", "Zeil", "Steig", "Hof",
"Markt", "Kai", "Ufer", "Brücke", "Tor", "Stiege", "Promenade", "Gang", "Siedlung"];
private static readonly HashSet<string> StreetArticles = ["der", "die", "das", "des", "dem", "den"];
private static readonly HashSet<string> StreetPrepositions = [
"an", "am", "auf", "aus", "bei", "beim", "hinter", "hinterm", "in", "im", "ins", "nach",
"neben", "über", "unter", "von", "vom", "van", "vor", "zu", "zum", "zur", "zwischen",
"durch", "mit"];
private static readonly HashSet<string> StreetNumbers = [ "Zwei", "Drei", "Vier", "Fünf", "Sechs", "Sieben" ];
private static readonly HashSet<string> StreetAdjectives = [
"alter", "alte", "alten", "altem", "altes",
"äußere", "äußere", "äußeren", "äußerem", "äußeres",
// "breiter", "breite", "breiten", "breitem", "breites",
"dritter", "dritte", "dritten", "drittem", "drittes",
"erster", "erste", "ersten", "erstem", "erstes",
"großer", "große", "großen", "großem", "großes",
"hoher", "hohe", "hohen", "hohem", "hohes",
"innerer", "innere", "inneren", "innerem", "inneres",
"kleiner", "kleine", "kleinen", "kleinem", "kleines",
"kurzer", "kurze", "kurzen", "kurzem", "kurzes",
"langer", "lange", "langen", "langem", "langes",
"linker", "linke", "linken", "linkem", "linkes",
"mittlerer", "mittlere", "mittleren", "mittlerem", "mittleres",
"neuer", "neue", "neuen", "neuem", "neues",
"oberer", "obere", "oberen", "oberem", "oberes",
"rechter", "rechte", "rechten", "rechtem", "rechtes",
"schmaler", "schmale", "schmalen", "schmalem", "schmales",
"schöner", "schöne", "schönen", "schönem", "schönes",
"unterer", "untere", "unteren", "unterem", "unteres",
"hinterer", "hintere", "hinteren", "hinterem", "hinteres",
"vorderer", "vordere", "vorderen", "vorderem", "vorderes",
"runder", "runde", "runden", "rundem", "rundes",
"kalter", "kalte", "kalten", "kaltem", "kaltes"];
private static readonly HashSet<string> StreetLastNames = [
"Habsburger", "Lothringer", "Leitzenberger", "Luyderer", "Preiner", "Radinger", "Seefelder", "Staribacher", "Gschmeidler",
"Hannover", "Münster", "Salzgitter", "Speyer", "Trier"];
private static readonly HashSet<string> StreetPersonTitles = [
"St.", "Familie",
"Dr.", "DDr.", "Hofrat", "Baurat", "Kommerzialrat", "Dir.", "Ing.", "Prof.", "Mag.", "DI", "OSR", "Präsident",
"Landeshauptmann", "LH", "Bürgermeister", "Bgm.", "Minister",
"Kaiser", "Kaiserin", "König", "Königin", "Erzherzog", "Erzherzogin", "Herzog", "Fürst", "Graf", "Gräfin", "Markgraf", "Kronprinz", "Prinz", "Ritter", "Freiherr", "Lord",
"Abt", "Bischof", "Propst", "Dechant", "Kardinal", "Monsignore", "Pfarrer", "Pater", "Prälat",
"Oberst"];
private static readonly HashSet<string> StreetGivenNames = [
"Adalbert", "Adam", "Adele", "Adelheid", "Adolf", "Adolph", "Adrian", "Albert", "Albrecht", "Alexander", "Alfons", "Alfred", "Alois", "Aloisia",
"Andreas", "Andrea", "Andre", "Anna", "Anton", "Arnold", "Arthur", "August", "Augustin", "Augustine", "Agnes", "Angelika", "Ada", "Adelheid", "Anni", "Anita",
"Barbara", "Beate", "Beatrix", "Benedikt", "Bernhard", "Berta", "Bertha", "Berthold", "Brüder", "Bruno", "Brigitte",
"Camillo", "Carl", "Caspar", "Catharina", "Christian", "Christine", "Christof", "Christoph", "Clara", "Clemens", "Conrad", "Claus", "Camillo", "Claudia",
"Daniel", "Dietmar", "Dietrich",
"Edgar", "Edi", "Edith", "Edmund", "Egon", "Elsa", "Eduard", "Emmerich", "Erhard", "Erich", "Ernst", "Erwin", "Eugen", "Ewald", "Eberhard",
"Elisabeth", "Emanuel", "Emerich", "Emil", "Emma", "Engelbert", "Elias",
"Franz", "Ferdinand", "Friedrich", "Felix", "Florian", "Fritz", "Franziska", "Fred",
"Georg", "Gregor", "Gustav", "Gabriel", "Gebrüder", "Gabriele", "Gerhard", "Gerhart", "Gertrude", "Gottfried", "Gottlieb", "Grete",
"Günther", "Günter",
"Hans", "Hanns", "Heinrich", "Heinz", "Helmut", "Helmuth", "Hermann", "Herta", "Herbert", "Hubert", "Harald", "Horst", "Helene", "Helga",
"Hieronymus", "Hilde", "Hugo",
"Isabelle", "Ignaz", "Ilse", "Ingeborg", "Isidor",
"Josef", "Josefine", "Joseph", "Johann", "Johanna", "Johan", "Johannes", "Hannes", "Harry", "Jakob", "Jacob", "Jan", "Julian", "Julius", "Julia", "Jürgen",
"Karl", "Kasimir", "Konrad", "Klemens", "Klara", "Katharina", "Kurt", "Kaspar", "Klaus", "Koloman", "Konstantin", "Killian",
"Leopold", "Leo", "Lorenz", "Lothar", "Louise", "Ludmilla", "Ludwig", "Lukas", "Ladislaus", "Lambert", "Laurenz", "Liese",
"Lise", "Lilly", "Lina", "Lothar", "Lotte", "Luise", "Leonhard", "Lotte", "Martha",
"Maria", "Marie", "Michael", "Martin", "Max", "Maximilian", "Manuel", "Manuela", "Moritz", "Marianne", "Manfred", "Marc",
"Mathias", "Matthias", "Matthäus",
"Margarete", "Margarethe",
"Nico", "Niko", "Niklas", "Niclas", "Nikolaus", "Norbert",
"Otto", "Ottokar", "Oskar", "Olga", "Oswald", "Othmar",
"Paul", "Pauline", "Patrick", "Pepi", "Peter", "Philipp", "Philip", "Petrus",
"Robert", "Rudolf", "Richard", "Ruprecht", "Raffael", "Rolf", "Roman", "Rosa", "Rosalia", "Rupert", "Raimund",
"Sigmund", "Siegfried", "Simon", "Stanislaus", "Stefan", "Stephan", "Sepp", "Samuel", "Sebastian", "Susi",
"Theodor", "Thomas", "Theresia", "Thaddäus", "Theo", "Theophil", "Tina", "Titus", "Toni", "Trude",
"Ullrich", "Ulrich", "Udo",
"Viktor", "Vinzenz", "Victor",
"Wolfgang", "Willibald", "Wilhelm", "Willhelm", "Wilhelmine", "Walter", "Walther", "Wenzel", "Werner", "Willi",
"Zacharias"];
private static readonly HashSet<string> LocalityNames = [
"Groß-Enzersdorf",
"Groß Burgstall", "Klein Eberharts", "Klein-Engersdorf", "Groß-Enzersdorf", "Klein-Erlauf",
"Groß Gerungs", "Klein Gundholz", "Klein-Harras", "Klein-Heinrichschlag", "Groß-Höbarten",
"Klein-Jetzelsdorf", "Klein-Mariazell", "Groß Meinharts", "Klein-Meiseldorf", "Groß-Neusiedl", "Klein-Neusiedl", "Klein-Pöchlarn",
"Groß-Radischen", "Klein-Radischen", "Groß Raßberg", "Klein Reinprechts", "Klein-Reinprechtsdorf", "Groß-Reipersdorf", "Klein-Ruprechts",
"Groß-Schollach", "Klein-Schollach", "Groß-Schweinbarth", "Groß-Siegharts", "Klein-Siegharts", "Groß Sierning",
"Klein-Ulrichschlag", "Klein Weinberg", "Klein Wetzles", "Klein-Wien",
];
private static bool IsGivenName(this string word) {
return StreetGivenNames.Contains(word);
}
private static bool IsFamilyName(this string word) {
if (StreetLastNames.Contains(word))
return true;
return false;
}
private static bool IsPersonalName(this string word) {
return word.IsGivenName() || word.IsFamilyName();
}
private static bool IsGeographicalDerivativeName(this string word) {
if (!word.EndsWith("er") || word.IsPersonalName() || word == "Weißer")
return false;
return word.StartsWith("St. ") || word.StartsWith("Ober ") || word.StartsWith("Unter ") || word.StartsWith("Maria") ||
word.EndsWith("sdorfer") || word.EndsWith("ndorfer") || word.EndsWith("ldorfer") ||
word.EndsWith("felder") ||
word.EndsWith("sthaler") || word.EndsWith("nthaler") || word.EndsWith("lthaler") ||
word.EndsWith("dauer") || word.EndsWith("lauer") || word.EndsWith("mauer") || word.EndsWith("nauer") || word.EndsWith("ßauer") ||
word.EndsWith("lbacher") || word.EndsWith("nbacher") || word.EndsWith("ßbacher") ||
word.EndsWith("ringer") || word.EndsWith("singer") ||
word.EndsWith("sbrunner") || word.EndsWith("nbrunner") || word.EndsWith("lbrunner") || word.EndsWith("abrunner") || word.EndsWith("ingbrunner") ||
word.EndsWith("sburger") || word.EndsWith("nburger") || word.EndsWith("mburger") || word.EndsWith("lburger") || word.EndsWith("neuburger") ||
word.EndsWith("berger") ||
word.EndsWith("winkler") ||
word.EndsWith("lsteiner") || word.EndsWith("nsteiner") ||
word.EndsWith("städter") ||
word.EndsWith("stättner") || word.EndsWith("stettner") ||
word.EndsWith("brücker") ||
word.EndsWith("further") || word.EndsWith("furter") ||
word.EndsWith("aaer") ||
word.EndsWith("seer") || word.EndsWith("leer") ||
word.EndsWith("tzner") ||
word.EndsWith("zeller") ||
word.EndsWith("rther") ||
word.EndsWith("sdner") ||
word.EndsWith("gerner") || word.EndsWith("berner") ||
word.EndsWith("frieder") ||
word.EndsWith("litzer") || word.EndsWith("mitzer") || word.EndsWith("eutzer") ||
word.EndsWith("loner") ||
word.EndsWith("skirchner") || word.EndsWith("nkirchner") || word.EndsWith("ingkirchner") ||
word.EndsWith("ziger") ||
word.EndsWith("ließer") ||
word.EndsWith("gerer") ||
word.EndsWith("kreuzer") ||
word == "Wiener" || word == "Brünner" || word == "Znaimer" || word == "Triester" || word == "Prager" || word == "Berliner" || word == "Basler" ||
word == "Retzer" || word == "Tullner" || word == "Badner" ||
word == "Gruber" || word == "Sulzer" || word == "Harraser" || word == "Dörfleser" || word == "Neusiedler" ||
word == "Dornauer" || word == "Siebenhauser" || word == "Alser" ||
word == "Bockenheimer";
}
public static string NormalizeAddress(string address, string? locality = null) {
var idx = address.IndexOf(',');
if (idx >= 0) address = address[..idx];
var (name, nr) = Utils.SplitAddress(address);
return (NormalizeStreetName(name, locality) + " " + nr).Trim();
}
public static string NormalizeStreetName(string name, string? locality = null) {
name = name.Trim().ToLowerInvariant();
if (name == "") {
return "";
} else if (name == "nr.") {
if (locality == null)
return "Nr.";
name = locality.Trim().ToLowerInvariant();
}
name = name.Replace("hautp", "haupt")
.Replace("leop.", "leopold ").Replace("ferd.", "ferdinand ").Replace("gottfr.", "gottfried ")
.Replace("heinr.", "heinrich").Replace("joh.", "johann ").Replace("jos.", "josef ")
.Replace("msgr.", "monsignore ").Replace("pf.", "pfarrer ")
.Replace("präs.", "präsident ");
name = name.Replace("str.", "straße").Replace("strasse", "straße")
.Replace("bruecke", "brücke").Replace("pl.", "platz")
.Replace("gr.", "groß").Replace("kl.", "klein")
.Replace("sankt-", "st.").Replace("sankt ", "st.")
.Replace("wr.", "wiener ")
.Replace(".", ". ");
List<string> words = [.. StreetNameWordsRegex.Split(name)
.Where(w => !string.IsNullOrWhiteSpace(w))
.Select(w => StreetArticles.Contains(w) || StreetPrepositions.Contains(w) ? w : string.Concat(w[0..1].ToUpper(), w.AsSpan(1)))
.Select(w => w == "Ddr." ? "DDr." : w == "Lh" || w == "Osr" ? w.ToUpper() : w)
];
if (words[0] == "Di") words[0] = "DI";
var temp = "|" + string.Join('|', words) + "|";
temp = temp.Replace("|Dipl.|Ing.|", "|DI|").Replace("|Dipl|Ing.|", "|DI|")
.Replace("|Kaiser|Ebersdorf|", "|Kaiser-Ebersdorf|")
.Replace("|Kalk|Deutz|", "|Kalk-Deutz|").Replace("|Kalk|Deutzer|", "|Kalk-Deutzer|")
.Replace("|Hohe|Wand|", "|Hohe Wand|").Replace("|Hohen|Wand|", "|Hohen Wand|").Replace("|Hohewand|", "|Hohe Wand|")
.Replace("|Alten|Markt|", "|Altenmarkt|")
.Replace("|Kirch|Hof|", "|Kirchhof|").Replace("|Bisc|Hof|", "|Bischof|")
.Replace("|Wiener|Neustadt|", "|Wiener Neustadt|").Replace("|Wiener|Neustädter|", "|Wiener Neustädter|")
.Replace("|St.|Pölten|", "|St. Pölten|").Replace("|St.|Pöltner|", "|St. Pöltner|")
.Replace("|Gross|", "|Groß|")
.Replace("|Nr|", "|Nr.|").Replace("|Und|", "|und|");
words = [.. temp.Trim('|').Split('|')];
if (words.Count > 1 && words[0] == "Groß" || words[0] == "Klein" && !StreetTypes.Contains(words[1])) {
var w = words[0] + words[1].ToLowerInvariant();
words.RemoveRange(0, 2);
words.Insert(0, w);
}
if (words[0].StartsWith("Groß") || words[0].StartsWith("Klein")) {
var repl = LocalityNames.FirstOrDefault(n => n.Replace(" ", "").Replace("-", "").Equals(words[0], StringComparison.InvariantCultureIgnoreCase));
if (repl != null) words[0] = repl;
}
if (words.Count > 2 && words[^2] == "in") {
// remove suffix "in ..."
words.RemoveRange(words.Count - 2, 2);
}
char sep = name.Contains('-') ? '-' : ' ';
if (words.Count <= 1) {
// single word
} else if (StreetTypes.Contains(words[0]) && !StreetTypes.Contains(words[^1])) {
// "Straße der ..." / "Straße des ..."
sep = ' ';
} else if (StreetTypes.Contains(words[^1])) {
// "... Straße" / "...straße"
var type = words[^1];
var isCompound = true;
var isAdjective = StreetAdjectives.Contains(words[^2].ToLowerInvariant());
var isGeographical = false;
var isPerson = false;
var geo = words.FindLastIndex(IsGeographicalDerivativeName);
if (geo >= 0 && geo < words.Count - 2 && words[^2] != "Haupt" && words[^2] != "Bundes" && words[^2] != "Keller") {
isPerson = true;
isGeographical = true;
} else if (words.Any(w => w.Contains(' ') && !w.IsGeographicalDerivativeName())) {
isPerson = true;
isGeographical = true;
} else if (StreetNumbers.Contains(words[0])) {
isPerson = true;
isGeographical = true;
} else if (!StreetPrepositions.Contains(words[0]) && !StreetArticles.Contains(words[0]) && words.FindIndex(StreetPrepositions.Contains) > 0) {
isPerson = true;
isGeographical = true;
}
if (words.Count(w => w.Contains('.')) >= 2 || words.Contains("van") || words.Contains("von") || words.Contains("St.") ||
StreetPersonTitles.Contains(words[0]) || words.Any(w => w.IsGivenName()))
{
isPerson = true;
}
if (!isPerson && isAdjective || StreetArticles.Contains(words[^2]) || StreetPrepositions.Contains(words[^2])) {
isCompound = false;
}
if (!isPerson && words[^2].EndsWith("er") && !StreetLastNames.Contains(words[^2])) {
isGeographical = IsGeographicalDerivativeName(words[^2]);
} else if (words.Count >= 3 && words.Contains("Bad")) {
isPerson = true;
isGeographical = true;
}
sep = isPerson ? '-' : ' ';
if (isPerson && words.Count == 2 && !isGeographical) {
isPerson = false;
isCompound = true;
}
if (isGeographical || isPerson) {
isCompound = false;
}
if (isCompound) {
var w = words[^2] + type.ToLowerInvariant();
words.RemoveRange(words.Count - 2, 2);
words.Add(w);
}
if (isPerson) {
words = [.. words.Select(w => w.Replace(' ', '-'))];
}
} else {
// unknown
}
if (sep == '-' && StreetPrepositions.Contains(words[0]) && words[0] != "von" && words[0] != "van") {
sep = ' ';
int i;
for (i = 0; StreetPrepositions.Contains(words[i]) && i < words.Count; i++);
var word = string.Join('-', words.Skip(i));
words = [.. words.Take(i).Append(word)];
}
var norm = string.Join(sep, words);
return string.Concat(norm[0..1].ToUpperInvariant(), norm.AsSpan(1));
}
}
}
+36 -9
View File
@@ -323,7 +323,7 @@ namespace Elwig.Helpers {
} }
public class Footer { public class Footer {
private readonly List<List<object>> Items = [[]]; private readonly List<List<List<object>>> Items = [[[]]];
private readonly string LineBreak; private readonly string LineBreak;
private readonly string Separator; private readonly string Separator;
@@ -334,7 +334,7 @@ namespace Elwig.Helpers {
public Footer Item(object? text) { public Footer Item(object? text) {
if (text == null) return this; if (text == null) return this;
Items[^1].Add(text); Items[^1][^1].Add(text);
return this; return this;
} }
@@ -342,13 +342,38 @@ namespace Elwig.Helpers {
return text == null ? this : Item($"{name}: {text}"); return text == null ? this : Item($"{name}: {text}");
} }
public Footer NextGroup() {
Items[^1].Add([]);
return this;
}
public Footer NextLine() { public Footer NextLine() {
Items.Add([]); Items.Add([[]]);
return this;
}
public Footer RebalanceLines() {
for (int i = 0; i < Items.Count - 1; i++) {
var groups = Items[i];
var nextGroups = Items[i + 1];
if (groups.Count <= 1) continue;
var g = groups[^1];
var gl = g.Sum(i => (i is Text txt ? txt.GetText().Length : i.ToString()?.Length ?? 0) + Separator.Length);
var l1a = groups.SelectMany(g => g).Sum(i => (i is Text txt ? txt.GetText().Length : i.ToString()?.Length ?? 0) + Separator.Length) - Separator.Length;
var l2a = nextGroups.SelectMany(g => g).Sum(i => (i is Text txt ? txt.GetText().Length : i.ToString()?.Length ?? 0) + Separator.Length) - Separator.Length;
var l1b = l1a - gl;
var l2b = l2a + gl;
if (Math.Abs(l1b - l2b) <= Math.Abs(l1a - l2a)) {
Items[i].Remove(g);
Items[i + 1].Insert(0, g);
i--;
}
}
return this; return this;
} }
public override string ToString() { public override string ToString() {
return string.Join(LineBreak, Items.Select(l => string.Join(Separator, l.ToString()))); return string.Join(LineBreak, Items.Select(l => string.Join(Separator, l.SelectMany(g => g).ToString())));
} }
public IList<ILeafElement> ToLeafElements() { public IList<ILeafElement> ToLeafElements() {
@@ -357,10 +382,12 @@ namespace Elwig.Helpers {
foreach (var line in Items) { foreach (var line in Items) {
if (!first1) l.Add(new Text(LineBreak)); if (!first1) l.Add(new Text(LineBreak));
var first2 = true; var first2 = true;
foreach (var item in line) { foreach (var group in line) {
if (!first2) l.Add(new Text(Separator)); foreach (var item in group) {
l.Add(item as ILeafElement ?? new Text(item.ToString() ?? "")); if (!first2) l.Add(new Text(Separator));
first2 = false; l.Add(item as ILeafElement ?? new Text(item.ToString() ?? ""));
first2 = false;
}
} }
first1 = false; first1 = false;
} }
@@ -675,7 +702,7 @@ namespace Elwig.Helpers {
p[2], p[2],
int.Parse(p[3]), int.Parse(p[3]),
p[4], p[4],
p[5].Split(',').Select(a => a.Replace(" | ", "\n")).ToArray(), [.. p[5].Split(',').Select(a => a.Replace(" | ", "\n"))],
p[6], p[6],
p[7].Split(',') p[7].Split(',')
)); ));
+66 -7
View File
@@ -623,6 +623,37 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static ValidationResult CheckOrganicOperatorId(TextBox input, bool required) {
string text = "";
int pos = input.CaretIndex;
for (int i = 0, v = 0; i < input.Text.Length; i++) {
char ch = input.Text[i];
if (char.IsAsciiDigit(ch)) {
if (v == 3 && text[^1] != '-')
text += "-";
v++;
text += ch;
}
if (i == input.CaretIndex - 1) {
pos = text.Length;
} else if (v >= 10) {
break;
}
}
input.Text = text;
input.CaretIndex = pos;
if (text.Length == 0) {
return required ? new(false, "OOC-ID ist nicht optional") : new(true, null);
} else if (!text.StartsWith("040-") || text.Length != 11) {
return new(false, "OOC-ID ist ungültig");
}
return new(true, null);
}
public static ValidationResult CheckOrganicAuthorityCode(TextBox input, bool required) { public static ValidationResult CheckOrganicAuthorityCode(TextBox input, bool required) {
string text = ""; string text = "";
int pos = input.CaretIndex; int pos = input.CaretIndex;
@@ -631,15 +662,18 @@ namespace Elwig.Helpers {
if (v < 2 && char.IsAsciiLetter(ch)) { if (v < 2 && char.IsAsciiLetter(ch)) {
v++; v++;
text += ch; text += ch;
} else if ((v == 2 || v == 6) && ch == '-') { } else if ((v == 2 || v == 5) && ch == '-' && text[^1] != '-') {
v++;
text += ch; 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 (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++; v++;
text += ch; 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++; v++;
text += ch; text += ch;
} }
@@ -647,11 +681,16 @@ namespace Elwig.Helpers {
v++; v++;
text += ch; 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) { if (i == input.CaretIndex - 1) {
pos = text.Length; pos = text.Length;
} else if (text.StartsWith("AT") && v >= 10) { } else if ((text.StartsWith("AT") || text.StartsWith("DE")) && v >= 10) {
break; break;
} }
} }
@@ -662,7 +701,7 @@ namespace Elwig.Helpers {
if (text.Length == 0) if (text.Length == 0)
return required ? new(false, "Bio-Kontrollstellen-Code ist nicht optional") : new(true, null); 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) { if (text.Length != 10) {
return new(false, "Bio-Kontrollstellen-Code ist ungültig"); return new(false, "Bio-Kontrollstellen-Code ist ungültig");
} }
@@ -672,5 +711,25 @@ namespace Elwig.Helpers {
return new(true, null); return new(true, null);
} }
public static ValidationResult CheckAddress(TextBox input, bool required, string? locality = null) {
var text = input.Text;
var (name, nr) = Utils.SplitAddress(text);
if (!string.IsNullOrWhiteSpace(nr)) {
var street = Normalizer.NormalizeStreetName(name.Replace(",", ""), locality);
var idx = input.CaretIndex;
input.Text = $"{street} {nr}".Trim();
if (idx >= (text.Length - nr.Length)) {
input.CaretIndex = street.Length + (idx - (text.Length - nr.Length)) + 1;
} else if (idx > 0) {
input.CaretIndex = idx;
}
}
if (text.Length == 0)
return required ? new(false, "Adresse ist nicht optional") : new(true, null);
return new(true, null);
}
} }
} }
+6
View File
@@ -96,6 +96,12 @@ namespace Elwig.Models.Entities {
[Column("ustid_nr")] [Column("ustid_nr")]
public string? UstIdNr { get; set; } 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")] [Column("juridical_pers")]
public bool IsJuridicalPerson { get; set; } public bool IsJuridicalPerson { get; set; }
+4 -1
View File
@@ -76,7 +76,10 @@ namespace Elwig.Models.Entities {
[Column("shares")] [Column("shares")]
public int Shares { get; set; } public int Shares { get; set; }
[Column("signed")] [Column("signed_company")]
public bool CompanyHasSigned { get; set; }
[Column("signed_member")]
public bool MemberHasSigned { get; set; } public bool MemberHasSigned { get; set; }
[Column("value_per_share")] [Column("value_per_share")]
+3
View File
@@ -11,6 +11,9 @@ namespace Elwig.Models.Entities {
[Column("name")] [Column("name")]
public required string Name { get; set; } public required string Name { get; set; }
[Column("organic")]
public bool IsOrganic { get; set; }
[Column("description")] [Column("description")]
public string? Description { get; set; } public string? Description { get; set; }
+39
View File
@@ -0,0 +1,39 @@
-- 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;
ALTER TABLE wine_cultivation ADD COLUMN organic INTEGER NOT NULL CHECK (organic IN (TRUE, FALSE)) DEFAULT FALSE;
ALTER TABLE member_history RENAME COLUMN signed TO signed_member;
ALTER TABLE member_history ADD COLUMN signed_company INTEGER NOT NULL CHECK (signed_company IN (TRUE, FALSE)) DEFAULT FALSE;
UPDATE client_parameter SET value = '0' WHERE param = 'ENABLE_TIME_TRIGGERS';
UPDATE member SET org_auth_code = UPPER(SUBSTR(a.name, -11, 10))
FROM member m JOIN member_billing_address a ON a.mgnr = m.mgnr
WHERE m.mgnr = member.mgnr AND a.name LIKE '%AT-BIO-___)';
UPDATE member SET org_auth_code = UPPER(suffix) WHERE suffix LIKE 'AT-BIO-___';
UPDATE wine_cultivation SET organic = TRUE WHERE name LIKE '%bio%' OR name LIKE '%org%' OR description LIKE '%AT-BIO-%';
UPDATE wine_cultivation SET description = NULL WHERE description LIKE '%AT-BIO-%';
UPDATE wine_cultivation SET description = 'Biologische Produktion' WHERE organic;
UPDATE client_parameter SET param = 'CLIENT_ORGANIC_AUTHORITYCODE' WHERE param = 'CLIENT_ORGANIC_AUTHORITY';
UPDATE member_telephone_number SET comment = NULL WHERE comment = '';
UPDATE member_email_address SET comment = NULL WHERE comment = '';
UPDATE member SET address = 'Untere Weißgerberstraße' WHERE address = 'U. Weißgasse Straße';
UPDATE member SET address = 'Edgar-Penzig-Franz-Straße' WHERE address = 'Edgar-Penzing-Franz-Straße';
UPDATE member
SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest))
WHERE address != NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member.postal_dest));
UPDATE member_billing_address
SET address = NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member_billing_address.postal_dest))
WHERE address != NORMALIZE_ADDRESS(address, (SELECT o.name FROM AT_plz_dest p JOIN AT_ort o ON o.okz = p.okz WHERE p.id = member_billing_address.postal_dest));
UPDATE client_parameter SET value = '1' WHERE param = 'ENABLE_TIME_TRIGGERS';
@@ -26,6 +26,7 @@ namespace Elwig.Services {
vm.DateEffective = h.DateEffective; vm.DateEffective = h.DateEffective;
vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : ""); vm.Reason = h.Reason + (h.Reason == MemberHistory.REASON_TRANSFER ? $"_{(h.ToMgNr == mgnr ? "from" : "to")}" : "");
vm.Shares = h.Shares; vm.Shares = h.Shares;
vm.CompanyHasSigned = h.CompanyHasSigned;
vm.MemberHasSigned = h.MemberHasSigned; vm.MemberHasSigned = h.MemberHasSigned;
vm.ValuePerShare = h.ValuePerShare; vm.ValuePerShare = h.ValuePerShare;
vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null; vm.OtherMgNr = h.FromMgNr != null && h.FromMgNr != mgnr ? h.FromMgNr : h.ToMgNr != null && h.ToMgNr != mgnr ? h.ToMgNr : null;
@@ -63,6 +64,7 @@ namespace Elwig.Services {
Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT, Reason = vm.Reason?.Split("_")[0] ?? MemberHistory.REASON_CONVERT,
Source = "manual", Source = "manual",
Shares = vm.Shares ?? 0, Shares = vm.Shares ?? 0,
CompanyHasSigned = vm.CompanyHasSigned,
MemberHasSigned = vm.MemberHasSigned, MemberHasSigned = vm.MemberHasSigned,
ValuePerShare = vm.ValuePerShare, ValuePerShare = vm.ValuePerShare,
CurrencyCode = vm.ValuePerShare != null ? "EUR" : null, CurrencyCode = vm.ValuePerShare != null ? "EUR" : null,
+79 -5
View File
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Media;
namespace Elwig.Services { namespace Elwig.Services {
public static class MemberService { public static class MemberService {
@@ -19,6 +20,7 @@ namespace Elwig.Services {
}; };
public static async Task InitInputs(this MemberAdminViewModel vm) { public static async Task InitInputs(this MemberAdminViewModel vm) {
vm.IsMemberSelectedOrCreating = true;
using var ctx = new AppDbContext(); using var ctx = new AppDbContext();
vm.MgNr = await ctx.NextMgNr(); vm.MgNr = await ctx.NextMgNr();
vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy"); vm.EntryDate = DateTime.Now.ToString("dd.MM.yyyy");
@@ -31,6 +33,7 @@ namespace Elwig.Services {
public static void ClearInputs(this MemberAdminViewModel vm) { public static void ClearInputs(this MemberAdminViewModel vm) {
vm.IsMemberSelected = false; vm.IsMemberSelected = false;
vm.IsMemberSelectedOrCreating = false;
vm.MemberHasEmail = false; vm.MemberHasEmail = false;
vm.MemberCanSendEmail = false; vm.MemberCanSendEmail = false;
vm.EnableMemberReferenceButton = false; vm.EnableMemberReferenceButton = false;
@@ -49,10 +52,15 @@ namespace Elwig.Services {
vm.ModifiedAtShort = "-"; vm.ModifiedAtShort = "-";
vm.ExportedAt = "-"; vm.ExportedAt = "-";
vm.ImportedAt = "-"; vm.ImportedAt = "-";
vm.OrganicState = null;
vm.OrganicColor = Brushes.Black;
vm.OrganicDetails = "-";
vm.OrganicToolTip = null;
} }
public static void FillInputs(this MemberAdminViewModel vm, Member m) { public static void FillInputs(this MemberAdminViewModel vm, Member m) {
vm.IsMemberSelected = true; vm.IsMemberSelected = true;
vm.IsMemberSelectedOrCreating = true;
vm.MgNr = m.MgNr; vm.MgNr = m.MgNr;
vm.PredecessorMgNr = m.PredecessorMgNr; vm.PredecessorMgNr = m.PredecessorMgNr;
vm.IsJuridicalPerson = m.IsJuridicalPerson; vm.IsJuridicalPerson = m.IsJuridicalPerson;
@@ -106,6 +114,8 @@ namespace Elwig.Services {
vm.UstIdNr = m.UstIdNr; vm.UstIdNr = m.UstIdNr;
vm.LfbisNr = m.LfbisNr; vm.LfbisNr = m.LfbisNr;
vm.OrganicOperatorId = m.OrganicOperatorId;
vm.OrganicAuthorityCode = m.OrganicAuthorityCode;
vm.IsBuchführend = m.IsBuchführend; vm.IsBuchführend = m.IsBuchführend;
vm.IsOrganic = m.IsOrganic; vm.IsOrganic = m.IsOrganic;
@@ -187,10 +197,9 @@ namespace Elwig.Services {
.GroupBy(d => d.Year) .GroupBy(d => d.Year)
.ToDictionaryAsync(g => g.Key, g => g.Any()); .ToDictionaryAsync(g => g.Key, g => g.Any());
if (m.MgNr != vm.MgNr)
return;
await App.MainDispatcher.BeginInvoke(() => { await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr)
return;
var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []); var (d1Grid, _) = DeliveryService.GenerateToolTip(d1GridData, []);
var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []); var (d2Grid, _) = DeliveryService.GenerateToolTip(d2GridData, []);
var grid = AreaComService.GenerateToolTip(gridData); var grid = AreaComService.GenerateToolTip(gridData);
@@ -203,13 +212,76 @@ namespace Elwig.Services {
vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}"; vm.StatusAreaCommitmentInfo = $"{Utils.CurrentLastSeason}";
vm.StatusAreaCommitment = text; vm.StatusAreaCommitment = text;
vm.StatusAreaCommitmentToolTip = grid; vm.StatusAreaCommitmentToolTip = grid;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, false)).ToList(); vm.MemberHasDeliveries = [.. Enumerable.Range(0, 9999).Select(i => deliveries.GetValueOrDefault(i, false))];
}); });
}); });
vm.OrganicState = null;
vm.OrganicColor = Brushes.Black;
vm.OrganicToolTip = null;
if (!vm.IsOrganic) {
vm.OrganicDetails = "-";
} else {
vm.OrganicDetails = "...";
Utils.RunBackground("Bio-Zertifikatsdaten laden", async () => {
if (App.MainDispatcher == null) return;
var certs = await OrganicService.GetTracesCertificates(m);
await App.MainDispatcher.BeginInvoke(() => {
if (m.MgNr != vm.MgNr) return;
vm.FillOrganicInputs(certs.Length > 0 ? certs[0] : null);
});
});
}
vm.MemberHasEmail = m.EmailAddresses.Count > 0; vm.MemberHasEmail = m.EmailAddresses.Count > 0;
vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0; vm.MemberCanSendEmail = App.Config.Smtp != null && m.EmailAddresses.Count > 0;
vm.MemberHasDeliveries = Enumerable.Range(0, 9999).Select(i => false).ToList(); vm.MemberHasDeliveries = [.. Enumerable.Range(0, 9999).Select(i => false)];
}
public static void FillOrganicInputs(this MemberAdminViewModel vm, OrganicService.TracesCertificate? cert) {
vm.OrganicDetails = null;
if (cert == null) {
vm.OrganicState = "Nicht gefunden";
vm.OrganicColor = Brushes.DarkRed;
vm.OrganicToolTip = null;
return;
}
vm.OrganicToolTip = $"""
Zertifikat-Nr.: {cert.Id}
Status: {cert.Status}
Bio-KSt.: {cert.AuthorityCode}
EU Operator-ID: {cert.OperatorId}
Operator: {cert.OperatorName}
Ausgestellt am: {cert.IssuedOn:dd.MM.yyyy}
Aktualisiert am: {cert.LastStatusUpdateOn:dd.MM.yyyy}
Erlischt am: {cert.ExpiresOn:dd.MM.yyyy}
""";
if (cert.IsValidForWineProduction) {
vm.OrganicState = "Gültig";
vm.OrganicDetails = $" ({cert.ExpiresOn:MMM. yyyy})";
vm.OrganicColor = Brushes.DarkGreen;
} else if (cert.IsValid) {
vm.OrganicState = "Gültig";
vm.OrganicDetails = $" ({cert.ExpiresOn:MMM. yyyy})";
vm.OrganicColor = Brushes.OrangeRed;
} else {
vm.OrganicColor = Brushes.DarkRed;
switch (cert.Status) {
case OrganicService.TracesStatus.ISSUED:
vm.OrganicState = "Ausgestellt";
vm.OrganicDetails = $" ({cert.LastStatusUpdateOn:MMM. yyyy})"; break;
case OrganicService.TracesStatus.SUSPENDED:
vm.OrganicState = "Gesperrt";
vm.OrganicDetails = $" ({cert.LastStatusUpdateOn:MMM. yyyy})"; break;
case OrganicService.TracesStatus.EXPIRED:
vm.OrganicState = "Ausgelaufen";
vm.OrganicDetails = $" ({cert.LastStatusUpdateOn:MMM. yyyy})"; break;
case OrganicService.TracesStatus.WITHDRAWN:
vm.OrganicState = "Widerrufen";
vm.OrganicDetails = $" ({cert.LastStatusUpdateOn:MMM. yyyy})"; break;
}
}
} }
public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) { public static async Task<(List<string>, IQueryable<Member>, List<string>)> GetFilters(this MemberAdminViewModel vm, AppDbContext ctx) {
@@ -555,6 +627,8 @@ namespace Elwig.Services {
UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr, UstIdNr = string.IsNullOrWhiteSpace(vm.UstIdNr) ? null : vm.UstIdNr,
LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr, LfbisNr = string.IsNullOrWhiteSpace(vm.LfbisNr) ? null : vm.LfbisNr,
OrganicOperatorId = string.IsNullOrWhiteSpace(vm.OrganicOperatorId) ? null : vm.OrganicOperatorId,
OrganicAuthorityCode = string.IsNullOrWhiteSpace(vm.OrganicAuthorityCode) ? null : vm.OrganicAuthorityCode,
IsBuchführend = vm.IsBuchführend, IsBuchführend = vm.IsBuchführend,
IsOrganic = vm.IsOrganic, IsOrganic = vm.IsOrganic,
+162
View File
@@ -0,0 +1,162 @@
using Elwig.Helpers;
using Elwig.Models.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
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 static readonly Dictionary<int, (DateTime Timestamp, TracesCertificate[] Certificates)> _memberCerts = [];
public async static Task<TracesCertificate[]> GetTracesCertificates(Member m, bool? tryNameAndAddress = null) {
if (_memberCerts.TryGetValue(m.MgNr, out var entry) && entry.Timestamp >= DateTime.Now.AddDays(-1))
return entry.Certificates;
var certs = await FetchTracesCertificates(m, tryNameAndAddress);
_memberCerts[m.MgNr] = (DateTime.Now, certs);
return certs;
}
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());
return resJson?.AsArray() ?? throw new JsonException();
}
public static Task<TracesCertificate[]> FetchTracesCertificates(Member m, bool? tryNameAndAddress = null) {
return FetchTracesCertificates(m.OrganicOperatorId, m.LfbisNr,
(tryNameAndAddress ?? m.IsOrganic) ? m.BillingAddress is BillingAddr a ?
(a.FullName, a.Address, $"{a.PostalDest.AtPlz?.Plz}") :
(m.FullName, m.Address, $"{m.PostalDest.AtPlz?.Plz}") :
null);
}
public async static Task<TracesCertificate[]> FetchTracesCertificates(string? oocId = null, string? lfbisNr = null, (string Name, string Address, string PostalCode)? address = null) {
bool searchedByName = false;
JsonArray jsonCerts = [];
if (jsonCerts.Count == 0 && oocId != null) {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "ooc_identifier" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", oocId },
});
}
if (jsonCerts.Count == 0 && lfbisNr != null) {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorIdentifierType", "comp_reg" },
{ "operatorIdentifierSearchOperator", "STRICT" },
{ "operatorIdentifier", lfbisNr.TrimStart('0') },
});
}
if (jsonCerts.Count == 0 && address != null) {
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
{ "operatorPostalCode", address.Value.PostalCode },
// FIXME maxlen 40!
{ "query", $"{Regex.Replace(address.Value.Name, @"\(.*?\)", "")} {address.Value.Address}" },
});
searchedByName = true;
}
if (jsonCerts.Count == 0)
return [];
TracesCertificate[] 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()),
})];
return (!searchedByName || certs.Where(c => c.Status == TracesStatus.ISSUED).Select(c => c.OperatorId).ToHashSet().Count == 1) ? certs : [];
}
public static string? GetTracesSearchUrl(Member m, bool? tryNameAndAddress = null) {
return GetTracesSearchUrl(m.OrganicOperatorId, m.LfbisNr,
(tryNameAndAddress ?? m.IsOrganic) ? m.BillingAddress is BillingAddr a ?
(a.FullName, a.Address, $"{a.PostalDest.AtPlz?.Plz}") :
(m.FullName, m.Address, $"{m.PostalDest.AtPlz?.Plz}") :
null);
}
public static string? GetTracesSearchUrl(string? oocId = null, string? lfbisNr = null, (string Name, string Address, string PostalCode)? address = null) {
var baseUrl = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/index";
if (oocId != null) {
return $"{baseUrl}#!?countryCode=AT&operatorIdentifierType=ooc_identifier&operatorIdentifierSearchOperator=STRICT&operatorIdentifier={oocId}&states=ISSUED";
} else if (lfbisNr != null) {
return $"{baseUrl}#!?countryCode=AT&operatorIdentifierType=comp_reg&operatorIdentifierSearchOperator=STRICT&operatorIdentifier={lfbisNr.TrimStart('0')}&states=ISSUED";
} else if (address != null) {
return $"{baseUrl}#!?countryCode=AT&operatorPostalCode={address?.PostalCode}&query={address?.Name.Replace(' ', '+')}+{address?.Address.Replace(' ', '+')}&states=ISSUED";
} else {
return null;
}
}
public static string? GetEasyCertSearchUrl(string lfbisNr) {
var baseUrl = "https://www.easy-cert.com/htm/suchergebnis.htm";
return $"{baseUrl}?CountryCode=AT&CustomerNumber={lfbisNr.TrimStart('0')}";
}
public static string? GetBiocSearchUrl(string lfbisNr) {
var baseUrl = "https://www.bioc.info/search/producersearchresult";
return $"{baseUrl}?producerSearch[country]=a1e51f85-27b5-a1e3-65f7-561569884d38&producerSearch[operatorId]={lfbisNr.TrimStart('0')}";
}
}
}
+16
View File
@@ -6,6 +6,7 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
namespace Elwig.ViewModels { namespace Elwig.ViewModels {
public partial class MemberAdminViewModel : ObservableObject { public partial class MemberAdminViewModel : ObservableObject {
@@ -33,6 +34,8 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _isMemberSelected; private bool _isMemberSelected;
[ObservableProperty] [ObservableProperty]
private bool _isMemberSelectedOrCreating;
[ObservableProperty]
private bool _memberHasEmail; private bool _memberHasEmail;
[ObservableProperty] [ObservableProperty]
private bool _memberCanSendEmail; private bool _memberCanSendEmail;
@@ -118,10 +121,23 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private string? _lfbisNr; private string? _lfbisNr;
[ObservableProperty] [ObservableProperty]
private string? _organicOperatorId;
[ObservableProperty]
private string? _organicAuthorityCode;
[ObservableProperty]
private bool _isBuchführend; private bool _isBuchführend;
[ObservableProperty] [ObservableProperty]
private bool _isOrganic; private bool _isOrganic;
[ObservableProperty]
private string? _organicState;
[ObservableProperty]
private string? _organicDetails = "-";
[ObservableProperty]
private Brush? _organicColor = Brushes.Black;
[ObservableProperty]
private string? _organicToolTip;
[ObservableProperty] [ObservableProperty]
private string? _entryDate; private string? _entryDate;
[ObservableProperty] [ObservableProperty]
@@ -52,6 +52,8 @@ namespace Elwig.ViewModels {
[ObservableProperty] [ObservableProperty]
private bool _deduct; private bool _deduct;
[ObservableProperty] [ObservableProperty]
private bool _companyHasSigned;
[ObservableProperty]
private bool _memberHasSigned; private bool _memberHasSigned;
[ObservableProperty] [ObservableProperty]
private string? _valuePerShareString; private string? _valuePerShareString;
+17 -1
View File
@@ -434,7 +434,7 @@ namespace Elwig.Windows {
UpdateButtons(); UpdateButtons();
} }
protected void TextBox_TextChanged(object sender, RoutedEventArgs? evt) { protected void TextBox_TextChanged(object sender, TextChangedEventArgs? evt) {
var input = (Control)sender; var input = (Control)sender;
var tb = input as TextBox; var tb = input as TextBox;
if (SenderIsRequired(input) && tb?.Text.Length == 0) { if (SenderIsRequired(input) && tb?.Text.Length == 0) {
@@ -576,6 +576,14 @@ namespace Elwig.Windows {
InputLostFocus((TextBox)sender, Validator.CheckLfbisNr); InputLostFocus((TextBox)sender, Validator.CheckLfbisNr);
} }
protected void OrganicOperatorIdInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckOrganicOperatorId);
}
protected void OrganicOperatorIdInput_LostFocus(object sender, RoutedEventArgs? evt) {
InputLostFocus((TextBox)sender, Validator.CheckOrganicOperatorId);
}
protected void OrganicAuthorityCodeInput_TextChanged(object sender, TextChangedEventArgs? evt) { protected void OrganicAuthorityCodeInput_TextChanged(object sender, TextChangedEventArgs? evt) {
InputTextChanged((TextBox)sender, Validator.CheckOrganicAuthorityCode); InputTextChanged((TextBox)sender, Validator.CheckOrganicAuthorityCode);
} }
@@ -595,5 +603,13 @@ namespace Elwig.Windows {
protected void AreaComTypeDetailsButton_Click(object sender, RoutedEventArgs? evt) { protected void AreaComTypeDetailsButton_Click(object sender, RoutedEventArgs? evt) {
App.FocusBaseDataAreaComType(); App.FocusBaseDataAreaComType();
} }
protected void AddressInput_TextChanged(object sender, TextChangedEventArgs? evt) {
if ((IsCreating || IsEditing) && sender is TextBox b) {
InputTextChanged(b, (i, r) => Validator.CheckAddress(i, r));
} else {
TextBox_TextChanged(sender, evt);
}
}
} }
} }
+23 -13
View File
@@ -90,7 +90,7 @@
<Label Content="Adresse:" <Label Content="Adresse:"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,160,0,10"/> VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="0" Margin="10,160,0,10"/>
<TextBox x:Name="ClientAddressInput" <TextBox x:Name="ClientAddressInput"
TextChanged="TextBox_TextChanged" TextChanged="AddressInput_TextChanged"
VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="0,160,10,10"/> VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Column="1" Margin="0,160,10,10"/>
<Label Content="PLZ/Ort:" <Label Content="PLZ/Ort:"
@@ -109,24 +109,30 @@
<TextBox x:Name="ClientBicInput" Margin="0,250,10,0" Grid.Column="1" <TextBox x:Name="ClientBicInput" Margin="0,250,10,0" Grid.Column="1"
TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/> TextChanged="BicInput_TextChanged" LostFocus="BicInput_LostFocus"/>
<Label Content="UID:"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,100,0,10"/>
<TextBox x:Name="ClientUstIdNrInput"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,100,10,10" Width="96"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Betriebs-Nr.:" <Label Content="Betriebs-Nr.:"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,130,0,10"/> VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,100,0,10"/>
<TextBox x:Name="ClientLfbisNrInput" <TextBox x:Name="ClientLfbisNrInput"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,130,10,10" Width="64" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,100,10,10" Width="64"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/> TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<Label Content="Bio-Kontrollstelle:" <Label Content="UID:"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,110,10"/> VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,130,0,10"/>
<TextBox x:Name="ClientOrganicAuthorityInput" <TextBox x:Name="ClientUstIdNrInput"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,10,10" Width="90" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="3" Margin="0,130,10,10" Width="96"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Bio-KSt.:" ToolTip="Bio-Konstrollstelle"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,100,110,10"/>
<TextBox x:Name="ClientOrganicAuthorityCodeInput"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,100,10,10" Width="91"
TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/> TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/>
<Label Content="OOC-ID:" ToolTip="Bio-EU-Operator-ID / Organic Operator Certificate ID"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,110,10"/>
<TextBox x:Name="ClientOrganicOperatorIdInput"
VerticalAlignment="Top" HorizontalAlignment="Right" Grid.Column="3" Margin="10,130,10,10" Width="91"
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
<Label Content="Telefon-Nr.:" <Label Content="Telefon-Nr.:"
VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,160,0,10"/> VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="2" Margin="10,160,0,10"/>
<TextBox x:Name="ClientPhoneNrInput" Margin="0,160,10,10" Grid.Column="3" <TextBox x:Name="ClientPhoneNrInput" Margin="0,160,10,10" Grid.Column="3"
@@ -320,6 +326,10 @@
<Label Content="Beschreibung:" Margin="10,70,0,10"/> <Label Content="Beschreibung:" Margin="10,70,0,10"/>
<TextBox x:Name="WineCultivationDescriptionInput" Grid.Column="1" Margin="0,70,10,10" <TextBox x:Name="WineCultivationDescriptionInput" Grid.Column="1" Margin="0,70,10,10"
TextChanged="WineCultivation_Changed"/> TextChanged="WineCultivation_Changed"/>
<CheckBox x:Name="WineCultivationOrganicInput" Content="Bio" Grid.Column="1" Margin="0,105,10,10"
Checked="WineCultivation_Changed" Unchecked="WineCultivation_Changed"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid> </Grid>
</Grid> </Grid>
</TabItem> </TabItem>
@@ -72,10 +72,12 @@ namespace Elwig.Windows {
WineCultivationIdInput.Text = ""; WineCultivationIdInput.Text = "";
WineCultivationNameInput.Text = ""; WineCultivationNameInput.Text = "";
WineCultivationDescriptionInput.Text = ""; WineCultivationDescriptionInput.Text = "";
WineCultivationOrganicInput.IsChecked = false;
} else { } else {
WineCultivationIdInput.Text = cult.CultId; WineCultivationIdInput.Text = cult.CultId;
WineCultivationNameInput.Text = cult.Name; WineCultivationNameInput.Text = cult.Name;
WineCultivationDescriptionInput.Text = cult.Description; WineCultivationDescriptionInput.Text = cult.Description;
WineCultivationOrganicInput.IsChecked = cult.IsOrganic;
} }
_cultUpdate = false; _cultUpdate = false;
} }
@@ -105,7 +107,8 @@ namespace Elwig.Windows {
_cultChanged = _cultChanged || _cultChanged = _cultChanged ||
WineCultivationIdInput.Text != cult.CultId || WineCultivationIdInput.Text != cult.CultId ||
WineCultivationNameInput.Text != cult.Name || WineCultivationNameInput.Text != cult.Name ||
WineCultivationDescriptionInput.Text != (cult.Description ?? ""); WineCultivationDescriptionInput.Text != (cult.Description ?? "") ||
WineCultivationOrganicInput.IsChecked != cult.IsOrganic;
var old = _cultIds.GetValueOrDefault(cult); var old = _cultIds.GetValueOrDefault(cult);
var id = WineCultivationIdInput.Text ?? ""; var id = WineCultivationIdInput.Text ?? "";
@@ -113,7 +116,8 @@ namespace Elwig.Windows {
cult.CultId = id; cult.CultId = id;
cult.Name = WineCultivationNameInput.Text ?? ""; cult.Name = WineCultivationNameInput.Text ?? "";
cult.Description = WineCultivationDescriptionInput.Text ?? ""; cult.Description = WineCultivationDescriptionInput.Text ?? "";
if (cult.Description.Length == 0) cult.Description = null; cult.IsOrganic = WineCultivationOrganicInput.IsChecked ?? false;
if (string.IsNullOrWhiteSpace(cult.Description)) cult.Description = null;
CollectionViewSource.GetDefaultView(_cultList).Refresh(); CollectionViewSource.GetDefaultView(_cultList).Refresh();
UpdateButtons(); UpdateButtons();
+7 -3
View File
@@ -25,7 +25,7 @@ namespace Elwig.Windows {
BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput, BranchAddressInput, BranchPhoneNrInput, BranchFaxNrInput, BranchMobileNrInput,
WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput, WineAttributeIdInput, WineAttributeNameInput, WineAttributeActiveInput,
WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput, WineAttributeMaxKgPerHaInput, WineAttributeStrictInput, WineAttributeFillLowerInput,
WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput, WineCultivationIdInput, WineCultivationNameInput, WineCultivationDescriptionInput, WineCultivationOrganicInput,
AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput, AreaCommitmentTypeIdInput, AreaCommitmentTypeWineVariantInput, AreaCommitmentTypeWineAttributeInput,
AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput, AreaCommitmentTypeMinKgPerHaInput, AreaCommitmentTypePenaltyPerKgInput,
AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput, AreaCommitmentTypePenaltyInput, AreaCommitmentTypePenaltyNoneInput,
@@ -67,6 +67,7 @@ namespace Elwig.Windows {
WineCultivationIdInput.IsReadOnly = true; WineCultivationIdInput.IsReadOnly = true;
WineCultivationNameInput.IsReadOnly = true; WineCultivationNameInput.IsReadOnly = true;
WineCultivationDescriptionInput.IsReadOnly = true; WineCultivationDescriptionInput.IsReadOnly = true;
WineCultivationOrganicInput.IsEnabled = false;
AreaCommitmentTypeWineVariantInput.IsEnabled = false; AreaCommitmentTypeWineVariantInput.IsEnabled = false;
AreaCommitmentTypeWineAttributeInput.IsEnabled = false; AreaCommitmentTypeWineAttributeInput.IsEnabled = false;
@@ -115,6 +116,7 @@ namespace Elwig.Windows {
WineCultivationIdInput.IsReadOnly = false; WineCultivationIdInput.IsReadOnly = false;
WineCultivationNameInput.IsReadOnly = false; WineCultivationNameInput.IsReadOnly = false;
WineCultivationDescriptionInput.IsReadOnly = false; WineCultivationDescriptionInput.IsReadOnly = false;
WineCultivationOrganicInput.IsEnabled = true;
AreaCommitmentTypeWineVariantInput.IsEnabled = true; AreaCommitmentTypeWineVariantInput.IsEnabled = true;
AreaCommitmentTypeWineAttributeInput.IsEnabled = true; AreaCommitmentTypeWineAttributeInput.IsEnabled = true;
@@ -329,7 +331,8 @@ namespace Elwig.Windows {
ClientBicInput.Text = p.Bic; ClientBicInput.Text = p.Bic;
ClientUstIdNrInput.Text = p.UstIdNr; ClientUstIdNrInput.Text = p.UstIdNr;
ClientLfbisNrInput.Text = p.LfbisNr; ClientLfbisNrInput.Text = p.LfbisNr;
ClientOrganicAuthorityInput.Text = p.OrganicAuthority; ClientOrganicOperatorIdInput.Text = p.OrganicOperatorId;
ClientOrganicAuthorityCodeInput.Text = p.OrganicAuthorityCode;
ClientPhoneNrInput.Text = p.PhoneNr; ClientPhoneNrInput.Text = p.PhoneNr;
ClientFaxNrInput.Text = p.FaxNr; ClientFaxNrInput.Text = p.FaxNr;
ClientEmailAddressInput.Text = p.EmailAddress; ClientEmailAddressInput.Text = p.EmailAddress;
@@ -369,7 +372,8 @@ namespace Elwig.Windows {
p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text; p.Bic = string.IsNullOrWhiteSpace(ClientBicInput.Text) ? null : ClientBicInput.Text;
p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text; p.UstIdNr = string.IsNullOrWhiteSpace(ClientUstIdNrInput.Text) ? null : ClientUstIdNrInput.Text;
p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text; p.LfbisNr = string.IsNullOrWhiteSpace(ClientLfbisNrInput.Text) ? null : ClientLfbisNrInput.Text;
p.OrganicAuthority = string.IsNullOrWhiteSpace(ClientOrganicAuthorityInput.Text) ? null : ClientOrganicAuthorityInput.Text; p.OrganicOperatorId = string.IsNullOrWhiteSpace(ClientOrganicOperatorIdInput.Text) ? null : ClientOrganicOperatorIdInput.Text;
p.OrganicAuthorityCode = string.IsNullOrWhiteSpace(ClientOrganicAuthorityCodeInput.Text) ? null : ClientOrganicAuthorityCodeInput.Text;
p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text; p.PhoneNr = string.IsNullOrWhiteSpace(ClientPhoneNrInput.Text) ? null : ClientPhoneNrInput.Text;
p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text; p.FaxNr = string.IsNullOrWhiteSpace(ClientFaxNrInput.Text) ? null : ClientFaxNrInput.Text;
p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text; p.EmailAddress = string.IsNullOrWhiteSpace(ClientEmailAddressInput.Text) ? null : ClientEmailAddressInput.Text;
+64 -33
View File
@@ -371,7 +371,7 @@
<Label Content="Adresse:" Margin="10,130,0,0"/> <Label Content="Adresse:" Margin="10,130,0,0"/>
<TextBox x:Name="AddressInput" Text="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="AddressInput" Text="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3" Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"
TextChanged="TextBox_TextChanged"/> TextChanged="AddressInput_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/> <Label Content="PLZ/Ort:" Margin="10,160,0,0" Grid.Column="0"/>
<TextBox x:Name="PlzInput" Margin="0,160,0,0" Text="{Binding PlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="PlzInput" Margin="0,160,0,0" Text="{Binding PlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
@@ -379,6 +379,7 @@
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/> TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="OrtInput" SelectedItem="{Binding Ort, Mode=TwoWay}" ItemsSource="{Binding OrtSource, Mode=TwoWay}" <ComboBox x:Name="OrtInput" SelectedItem="{Binding Ort, Mode=TwoWay}" ItemsSource="{Binding OrtSource, Mode=TwoWay}"
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="OrtInput_SelectionChanged"
Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/> Margin="47,160,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@@ -547,37 +548,47 @@
<GroupBox Header="Betrieb" Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5"> <GroupBox Header="Betrieb" Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Margin="5,5,5,5">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/> <ColumnDefinition Width="85"/>
<ColumnDefinition Width="150"/> <ColumnDefinition Width="69"/>
<ColumnDefinition/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="55"/>
<ColumnDefinition Width="101"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="UID:" Margin="10,10,0,0" Grid.Column="0" ToolTip="USt-IdNr. / Umsatzsteuer-Identifikationsnummer"/> <Label Content="Betriebs-Nr.:" Margin="10,10,0,0" Grid.Column="0"/>
<TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,10,5,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<Label Content="UID:" Margin="10,40,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" Margin="0,40,5,0" Grid.Column="0" Grid.ColumnSpan="2" Width="96" HorizontalAlignment="Right"
TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/> TextChanged="UstIdNrInput_TextChanged" LostFocus="UstIdNrInput_LostFocus"/>
<Label Content="Betriebs-Nr.:" Margin="10,40,0,0" Grid.Column="0"/> <CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False"
<TextBox x:Name="LfbisNrInput" Text="{Binding LfbisNr, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Checked="OrganicInput_Changed" Unchecked="OrganicInput_Changed"
Margin="0,40,10,0" Grid.Column="1" Width="64" HorizontalAlignment="Left" TextAlignment="Right" Grid.Column="2" HorizontalAlignment="Left" Margin="5,15,0,0" VerticalAlignment="Top"/>
TextChanged="LfbisNrInput_TextChanged" LostFocus="LfbisNrInput_LostFocus"/>
<CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="BuchführendInput" Content="Buchführend" IsChecked="{Binding IsBuchführend, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="5,45,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="OrganicInput" Content="Bio" IsChecked="{Binding IsOrganic, Mode=TwoWay}" IsEnabled="False" <Label Content="Kontrollstelle:" Margin="0,10,5,0" Grid.Column="2" Grid.ColumnSpan="2" HorizontalAlignment="Right" ToolTip="Bio-Kontrollstelle"/>
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" <TextBox x:Name="OrganicAuthorityCodeInput" Text="{Binding OrganicAuthorityCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/> Margin="0,10,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
<Button x:Name="OrganicButton" Content="easy-cert.com" IsEnabled="{Binding IsMemberSelected}" TextChanged="OrganicAuthorityCodeInput_TextChanged" LostFocus="OrganicAuthorityCodeInput_LostFocus"/>
Height="25" FontSize="12"
Click="OrganicButton_Click" <Label Content="OOC-ID:" Margin="0,40,0,0" Grid.Column="3" HorizontalAlignment="Left" ToolTip="Bio-EU-Operator-ID / Organic Operator Certificate ID"/>
Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="60,40,0,0"/> <TextBox x:Name="OrganicOperatorIdInput" Text="{Binding OrganicOperatorId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
<GroupBox Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5"> <GroupBox Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Margin="5,5,5,5">
<GroupBox.Header> <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> </GroupBox.Header>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -593,7 +604,7 @@
<Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/> <Label Content="Adresse:" Margin="10,40,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingAddressInput" Text="{Binding BillingAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="BillingAddressInput" Text="{Binding BillingAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,40,10,0" Grid.Column="1" Margin="0,40,10,0" Grid.Column="1"
TextChanged="TextBox_TextChanged"/> TextChanged="AddressInput_TextChanged"/>
<Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/> <Label Content="PLZ/Ort:" Margin="10,70,0,0" Grid.Column="0"/>
<TextBox x:Name="BillingPlzInput" Text="{Binding BillingPlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="BillingPlzInput" Text="{Binding BillingPlzString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
@@ -601,6 +612,7 @@
TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/> TextChanged="PlzInput_TextChanged" LostFocus="PlzInput_LostFocus" Tag="PLZ"/>
<ComboBox x:Name="BillingOrtInput" SelectedItem="{Binding BillingOrt, Mode=TwoWay}" ItemsSource="{Binding BillingOrtSource, Mode=TwoWay}" <ComboBox x:Name="BillingOrtInput" SelectedItem="{Binding BillingOrt, Mode=TwoWay}" ItemsSource="{Binding BillingOrtSource, Mode=TwoWay}"
ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name" ItemTemplate="{StaticResource PostalDestTemplate}" TextSearch.TextPath="Ort.Name"
SelectionChanged="OrtInput_SelectionChanged"
Margin="47,70,10,0" Grid.Column="1"/> Margin="47,70,10,0" Grid.Column="1"/>
</Grid> </Grid>
</GroupBox> </GroupBox>
@@ -608,7 +620,8 @@
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/> <ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition/> <ColumnDefinition/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@@ -645,46 +658,64 @@
<CheckBox x:Name="ActiveInput" Content="Aktiv" IsChecked="{Binding IsActive, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="ActiveInput" Content="Aktiv" IsChecked="{Binding IsActive, Mode=TwoWay}" IsEnabled="False"
Checked="ActiveInput_Changed" Unchecked="ActiveInput_Changed" Checked="ActiveInput_Changed" Unchecked="ActiveInput_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,15,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="0,15,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsChecked="{Binding IsVollLieferant, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="VollLieferantInput" Content="Volllieferant" IsChecked="{Binding IsVollLieferant, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,45,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="0,45,0,0" VerticalAlignment="Top"/>
<CheckBox x:Name="FunkionärInput" Content="Funktionär" IsChecked="{Binding IsFunktionär, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="FunkionärInput" Content="Funktionär" IsChecked="{Binding IsFunktionär, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
Grid.Column="2" HorizontalAlignment="Left" Margin="10,75,0,0" VerticalAlignment="Top"/> Grid.Column="2" HorizontalAlignment="Left" Margin="0,75,0,0" VerticalAlignment="Top"/>
<Label Content="Bio-Zertifikate:" Margin="0,-2.5,20,0" Grid.Column="3" HorizontalAlignment="Right"/>
<Button x:Name="OrganicTracesButton" IsEnabled="{Binding IsMemberSelectedOrCreating}" ToolTip="{Binding OrganicToolTip}"
Click="OrganicTracesButton_Click"
HorizontalAlignment="Right" Margin="0,20,10,0" Width="100" Padding="0" Height="35" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2" HorizontalContentAlignment="Stretch">
<StackPanel Orientation="Vertical">
<TextBlock FontSize="12" TextAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,-3">EU TRACES</TextBlock>
<TextBlock FontSize="10" TextAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,0">
<Run Text="{Binding OrganicState}" FontWeight="Bold" Foreground="{Binding OrganicColor}"/><Run Text="{Binding OrganicDetails}"/>
</TextBlock>
</StackPanel>
</Button>
<Button x:Name="OrganicEasyCertButton" IsEnabled="{Binding IsMemberSelectedOrCreating}" Content="Easy-Cert"
Click="OrganicEasyCertButton_Click"
HorizontalAlignment="Right" Margin="0,60,10,0" Width="100" Height="30" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2"/>
<Button x:Name="OrganicBiocButton" IsEnabled="{Binding IsMemberSelectedOrCreating}" Content="BioC"
Click="OrganicBiocButton_Click"
HorizontalAlignment="Right" Margin="0,95,10,0" Width="100" Height="30" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2"/>
<Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/> <Label Content="Stamm-Zwst.:" Margin="10,130,0,0" Grid.Column="0"/>
<ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}" <ComboBox x:Name="BranchInput" SelectedItem="{Binding Branch, Mode=TwoWay}" ItemsSource="{Binding BranchSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name" DisplayMemberPath="Name" TextSearch.TextPath="Name"
Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="2"/> Margin="0,130,10,0" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/> <Label Content="Stammgemeinde:" Margin="10,160,0,0" Grid.Column="0"/>
<ComboBox x:Name="DefaultKgInput" SelectedItem="{Binding DefaultKg, Mode=TwoWay}" ItemsSource="{Binding DefaultKgSource, Mode=TwoWay}" <ComboBox x:Name="DefaultKgInput" SelectedItem="{Binding DefaultKg, Mode=TwoWay}" ItemsSource="{Binding DefaultKgSource, Mode=TwoWay}"
DisplayMemberPath="Name" TextSearch.TextPath="Name" DisplayMemberPath="Name" TextSearch.TextPath="Name"
Margin="0,160,40,10" Grid.Column="1" Grid.ColumnSpan="2"/> Margin="0,160,40,10" Grid.Column="1" Grid.ColumnSpan="3"/>
<Button x:Name="KgDetailsButton" Content="&#xE712;" FontFamily="Segoe MDL2 Assets" FontSize="14" Padding="0,1,0,0" <Button x:Name="KgDetailsButton" Content="&#xE712;" FontFamily="Segoe MDL2 Assets" FontSize="14" Padding="0,1,0,0"
Click="KgDetailsButton_Click" Click="KgDetailsButton_Click"
Grid.Column="2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,160,10,10"/> Grid.Column="3" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Height="25" Margin="10,160,10,10"/>
<Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/> <Label Content="Anmerkung:" Margin="10,190,0,0" Grid.Column="0"/>
<TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" <TextBox x:Name="CommentInput" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="2" FontSize="12" Margin="0,190,10,70" Grid.Column="1" Grid.ColumnSpan="3" FontSize="12"
TextChanged="TextBox_TextChanged" TextChanged="TextBox_TextChanged"
VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/> VerticalAlignment="Stretch" Height="auto" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
<Label Content="Kontaktart:" Margin="10,10,0,40" Grid.Column="0" VerticalAlignment="Bottom"/> <Label Content="Kontaktart:" Margin="10,10,0,40" Grid.Column="0" VerticalAlignment="Bottom"/>
<CheckBox x:Name="ContactPostalInput" Content="Post" IsChecked="{Binding ContactViaPost, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="ContactPostalInput" Content="Post" IsChecked="{Binding ContactViaPost, Mode=TwoWay}" IsEnabled="False"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed" Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
HorizontalAlignment="Left" Margin="0,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="0,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="3"/>
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsChecked="{Binding ContactViaEmail, Mode=TwoWay}" IsEnabled="False" <CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsChecked="{Binding ContactViaEmail, Mode=TwoWay}" IsEnabled="False"
Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed" Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed"
HorizontalAlignment="Left" Margin="60,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/> HorizontalAlignment="Left" Margin="60,0,0,45" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="3"/>
<Label Content="Zuletzt geändert:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/> <Label Content="Zuletzt geändert:" Margin="10,10,0,10" Grid.Column="0" VerticalAlignment="Bottom"/>
<TextBlock x:Name="LastModified" Text="{Binding ModifiedAtShort}" <TextBlock x:Name="LastModified" Text="{Binding ModifiedAtShort}"
Margin="0,10,0,15" Grid.Column="1" TextWrapping="NoWrap" VerticalAlignment="Bottom"> Margin="0,10,0,15" Grid.Column="1" Grid.ColumnSpan="2" TextWrapping="NoWrap" VerticalAlignment="Bottom">
<TextBlock.ToolTip> <TextBlock.ToolTip>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@@ -711,7 +742,7 @@
<Button x:Name="DeliveryButton" IsEnabled="{Binding IsMemberSelected}" <Button x:Name="DeliveryButton" IsEnabled="{Binding IsMemberSelected}"
Click="DeliveryButton_Click" Click="DeliveryButton_Click"
HorizontalAlignment="Right" Margin="10,00,10,37" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch"> HorizontalAlignment="Right" Margin="10,00,10,37" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="4" HorizontalContentAlignment="Stretch">
<Grid> <Grid>
<TextBlock Margin="0,0,10,0" TextAlignment="Center">Lieferungen</TextBlock> <TextBlock Margin="0,0,10,0" TextAlignment="Center">Lieferungen</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;" <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
@@ -720,7 +751,7 @@
</Button> </Button>
<Button x:Name="AreaCommitmentButton" IsEnabled="{Binding IsMemberSelected}" <Button x:Name="AreaCommitmentButton" IsEnabled="{Binding IsMemberSelected}"
Click="AreaCommitmentButton_Click" Click="AreaCommitmentButton_Click"
HorizontalAlignment="Right" Margin="10,10,10,5" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch"> HorizontalAlignment="Right" Margin="10,10,10,5" Width="160" VerticalAlignment="Bottom" Grid.ColumnSpan="4" HorizontalContentAlignment="Stretch">
<Grid> <Grid>
<TextBlock Margin="0,0,15,0" TextAlignment="Center">Flächenbindungen</TextBlock> <TextBlock Margin="0,0,15,0" TextAlignment="Center">Flächenbindungen</TextBlock>
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;" <TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text="&#xE8A7;"
+98 -12
View File
@@ -503,6 +503,8 @@ namespace Elwig.Windows {
UnlockInputs(); UnlockInputs();
UpdateContactInfoVisibility(true); UpdateContactInfoVisibility(true);
ViewModel.EnableSearchInputs = false; ViewModel.EnableSearchInputs = false;
UpdateOrganic();
} }
protected override void ShortcutDelete() { protected override void ShortcutDelete() {
@@ -966,23 +968,13 @@ 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) { private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return; if (ViewModel.SelectedMember is not Member m || m.PredecessorMgNr == null) return;
FocusMember((int)m.PredecessorMgNr); FocusMember((int)m.PredecessorMgNr);
} }
private async void ActiveInput_Changed(object sender, RoutedEventArgs evt) { private async void ActiveInput_Changed(object sender, RoutedEventArgs evt) {
CheckBox_Changed(sender, evt);
if (MemberList.SelectedItem is not Member m) if (MemberList.SelectedItem is not Member m)
return; return;
if ((IsEditing || IsCreating) && ViewModel.IsActive == false && ViewModel.MgNr is int mgnr) { if ((IsEditing || IsCreating) && ViewModel.IsActive == false && ViewModel.MgNr is int mgnr) {
@@ -997,7 +989,6 @@ namespace Elwig.Windows {
} }
} }
} }
CheckBox_Changed(sender, evt);
} }
private void JuridicalPersonInput_Changed(object sender, RoutedEventArgs evt) { private void JuridicalPersonInput_Changed(object sender, RoutedEventArgs evt) {
@@ -1020,5 +1011,100 @@ namespace Elwig.Windows {
ValidateRequiredInputs(); ValidateRequiredInputs();
UpdateButtons(); UpdateButtons();
} }
new protected void AddressInput_TextChanged(object sender, TextChangedEventArgs? evt) {
if ((IsCreating || IsEditing) && sender is TextBox b) {
InputTextChanged(b, (i, r) => Validator.CheckAddress(i, r, b == BillingAddressInput ? ViewModel.BillingOrt?.Ort.Name : ViewModel.Ort?.Ort.Name));
} else {
TextBox_TextChanged(sender, evt);
}
}
private void OrtInput_SelectionChanged(object sender, SelectionChangedEventArgs? evt) {
if (!IsCreating && !IsEditing)
return;
if (sender == BillingOrtInput) {
InputTextChanged(BillingAddressInput, (i, r) => Validator.CheckAddress(i, r, ViewModel.BillingOrt?.Ort.Name));
} else {
InputTextChanged(AddressInput, (i, r) => Validator.CheckAddress(i, r, ViewModel.Ort?.Ort.Name));
}
}
private void OrganicInput_Changed(object sender, RoutedEventArgs evt) {
CheckBox_Changed(sender, evt);
if (IsEditing || IsCreating) {
UpdateOrganic();
}
}
private new void LfbisNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
base.LfbisNrInput_TextChanged(sender, evt);
if (IsEditing || IsCreating) {
UpdateOrganic();
}
}
private void UpdateOrganic() {
if (!ViewModel.IsOrganic || string.IsNullOrWhiteSpace(ViewModel.LfbisNr) || !GetInputValid(LfbisNrInput)) {
ViewModel.OrganicState = null;
ViewModel.OrganicColor = Brushes.Black;
ViewModel.OrganicDetails = "-";
ViewModel.OrganicToolTip = null;
return;
} else {
ViewModel.OrganicState = null;
ViewModel.OrganicColor = Brushes.Black;
ViewModel.OrganicToolTip = null;
ViewModel.OrganicDetails = "...";
var lfbisNr = ViewModel.LfbisNr;
Utils.RunBackground("Bio-Zertifikatsdaten laden", async () => {
if (App.MainDispatcher == null) return;
var certs = await OrganicService.FetchTracesCertificates(lfbisNr: lfbisNr);
await App.MainDispatcher.BeginInvoke(() => {
if (certs.Length == 0) {
ViewModel.FillOrganicInputs(null);
} else {
var cert = certs[0];
ViewModel.FillOrganicInputs(cert);
ViewModel.OrganicAuthorityCode = cert.AuthorityCode;
ViewModel.OrganicOperatorId = cert.OperatorId;
}
});
});
}
}
private void OrganicTracesButton_Click(object sender, RoutedEventArgs evt) {
string? url = null;
if ((IsCreating || IsEditing) && !string.IsNullOrWhiteSpace(ViewModel.LfbisNr) && GetInputValid(LfbisNrInput)) {
url = OrganicService.GetTracesSearchUrl(lfbisNr: ViewModel.LfbisNr);
} else if (ViewModel.SelectedMember is Member m) {
url = OrganicService.GetTracesSearchUrl(m);
}
if (url != null)
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
private void OrganicEasyCertButton_Click(object sender, RoutedEventArgs evt) {
string? url = null;
if ((IsCreating || IsEditing) && !string.IsNullOrWhiteSpace(ViewModel.LfbisNr) && GetInputValid(LfbisNrInput)) {
url = OrganicService.GetEasyCertSearchUrl(ViewModel.LfbisNr);
} else if (ViewModel.SelectedMember is Member m && m.LfbisNr != null) {
url = OrganicService.GetEasyCertSearchUrl(m.LfbisNr);
}
if (url != null)
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
private void OrganicBiocButton_Click(object sender, RoutedEventArgs evt) {
string? url = null;
if ((IsCreating || IsEditing) && !string.IsNullOrWhiteSpace(ViewModel.LfbisNr) && GetInputValid(LfbisNrInput)) {
url = OrganicService.GetBiocSearchUrl(ViewModel.LfbisNr);
} else if (ViewModel.SelectedMember is Member m && m.LfbisNr != null) {
url = OrganicService.GetBiocSearchUrl(m.LfbisNr);
}
if (url != null)
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
} }
} }
@@ -173,8 +173,11 @@
Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right" Margin="0,100,10,0" Width="78" Grid.Column="1" HorizontalAlignment="Left" TextAlignment="Right"
TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/> TextChanged="DateNoticeInput_TextChanged" LostFocus="DateInput_LostFocus"/>
<CheckBox x:Name="HasCompanySignedInput" IsChecked="{Binding CompanyHasSigned, Mode=TwoWay}" Content="Von WG unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt" <CheckBox x:Name="HasMemberSignedInput" IsChecked="{Binding MemberHasSigned, Mode=TwoWay}" Content="Von Mg. unterfertigt"
Margin="88,105,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="TOp" Margin="88,135,10,0" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/> <Label Content="Wirksam:" Margin="10,130,0,0" Grid.Column="0"/>
+4 -4
View File
@@ -84,12 +84,12 @@ namespace Tests.E2ETests {
Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999"); Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
Thread.Sleep(500); Thread.Sleep(500);
var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow")); var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow"));
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(memberListRow, Is.Not.Null); Assert.That(memberListRow, Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
}); }
} }
[Test] [Test]
@@ -128,12 +128,12 @@ namespace Tests.E2ETests {
Assert.That(memberListRows, Has.Count.EqualTo(1)); Assert.That(memberListRows, Has.Count.EqualTo(1));
var memberListRow = memberListRows.First(); var memberListRow = memberListRows.First();
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(memberListRow, Is.Not.Null); Assert.That(memberListRow, Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null); Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
}); }
Window.FindElement(By.WpfId("DeleteMemberButton")).Click(); Window.FindElement(By.WpfId("DeleteMemberButton")).Click();
var dialog = Session.CreateWindowDriver("DeleteMemberDialog"); var dialog = Session.CreateWindowDriver("DeleteMemberDialog");
+3 -3
View File
@@ -1,8 +1,8 @@
-- inserts for HelperTests.BillingTest -- inserts for HelperTests.BillingTest
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'), ('KIP', 'KIP', 'Kontrollierte Integrierte Produktion', FALSE),
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0), ('K', 'Kabinett', TRUE, NULL, FALSE, 0),
+2 -2
View File
@@ -1,7 +1,7 @@
-- inserts for DocumentTests -- inserts for DocumentTests
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0); ('K', 'Kabinett', TRUE, NULL, FALSE, 0);
+3 -3
View File
@@ -1,8 +1,8 @@
-- inserts for E2ETests -- inserts for E2ETests
INSERT INTO wine_cultivation (cultid, name, description) VALUES INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'), ('KIP', 'KIP', 'Kontrollierte Integrierte Produktion', FALSE),
('B', 'Bio', 'AT-BIO-302'); ('B', 'Bio', 'Biologische Produktion', TRUE);
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0), ('K', 'Kabinett', TRUE, NULL, FALSE, 0),
+5 -5
View File
@@ -66,11 +66,11 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
(15216, 2), (15216, 2),
(15224, 2); (15224, 2);
INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES INSERT INTO member (mgnr, given_name, name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr, org_auth_code) VALUES
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ), (101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL , NULL ),
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'), (102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684', NULL ),
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ), (103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL , 'AT-BIO-302'),
(104, 'Waltraud', 'Winzer', 'X', FALSE, TRUE , 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693'); (104, 'Waltraud', 'Winzer', 'X', FALSE, TRUE , 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693', NULL );
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
(102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'), (102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'),
+2 -3
View File
@@ -1,8 +1,7 @@
-- inserts for ServiceTests -- inserts for ServiceTests
INSERT INTO wine_cultivation (cultid, name, description, organic) VALUES
INSERT INTO wine_cultivation (cultid, name, description) VALUES ('B', 'Bio', 'Biologische Produktion', TRUE);
('B', 'Bio', 'AT-BIO-302');
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
('K', 'Kabinett', TRUE, NULL, FALSE, 0); ('K', 'Kabinett', TRUE, NULL, FALSE, 0);
@@ -8,7 +8,7 @@ namespace Tests.UnitTests.DocumentTests {
public async Task Test_01_OneDeliveryPart() { public async Task Test_01_OneDeliveryPart() {
using var doc = await DeliveryNote.Initialize(2020, 1); using var doc = await DeliveryNote.Initialize(2020, 1);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERMANN Max MUSTERMANN Max
Winzerstraße 1 Winzerstraße 1
@@ -26,14 +26,14 @@ namespace Tests.UnitTests.DocumentTests {
Waage/Terminal: ?/1, ID: 321 – 09:02, 01.10.2020 Waage/Terminal: ?/1, ID: 321 – 09:02, 01.10.2020
Brutto: 3 219 kg – Tara: 0 kg – Netto: 3 219 kg – gerebelt gewogen Brutto: 3 219 kg – Tara: 0 kg – Netto: 3 219 kg – gerebelt gewogen
""")); """));
}); }
} }
[Test] [Test]
public async Task Test_02_TwoDeliveryParts() { public async Task Test_02_TwoDeliveryParts() {
using var doc = await DeliveryNote.Initialize(2020, 4); using var doc = await DeliveryNote.Initialize(2020, 4);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
W&B Weinbauer GesbR W&B Weinbauer GesbR
WEINBAUER Wernhardt WEINBAUER Wernhardt
@@ -58,14 +58,14 @@ namespace Tests.UnitTests.DocumentTests {
Waage/Terminal: ?/?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 81 16,5 4 483")); Assert.That(text, Contains.Substring("Gesamt: 81 16,5 4 483"));
}); }
} }
[Test] [Test]
public async Task Test_03_DeliveryPartsWithAttribute() { public async Task Test_03_DeliveryPartsWithAttribute() {
using var doc = await DeliveryNote.Initialize(2020, 3); using var doc = await DeliveryNote.Initialize(2020, 3);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERMANN Max MUSTERMANN Max
Winzerstraße 1 Winzerstraße 1
@@ -95,14 +95,14 @@ namespace Tests.UnitTests.DocumentTests {
Waage/Terminal: ?/?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 81 16,5 6 970")); Assert.That(text, Contains.Substring("Gesamt: 81 16,5 6 970"));
}); }
} }
[Test] [Test]
public async Task Test_04_DeliveryPartsWithCultivation() { public async Task Test_04_DeliveryPartsWithCultivation() {
using var doc = await DeliveryNote.Initialize(2020, 7); using var doc = await DeliveryNote.Initialize(2020, 7);
var text = await Utils.GeneratePdfText(doc); var text = await Utils.GeneratePdfText(doc);
Assert.Multiple(() => { using (Assert.EnterMultipleScope()) {
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
MUSTERBAUER Matthäus MUSTERBAUER Matthäus
Brünner Straße 10 Brünner Straße 10
@@ -115,20 +115,20 @@ namespace Tests.UnitTests.DocumentTests {
Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht")); Assert.That(text, Contains.Substring("Das Mitglied erklärt, dass die gelieferte Ware dem österreichischen Weingesetz entspricht"));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
1 Grüner Veltliner Wein 80 16,3 3 198 1 Grüner Veltliner Wein 80 16,3 3 198
Bewirtschaftung: Bio (AT-BIO-302) Bewirtschaftung: Bio (Biologische Produktion) – Kontrollstelle: AT-BIO-302
Herkunft: Österreich Herkunft: Österreich
/ Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf / Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf
Waage/Terminal: ?/?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring(""" Assert.That(text, Contains.Substring("""
2 Grüner Veltliner Qualitätswein 75 15,4 2 134 2 Grüner Veltliner Qualitätswein 75 15,4 2 134
Bewirtschaftung: Bio (AT-BIO-302) Bewirtschaftung: Bio (Biologische Produktion) – Kontrollstelle: AT-BIO-302
Herkunft: Österreich / Weinland / Niederösterreich Herkunft: Österreich / Weinland / Niederösterreich
/ Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf / Wolkersdorfer Hochleithen / Wolkersdorf im Weinviertel / KG Wolkersdorf
Waage/Terminal: ?/?, ID: ? (gerebelt gewogen) Waage/Terminal: ?/?, ID: ? (gerebelt gewogen)
""")); """));
Assert.That(text, Contains.Substring("Gesamt: 78 15,9 5 332")); Assert.That(text, Contains.Substring("Gesamt: 78 15,9 5 332"));
}); }
} }
[Test] [Test]
File diff suppressed because it is too large. Load diff
+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"