Compare commits

...

17 Commits

Author SHA1 Message Date
Elias Fierke
4cfbcd0ab4 [feat:] added Country implementation for country code editing (unused) 2026-01-01 14:45:57 +01:00
Elias Fierke
b82473ada2 [chore:] PdfBuilder.cs skips empty addresses 2025-12-21 11:43:41 +01:00
Elias Fierke
1cba67253a [chore:] selectable pdf file path for label creation 2025-12-21 11:32:38 +01:00
Elias Fierke
63c1559110 [chore:] added "Postfach" to address creation 2025-12-21 11:25:48 +01:00
Elias Fierke
b670ba11fa [chore:] sender address usage in PdfBuilder.cs 2025-12-15 10:17:10 +01:00
Elias Fierke
1ad57543d1 [chore:] sender address editing 2025-12-15 10:04:33 +01:00
Elias Fierke
6cd4ea2df6 [chore:] added sender address field 2025-12-15 10:01:07 +01:00
Elias Fierke
30e42afe35 [chore:] rearrangend some result window features for debugging purposes (unfinal) 2025-12-14 14:33:55 +01:00
Elias Fierke
70e127b2f0 [init:] Initialized PDF Creation 2025-12-14 14:33:25 +01:00
Elias Fierke
7c73170b46 [chore:] implemented label generation button handler 2025-12-14 14:33:07 +01:00
Elias Fierke
290f69e976 [chore:] the IDE wanted to add some spaces 2025-12-14 14:32:45 +01:00
Elias Fierke
6ce08d7d4a [gui:] added a few tabs that will be used later 2025-12-14 14:32:02 +01:00
Elias Fierke
86df6f6a63 [file:] imported PdfSharp 2025-12-14 14:31:33 +01:00
Elias Fierke
eae0568ae0 [feat:] added KasPersonError.GetString() 2025-12-14 14:31:20 +01:00
Elias Fierke
4ebd6bc407 [fix:] fixed some logical errors in address creation 2025-12-14 14:30:53 +01:00
Elias Fierke
2c22306fef [fix:] plz check was bad 2025-12-14 14:30:33 +01:00
Elias Fierke
174223ba9e [fix:] Address Creation used an empty address instead of the one from the address set 2025-12-14 14:30:07 +01:00
9 changed files with 875 additions and 77 deletions

View File

@@ -177,4 +177,14 @@ public class KasPersonError
//public int refsid { get; set; } //public int refsid { get; set; }
public List<AddressCheck.ErrorTypes> errors { get; set; } = new(); public List<AddressCheck.ErrorTypes> errors { get; set; } = new();
public List<AddressCheck.WarningTypes> warnings { get; set; } = new(); public List<AddressCheck.WarningTypes> warnings { get; set; } = new();
public string GetString()
{
var output = "";
foreach (var error in errors) output += error + ", ";
foreach (var warning in warnings) output += warning + ", ";
return output;
}
} }

View File

