Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034c0c4eb7 |
@@ -34,19 +34,23 @@ namespace Elwig.Documents {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ?? "-") + (Member.OrganicAuthorityCode != null ? $" ({Member.OrganicAuthorityCode})" : ""), 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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
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;
|
||||||
@@ -68,11 +70,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 +89,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 +108,52 @@ 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 zu laden. Dies könnte einen kurzen Moment in Anspruch nehmen.");
|
||||||
|
|
||||||
|
foreach (var m in members) {
|
||||||
|
string? oocId = null;
|
||||||
|
string? authCode = null;
|
||||||
|
|
||||||
|
var certs = await OrganicService.FetchTracesCertificates(null, m.LfbisNr, (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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -189,10 +190,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());
|
||||||
|
|
||||||
|
await App.MainDispatcher.BeginInvoke(() => {
|
||||||
if (m.MgNr != vm.MgNr)
|
if (m.MgNr != vm.MgNr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await App.MainDispatcher.BeginInvoke(() => {
|
|
||||||
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);
|
||||||
@@ -205,13 +205,65 @@ 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.OrganicTest1 = null;
|
||||||
|
vm.OrganicTest2 = "...";
|
||||||
|
vm.OrganicTest3 = Brushes.Black;
|
||||||
|
vm.OrganicTest4 = null;
|
||||||
|
Utils.RunBackground("Bio-Zertifikatsdaten laden", async () => {
|
||||||
|
if (App.MainDispatcher == null || !m.IsOrganic)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var certs = await OrganicService.GetTracesCertificates(m);
|
||||||
|
|
||||||
|
// TODO button for TRACES search url
|
||||||
|
// TODO button for authority search url
|
||||||
|
|
||||||
|
await App.MainDispatcher.BeginInvoke(() => {
|
||||||
|
if (m.MgNr != vm.MgNr)
|
||||||
|
return;
|
||||||
|
vm.OrganicTest2 = null;
|
||||||
|
if (certs.Length == 0) {
|
||||||
|
vm.OrganicTest1 = "Nicht gefunden";
|
||||||
|
vm.OrganicTest3 = Brushes.DarkRed;
|
||||||
|
} else {
|
||||||
|
var cert = certs[0];
|
||||||
|
vm.OrganicTest4 = $"Zertifikat-Nr.: {cert.Id}\nBio-KSt.: {cert.AuthorityCode}\nEU Operator-ID: {cert.OperatorId}\nOperator: {cert.OperatorName}";
|
||||||
|
if (cert.IsValidForWineProduction) {
|
||||||
|
vm.OrganicTest1 = "Gültig";
|
||||||
|
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
|
||||||
|
vm.OrganicTest3 = Brushes.DarkGreen;
|
||||||
|
} else if (cert.IsValid) {
|
||||||
|
vm.OrganicTest1 = "Teilw. gültig";
|
||||||
|
vm.OrganicTest2 = $" (bis {cert.ExpiresOn:dd.MM.yyyy})";
|
||||||
|
vm.OrganicTest3 = Brushes.OrangeRed;
|
||||||
|
} else {
|
||||||
|
vm.OrganicTest3 = Brushes.DarkRed;
|
||||||
|
switch (cert.Status) {
|
||||||
|
case OrganicService.TracesStatus.ISSUED:
|
||||||
|
vm.OrganicTest1 = "Ausgestellt";
|
||||||
|
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
|
||||||
|
case OrganicService.TracesStatus.SUSPENDED:
|
||||||
|
vm.OrganicTest1 = "Gesperrt";
|
||||||
|
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
|
||||||
|
case OrganicService.TracesStatus.EXPIRED:
|
||||||
|
vm.OrganicTest1 = "Ausgelaufen";
|
||||||
|
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
|
||||||
|
case OrganicService.TracesStatus.WITHDRAWN:
|
||||||
|
vm.OrganicTest1 = "Widerrufen";
|
||||||
|
vm.OrganicTest2 = $" (am {cert.LastStatusUpdateOn:dd.MM.yyyy})"; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.MemberHasEmail = m.EmailAddresses.Count > 0;
|
vm.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 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) {
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
using Elwig.Helpers;
|
||||||
|
using Elwig.Models.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
namespace Elwig.Services {
|
||||||
|
public static class OrganicService {
|
||||||
|
|
||||||
|
public const string TRACES_API_URL = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/for/query";
|
||||||
|
public const string TRACES_PDF_URL_PREFIX = "https://webgate.ec.europa.eu/tracesnt/directory/publication/organic-operator/digitally-signed/";
|
||||||
|
|
||||||
|
public enum TracesStatus {
|
||||||
|
ISSUED, SUSPENDED, WITHDRAWN, EXPIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TracesActivity {
|
||||||
|
PREPARATION, EXPORT, IMPORT, STORING, PRODUCTION, DISTRIBUTION, DISTRIBUTION_PLACING_ON_THE_MARKET
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TracesCategoryOfProduct {
|
||||||
|
UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS, LIVESTOCK_AND_UNPROCESSED_LIVESTOCK_PRODUCTS, ALGAE_AND_UNPROCESSED_AQUACULTURE_PRODUCTS,
|
||||||
|
PROCESSED_AGRICULTURAL_PRODUCTS_INCLUDING_AQUACULTURE_FOR_USE_AS_FOOD, FEED, WINE, OTHER_PRODUCTS_NOT_COVERED_BY_PREVIOUS_CATEGORIES
|
||||||
|
}
|
||||||
|
|
||||||
|
public record TracesCertificate {
|
||||||
|
public required string Id { get; init; }
|
||||||
|
public required string AuthorityCode { get; init; }
|
||||||
|
public required string OperatorId { get; init; }
|
||||||
|
public required string OperatorName { get; init; }
|
||||||
|
public required TracesActivity[] Activities { get; init; }
|
||||||
|
public required TracesCategoryOfProduct[] CategoriesOfProduct { get; init; }
|
||||||
|
public required DateOnly IssuedOn { get; init; }
|
||||||
|
public required DateOnly ExpiresOn { get; init; }
|
||||||
|
public required DateOnly LastStatusUpdateOn { get; init; }
|
||||||
|
public required TracesStatus Status { get; init; }
|
||||||
|
|
||||||
|
public string PdfUrl => TRACES_PDF_URL_PREFIX + Id + ".pdf";
|
||||||
|
public bool IsValid => Status == TracesStatus.ISSUED && IssuedOn.ToDateTime(new()) <= DateTime.Today && ExpiresOn.ToDateTime(new()) >= DateTime.Today;
|
||||||
|
public bool IsValidForWineProduction => IsValid && Activities.Contains(TracesActivity.PRODUCTION) &&
|
||||||
|
(CategoriesOfProduct.Contains(TracesCategoryOfProduct.UNPROCESSED_PLANT_PRODUCTS_INCLUDING_SEEDS) || CategoriesOfProduct.Contains(TracesCategoryOfProduct.WINE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async static Task<TracesCertificate[]> GetTracesCertificates(Member m, bool? tryNameAndAddress = null) {
|
||||||
|
// TODO cache
|
||||||
|
return await FetchTracesCertificates(m, tryNameAndAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async static Task<JsonArray> TryFetchTracesCertificates(Dictionary<string, string> query) {
|
||||||
|
var q = HttpUtility.ParseQueryString("");
|
||||||
|
q.Add("sort", "-issuedOn");
|
||||||
|
q.Add("countryCode", "AT");
|
||||||
|
foreach (var (k, v) in query) {
|
||||||
|
q.Add(k, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var client = Utils.GetHttpClient();
|
||||||
|
using var res = await client.GetAsync($"{TRACES_API_URL}?{q}");
|
||||||
|
res.EnsureSuccessStatusCode();
|
||||||
|
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
|
||||||
|
var certs = resJson?.AsArray() ?? throw new Exception();
|
||||||
|
if (certs.Count == 0)
|
||||||
|
throw new Exception();
|
||||||
|
return certs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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, string? lfbisNr, (string Name, string Address, string PostalCode)? address) {
|
||||||
|
bool searchedByName = false;
|
||||||
|
|
||||||
|
JsonArray? jsonCerts = null;
|
||||||
|
if (oocId != null) {
|
||||||
|
try {
|
||||||
|
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
|
||||||
|
{ "operatorIdentifierType", "ooc_identifier" },
|
||||||
|
{ "operatorIdentifierSearchOperator", "STRICT" },
|
||||||
|
{ "operatorIdentifier", oocId },
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
jsonCerts = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonCerts == null && lfbisNr != null) {
|
||||||
|
try {
|
||||||
|
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
|
||||||
|
{ "operatorIdentifierType", "comp_reg" },
|
||||||
|
{ "operatorIdentifierSearchOperator", "STRICT" },
|
||||||
|
{ "operatorIdentifier", lfbisNr.TrimStart('0') },
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
jsonCerts = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonCerts == null && address != null) {
|
||||||
|
try {
|
||||||
|
jsonCerts = await TryFetchTracesCertificates(new Dictionary<string, string> {
|
||||||
|
{ "operatorPostalCode", address.Value.PostalCode },
|
||||||
|
{ "query", $"{address.Value.Name} {address.Value.Address}" },
|
||||||
|
});
|
||||||
|
searchedByName = true;
|
||||||
|
} catch {
|
||||||
|
jsonCerts = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonCerts == null)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
TracesCertificate[] certs = [];
|
||||||
|
try {
|
||||||
|
certs = [.. jsonCerts.Select(j => new TracesCertificate {
|
||||||
|
Id = j!["reference"]?.GetValue<string>() ?? throw new Exception(),
|
||||||
|
AuthorityCode = j["issuingBody"]?["code"]?.GetValue<string>() ?? throw new Exception(),
|
||||||
|
OperatorId = j["operatorIdentifier"]?.GetValue<string>() ?? throw new Exception(),
|
||||||
|
OperatorName = j["operator"]?["name"]?.GetValue<string>() ?? throw new Exception(),
|
||||||
|
Activities = [.. j["activities"]?.AsArray().Select(a => Enum.Parse<TracesActivity>(a?["id"]?.GetValue<string>().ToUpper().Replace("_IMPORT", "IMPORT") ?? throw new Exception())) ?? []],
|
||||||
|
CategoriesOfProduct = [.. j["categoriesOfProduct"]?.AsArray().Select(c => Enum.Parse<TracesCategoryOfProduct>(c?["id"]?.GetValue<string>() ?? throw new Exception())) ?? []],
|
||||||
|
IssuedOn = DateOnly.FromDateTime(DateTime.ParseExact(j["issuedOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
|
||||||
|
ExpiresOn = DateOnly.ParseExact(j["expiresOn"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-dd"),
|
||||||
|
LastStatusUpdateOn = DateOnly.FromDateTime(DateTime.ParseExact(j["lastStatusUpdateDateTime"]?.GetValue<string>() ?? throw new Exception(), "yyyy-MM-ddTHH:mm:ss.fffK", CultureInfo.InvariantCulture, DateTimeStyles.None)),
|
||||||
|
Status = Enum.Parse<TracesStatus>(j["status"]?["id"]?.GetValue<string>() ?? throw new Exception()),
|
||||||
|
})];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!searchedByName || certs.Where(c => c.Status == TracesStatus.ISSUED).Select(c => c.OperatorId).ToHashSet().Count == 1) ? certs : [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
@@ -126,6 +127,15 @@ namespace Elwig.ViewModels {
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isOrganic;
|
private bool _isOrganic;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _organicTest1;
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _organicTest2;
|
||||||
|
[ObservableProperty]
|
||||||
|
private Brush? _organicTest3;
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _organicTest4;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _entryDate;
|
private string? _entryDate;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|||||||
@@ -581,6 +581,11 @@
|
|||||||
<TextBox x:Name="OrganicOperatorIdInput" Text="{Binding OrganicOperatorId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
<TextBox x:Name="OrganicOperatorIdInput" Text="{Binding OrganicOperatorId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
Margin="0,40,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
|
Margin="0,40,10,0" Grid.Column="4" Width="91" HorizontalAlignment="Left"
|
||||||
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
|
TextChanged="OrganicOperatorIdInput_TextChanged" LostFocus="OrganicOperatorIdInput_LostFocus"/>
|
||||||
|
|
||||||
|
<Label Content="Bio-Zert.:" Margin="50,10,0,0" Grid.Column="2" HorizontalAlignment="Left"/>
|
||||||
|
<TextBlock FontSize="14" Margin="105,12,10,0" Grid.Column="2" HorizontalAlignment="Left" ToolTip="{Binding OrganicTest4}">
|
||||||
|
<Run Text="{Binding OrganicTest1}" FontWeight="Bold" Foreground="{Binding OrganicTest3}"/><Run FontSize="12" Text="{Binding OrganicTest2}"/>
|
||||||
|
</TextBlock>
|
||||||
</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">
|
||||||
|
|||||||
Reference in New Issue
Block a user