.gitea
Elwig
Controls
Dialogs
Documents
Helpers
Billing
Export
Printing
Weighing
ActionCommand.cs
AppDbContext.cs
AppDbUpdater.cs
ClientParameters.cs
Config.cs
ControlUtils.cs
ExportMode.cs
Extensions.cs
NullItem.cs
TempFile.cs
Utils.cs
Validator.cs
Models
Properties
Resources
Services
Themes
ViewModels
Windows
App.xaml
App.xaml.cs
AssemblyInfo.cs
Elwig.csproj
app.manifest
fetch-resources.bat
Installer
Setup
Tests
.gitignore
CHANGELOG.md
Elwig.sln
README.md
472 lines
23 KiB
C#
472 lines
23 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Elwig.Models.Entities;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.IO;
|
|
using System;
|
|
using System.Windows;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Data.Sqlite;
|
|
using System.Text.RegularExpressions;
|
|
using System.Collections.Generic;
|
|
using Elwig.Models.Dtos;
|
|
using System.Reflection;
|
|
using System.Data;
|
|
|
|
namespace Elwig.Helpers {
|
|
|
|
public record struct AreaComBucket(int Area, int Obligation, int Right);
|
|
public record struct UnderDelivery(int Weight, int Diff);
|
|
public record struct MemberBucket(string Name, int Area, int Obligation, int Right, int Delivery, int DeliveryStrict, int Payment);
|
|
public record struct MemberStat(string Variety, string Discr, int Weight);
|
|
public record struct ModifierStat(string ModId, string Name, int Count, decimal? Min, decimal? Max, decimal Sum);
|
|
|
|
public class AppDbContext : DbContext {
|
|
|
|
public DbSet<Country> Countries { get; private set; }
|
|
public DbSet<Currency> Currencies { get; private set; }
|
|
public DbSet<AT_Gem> Gemeinden { get; private set; }
|
|
public DbSet<AT_Kg> Katastralgemeinden { get; private set; }
|
|
public DbSet<AT_Ort> Orte { get; private set; }
|
|
public DbSet<AT_Plz> Postleitzahlen { get; private set; }
|
|
public DbSet<AT_PlzDest> PlzDestinations { get; private set; }
|
|
public DbSet<PostalDest> PostalDestinations { get; private set; }
|
|
public DbSet<WineOrigin> WineOrigins { get; private set; }
|
|
public DbSet<WineQualLevel> WineQualityLevels { get; private set; }
|
|
public DbSet<WineVar> WineVarieties { get; private set; }
|
|
|
|
public DbSet<ClientParam> ClientParameters { get; private set; }
|
|
public DbSet<WbGl> WbGls { get; private set; }
|
|
public DbSet<WbGem> WbGems { get; private set; }
|
|
public DbSet<WbKg> WbKgs { get; private set; }
|
|
public DbSet<WbRd> WbRde { get; private set; }
|
|
public DbSet<WineAttr> WineAttributes { get; private set; }
|
|
public DbSet<WineCult> WineCultivations { get; private set; }
|
|
public DbSet<Branch> Branches { get; private set; }
|
|
public DbSet<AreaComType> AreaCommitmentTypes { get; private set; }
|
|
public DbSet<Member> Members { get; private set; }
|
|
public DbSet<BillingAddr> BillingAddresses { get; private set; }
|
|
public DbSet<MemberTelNr> MemberTelephoneNrs { get; private set; }
|
|
public DbSet<MemberEmailAddr> MemberEmailAddrs { get; private set; }
|
|
public DbSet<MemberHistory> MemberHistory { get; private set; }
|
|
public DbSet<AreaCom> AreaCommitments { get; private set; }
|
|
public DbSet<Season> Seasons { get; private set; }
|
|
public DbSet<Modifier> Modifiers { get; private set; }
|
|
public DbSet<Delivery> Deliveries { get; private set; }
|
|
public DbSet<DeliveryPart> DeliveryParts { get; private set; }
|
|
public DbSet<DeliveryPartModifier> DeliveryPartModifiers { get; private set; }
|
|
public DbSet<PaymentVar> PaymentVariants { get; private set; }
|
|
public DbSet<PaymentMember> MemberPayments { get; private set; }
|
|
public DbSet<PaymentDeliveryPart> PaymentDeliveryParts { get; private set; }
|
|
public DbSet<PaymentCustom> CustomPayments { get; private set; }
|
|
public DbSet<Credit> Credits { get; private set; }
|
|
|
|
public DbSet<OverUnderDeliveryRow> OverUnderDeliveryRows { get; private set; }
|
|
public DbSet<AreaComUnderDeliveryRowSingle> AreaComUnderDeliveryRows { get; private set; }
|
|
public DbSet<MemberDeliveryPerVariantRowSingle> MemberDeliveryPerVariantRows { get; private set; }
|
|
public DbSet<CreditNoteDeliveryRowSingle> CreditNoteDeliveryRows { get; private set; }
|
|
public DbSet<CreditNoteRowSingle> CreditNoteRows { get; private set; }
|
|
public DbSet<WeightBreakdownRow> WeightBreakDownRows { get; private set; }
|
|
public DbSet<PaymentVariantSummaryRow> PaymentVariantSummaryRows { get; private set; }
|
|
|
|
private readonly StreamWriter? LogFile = null;
|
|
public static DateTime LastWriteTime => File.GetLastWriteTime(App.Config.DatabaseFile);
|
|
public DateTime SavedLastWriteTime { get; private set; }
|
|
public bool HasBackendChanged => SavedLastWriteTime != LastWriteTime;
|
|
|
|
public static string? ConnectionStringOverride { get; set; } = null;
|
|
public static string ConnectionString => ConnectionStringOverride ?? $"Data Source=\"{App.Config.DatabaseFile}\"; Mode=ReadWrite; Foreign Keys=True; Cache=Default";
|
|
|
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, AreaComBucket>>> _memberAreaCommitmentBuckets = [];
|
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBuckets = [];
|
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberDeliveryBucketsStrict = [];
|
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, int>>> _memberPaymentBuckets = [];
|
|
private readonly Dictionary<int, Dictionary<int, Dictionary<string, UnderDelivery>>> _memberUnderDelivery = [];
|
|
|
|
public AppDbContext() {
|
|
if (App.Config.DatabaseLog != null) {
|
|
try {
|
|
var file = File.Open(App.Config.DatabaseLog, FileMode.Append, FileAccess.Write, FileShare.Write);
|
|
LogFile = new(file) {
|
|
AutoFlush = true
|
|
};
|
|
} catch (Exception e) {
|
|
MessageBox.Show($"Unable to open database log file:\n\n{e.Message}", "Database Log", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
SavedLastWriteTime = LastWriteTime;
|
|
SavedChanges += OnSavedChanges;
|
|
}
|
|
|
|
public static SqliteConnection Connect(string? connectionString = null) {
|
|
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
|
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
|
cnx.Open();
|
|
return cnx;
|
|
}
|
|
|
|
public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) {
|
|
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
|
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
|
await cnx.OpenAsync();
|
|
return cnx;
|
|
}
|
|
|
|
public static async Task ExecuteBatch(SqliteConnection cnx, string sql) {
|
|
using var cmd = cnx.CreateCommand();
|
|
cmd.CommandText = sql;
|
|
await (await cmd.ExecuteReaderAsync()).CloseAsync();
|
|
}
|
|
|
|
public static async Task ExecuteEmbeddedScript(SqliteConnection cnx, Assembly asm, string name) {
|
|
using var stream = asm.GetManifestResourceStream(name) ?? throw new FileNotFoundException("Unable to load embedded resource");
|
|
using var reader = new StreamReader(stream);
|
|
await ExecuteBatch(cnx, await reader.ReadToEndAsync());
|
|
}
|
|
|
|
public static async Task<object?> ExecuteScalar(SqliteConnection cnx, string sql) {
|
|
using var cmd = cnx.CreateCommand();
|
|
cmd.CommandText = sql;
|
|
return await cmd.ExecuteScalarAsync();
|
|
}
|
|
|
|
public static async Task<(string Table, long RowId, string Parent, long FkId)[]> ForeignKeyCheck(SqliteConnection cnx) {
|
|
using var cmd = cnx.CreateCommand();
|
|
cmd.CommandText = "PRAGMA foreign_key_check";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
var list = new List<(string, long, string, long)>();
|
|
while (await reader.ReadAsync()) {
|
|
var table = reader.GetString(0);
|
|
var rowid = reader.GetInt64(1);
|
|
var parent = reader.GetString(2);
|
|
var fkid = reader.GetInt64(3);
|
|
list.Add((table, rowid, parent, fkid));
|
|
}
|
|
return [.. list];
|
|
}
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
|
optionsBuilder.UseSqlite(ConnectionString);
|
|
optionsBuilder.UseLazyLoadingProxies();
|
|
optionsBuilder.LogTo(Log, LogLevel.Information);
|
|
base.OnConfiguring(optionsBuilder);
|
|
}
|
|
|
|
public override void Dispose() {
|
|
base.Dispose();
|
|
LogFile?.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void OnSavedChanges(object? sender, SavedChangesEventArgs evt) {
|
|
SavedLastWriteTime = LastWriteTime;
|
|
}
|
|
|
|
protected void Log(string msg) {
|
|
LogFile?.WriteLine(msg);
|
|
}
|
|
|
|
public async Task<bool> MgNrExists(int mgnr) {
|
|
return await Members.FindAsync(mgnr) != null;
|
|
}
|
|
|
|
public async Task<bool> FbNrExists(int fbnr) {
|
|
return await AreaCommitments.FindAsync(fbnr) != null;
|
|
}
|
|
|
|
public async Task<bool> SortIdExists(string sortId) {
|
|
return await WineVarieties.FindAsync(sortId) != null;
|
|
}
|
|
|
|
public async Task<bool> AttrIdExists(string attrId) {
|
|
return await WineAttributes.FindAsync(attrId) != null;
|
|
}
|
|
|
|
public async Task<bool> CultIdExists(string cultId) {
|
|
return await WineCultivations.FindAsync(cultId) != null;
|
|
}
|
|
|
|
public async Task<int> NextMgNr() {
|
|
int c = 0;
|
|
(await Members.OrderBy(m => m.MgNr).Select(m => m.MgNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 10000) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextFbNr() {
|
|
int c = 0;
|
|
(await AreaCommitments.OrderBy(ac => ac.FbNr).Select(ac => ac.FbNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 10000) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextLNr(DateOnly date) {
|
|
var dateStr = date.ToString("yyyy-MM-dd");
|
|
int c = 0;
|
|
(await Deliveries.Where(d => d.DateString == dateStr).Select(d => d.LNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 100) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextDId(int year) {
|
|
int c = 0;
|
|
(await Deliveries.Where(d => d.Year == year).Select(d => d.DId).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 100) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextDPNr(int year, int did) {
|
|
int c = 0;
|
|
(await DeliveryParts.Where(p => p.Year == year && p.DId == did).Select(d => d.DPNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 100) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextRdNr(int kgnr) {
|
|
int c = 0;
|
|
(await WbRde.Where(r => r.KgNr == kgnr).Select(r => r.RdNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 100) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<int> NextAvNr(int year) {
|
|
int c = 0;
|
|
(await PaymentVariants.Where(v => v.Year == year).Select(v => v.AvNr).ToListAsync())
|
|
.ForEach(a => { if (a <= c + 100) c = a; });
|
|
return c + 1;
|
|
}
|
|
|
|
public async Task<WineQualLevel> GetWineQualityLevel(double kmw) {
|
|
return await WineQualityLevels
|
|
.Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
|
|
.OrderBy(q => q.MinKmw)
|
|
.LastAsync();
|
|
}
|
|
|
|
public void UpdateDeliveryPartModifiers(DeliveryPart part, IEnumerable<Modifier> oldModifiers, IEnumerable<Modifier> newModifiers) {
|
|
foreach (var m in Modifiers.Where(m => m.Year == part.Year)) {
|
|
var mod = new DeliveryPartModifier {
|
|
Year = part.Year,
|
|
DId = part.DId,
|
|
DPNr = part.DPNr,
|
|
ModId = m.ModId,
|
|
};
|
|
var old = oldModifiers.Where(pa => pa.ModId == m.ModId).FirstOrDefault();
|
|
if (newModifiers.Any(md => md.ModId == m.ModId)) {
|
|
if (old == null) {
|
|
Add(mod);
|
|
} else {
|
|
Update(mod);
|
|
}
|
|
} else {
|
|
if (old != null) {
|
|
Remove(mod);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task FetchMemberAreaCommitmentBuckets(int year, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var buckets = new Dictionary<int, Dictionary<string, AreaComBucket>>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT mgnr, bucket, area, min_kg, max_kg FROM v_area_commitment_bucket WHERE year = {year}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var mgnr = reader.GetInt32(0);
|
|
var vtrgid = reader.GetString(1);
|
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
|
buckets[mgnr][vtrgid] = new(reader.GetInt32(2), reader.GetInt32(3), reader.GetInt32(4));
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
_memberAreaCommitmentBuckets[year] = buckets;
|
|
}
|
|
|
|
private async Task FetchMemberDeliveryBuckets(int year, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var buckets = new Dictionary<int, Dictionary<string, int>>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT mgnr, bucket, weight FROM v_delivery_bucket WHERE year = {year}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var mgnr = reader.GetInt32(0);
|
|
var bucket = reader.GetString(1);
|
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
_memberDeliveryBuckets[year] = buckets;
|
|
}
|
|
|
|
private async Task FetchMemberDeliveryBucketsStrict(int year, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var buckets = new Dictionary<int, Dictionary<string, int>>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT mgnr, bucket, weight FROM v_delivery_bucket_strict WHERE year = {year}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var mgnr = reader.GetInt32(0);
|
|
var bucket = reader.GetString(1);
|
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
_memberDeliveryBucketsStrict[year] = buckets;
|
|
}
|
|
|
|
private async Task FetchMemberPaymentBuckets(int year, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var buckets = new Dictionary<int, Dictionary<string, int>>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT mgnr, bucket, weight FROM v_payment_bucket WHERE year = {year}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var mgnr = reader.GetInt32(0);
|
|
var bucket = reader.GetString(1);
|
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
|
buckets[mgnr][bucket] = reader.GetInt32(2);
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
_memberPaymentBuckets[year] = buckets;
|
|
}
|
|
|
|
private async Task FetchMemberUnderDelivery(int year, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var buckets = new Dictionary<int, Dictionary<string, UnderDelivery>>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"SELECT mgnr, bucket, weight, diff FROM v_under_delivery WHERE year = {year}";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var mgnr = reader.GetInt32(0);
|
|
var bucket = reader.GetString(1);
|
|
if (!buckets.ContainsKey(mgnr)) buckets[mgnr] = [];
|
|
buckets[mgnr][bucket] = new(reader.GetInt32(2), reader.GetInt32(3));
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
_memberUnderDelivery[year] = buckets;
|
|
}
|
|
|
|
public async Task<Dictionary<string, AreaComBucket>> GetMemberAreaCommitmentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
if (!_memberAreaCommitmentBuckets.ContainsKey(year))
|
|
await FetchMemberAreaCommitmentBuckets(year, cnx);
|
|
return _memberAreaCommitmentBuckets[year].GetValueOrDefault(mgnr, []);
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetMemberDeliveryBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
if (!_memberDeliveryBuckets.ContainsKey(year))
|
|
await FetchMemberDeliveryBuckets(year, cnx);
|
|
return _memberDeliveryBuckets[year].GetValueOrDefault(mgnr, []);
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetMemberDeliveryBucketsStrict(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
if (!_memberDeliveryBucketsStrict.ContainsKey(year))
|
|
await FetchMemberDeliveryBucketsStrict(year, cnx);
|
|
return _memberDeliveryBucketsStrict[year].GetValueOrDefault(mgnr, []);
|
|
}
|
|
|
|
public async Task<Dictionary<string, int>> GetMemberPaymentBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
if (!_memberPaymentBuckets.ContainsKey(year))
|
|
await FetchMemberPaymentBuckets(year, cnx);
|
|
return _memberPaymentBuckets[year].GetValueOrDefault(mgnr, []);
|
|
}
|
|
|
|
public async Task<Dictionary<string, UnderDelivery>> GetMemberUnderDelivery(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
if (!_memberUnderDelivery.ContainsKey(year))
|
|
await FetchMemberUnderDelivery(year, cnx);
|
|
return _memberUnderDelivery[year].GetValueOrDefault(mgnr, []);
|
|
}
|
|
|
|
public async Task<Dictionary<string, MemberBucket>> GetMemberBuckets(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var rightsAndObligations = await GetMemberAreaCommitmentBuckets(year, mgnr, cnx);
|
|
var deliveryBuckets = await GetMemberDeliveryBuckets(year, mgnr, cnx);
|
|
var deliveryBucketsStrict = await GetMemberDeliveryBucketsStrict(year, mgnr, cnx);
|
|
var paymentBuckets = await GetMemberPaymentBuckets(year, mgnr, cnx);
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
|
|
var buckets = new Dictionary<string, MemberBucket>();
|
|
foreach (var id in rightsAndObligations.Keys.Union(deliveryBuckets.Keys).Union(paymentBuckets.Keys)) {
|
|
var variety = await WineVarieties.FindAsync(id[..2]);
|
|
var attribute = await WineAttributes.FindAsync(id[2..]);
|
|
var name = (variety?.Name ?? "") + (id[2..] == "_" ? " (kein Qual.Wein)" : attribute != null ? $" ({attribute})" : "");
|
|
buckets[id] = new(
|
|
name,
|
|
rightsAndObligations.GetValueOrDefault(id).Area,
|
|
rightsAndObligations.GetValueOrDefault(id).Obligation,
|
|
rightsAndObligations.GetValueOrDefault(id).Right,
|
|
deliveryBuckets.GetValueOrDefault(id),
|
|
deliveryBucketsStrict.GetValueOrDefault(id),
|
|
paymentBuckets.GetValueOrDefault(id)
|
|
);
|
|
}
|
|
return buckets;
|
|
}
|
|
|
|
public static async Task<List<MemberStat>> GetMemberStats(int year, int mgnr, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var list = new List<MemberStat>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"""
|
|
SELECT v.name AS variety,
|
|
COALESCE(a.name, '') || IIF(a.name IS NOT NULL AND c.name IS NOT NULL, ' / ', '') || COALESCE(c.name, '') AS disc,
|
|
SUM(weight) AS weight
|
|
FROM v_delivery d
|
|
LEFT JOIN wine_variety v ON v.sortid = d.sortid
|
|
LEFT JOIN wine_attribute a ON a.attrid = d.attrid
|
|
LEFT JOIN wine_cultivation c ON c.cultid = d.cultid
|
|
WHERE d.year = {year} AND d.mgnr = {mgnr}
|
|
GROUP BY d.sortid, d.attrid, d.cultid
|
|
ORDER BY variety, disc;
|
|
""";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
list.Add(new(reader.GetString(0), reader.GetString(1), reader.GetInt32(2)));
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
return list;
|
|
}
|
|
|
|
public static async Task<List<ModifierStat>> GetModifierStats(int year, int avnr, SqliteConnection? cnx = null) {
|
|
var ownCnx = cnx == null;
|
|
cnx ??= await ConnectAsync();
|
|
var list = new List<ModifierStat>();
|
|
using (var cmd = cnx.CreateCommand()) {
|
|
cmd.CommandText = $"""
|
|
SELECT m.modid, m.name, m.count, m.min, m.max, m.sum, s.precision
|
|
FROM v_stat_modifier m
|
|
JOIN season s ON s.year = m.year
|
|
WHERE m.year = {year} AND m.avnr = {avnr}
|
|
""";
|
|
using var reader = await cmd.ExecuteReaderAsync();
|
|
while (await reader.ReadAsync()) {
|
|
var prec = (byte)reader.GetInt16(6);
|
|
long? min = reader.IsDBNull(3) ? null : reader.GetInt64(3);
|
|
long? max = reader.IsDBNull(4) ? null : reader.GetInt64(4);
|
|
var sum = reader.GetInt64(5);
|
|
if (min != null && max != null && Math.Abs((long)min) > Math.Abs((long)max))
|
|
(min, max) = (max, min);
|
|
list.Add(new(reader.GetString(0), reader.GetString(1), reader.GetInt32(2),
|
|
min == null ? null : Utils.DecFromDb((long)min, prec),
|
|
max == null ? null : Utils.DecFromDb((long)max, prec),
|
|
Utils.DecFromDb(sum, prec)));
|
|
}
|
|
}
|
|
if (ownCnx) await cnx.DisposeAsync();
|
|
return list;
|
|
}
|
|
}
|
|
}
|