@@ -21,6 +21,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Lucide.Avalonia" Version="0.1.35"/> <PackageReference Include="Lucide.Avalonia" Version="0.1.35"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/> <PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
<PackageReference Include="PdfSharp" Version="6.1.1"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -135,6 +135,7 @@
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" IsEnabled="False" <Button HorizontalAlignment="Stretch" IsEnabled="False"
Click="BtnGenerateLabels_OnClick"
HorizontalContentAlignment="Center" x:Name="BtnGenerateLabels" HorizontalContentAlignment="Center" x:Name="BtnGenerateLabels"
Margin="0,0,0,10"> Margin="0,0,0,10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -160,10 +161,12 @@
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="SquaresUnite" Width="36" Height="36" /> <LucideIcon Kind="SquaresUnite" Width="36" Height="36" />
<Label Content="Vereinigung" VerticalContentAlignment="Center" FontSize="15" <Label Content="Vereinigung" VerticalContentAlignment="Center"
FontSize="15"
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
<Label FontSize="9" Content="Fügt Elemente beider Mengen in eine Menge zusammen"></Label> <Label FontSize="9"
Content="Fügt Elemente beider Mengen in eine Menge zusammen" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" MinWidth="240" <Button HorizontalAlignment="Stretch" MinWidth="240"
@@ -177,9 +180,10 @@
FontSize="15" FontSize="15"
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
<Label FontSize="9" Content="Überträgt nur doppelte Elemente in die neue Menge"></Label> <Label FontSize="9"
Content="Überträgt nur doppelte Elemente in die neue Menge" />
</StackPanel> </StackPanel>
</Button> </Button>
<Button HorizontalAlignment="Stretch" MinWidth="240" <Button HorizontalAlignment="Stretch" MinWidth="240"
HorizontalContentAlignment="Center" x:Name="BtnCombineDifference" HorizontalContentAlignment="Center" x:Name="BtnCombineDifference"
@@ -192,7 +196,8 @@
FontSize="15" FontSize="15"
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
<Label FontSize="9" Content="Elemente der ersten Menge ohne Elemente der zweiten Menge"></Label> <Label FontSize="9"
Content="Elemente der ersten Menge ohne Elemente der zweiten Menge" />
</StackPanel> </StackPanel>
</Button> </Button>
@@ -203,11 +208,12 @@
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<LucideIcon Kind="SquaresExclude" Width="36" Height="36" /> <LucideIcon Kind="SquaresExclude" Width="36" Height="36" />
<Label Content="Symmetrische Differenz" VerticalContentAlignment="Center" <Label Content="Symmetrische Differenz"
VerticalContentAlignment="Center"
FontSize="15" FontSize="15"
FontWeight="Bold" /> FontWeight="Bold" />
</StackPanel> </StackPanel>
<Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind"></Label> <Label FontSize="9" Content="Nur Elemente, die NICHT doppelt sind" />
</StackPanel> </StackPanel>
</Button> </Button>
@@ -241,6 +247,14 @@
</StackPanel> </StackPanel>
</TabItem.Header> </TabItem.Header>
</TabItem> </TabItem>
<TabItem IsEnabled="False">
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="LibraryBig" Width="32" Height="32" Size="32" />
<Label FontSize="20" Content="Wiki" VerticalContentAlignment="Center" />
</StackPanel>
</TabItem.Header>
</TabItem>
<TabItem IsEnabled="True"> <TabItem IsEnabled="True">
<TabItem.Header> <TabItem.Header>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -315,6 +329,13 @@
TextChanged="TbSettingsCustomerDescription_OnTextChanged" TextChanged="TbSettingsCustomerDescription_OnTextChanged"
x:Name="TbSettingsCustomerDescription" /> x:Name="TbSettingsCustomerDescription" />
</Grid> </Grid>
<Grid ColumnDefinitions="150,*">
<Label Content="Absenderadresse" />
<TextBox Grid.Column="1" Watermark="Absenderadresse"
HorizontalAlignment="Stretch"
TextChanged="TbSettingsCustomerSenderAddress_OnTextChanged"
x:Name="TbSettingsCustomerSenderAddress" />
</Grid>
<Grid ColumnDefinitions="150,*"> <Grid ColumnDefinitions="150,*">
<Label Content="CSV-Trennzeichen" /> <Label Content="CSV-Trennzeichen" />
<TextBox Grid.Column="1" Watermark="," <TextBox Grid.Column="1" Watermark=","
@@ -357,6 +378,87 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<LucideIcon Kind="Globe" Width="16" Height="16" Size="16" />
<Label FontSize="16" Content="Länder" VerticalContentAlignment="Center" />
</StackPanel>
</TabItem.Header>
<Grid ColumnDefinitions="*,*">
<Grid Grid.Column="0" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="CountryList" Grid.Row="0"
SelectionChanged="CountryList_OnSelectionChanged" />
<Grid Grid.Row="1" ColumnDefinitions="*,250" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountry" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsNewCountry_OnClick"
HorizontalContentAlignment="Center" Content="+ Hinzufügen"
Margin="5,0,0,0" />
</Grid>
</Grid>
<Grid Grid.Column="1" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid ColumnDefinitions="300,*" Margin="0,0,0,5">
<TextBlock Text="Name:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryName"
TextChanged="TbSettingsCountryName_OnTextChanged" />
</Grid>
<Grid ColumnDefinitions="300,*" Margin="0,0,0,5" Grid.Row="1">
<TextBlock Text="Übersetzung:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" x:Name="TbSettingsCountryTranslation"
TextChanged="TbSettingsCountryTranslation_OnTextChanged" />
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="LbSettingsAlternatives" Grid.Row="0" SelectionMode="Multiple" />
<Grid Grid.Row="1" ColumnDefinitions="*,200" Margin="0,5,0,0">
<TextBox x:Name="TbSettingsNewCountryAlternative"
Watermark="Kürzel/Alternative" />
<Button Grid.Column="1" x:Name="BtnSettingsNewCountryAlternative"
Content="+ Hinzufügen" Margin="5 0 0 0"
HorizontalContentAlignment="Center"
Click="BtnSettingsNewCountryAlternative_OnClick"
HorizontalAlignment="Stretch" />
</Grid>
<Button x:Name="BtnSettingsRemoveSelectedAlternatives" Grid.Row="2"
Content="Ausgewählte Entfernen" Background="#99963434"
HorizontalContentAlignment="Center"
HorizontalAlignment="Stretch"
Click="BtnSettingsRemoveSelectedAlternatives_OnClick"
Margin="0,5,0,0" />
<Button Grid.Row="3"
Content="Land Entfernen" Background="#99963434"
HorizontalContentAlignment="Center" x:Name="BtnSettingsRemoveCountry"
HorizontalAlignment="Stretch" Click="BtnSettingsRemoveCountry_OnClick"
Margin="0,5,0,0" />
</Grid>
</Grid>
</Grid>
</TabItem>
</TabControl> </TabControl>
</TabItem> </TabItem>
</TabControl> </TabControl>

View File

