Compare commits
	
		
			7 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7cc2f75e7c | |||
| c7a2f2241d | |||
| bd4ebb8c35 | |||
| 6d88c5645c | |||
| 5d017cc8ea | |||
| 0b8a1b321f | |||
| 95927c3f1a | 
							
								
								
									
										19
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -3,6 +3,25 @@ Changelog | ||||
| ========= | ||||
|  | ||||
|  | ||||
| [v0.13.7][v0.13.7] (2025-01-21) {#v0.13.7} | ||||
| ------------------------------------------ | ||||
|  | ||||
| ### Behobene Fehler {#0.13.7-bugfixes} | ||||
|  | ||||
| * In seltenen Fällen konnten im Auszahlungsvariante-Fenster (`ChartWindow`) manche (Sorten-/Attribut-/Bewirtschaftungsart-)Zuordnungen zu Kurven nicht richtig gespeichert werden. (0b8a1b321f) | ||||
| * Beim Öffnen des Ausgangs-Protokoll-Fensters (`MailLogWindow`) kam es zu einem Absturz. (5d017cc8ea) | ||||
| * Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) war es nicht möglich die Überweisungsdaten zu exportieren, sofern mindestens eine Traubengutschrift einen negativen Betrag aufwies. | ||||
|   Jetzt wird der Benutzer nur gewarnt und es ist möglich alle anderen Gutschriften zu exportieren. (6d88c5645c, c7a2f2241d) | ||||
|  | ||||
| ### Sonstiges {#0.13.7-misc} | ||||
|  | ||||
| * Im Auszahlungsvarianten-Fenster (`PaymentVariantsWindow`) ist es nun möglich das Datum einer Auszahlungsvariante zu ändern. (bd4ebb8c35) | ||||
|  | ||||
| [v0.13.7]: https://git.necronda.net/winzer/elwig/releases/tag/v0.13.7 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| [v0.13.6][v0.13.6] (2025-01-14) {#v0.13.6} | ||||
| ------------------------------------------ | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <UseWPF>true</UseWPF> | ||||
|     <PreserveCompilationContext>true</PreserveCompilationContext> | ||||
|     <ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon> | ||||
|     <Version>0.13.6</Version> | ||||
|     <Version>0.13.7</Version> | ||||
|     <SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages> | ||||
|     <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | ||||
|     <ApplicationManifest>app.manifest</ApplicationManifest> | ||||
|   | ||||
| @@ -306,6 +306,7 @@ namespace Elwig.Helpers.Billing { | ||||
|                     if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) { | ||||
|                         foreach (var k in ks) { | ||||
|                             if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<string>(out var o) ?? false) || o == v) | ||||
|                                 if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<string>(out var o2) ?? false) || o2 == v) | ||||
|                                     data.Remove(k); | ||||
|                         } | ||||
|                         data["default"] = v; | ||||
| @@ -317,6 +318,7 @@ namespace Elwig.Helpers.Billing { | ||||
|                     if ((ks.Count > vaributes.Count() * 0.5 && useDefault) || ks.Count == vaributes.Count()) { | ||||
|                         foreach (var k in ks) { | ||||
|                             if (!(originalData[$"{k[..2]}/"]?.AsValue().TryGetValue<decimal>(out var o) ?? false) || o == v) | ||||
|                                 if (!(originalData[k.Split('-')[0]]?.AsValue().TryGetValue<decimal>(out var o2) ?? false) || o2 == v) | ||||
|                                     data.Remove(k); | ||||
|                         } | ||||
|                         data["default"] = v; | ||||
| @@ -371,6 +373,15 @@ namespace Elwig.Helpers.Billing { | ||||
|                 } else if (k.Contains("/-")) { | ||||
|                     data.Remove(k, out var val); | ||||
|                     data.Add(k.Replace("/-", "-"), val); | ||||
|                     if (k[0] == '/' || k.Contains('-')) { | ||||
|                         foreach (var (k2, o) in originalData) { | ||||
|                             if (!data.ContainsKey(k2) && k2.Contains('-') && k2.Contains("-" + k.Split('-')[1]) && !k2.Contains("/-") | ||||
|                                 && (!k2.Contains('/') || k2.Length <= 4 || !data.ContainsKey(k2[2..]))) | ||||
|                             { | ||||
|                                 data[k2] = o?.DeepClone(); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -46,14 +46,14 @@ namespace Elwig.Helpers.Billing { | ||||
|                            m.mgnr, | ||||
|                            v.avnr, | ||||
|                            ROUND(p.amount / POW(10, s.precision - 2)) AS net_amount, | ||||
|                            ROUND(lp.amount / POW(10, s.precision - 2)) AS prev_amount, | ||||
|                            IIF(lc.amount >= 0, ROUND(lp.amount / POW(10, s.precision - 2)), 0) AS prev_net_amount, | ||||
|                            IIF(m.buchführend, s.vat_normal, s.vat_flatrate) AS vat, | ||||
|                            ROUND(IIF({Data.ConsiderTotalPenalty}, COALESCE(b.total_penalty, 0), 0) / POW(10, s.precision - 2)) + | ||||
|                            ROUND(IIF({Data.ConsiderContractPenalties}, COALESCE(u.total_penalty, 0), 0) / POW(10, 4 - 2)) + | ||||
|                            ROUND(IIF({Data.ConsiderAutoBusinessShares}, -COALESCE(a.total_amount, 0), 0) / POW(10, s.precision - 2)) + | ||||
|                            IIF({Data.ConsiderCustomModifiers}, COALESCE(x.amount, 0), 0) | ||||
|                            AS modifiers, | ||||
|                            lc.modifiers AS prev_modifiers | ||||
|                            IIF(lc.amount >= 0, lc.modifiers, 0) AS prev_modifiers | ||||
|                     FROM season s | ||||
|                         JOIN payment_variant v ON v.year = s.year | ||||
|                         LEFT JOIN payment_variant l ON l.year = s.year | ||||
|   | ||||
| @@ -15,7 +15,7 @@ namespace Elwig.Models.Dtos { | ||||
|  | ||||
|         public static IEnumerable<Transaction> FromPaymentVariant(PaymentVar variant) { | ||||
|             return variant.Credits | ||||
|                 .Where(c => c.Member.Iban != null) | ||||
|                 .Where(c => c.Member.Iban != null && c.Amount > 0) | ||||
|                 .OrderBy(c => c.TgNr) | ||||
|                 .Select(c => new Transaction(c)) | ||||
|                 .ToList(); | ||||
|   | ||||
| @@ -225,30 +225,8 @@ namespace Elwig.Windows { | ||||
|             DataPlot.Color = ColorUngebunden; | ||||
|             DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden); | ||||
|  | ||||
|             //OechslePricePlot.Interaction.Enable(new PlotActions() { | ||||
|             //    ZoomIn = StandardActions.ZoomIn, | ||||
|             //    ZoomOut = StandardActions.ZoomOut, | ||||
|             //    PanUp = StandardActions.PanUp, | ||||
|             //    PanDown = StandardActions.PanDown, | ||||
|             //    PanLeft = StandardActions.PanLeft, | ||||
|             //    PanRight = StandardActions.PanRight, | ||||
|             //    DragPan = StandardActions.DragPan, | ||||
|             //    DragZoom = StandardActions.DragZoom, | ||||
|             //    DragZoomRectangle = StandardActions.DragZoomRectangle, | ||||
|             //    ZoomRectangleClear = StandardActions.ZoomRectangleClear, | ||||
|             //    ZoomRectangleApply = StandardActions.ZoomRectangleApply, | ||||
|             //    AutoScale = StandardActions.AutoScale, | ||||
|             //}); | ||||
|  | ||||
|             //OechslePricePlot.Plot.XAxis.ManualTickSpacing(1); | ||||
|             //OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1); | ||||
|             OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5); | ||||
|  | ||||
|             //OechslePricePlot.Plot.Layout(padding: 0); | ||||
|             //OechslePricePlot.Plot.XAxis2.Layout(padding: 0); | ||||
|             //OechslePricePlot.Plot.YAxis.Layout(padding: 0); | ||||
|             //OechslePricePlot.Plot.YAxis2.Layout(padding: 0); | ||||
|  | ||||
|             HighlightedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.OpenCircle, 10, Colors.Red); | ||||
|             HighlightedPointPlot.IsVisible = false; | ||||
|  | ||||
|   | ||||
| @@ -19,6 +19,7 @@ namespace Elwig.Windows { | ||||
|         } | ||||
|  | ||||
|         private async void TimeSpanInput_SelectionChanged(object sender, RoutedEventArgs evt) { | ||||
|             if (!IsLoaded) return; | ||||
|             DateTime? fromDate = DateTime.Now; | ||||
|             if (TimeSpanInput.SelectedIndex == 0) { | ||||
|                 fromDate = fromDate.Value.AddDays(-7); | ||||
| @@ -50,6 +51,7 @@ namespace Elwig.Windows { | ||||
|         } | ||||
|  | ||||
|         private void ApplyFilters() { | ||||
|             if (!IsLoaded) return; | ||||
|             var filters = FilterInput.Text.Split(' '); | ||||
|             IEnumerable<Row> data = Data; | ||||
|             switch (TypeInput.SelectedIndex) { | ||||
|   | ||||
| @@ -140,8 +140,9 @@ | ||||
|             <TextBox x:Name="CommentInput" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0,40,10,0" | ||||
|                      TextChanged="CommentInput_TextChanged"/> | ||||
|  | ||||
|             <Label Content="Erstellt am:" Margin="10,70,0,0" Grid.Column="0"/> | ||||
|             <TextBox x:Name="DateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0" IsReadOnly="True"/> | ||||
|             <Label Content="Datum:" Margin="10,70,0,0" Grid.Column="0"/> | ||||
|             <TextBox x:Name="DateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,70,10,0" | ||||
|                      TextChanged="DateInput_TextChanged"/> | ||||
|  | ||||
|             <Label Content="Überwiesen am:" Margin="10,100,0,0" Grid.Column="0"/> | ||||
|             <TextBox x:Name="TransferDateInput" Grid.Column="1" Width="77" HorizontalAlignment="Left" Margin="0,100,10,0" | ||||
|   | ||||
| @@ -19,7 +19,7 @@ namespace Elwig.Windows { | ||||
|  | ||||
|         public readonly int Year; | ||||
|         public readonly bool SeasonLocked; | ||||
|         private bool DataValid, DataChanged, NameChanged, CommentChanged, TransferDateValid, TransferDateChanged; | ||||
|         private bool DataValid, DataChanged, NameChanged, CommentChanged, DateValid, DateChanged, TransferDateValid, TransferDateChanged; | ||||
|         private BillingData? BillingData; | ||||
|         private bool WeightModifierChanged = false; | ||||
|  | ||||
| @@ -175,6 +175,7 @@ namespace Elwig.Windows { | ||||
|         private void UpdateSaveButton() { | ||||
|             SaveButton.IsEnabled = PaymentVariantList.SelectedItem != null && | ||||
|                 ((DataChanged && DataValid) || NameChanged || CommentChanged || | ||||
|                 (DateChanged && DateValid) || | ||||
|                 (TransferDateChanged && TransferDateValid) || | ||||
|                 (ConsiderModifiersInput.IsChecked != BillingData?.ConsiderDelieryModifiers) || | ||||
|                 (ConsiderPenaltiesInput.IsChecked != BillingData?.ConsiderContractPenalties) || | ||||
| @@ -433,10 +434,16 @@ namespace Elwig.Windows { | ||||
|  | ||||
|             var withoutIban = v.Credits.Count(c => c.Member.Iban == null); | ||||
|             if (withoutIban > 0) { | ||||
|                 var r = MessageBox.Show($"Achtung: Für {withoutIban} Mitglieder ist kein IBAN hinterlegt.\nDiese werden NICHT exportiert.", | ||||
|                 var r = MessageBox.Show($"Achtung: Für {withoutIban:N0} Mitglieder ist kein IBAN hinterlegt.\n\nDiese werden NICHT exportiert.", | ||||
|                     "Mitglieder ohne IBAN", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel); | ||||
|                 if (r != MessageBoxResult.OK) return; | ||||
|             } | ||||
|             var withNegAmount = v.Credits.Count(c => c.Amount <= 0); | ||||
|             if (withNegAmount > 0) { | ||||
|                 var r = MessageBox.Show($"Achtung: Es gibt {withNegAmount:N0} Traubengutschriften mit negativem Betrag.\n\nDiese werden NICHT exportiert.", | ||||
|                     "Traubengutschriften mit negativem Betrag", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.OK); | ||||
|                 if (r != MessageBoxResult.OK) return; | ||||
|             } | ||||
|  | ||||
|             var d = new SaveFileDialog() { | ||||
|                 FileName = $"{App.Client.NameToken}-Überweisungsdaten-{v.Year}-{v.Name.Trim().Replace(' ', '-')}.{Ebics.FileExtension}", | ||||
| @@ -489,6 +496,7 @@ namespace Elwig.Windows { | ||||
|             try { | ||||
|                 v.Name = NameInput.Text; | ||||
|                 v.Comment = (CommentInput.Text != "") ? CommentInput.Text : null; | ||||
|                 v.DateString = string.Join("-", DateInput.Text.Split(".").Reverse()); | ||||
|                 v.TransferDateString = (TransferDateInput.Text != "") ? string.Join("-", TransferDateInput.Text.Split(".").Reverse()) : null; | ||||
|                 var d = App.Config.Debug ? BillingData.FromJson(DataInput.Text) : BillingData; | ||||
|                 d.ConsiderDelieryModifiers = ConsiderModifiersInput.IsChecked ?? false; | ||||
| @@ -557,6 +565,27 @@ namespace Elwig.Windows { | ||||
|             UpdateSaveButton(); | ||||
|         } | ||||
|  | ||||
|         private void DateInput_TextChanged(object sender, TextChangedEventArgs evt) { | ||||
|             if (PaymentVariantList.SelectedItem is not PaymentVar v) { | ||||
|                 ControlUtils.ClearInputState(DateInput); | ||||
|                 return; | ||||
|             } | ||||
|             var res = Validator.CheckDate(DateInput, true); | ||||
|             if (!res.IsValid) { | ||||
|                 ControlUtils.SetInputInvalid(DateInput); | ||||
|                 DateValid = false; | ||||
|             } else if (DateInput.Text != $"{v.Date:dd.MM.yyyy}") { | ||||
|                 ControlUtils.SetInputChanged(DateInput); | ||||
|                 DateValid = true; | ||||
|                 DateChanged = true; | ||||
|             } else { | ||||
|                 ControlUtils.ClearInputState(DateInput); | ||||
|                 DateValid = true; | ||||
|                 DateChanged = false; | ||||
|             } | ||||
|             UpdateSaveButton(); | ||||
|         } | ||||
|  | ||||
|         private void TransferDateInput_TextChanged(object sender, TextChangedEventArgs evt) { | ||||
|             if (PaymentVariantList.SelectedItem is not PaymentVar v) { | ||||
|                 ControlUtils.ClearInputState(TransferDateInput); | ||||
|   | ||||
| @@ -972,5 +972,67 @@ namespace Tests.HelperTests { | ||||
|                 } | ||||
|                 """)); | ||||
|         } | ||||
|  | ||||
|         private static IEnumerable<List<T>> GetPermutations<T>(T[] input) { | ||||
|             if (input.Length <= 1) | ||||
|                 yield return [..input]; | ||||
|             for (int i = 0; i < input.Length; i++) { | ||||
|                 yield return [input[i]]; | ||||
|                 foreach (var p in GetPermutations(input.Where((_, j) => j != i).ToArray())) { | ||||
|                     p.Insert(0, input[i]); | ||||
|                     yield return p; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static IEnumerable<List<HashSet<RawVaribute>>> GetCurves(RawVaribute[] vaributes, int minNumCurves = 1, int maxNumCurves = 10) { | ||||
|             foreach (var p in GetPermutations(vaributes)) { | ||||
|                 for (int nCurves = minNumCurves; nCurves <= Math.Min(maxNumCurves, p.Count); nCurves++) { | ||||
|                     yield return Enumerable.Range(0, nCurves) | ||||
|                         .Select(n => p.Where((v, idx) => idx % nCurves == n).ToHashSet()) | ||||
|                         .ToList(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private readonly HashSet<string> TestedCurves = []; | ||||
|  | ||||
|         private void TestCollapse(List<HashSet<RawVaribute>> curves) { | ||||
|             var str = string.Join("\n", curves.Select(c => string.Join("  ", c.Select(v => v.ToString().PadRight(6)).Order())).Order().Select((c, n) => $"{n + 1}: [ " + c + " ]")); | ||||
|             if (!TestedCurves.Add(str)) | ||||
|                 return; | ||||
|             var vaributes = curves.SelectMany(v => v).ToList(); | ||||
|             List<GraphEntry> entries = curves | ||||
|                 .Select((l, n) => new GraphEntry(n, 4, new BillingData.Curve(BillingData.CurveMode.Oe, new() { | ||||
|                     [73] = n + 1, | ||||
|                 }, null), GetSelection(l.Select(v => v.ToString())))) | ||||
|                 .ToList(); | ||||
|             var data = BillingData.FromGraphEntries(entries); | ||||
|             var test = PaymentBillingData.FromJson(data.ToJsonString(), vaributes); | ||||
|             for (int i = 0; i < curves.Count; i++) { | ||||
|                 foreach (var v in curves[i]) { | ||||
|                     var val = test.CalculatePrice(v.SortId!, v.AttrId, v.CultId, "QUW", false, 73.0, 15.0); | ||||
|                     var actualCurve = (int)val; | ||||
|                     Assert.That(actualCurve, Is.EqualTo(i + 1), $"Invalid: {v} (Curve {i + 1} -> {actualCurve})\n\n{str}\n\n{data.ToJsonString(JsonOpts)}\n"); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [Test] | ||||
|         public void TestCollapse_01_Permutations() { | ||||
|             RawVaribute[][] configurations = [ | ||||
|                 [new("GV/-"), new("WR/-"), new("ZW/-"), new("GV/K-"), new("WR/K-"), new("ZW/K-")], | ||||
|                 [new("GV/-"), new("WR/-"), new("GV/K-"), new("WR/K-"), new("GV/S-"), new("WR/S-")], | ||||
|                 [new("GV/-"), new("WR/-"), new("ZW/-"), new("GV/-B"), new("WR/-B"), new("ZW/-B")], | ||||
|                 [new("GV/-"), new("WR/-"), new("GV/-B"), new("WR/-B"), new("GV/-KIP"), new("WR/-KIP")], | ||||
|                 [new("GV/-"), new("GV/K-"), new("ZW/-"), new("ZW/K-"), new("GV/-B"), new("GV/K-B"), new("ZW/-B"), new("ZW/K-B")], | ||||
|             ]; | ||||
|             Assert.Multiple(() => { | ||||
|                 foreach (var config in configurations) { | ||||
|                     foreach (var c in GetCurves(config)) | ||||
|                         TestCollapse(c); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user