@@ -11,6 +11,9 @@ namespace Logof_Client;
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public static MainWindow _instance; public static MainWindow _instance;
private Country _selectedCountry;
private bool _suppressCountryRefresh = false;
public Uri filePath; public Uri filePath;
public MainWindow() public MainWindow()
@@ -26,6 +29,8 @@ public partial class MainWindow : Window
Global.Load(); Global.Load();
Settings.Load(); Settings.Load();
RefreshCountryView();
//Thread.Sleep(3000); //Thread.Sleep(3000);
//Show(); //Show();
} }
@@ -249,13 +254,13 @@ public partial class MainWindow : Window
var processor = new CombineAddresses(progressWindow); var processor = new CombineAddresses(progressWindow);
var result = await processor.Perform(address_lists, type, CbMergeExportUnmerged.IsChecked); var result = await processor.Perform(address_lists, type, CbMergeExportUnmerged.IsChecked);
if(result.Item1 != null) if (result.Item1 != null)
result.Item1.owner_id = owner_id; result.Item1.owner_id = owner_id;
if(result.Item2 != null) if (result.Item2 != null)
result.Item2.owner_id = owner_id; result.Item2.owner_id = owner_id;
if(result.Item1 != null) if (result.Item1 != null)
Settings._instance.addressSets.addresses.Add(result.Item1); Settings._instance.addressSets.addresses.Add(result.Item1);
if(result.Item2 != null) if (result.Item2 != null)
Settings._instance.addressSets.addresses.Add(result.Item2); Settings._instance.addressSets.addresses.Add(result.Item2);
Settings.Save(); Settings.Save();
progressWindow.Close(); progressWindow.Close();
@@ -303,6 +308,7 @@ public partial class MainWindow : Window
{ {
TbSettingsCustomerDescription.Text = customer.description; TbSettingsCustomerDescription.Text = customer.description;
TbSettingsCustomerName.Text = customer.name; TbSettingsCustomerName.Text = customer.name;
TbSettingsCustomerSenderAddress.Text = customer.sender_address;
TbSettingsCustomerCsvSeparator.Text = customer.separator.ToString(); TbSettingsCustomerCsvSeparator.Text = customer.separator.ToString();
if (customer.patch != null) if (customer.patch != null)
TbSettingsCustomerPatchInfo.Text = customer.patch.ToString(); TbSettingsCustomerPatchInfo.Text = customer.patch.ToString();
@@ -457,8 +463,8 @@ public partial class MainWindow : Window
{ {
BtnCheck.IsEnabled = true; BtnCheck.IsEnabled = true;
BtnCombine.IsEnabled = true; BtnCombine.IsEnabled = true;
BtnGenerateLabels.IsEnabled = true;
// BtnGenerateLabels.IsEnabled = true;
// BtnRepair.IsEnabled = true; // BtnRepair.IsEnabled = true;
// BtnShorten.IsEnabled = true; // BtnShorten.IsEnabled = true;
} }
@@ -554,4 +560,163 @@ public partial class MainWindow : Window
StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff"); StartCombine(list, Convert.ToInt32(LstCustomers.SelectedItem.ToString().Split(" - ")[0]), "symdiff");
} }
private async void BtnGenerateLabels_OnClick(object? sender, RoutedEventArgs e)
{
var saveDialog = new SaveFileDialog
{
DefaultExtension = "pdf",
Filters = { new FileDialogFilter { Name = "PDF-Dateien", Extensions = { "pdf" } } }
};
var filePath = await saveDialog.ShowAsync(this);
if (!string.IsNullOrEmpty(filePath))
{
var builder = new PdfBuilder();
builder.CreateAddressLabelPdfFromAddressSetWithPlaceholder(
Convert.ToInt32(LstCustomerAdressSets.SelectedItems[0].ToString().Split(" - ")[0]),
"Company Logo/Info",
filePath
);
//return true;
}
}
private void TbSettingsCustomerSenderAddress_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (LstSettingsCustomers.SelectedIndex == null || LstSettingsCustomers.SelectedIndex == -1) return;
foreach (var customer in Settings._instance.customers.customers)
if (customer.ID == Settings._instance.customers.current)
customer.sender_address = TbSettingsCustomerSenderAddress.Text;
}
// Country Section
private void BtnSettingsRemoveCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (_selectedCountry != null)
{
Global._instance.countries.Remove(_selectedCountry);
_selectedCountry = null;
}
RefreshCountryView();
}
public void RefreshCountryView()
{
if (_suppressCountryRefresh) return;
try
{
_suppressCountryRefresh = true;
Global.Save();
var alt_index = LbSettingsAlternatives.SelectedIndex;
var country_index = CountryList.SelectedIndex;
LbSettingsAlternatives.Items.Clear();
try
{
CountryList.Items.Clear();
}
catch
{
}
foreach (var c in Global._instance.countries) CountryList.Items.Add(c.name);
try
{
CountryList.SelectedIndex = country_index;
}
catch
{
}
try
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
}
catch
{
_selectedCountry = null;
}
if (_selectedCountry == null) return;
//Console.WriteLine("Refreshing alternatives...");
foreach (var a in _selectedCountry.alternatives)
{
LbSettingsAlternatives.Items.Add(a);
//Console.WriteLine(a);
}
TbSettingsCountryName.Text = _selectedCountry.name;
TbSettingsCountryTranslation.Text = _selectedCountry.translation;
try
{
LbSettingsAlternatives.SelectedIndex = alt_index;
}
catch
{
}
}
finally
{
_suppressCountryRefresh = false;
}
}
private void BtnSettingsRemoveSelectedAlternatives_OnClick(object? sender, RoutedEventArgs e)
{
foreach (var selected in LbSettingsAlternatives.SelectedItems)
try
{
_selectedCountry.alternatives.Remove(selected.ToString());
}
catch
{
Console.WriteLine("Error while removing country alternative");
}
RefreshCountryView();
}
private void BtnSettingsNewCountryAlternative_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountryAlternative.Text)) return;
_selectedCountry.alternatives.Add(TbSettingsNewCountryAlternative.Text.Trim());
RefreshCountryView();
}
private void TbSettingsCountryTranslation_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryTranslation.Text))
{
_selectedCountry.translation = TbSettingsCountryTranslation.Text.Trim();
RefreshCountryView();
}
}
private void TbSettingsCountryName_OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(TbSettingsCountryName.Text))
{
_selectedCountry.name = TbSettingsCountryName.Text.Trim();
RefreshCountryView();
}
}
private void BtnSettingsNewCountry_OnClick(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(TbSettingsNewCountry.Text)) return;
Global._instance.countries.Add(new Country(TbSettingsNewCountry.Text));
RefreshCountryView();
}
private void CountryList_OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
_selectedCountry = Country.GetByName(CountryList.SelectedItems[0].ToString());
RefreshCountryView();
}
} }

View File

@@ -18,7 +18,9 @@
x:Name="BtnShowSelected" Click="BtnShowSelected_OnClick" x:Name="BtnShowSelected" Click="BtnShowSelected_OnClick"
Margin="10,10,10,10" /> Margin="10,10,10,10" />
</Grid> </Grid>
<DataGrid x:Name="DgResult" Grid.Column="1" AutoGenerateColumns="True" /> <ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="StkResults" Orientation="Vertical" Margin="10" />
</ScrollViewer>
</Grid> </Grid>
</Window> </Window>

View File

@@ -5,6 +5,7 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media;
namespace Logof_Client; namespace Logof_Client;
@@ -30,7 +31,43 @@ public partial class ResultWindow : Window
// Filter to only show persons with errors // Filter to only show persons with errors
var result_with_errors = result.Where(p => p.PersonError != null).ToList(); var result_with_errors = result.Where(p => p.PersonError != null).ToList();
LblResultCount.Content = $"{result_with_errors.Count}/{ur_result.Count} Ergebnisse"; LblResultCount.Content = $"{result_with_errors.Count}/{ur_result.Count} Ergebnisse";
DgResult.ItemsSource = result_with_errors;
StkResults.Children.Clear();
foreach (var person in result_with_errors) StkResults.Children.Add(CreatePersonGrid(person));
}
private Grid CreatePersonGrid(KasPerson person)
{
var grid = new Grid
{
ColumnDefinitions = ColumnDefinitions.Parse("100,*,100,*,100,*"),
RowDefinitions = RowDefinitions.Parse("Auto"),
Margin = new Thickness(0, 5, 0, 5),
Background = new SolidColorBrush(Color.Parse("#F0F0F0"))
};
// Refsid
grid.Children.Add(new TextBlock
{
Text = "refsid: ",
FontWeight = FontWeight.Bold, Margin = new Thickness(5)
});
grid.Children.Add(new TextBlock { Text = person.refsid.ToString(), Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[1], 1);
// PLZ
grid.Children.Add(new TextBlock { Text = "plz:", FontWeight = FontWeight.Bold, Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[2], 2);
grid.Children.Add(new TextBlock { Text = person.plz, Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[3], 3);
// PPLZ
grid.Children.Add(new TextBlock { Text = "errors:", FontWeight = FontWeight.Bold, Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[4], 4);
grid.Children.Add(new TextBlock { Text = person.PersonError.GetString(), Margin = new Thickness(5) });
Grid.SetColumn(grid.Children[5], 5);
return grid;
} }
private void ViewSingle(int refsid) private void ViewSingle(int refsid)
@@ -112,46 +149,74 @@ public partial class ResultWindow : Window
private void UpdateFilter() private void UpdateFilter()
{ {
var temp_result = new List<KasPerson>(); var temp_result = new List<KasPerson>();
var checked_types = new List<AddressCheck.ErrorTypes>();
var checked_types_war = new List<AddressCheck.WarningTypes>(); var checkedErrors = new HashSet<AddressCheck.ErrorTypes>();
var checkedWarnings = new HashSet<AddressCheck.WarningTypes>();
// safer parsing: use TryParse and trim the Content string
foreach (var cb in errortypecheckboxes) foreach (var cb in errortypecheckboxes)
if (cb.IsChecked == true) if (cb.IsChecked == true)
checked_types.Add( {
(AddressCheck.ErrorTypes)Enum.Parse(typeof(AddressCheck.ErrorTypes), cb.Content.ToString())); var s = cb.Content?.ToString()?.Trim();
if (!string.IsNullOrEmpty(s) &&
Enum.TryParse<AddressCheck.ErrorTypes>(s, true, out var et))
checkedErrors.Add(et);
}
foreach (var cb in warningtypecheckboxes) foreach (var cb in warningtypecheckboxes)
if (cb.IsChecked == true) if (cb.IsChecked == true)
checked_types_war.Add( {
(AddressCheck.WarningTypes)Enum.Parse(typeof(AddressCheck.WarningTypes), cb.Content.ToString())); var s = cb.Content?.ToString()?.Trim();
if (!string.IsNullOrEmpty(s) &&
Enum.TryParse<AddressCheck.WarningTypes>(s, true, out var wt))
checkedWarnings.Add(wt);
}
foreach (var person in ur_result) // If no checkboxes are selected, show all persons with errors (default behavior)
{ if (checkedErrors.Count == 0 && checkedWarnings.Count == 0)
if (person.PersonError == null) continue; temp_result = ur_result.Where(p => p.PersonError != null).ToList();
else
foreach (var person in ur_result)
{
if (person.PersonError == null) continue;
foreach (var err in person.PersonError.errors) var personErrors = person.PersonError.errors ?? Enumerable.Empty<AddressCheck.ErrorTypes>();
if (checked_types.Contains(err) && !temp_result.Contains(person)) var personWarnings = person.PersonError.warnings ?? Enumerable.Empty<AddressCheck.WarningTypes>();
var matchesError = false;
var matchesWarning = false;
// only test errors if the user selected error-types
if (checkedErrors.Count > 0)
matchesError = personErrors.Any(err => checkedErrors.Contains(err));
// only test warnings if the user selected warning-types
if (checkedWarnings.Count > 0)
matchesWarning = personWarnings.Any(war => checkedWarnings.Contains(war));
// If at least one category matches (OR across categories), include person
if ((matchesError || matchesWarning) && !temp_result.Contains(person))
temp_result.Add(person); temp_result.Add(person);
}
foreach (var war in person.PersonError.warnings)
if (checked_types_war.Contains(war) && !temp_result.Contains(person))
temp_result.Add(person);
}
LblResultCount.Content = $"{temp_result.Count}/{ur_result.Count} Ergebnisse"; LblResultCount.Content = $"{temp_result.Count}/{ur_result.Count} Ergebnisse";
DgResult.ItemsSource = temp_result;
StkResults.Children.Clear();
foreach (var person in temp_result) StkResults.Children.Add(CreatePersonGrid(person));
} }
private void BtnShowSelected_OnClick(object? sender, RoutedEventArgs e) private void BtnShowSelected_OnClick(object? sender, RoutedEventArgs e)
{ {
foreach (var selected in DgResult.SelectedItems) // foreach (var selected in DgResult.SelectedItems)
try // try
{ // {
var _asKas = (KasPerson)selected; // var _asKas = (KasPerson)selected;
ViewSingle(_asKas.refsid); // ViewSingle(_asKas.refsid);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
Console.WriteLine(ex.Message); // Console.WriteLine(ex.Message);
} // }
} }
} }

View File

@@ -62,6 +62,7 @@ public class Global
} }
public string config_path { get; set; } = ""; public string config_path { get; set; } = "";
public List<Country> countries { get; set; } = new();
public static void Save() public static void Save()
{ {
@@ -121,10 +122,12 @@ public class Customer
public string name { get; set; } = ""; public string name { get; set; } = "";
public string description { get; set; } = ""; public string description { get; set; } = "";
public string sender_address { get; set; } = "";
public AddressPatch patch { get; set; } public AddressPatch patch { get; set; }
public char separator { get; set; } = ','; public char separator { get; set; } = ',';
public int ID { get; } public int ID { get; }
public static int GetIDByCustomerListItem(string item_content) public static int GetIDByCustomerListItem(string item_content)
{ {
var id = item_content.Split(" - ")[0]; var id = item_content.Split(" - ")[0];
@@ -142,6 +145,40 @@ public class AddressSets
if (i.ID == ID) if (i.ID == ID)
return i; return i;
return null;
}
}
public class Country
{
public Country(string name, string translation, List<string> alternatives)
{
this.name = name;
this.translation = translation;
this.alternatives = alternatives;
}
public Country(string name)
{
this.name = name;
translation = "";
alternatives = new List<string>();
}
public Country()
{
}
public string? name { get; set; }
public string translation { get; set; }
public List<string> alternatives { get; set; }
public static Country GetByName(string name)
{
foreach (var country in Global._instance.countries)
if (country.name == name)
return country;
return null; return null;
} }
} }

View File

@@ -15,13 +15,17 @@ public static class AddressCreator
// Maximum seven lines of information // Maximum seven lines of information
// find the address // find the address
var address = new KasPerson(); KasPerson? address = null;
var string_address = ""; var string_address = "";
var address_line_count = 0; var address_line_count = 0;
foreach (var set in Settings._instance.addressSets.addresses) foreach (var set in Settings._instance.addressSets.addresses)
{ {
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid); var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid);
if (temp != null) break; if (temp != null)
{
address = temp;
break;
}
} }
// no address found // no address found
@@ -91,13 +95,17 @@ public static class AddressCreator
// Maximum seven lines of information // Maximum seven lines of information
// find the address // find the address
var address = new KasPerson(); KasPerson? address = null;
var string_address = ""; var string_address = "";
var address_line_count = 0; var address_line_count = 0;
foreach (var set in Settings._instance.addressSets.addresses) foreach (var set in Settings._instance.addressSets.addresses)
{ {
var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid); var temp = set.KasPersons.FirstOrDefault(obj => obj.refsid == refsid);
if (temp != null) break; if (temp != null)
{
address = temp;
break;
}
} }
// no address found // no address found
@@ -110,21 +118,21 @@ public static class AddressCreator
address.land.ToLower().Trim() != "" && address.land.ToLower().Trim() != "de" && address.land.ToLower().Trim() != "" && address.land.ToLower().Trim() != "de" &&
address.land.ToLower().Trim() != "deutschland") address.land.ToLower().Trim() != "deutschland")
{ {
string_address = "**" + address.land.Trim() + "**"; // Needs to be bold string_address = "\n**" + address.land.Trim() + "**"; // Needs to be bold
address_line_count++; address_line_count++;
} }
// Alternative A: pplz valid and city existing // Alternative A: pplz valid and city existing
if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.pplz, address.land)) if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.pplz, address.land))
{ {
string_address = address.pplz + " " + address.ort + "\n" + string_address; string_address = address.pplz + " " + address.ort + string_address;
address_line_count++; address_line_count++;
if (!string.IsNullOrWhiteSpace(address.postfach)) if (!string.IsNullOrWhiteSpace(address.postfach))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
else else if (!string.IsNullOrWhiteSpace(address.strasse.Trim()))
{ {
string_address = address.strasse.Trim() + "\n" + string_address; string_address = address.strasse.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
@@ -133,28 +141,33 @@ public static class AddressCreator
var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname, var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname,
address.adel, address.name, address.namezus); address.adel, address.name, address.namezus);
if (string.IsNullOrWhiteSpace(nameline)) if (!string.IsNullOrWhiteSpace(nameline))
{ {
string_address = nameline + string_address; string_address = nameline + string_address;
address_line_count++; address_line_count++;
} }
// REIHENFOLGE
var nameattribs = new[] var nameattribs = new[]
{ address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung }; { address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung };
for (var i = 0; i < nameattribs.Length - 1; i++) var names = "";
if (address_line_count < 7) for (var i = 0; i < nameattribs.Length; i++)
try
{ {
if (address_line_count >= 7) break;
if (!string.IsNullOrWhiteSpace(nameattribs[i])) if (!string.IsNullOrWhiteSpace(nameattribs[i]))
{ {
string_address += nameattribs[i] + "\n" + string_address; names += "\n" + nameattribs[i];
address_line_count++; address_line_count++;
} }
} }
else catch
{ {
break; Console.WriteLine("ERROR 15821");
} }
string_address = names + "\n" + string_address;
} // Alternative B: plz valid and city existing } // Alternative B: plz valid and city existing
else if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.plz, address.land)) else if (!string.IsNullOrEmpty(address.ort) && CheckPLZ(address.plz, address.land))
{ {
@@ -165,16 +178,16 @@ public static class AddressCreator
string_address = address.strasse.Trim() + "\n" + string_address; string_address = address.strasse.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
else else if (!string.IsNullOrWhiteSpace(address.postfach.Trim()))
{ {
string_address = address.postfach.Trim() + "\n" + string_address; string_address = "Postfach " + address.postfach.Trim() + "\n" + string_address;
address_line_count++; address_line_count++;
} }
var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname, var nameline = CreateNameLine(address.anredzus, address.anrede, address.titel, address.vorname,
address.adel, address.name, address.namezus); address.adel, address.name, address.namezus);
if (string.IsNullOrWhiteSpace(nameline)) if (!string.IsNullOrWhiteSpace(nameline))
{ {
string_address = nameline + string_address; string_address = nameline + string_address;
address_line_count++; address_line_count++;
@@ -183,19 +196,23 @@ public static class AddressCreator
var nameattribs = new[] var nameattribs = new[]
{ address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung }; { address.name1, address.name2, address.name3, address.name4, address.name5, address.abteilung };
for (var i = 0; i < nameattribs.Length - 1; i++) var names = "";
if (address_line_count < 7) for (var i = 0; i < nameattribs.Length; i++)
try
{ {
if (address_line_count >= 7) break;
if (!string.IsNullOrWhiteSpace(nameattribs[i])) if (!string.IsNullOrWhiteSpace(nameattribs[i]))
{ {
string_address += nameattribs[i] + "\n" + string_address; names += "\n" + nameattribs[i];
address_line_count++; address_line_count++;
} }
} }
else catch
{ {
break; Console.WriteLine("ERROR 15821");
} }
string_address = names + "\n" + string_address;
} // Error-Handling? } // Error-Handling?
return string_address; return string_address;
@@ -231,20 +248,28 @@ public static class AddressCreator
public static bool CheckPLZ(string plz, string land) public static bool CheckPLZ(string plz, string land)
{ {
if (string.IsNullOrWhiteSpace(plz)) return false; if (string.IsNullOrWhiteSpace(plz)) return false;
// For non-german countries, accept any non-empty postal code
if (land.ToLower().Trim() != "germany" && land.ToLower().Trim() != "ger" && land.ToLower().Trim() != "" &&
land.ToLower().Trim() != "de" && land.ToLower().Trim() != "deutschland")
return true;
// For Germany, check if it's a valid numeric postal code (5 digits) var trimmedPlz = plz.Trim();
if (int.TryParse(plz, out var iplz)) var trimmedLand = land.ToLower().Trim();
{
if (iplz > 99999 || iplz < 10000) return false; // Check if it's a German country code
return true; var isGermany = trimmedLand == "germany" || trimmedLand == "ger" || trimmedLand == "de" ||
} trimmedLand == "deutschland" || trimmedLand == "";
// If it can't be parsed as int, it's not a valid German postal code if (isGermany)
return false; // For Germany (including empty land), accept numeric postal codes with 5 digits
try
{
var iplz = Convert.ToInt32(trimmedPlz);
if (trimmedPlz.Length == 5) return true;
return false;
}
catch
{
return false;
}
// For non-German countries, accept any non-empty postal code
return true;
} }
} }

391
Tasks/PdfBuilder.cs Normal file
View File

@@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using System.Linq;
using PdfSharp;
using PdfSharp.Drawing;
using PdfSharp.Pdf;
namespace Logof_Client;
public class PdfBuilder
{
// Table layout
private const int CellsPerRow = 3;
private const int CellsPerPage = 21; // 3 columns × 7 rows
private readonly XFont _boldFont = new("Arial", 9, XFontStyleEx.Bold);
private readonly double _cellHeight = 42.43; // mm
private readonly double _cellPaddingBottom = 5; // mm
// Padding inside cells
private readonly double _cellPaddingLeft = 5; // mm
private readonly double _cellPaddingTop = 5; // mm
// Cell dimensions (in mm)
private readonly double _cellWidth = 70; // mm
// Font settings
private readonly double _fontSize = 9;
private readonly double _marginBottom = 1; // mm
// Paper and layout settings
private readonly double _marginLeft = 0; // mm
private readonly double _marginRight = 0; // mm
private readonly double _marginTop = 0; // mm
private readonly XFont _regularFont = new("Arial", 9, XFontStyleEx.Regular);
private readonly XFont _smallFont = new("Arial", 6, XFontStyleEx.Regular);
/// <summary>
/// Creates a PDF document with address stickers from an AddressSet in a 3×7 grid layout on A4 pages.
/// </summary>
/// <param name="addressSetId">The ID of the AddressSet to use</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfFromAddressSet(int addressSetId, string outputPath)
{
// Find the AddressSet by ID
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
if (addressSet == null)
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set
//var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
// ensure single line and wrap in a small-font tag
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++)
{
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid);
if (string.IsNullOrWhiteSpace(addr.Trim())) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + addr);
else
addresses.Add(addr);
}
CreateAddressLabelPdf(addresses, outputPath);
}
/// <summary>
/// Creates a PDF document with address stickers from an AddressSet with a placeholder in the first cell.
/// </summary>
/// <param name="addressSetId">The ID of the AddressSet to use</param>
/// <param name="placeholderText">Text for the first cell (top-left)</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfFromAddressSetWithPlaceholder(int addressSetId, string placeholderText,
string outputPath)
{
// Find the AddressSet by ID
var addressSet = Settings._instance.addressSets.GetAddressSetByID(addressSetId);
if (addressSet == null)
throw new ArgumentException($"AddressSet with ID {addressSetId} not found");
if (addressSet.KasPersons == null || addressSet.KasPersons.Count == 0)
throw new ArgumentException($"AddressSet with ID {addressSetId} contains no addresses");
// Generate markdown addresses from all KasPersons in the set
//var addresses = new string?[addressSet.KasPersons.Count];
var addresses = new List<string>();
// find customer (owner) to include sender_address
string senderLine = null;
try
{
var owner = Settings._instance.customers.customers.FirstOrDefault(c => c.ID == addressSet.owner_id);
if (owner != null && !string.IsNullOrWhiteSpace(owner.sender_address))
senderLine = "<font6>" + owner.sender_address.Replace("\n", " ").Trim() + "</font6>\n";
}
catch
{
senderLine = null;
}
for (var i = 0; i < addressSet.KasPersons.Count; i++)
{
var addr = AddressCreator.CreateFinalMarkdownString(addressSet.KasPersons[i].refsid);
if (string.IsNullOrWhiteSpace(addr.Trim())) continue;
if (!string.IsNullOrEmpty(senderLine))
addresses.Add(senderLine + (addr ?? ""));
else
addresses.Add(addr);
}
CreateAddressLabelPdfWithPlaceholder(addresses, placeholderText, outputPath);
}
/// <summary>
/// Creates a PDF document with address stickers in a 3×7 grid layout on A4 pages.
/// </summary>
/// <param name="addresses">Array of addresses (from CreateFinalMarkdownString)</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdf(List<string> addresses, string outputPath)
{
if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument();
var addressIndex = 0;
while (addressIndex < addresses.Count)
{
var page = document.AddPage();
page.Size = PageSize.A4;
using (var gfx = XGraphics.FromPdfPage(page))
{
// Draw the grid and fill cells
DrawPage(gfx, addresses, ref addressIndex);
}
}
// Save the document
document.Save(outputPath);
}
/// <summary>
/// Creates a PDF document with a single placeholder cell for other information.
/// </summary>
/// <param name="addresses">Array of addresses</param>
/// <param name="placeholderText">Text for the first cell (top-left)</param>
/// <param name="outputPath">Path where the PDF should be saved</param>
public void CreateAddressLabelPdfWithPlaceholder(List<string> addresses, string placeholderText, string outputPath)
{
if (addresses == null || addresses.Count == 0)
throw new ArgumentException("Addresses array cannot be null or empty");
var document = new PdfDocument();
var addressIndex = 0;
var isFirstCell = true;
while (addressIndex < addresses.Count || isFirstCell)
{
var page = document.AddPage();
page.Size = PageSize.A4;
using (var gfx = XGraphics.FromPdfPage(page))
{
DrawPageWithPlaceholder(gfx, addresses, ref addressIndex, ref isFirstCell, placeholderText);
}
}
document.Save(outputPath);
}
private void DrawPage(XGraphics gfx, List<string> addresses, ref int addressIndex)
{
var cellCount = 0;
for (var row = 0; row < 7; row++)
{
for (var col = 0; col < 3; col++)
{
if (addressIndex >= addresses.Count) break;
var x = MmToPoints(_marginLeft + col * _cellWidth);
var y = MmToPoints(_marginTop + row * _cellHeight);
DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++;
cellCount++;
}
if (addressIndex >= addresses.Count) break;
}
}
private void DrawPageWithPlaceholder(XGraphics gfx, List<string> addresses, ref int addressIndex,
ref bool isFirstCell, string placeholderText)
{
var cellCount = 0;
for (var row = 0; row < 7; row++)
for (var col = 0; col < 3; col++)
{
var x = MmToPoints(_marginLeft + col * _cellWidth);
var y = MmToPoints(_marginTop + row * _cellHeight);
// First cell: placeholder
if (isFirstCell)
{
DrawCell(gfx, x, y, placeholderText);
isFirstCell = false;
}
else if (addressIndex < addresses.Count)
{
DrawCell(gfx, x, y, addresses[addressIndex]);
addressIndex++;
}
else
{
DrawEmptyCell(gfx, x, y);
}
cellCount++;
}
}
private void DrawCell(XGraphics gfx, double x, double y, string? address)
{
var cellWidthPoints = MmToPoints(_cellWidth);
var cellHeightPoints = MmToPoints(_cellHeight);
// Draw cell border
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect);
// Draw address content if available
if (!string.IsNullOrEmpty(address)) DrawMarkdownText(gfx, address, x, y, cellWidthPoints, cellHeightPoints);
}
private void DrawEmptyCell(XGraphics gfx, double x, double y)
{
var cellWidthPoints = MmToPoints(_cellWidth);
var cellHeightPoints = MmToPoints(_cellHeight);
var rect = new XRect(x, y, cellWidthPoints, cellHeightPoints);
gfx.DrawRectangle(XPens.Black, rect);
}
private void DrawMarkdownText(XGraphics gfx, string text, double x, double y, double cellWidth, double cellHeight)
{
var paddingLeftPoints = MmToPoints(_cellPaddingLeft);
var paddingTopPoints = MmToPoints(_cellPaddingTop);
var paddingBottomPoints = MmToPoints(_cellPaddingBottom);
var maxWidth = cellWidth - paddingLeftPoints * 2;
// Split text by newlines and remove empty trailing lines
var rawLines = text.Split(new[] { "\n" }, StringSplitOptions.None);
var lines = rawLines.Where(l => l != null).ToArray();
// Use a conservative line height in points
var lineHeight = _regularFont.Size * 1.2;
// Calculate total height of the text block
var totalHeight = lines.Length * lineHeight;
// Start drawing from the top of the cell (align addresses to top)
var startY = y + paddingTopPoints;
var currentY = startY;
foreach (var line in lines)
{
// Stop if we've reached the top boundary
if (currentY + lineHeight > y + cellHeight - paddingBottomPoints + 0.001)
break;
// Parse and draw the line with markdown support
DrawLineWithMarkdown(gfx, line, x + paddingLeftPoints, currentY, maxWidth);
currentY += lineHeight;
}
}
private void DrawLineWithMarkdown(XGraphics gfx, string line, double x, double y, double maxWidth)
{
var currentX = x;
var i = 0;
while (i < line.Length)
{
// Check for small-font tag <font6> ... </font6>
if (i <= line.Length - 7 && line.Substring(i, 7) == "<font6>")
{
var endTag = line.IndexOf("</font6>", i + 7, StringComparison.Ordinal);
if (endTag != -1)
{
var inner = line.Substring(i + 7, endTag - (i + 7));
if (!string.IsNullOrEmpty(inner))
{
gfx.DrawString(inner, _smallFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _smallFont.Size * 1.2),
XStringFormats.TopLeft);
var measuredSmall = gfx.MeasureString(inner, _smallFont);
currentX += measuredSmall.Width;
}
i = endTag + 8; // move past </font6>
continue;
}
}
// Check for bold marker (**)
if (i < line.Length - 1 && line[i] == '*' && line[i + 1] == '*')
{
// Find closing **
var endIndex = line.IndexOf("**", i + 2, StringComparison.Ordinal);
if (endIndex != -1)
{
var boldText = line.Substring(i + 2, endIndex - (i + 2));
// Draw bold text and measure width accurately
gfx.DrawString(boldText, _boldFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _boldFont.Size * 1.2),
XStringFormats.TopLeft);
var measured = gfx.MeasureString(boldText, _boldFont);
currentX += measured.Width;
i = endIndex + 2;
continue;
}
}
// Regular text until next ** or end of line
var nextBoldIndex = line.IndexOf("**", i, StringComparison.Ordinal);
var textEnd = nextBoldIndex == -1 ? line.Length : nextBoldIndex;
var regularText = line.Substring(i, textEnd - i);
if (!string.IsNullOrEmpty(regularText))
{
gfx.DrawString(regularText, _regularFont, XBrushes.Black,
new XRect(currentX, y, maxWidth - (currentX - x), _regularFont.Size * 1.2), XStringFormats.TopLeft);
var measured = gfx.MeasureString(regularText, _regularFont);
currentX += measured.Width;
}
i = textEnd;
}
}
/// <summary>
/// Converts millimeters to points (1 mm = 2.834645669 points)
/// </summary>
private double MmToPoints(double mm)
{
return mm * 2.834645669;
}
// Configuration methods to allow customization
/// <summary>
/// Sets the cell dimensions in millimeters
/// </summary>
public void SetCellDimensions(double width, double height)
{
if (width <= 0 || height <= 0)
throw new ArgumentException("Cell dimensions must be positive");
}
/// <summary>
/// Sets the page margins in millimeters
/// </summary>
public void SetMargins(double left, double top, double right, double bottom)
{
if (left < 0 || top < 0 || right < 0 || bottom < 0)
throw new ArgumentException("Margins cannot be negative");
}
}