Merge pull request #91 from Basimodo/TexCreation

Create Tex-File from quoteVM and call pdflatex to create PDF quote
pull/1/head
Sascha 2021-06-23 18:03:34 +07:00 committed by GitHub
commit 16d3651113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1960 additions and 1134 deletions

4
.gitignore vendored

@ -9,10 +9,14 @@
*.user
*.userosscache
*.sln.docstates
*.log
# User-specific files: Tex-related
*.aux
*.tmp
*.tex
*.out
*.pdf
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

@ -5,4 +5,4 @@
<Application.Resources>
</Application.Resources>
</Application>
</Application>

@ -0,0 +1,6 @@
namespace Gremlin
{
internal class Context
{
}
}

@ -20,5 +20,10 @@ namespace Gremlin
{
Debug.WriteLine(ex.Message);
}
internal static void ShowMessageBox(string message)
{
_ = MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}

@ -13,10 +13,16 @@
<ApplicationIcon>gremlin.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Resources\Images\**" />
<EmbeddedResource Remove="Resources\Images\**" />
<None Remove="Resources\Images\**" />
<Page Remove="Resources\Images\**" />
</ItemGroup>
<ItemGroup>
<None Remove="GremlinUtilities\GUData\DataIdentifier.txt" />
<None Remove="GremlinUtilities\GUData\MappingDictionary.txt" />
<None Remove="Resources\Images\gremlin.jpg" />
</ItemGroup>
<ItemGroup>
@ -27,7 +33,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="2.12.3" />
<PackageReference Include="DocumentFormat.OpenXml" Version="2.13.0" />
<PackageReference Include="gong-wpf-dragdrop" Version="2.3.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7">
@ -40,6 +46,11 @@
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
@ -59,9 +70,24 @@
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\gremlin.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

@ -74,7 +74,7 @@ namespace Gremlin.GremlinData.DBClasses
//{
// Filepath = filepath;
// _dataHasHeadings = true;
// _encoding = FileHelper.GetEncoding(Filepath);
// _encoding = FileIO.GetEncoding(Filepath);
// TextFieldParser _csvParser = new(Filepath, _encoding);
// _csvParser.SetDelimiters(Separators);
// _csvParser.HasFieldsEnclosedInQuotes = true;
@ -94,7 +94,7 @@ namespace Gremlin.GremlinData.DBClasses
//{
// Filepath = filepath;
// _dataHasHeadings = true;
// _encoding = FileHelper.GetEncoding(Filepath);
// _encoding = FileIO.GetEncoding(Filepath);
// TextFieldParser _csvParser = new(Filepath, _encoding);
// Separators = separators;
// _csvParser.HasFieldsEnclosedInQuotes = true;
@ -113,7 +113,7 @@ namespace Gremlin.GremlinData.DBClasses
//Public methods
public static void SetFilepath()
{
Filepath = FileHelper.GetFilepathFromUser();
Filepath = FileIO.GetFilepathFromUser();
}
public static void ResetParser()
@ -136,7 +136,7 @@ namespace Gremlin.GremlinData.DBClasses
}
catch (Exception)
{
Encoding = FileHelper.GetEncoding(_filepath);
Encoding = FileIO.GetEncoding(_filepath);
}
Separators = new string[] { separator };
@ -154,7 +154,7 @@ namespace Gremlin.GremlinData.DBClasses
}
catch (Exception)
{
Encoding = FileHelper.GetEncoding(_filepath);
Encoding = FileIO.GetEncoding(_filepath);
}
Separators = new string[] { separator };
@ -178,7 +178,7 @@ namespace Gremlin.GremlinData.DBClasses
}
catch (Exception)
{
Encoding = FileHelper.GetEncoding(_filepath);
Encoding = FileIO.GetEncoding(_filepath);
}
}

@ -34,25 +34,5 @@ namespace Gremlin.GremlinData.EntityClasses
public decimal ExtendedListPrice { get; set; }
public decimal NetPrice { get; set; }
public decimal Total { get; set; }
internal string ToTex()
{
throw new NotImplementedException();
}
internal object GetAnschreiben()
{
throw new NotImplementedException();
}
internal object GetItem()
{
throw new NotImplementedException();
}
internal Product GetProduct()
{
throw new NotImplementedException();
}
}
}

@ -43,7 +43,7 @@ namespace Gremlin.GremlinUI
private void btnOpenFile_Click(object sender, RoutedEventArgs e)
{
_filepath = FileHelper.GetFilepathFromUser();
_filepath = FileIO.GetFilepathFromUser();
if (_filepath != "")
{
tbDataSource.Text = DbHelper.ExtractFileName(_filepath, true);
@ -59,8 +59,8 @@ namespace Gremlin.GremlinUI
private void btnGetEncoding_Click(object sender, RoutedEventArgs e)
{
_encodingName = FileHelper.GetEncoding(_filepath)?.HeaderName;
tbEncoding.Text = FileHelper.GetEncoding(_filepath)?.EncodingName;
_encodingName = FileIO.GetEncoding(_filepath)?.HeaderName;
tbEncoding.Text = FileIO.GetEncoding(_filepath)?.EncodingName;
}
private void btnGetSeparator_Click(object sender, RoutedEventArgs e)

@ -31,9 +31,8 @@
<Button Content="Import Custom Descriptions from CSV..." Margin="10,5,10,5" Name="btnImportCDFromCSV" Click="BtnImportCDFromCSV_Click" />
</StackPanel>
<StackPanel x:Name="Work" Grid.Row="1" Grid.Column="1" Orientation="Vertical" >
<Button Content="Select a quote..." VerticalAlignment="top" Margin="10,5,10,3" Name="btnOpenQuote" Click="BtnOpenQuote_Click" />
<Button Content="Refresh Account List" VerticalAlignment="top" Margin="10,5,10,3" Name ="btnRefreshAccountList" Click="BtnRefreshAccountList_Click"/>
<Button Content="Test" VerticalAlignment="Top" Margin="10,50,10,3" Name="btnTest" Click="BtnTest_Click" Height="60" />
<Button Content="Show Account List" VerticalAlignment="top" Margin="10,5,10,5" Name ="btnShowAccountList" Click="BtnShowAccountList_Click"/>
<Button Content="Show Contact List" VerticalAlignment="top" Margin="10,5,10,5" Name ="btnShowContactList" Click="BtnShowContactList_Click"/>
<Button Content="Open QuoteUI" VerticalAlignment="top" Margin="10,5,10,5" Name="btnQuoteUI" Click="BtnQuoteUI_Click" />
</StackPanel>
<StackPanel x:Name="DB" Grid.Row="2" Grid.Column="2" Orientation="Vertical">

@ -3,6 +3,7 @@ using Gremlin.GremlinUtilities;
using Gremlin.GremlinData.EntityClasses;
using Gremlin.GremlinUI.ViewModels;
using Gremlin.Models;
using Gremlin.ViewModels;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
@ -39,24 +40,6 @@ namespace Gremlin.GremlinUI
lblWelcome.Content = $"Welcome back to Gremlin, {userName}!";
}
private void BtnOpenQuote_Click(object sender, RoutedEventArgs e)
{
///Öffnet Fenster zur Auswahl eines oder mehrerer zu prozessierender Angebote.
OpenFileDialog dlg = new()
{
Multiselect = true,
Filter = "Angebote|*.docx; *.xlsx"
};
if (dlg.ShowDialog() == true)
{
//Auswahl dlg.filenames an passende Objekte übergeben
string message = dlg.FileNames[0];
string caption = "Sie haben u.a. diese Dateien ausgewählt:";
_ = MessageBox.Show(message, caption);
}
}
private async void BtnImportAccounts_Click(object sender, RoutedEventArgs e)
{
//DbHelper.ImportAccountsFromCSV();
@ -86,17 +69,13 @@ namespace Gremlin.GremlinUI
_ = await DbHelper.ImportLSAGContactListToolDataAsync();
}
private void BtnRefreshAccountList_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<Account> accounts = new(AccountVM.GetAccounts());
dg_Test.ItemsSource = accounts;
}
private void BtnChooseDb_Click(object sender, RoutedEventArgs e)
{
ChooseDB chooseDB = new();
if ((bool)chooseDB.ShowDialog())
if (chooseDB.ShowDialog() == true)
{
UpdateUI();
}
}
private void BtnResetDb_Click(object sender, RoutedEventArgs e)
@ -137,5 +116,17 @@ namespace Gremlin.GremlinUI
QuoteUI quoteUI = new();
_ = quoteUI.ShowDialog();
}
private void BtnShowAccountList_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<AccountVM> accounts = new(AccountVM.GetAllAccountsVM());
dg_Test.ItemsSource = accounts;
}
private void BtnShowContactList_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<ContactVM> contacts = new(ContactVM.GetAllContactsVM());
dg_Test.ItemsSource = contacts;
}
}
}

@ -3,6 +3,7 @@ using Gremlin.GremlinData.EntityClasses;
using Gremlin.GremlinUI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Gremlin.Models
@ -41,7 +42,12 @@ namespace Gremlin.Models
try
{
Dictionary<string, string> rsfruid = ReadSettingsForRegisteredUserID(registeredUserID);
return rsfruid == null ? "RUSettings not valid" : rsfruid.ContainsKey(settingKey) ? rsfruid[settingKey] : "NotFound";
if (!rsfruid.ContainsKey(settingKey) || rsfruid == null)
{
Debug.WriteLine($"SettingKey {settingKey} not found or null!");
return "NotFound";
}
return rsfruid[settingKey];
}
catch (Exception ex)
{

@ -1,15 +0,0 @@
using Gremlin.GremlinData.EntityClasses;
namespace Gremlin.Operations
{
public class LineItemOperations
{
public static string ToTex(LineItem lineItem)
{
Product product = lineItem.GetProduct();
return lineItem.OptionNumber == ""
? $"{lineItem.Position} &\\textbf{{{product.CustomDescription.Heading}}} ({lineItem.ProductNumber})\\newline {product.CustomDescription.DescriptionText}&{lineItem.Amount}&\\SI{{{lineItem.Total}}}{{\\sieuro}}\\\\"
: $"{lineItem.Position} &\\textbf{{{product.CustomDescription.Heading}}} ({lineItem.ProductNumber}\\#{lineItem.OptionNumber})\\newline {product.CustomDescription.DescriptionText}&{lineItem.Amount}&\\SI{{{lineItem.Total}}}{{\\sieuro}}\\\\";
}
}
}

@ -1,4 +1,5 @@
using System;
using Gremlin.GremlinUtilities;
using System;
using System.Diagnostics;
using System.IO;
@ -6,27 +7,23 @@ namespace Gremlin.Operations
{
internal class PDFHandler
{
public static void CreatePDF(string texFile, string angebotsNummerText, string angebotsPfad)
public static void CreatePDF(string fileName, string quotePath)
{
//Copy images to quotePath
File.WriteAllBytes($"{quotePath}\\agilentLogo.png", Properties.Resources.agilentLogo);
File.WriteAllBytes($"{quotePath}\\signWoitschetzki.png", Properties.Resources.signWoitschetzki);
//Create PDF twice
RunningPDFLaTeX(texFile, 2);
RunningPDFLaTeX(quotePath, fileName, 2);
//CleanUp
RemoveTempFiles(angebotsPfad);
//RemoveTempFiles(quotePath);
//OpenPDF
try
{
_ = Process.Start($"{angebotsPfad}\\{angebotsNummerText}.pdf");
}
catch (Exception ex)
{
ErrorHandler.ShowErrorInMessageBox(ex);
}
//FileIO.OpenFile(quotePath, fileName, "pdf");
}
public static void RemoveTempFiles(string angebotsPfad)
public static void RemoveTempFiles(string quotePath)
{
DirectoryInfo directoryInfo = new(AppDomain.CurrentDomain.BaseDirectory);
foreach (FileInfo file in directoryInfo.EnumerateFiles())
@ -42,28 +39,28 @@ namespace Gremlin.Operations
ErrorHandler.ShowErrorInMessageBox(ex);
}
}
if (file.Extension is ".pdf" or ".tex")
{
try
{
File.Move(file.ToString(), $"{angebotsPfad}\\{file}");
}
catch (Exception ex)
{
ErrorHandler.ShowErrorInMessageBox(ex);
}
}
//if (file.Extension is ".pdf" or ".tex")
//{
// try
// {
// File.Move(file.ToString(), quotePath);
// }
// catch (Exception ex)
// {
// ErrorHandler.ShowErrorInMessageBox(ex);
// }
//}
}
}
private static void RunningPDFLaTeX(string texFile, int runs)
private static void RunningPDFLaTeX(string quotePath, string fileName, int runs)
{
for (int i = 0; i < runs; i++)
{
using (Process process = new())
{
process.StartInfo.FileName = "pdflatex";
process.StartInfo.Arguments = texFile;
process.StartInfo.Arguments = quotePath == "" ? $"{fileName}.tex" : $"{quotePath}\\{fileName}.tex";
process.StartInfo.UseShellExecute = false;
try
{

@ -1,131 +0,0 @@
using Gremlin.GremlinData.EntityClasses;
using Gremlin.Models;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static Gremlin.GremlinData.EntityClasses.Enums;
namespace Gremlin.Operations
{
internal class TexFileHandler
{
public static void Write(StringBuilder texFile, string datei)
{
using (StreamWriter writer = new(datei, false, Encoding.UTF8))
{
writer.WriteLine(texFile.ToString());
}
}
public static StringBuilder CreateTexFile(List<LineItem> quote, string angebotsnummer, Contact contact, string angebotstitel = "ein Analysegerät", bool brutto = false, int warranty = 12)
{
float rand = Properties.Settings.Default.texRand;
decimal mehrwertsteuer = Properties.Settings.Default.MwSt;
StringBuilder texFile = new($"\\documentclass[a4paper,ngerman,parskip,10pt]{{scrlttr2}}" + $"\\usepackage{{lmodern}}" + $"\\usepackage[T1]{{fontenc}}" + $"\\usepackage[utf8]{{inputenc}}" + $"\\usepackage{{babel}}" + $"\\usepackage[hidelinks]{{hyperref}}");
_ = texFile.AppendLine($"\\usepackage[left={rand}cm, right={rand}cm, top={rand}cm, bottom={rand}cm]{{geometry}}");
_ = texFile.AppendLine($"\\usepackage[table]{{xcolor}}\r\n\\usepackage[right]{{eurosym}}\r\n\\usepackage[locale=DE]{{siunitx}}\r\n\\usepackage{{scrlayer-scrpage}}\r\n\\usepackage{{lastpage}}\r\n\\usepackage{{graphicx}}\r\n\\usepackage{{multirow}}\r\n\\usepackage{{longtable}}\r\n\\usepackage{{enumitem}}\r\n\\usepackage{{fp, xstring, spreadtab, numprint}}\r\n\\DeclareSIUnit{{\\sieuro}}{{\\mbox{{\\euro}}}}");
_ = texFile.AppendLine($"\\rohead{{{angebotsnummer}}}");
_ = texFile.AppendLine("\\cfoot{Seite \\thepage/\\pageref{LastPage}}\n" + "\\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}" + "\n\\newcommand{\\produkttitel}[1]{\\textsc{#1}}" + "\n\\renewcommand{\\arraystretch}{1.2}\r\n\\definecolor{AgilentBlau}{HTML}{0085d5}" + "\n\\setlist{noitemsep}\r\n\\begin{document}" + "\n\\begin{tabular}{p{0.4\\hsize}p{0.5\\hsize}}" + "\n\\multirow{4}{*}{\\includegraphics[width=0.9\\hsize]{Agilent_Logo_Tag_h_RGB.png}}" + "\n&\\normalsize{Agilent Technologies Sales \\& Services GmbH \\& Co.KG}\\\\" + "\n&\\normalsize{Life Sciences \\& Chemical Analysis}\\\\" + $"\n&\\normalsize{{Hewlett-Packard-Str. 8}}\\\\" + "\n&\\normalsize{D-76337 Waldbronn}" + "\n\\end{tabular}" + "\n\\par\r\n\\begin{flushright}" + "\n\\colorbox{AgilentBlau}{\\textcolor{white}{\\textsc{\\Huge{Angebot}}}}\r\n\\end{flushright}\r\n\\begin{tabular}{p{0.4\\hsize}p{0.6\\hsize}}" + "\n&\n\\multirow{4}{*}{" + "\n\\begin{tabular}{|ll|}" + "\n\\hline");
_ = texFile.AppendLine($"\\textbf{{Angebotsnummer:}}&{angebotsnummer}\\\\");
_ = texFile.Append("Angebotdatum:&\\today\\\\\r\nAngebotsgültigkeit:&60 Tage\\\\");
_ = texFile.AppendLine($"\\textbf{{Ansprechpartner:}}&{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userName")}\\\\");
_ = texFile.AppendLine($"Telefon: &{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userPhone")}\\\\");
_ = texFile.AppendLine($"Mobil:&{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMobile")}\\\\");
_ = texFile.AppendLine($"E-Mail:&\\href{{mailto:{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMail")}}}{{{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMail")}}}\\\\");
_ = texFile.AppendLine("\\textbf{Auftragsannahme:}&\\href{mailto:salesservices\\_germany@agilent.com}{salesservices\\_germany@agilent.com}\\\\\r\n\\hline\r\n\\end{tabular}\r\n}\\\\");
//_ = texFile.Append(ContactViewModel.CreateBriefkopf(contact));
_ = texFile.AppendLine("&\\\\\r\n&\\\\\r\n\\end{tabular}\r\n\\vspace{1cm}\\par ");
//Anrede
_ = contact.Gender == (byte)Gender.Male
? texFile.AppendLine($"Sehr geehrter Herr {contact.LastName},\\par ")
: texFile.AppendLine($"Sehr geehrte Frau {contact.LastName},\\par ");
//Anschreiben
_ = texFile.Append(ErstelleAnschreibenListe(quote, angebotstitel));
//Tabelle
_ = texFile.Append("\\begin{center}");
_ = texFile.Append("\\begin{longtable}");
_ = texFile.Append("{| cp{0.72\\textwidth} cr |} \\hline");
_ = texFile.Append(@"\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Preis}\\ \hline \endhead");
foreach (LineItem lineItem in quote) { _ = texFile.Append(lineItem.ToTex() + "\n"); }
_ = texFile.Append("\\hline\r\n\\end{longtable}\r\n\\end{center}\r\n\\vspace{-2cm}\r\n\\begin{flushright}\r\n\\begin{large}\r\n\\begin{tabular}{|rr|}\r\n\\hline");
//Gesamtsumme
_ = texFile.AppendLine($"\\textbf{{Gesamtsumme netto}} & \\SI{{{GetSum(quote)}}}{{\\sieuro}}\\\\");
//mit Mehrwertsteuer
if (brutto)
{
_ = texFile.AppendLine($"\\textbf{{Mehrwertsteuer {mehrwertsteuer}\\%}} & \\SI{{{GetSum(quote) * mehrwertsteuer / 100}}}{{\\sieuro}}\\\\");
_ = texFile.AppendLine($"\\textbf{{Gesamtsumme brutto}} & \\SI{{{GetSum(quote) * (1 + mehrwertsteuer / 100)}}}{{\\sieuro}}\\\\");
}
_ = texFile.Append("\\hline\r\n\\end{tabular}\r\n\\end{large}\r\n\\end{flushright}\r\n\r\nDer Betrag versteht sich zzgl. der gesetzlichen Steuern.\\\\\r\nDiese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\\\\r\nZahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\\\\r\nIncoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.\r\n\\begin{small}\r\n\r\n\\textbf{Gewährleistung:}\\\\\r\nDie Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt ");
_ = texFile.Append(warranty);
_ = texFile.Append(" Monate.\r\n\r\n\\textbf{Hinweis:}\\\\ \r\nFür den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \\emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \\emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.\r\n\\end{small}\r\n \r\n\\begin{scriptsize}\r\nAgilent Technologies Sales \\& Services GmbH \\& Co. KG, Hewlett-Packard-Str. 8, 76337 Waldbronn\\\\\r\nSitz der Gesellschaft: Waldbronn, Amtsgericht: Mannheim, HRA 706631, WEEE-Reg.-Nr. DE 86631749\\\\\r\nPersönlich haftende Gesellschafterin: Agilent Technologies Sales \\& Services Verwaltungs-GmbH, Hewlett-Packard-Str. 8, 76337 Waldbronn\\\\\r\nSitz der persönlich haftenden Gesellschafterin: Waldbronn, Amtsgericht Mannheim, HRB 723773\\\\\r\nGeschäftsführer: Armin Jehle\r\n\\href{www.agilent.com}{www.agilent.com}\r\n\\end{scriptsize}\r\n\\end{document}");
return texFile;
}
private static StringBuilder ErstelleAnschreibenListe(List<LineItem> quote, string angebotstyp)
{
bool subitem = false;
StringBuilder anschreibenListe = new($"nachfolgend erhalten Sie Ihr gewünschtes Angebot über eine {angebotstyp}.\\\\" + $"Es umfasst im Einzelnen:\n" + $"\\begin{{itemize}}");
foreach (LineItem lineItem in quote)
{
if (lineItem.OptionNumber == "")
{
//Hauptitem
if (subitem)
{
//vorheriges Subitem schließen
_ = anschreibenListe.AppendLine(@"\end{itemize}");
subitem = false;
}
_ = anschreibenListe.AppendLine($"\\item {lineItem.GetAnschreiben()} (\\#{lineItem.GetItem()})");
}
else
{
if (subitem)
{
//weiteres Subitem
_ = anschreibenListe.AppendLine($"\\item {lineItem.GetAnschreiben()} (\\#{lineItem.GetItem()})");
}
else
{
//neues Subitem
subitem = true;
_ = anschreibenListe.AppendLine("\\begin{itemize}");
_ = anschreibenListe.AppendLine($"\\item {lineItem.GetAnschreiben()} (\\#{lineItem.GetItem()})");
}
}
}
if (subitem)
{
//wenn das letzte Item ein Subitem war
_ = anschreibenListe.AppendLine("\\end{itemize}");
}
_ = anschreibenListe.AppendLine($"\\end{{itemize}}\r\n Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\\par \r\n Mit freundlichen Grüßen\\\\\r\n \\includegraphics[width = 5cm]{{sign.png}}\r\n \\vspace{{1cm}}");
return anschreibenListe;
}
public static decimal GetSum(List<LineItem> quote)
{
decimal gesamtnetto = new();
foreach (LineItem lineItem in quote)
{
gesamtnetto += lineItem.ListPrice;
}
return gesamtnetto;
}
}
}

@ -0,0 +1,256 @@
using Gremlin.GremlinUI.ViewModels;
using Gremlin.Models;
using Gremlin.ViewModels;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using static Gremlin.GremlinData.EntityClasses.Enums;
namespace Gremlin.Operations
{
internal class TexHandler
{
internal static void CreateTexAndOpen(QuoteVM quoteVM)
{
StringBuilder texStringBuilder = CreateTexFile(quoteVM);
WriteTextToFile(texStringBuilder, $"{quoteVM.QuoteNumber}.tex");
//FileIO.OpenFile(quotePath, fileName, "tex");
}
private static void WriteTextToFile(StringBuilder texFile, string datei)
{
using (StreamWriter writer = new(datei, false, Encoding.UTF8))
{
writer.WriteLine(texFile.ToString());
}
}
private static StringBuilder CreateTexFile(QuoteVM quoteVM)
{
string rand = RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "texRand");
StringBuilder texFile = new($"\\documentclass[a4paper,ngerman,parskip,10pt]{{scrlttr2}}" + $"\\usepackage{{lmodern}}" + $"\\usepackage[T1]{{fontenc}}" + $"\\usepackage[utf8]{{inputenc}}" + $"\\usepackage{{babel}}" + $"\\usepackage[hidelinks]{{hyperref}}\n");
_ = texFile.AppendLine($"\\usepackage[left={rand}cm, right={rand}cm, top={rand}cm, bottom={rand}cm]{{geometry}}");
_ = texFile.AppendLine($"\\usepackage[table]{{xcolor}}\n\\usepackage[right]{{eurosym}}\n\\usepackage[locale=DE]{{siunitx}}\n\\usepackage{{scrlayer-scrpage}}\n\\usepackage{{lastpage}}\n\\usepackage{{graphicx}}\n\\usepackage{{multirow}}\n\\usepackage{{longtable}}\n\\usepackage{{enumitem}}\n\\usepackage{{fp, xstring, spreadtab, numprint}}\n\\DeclareSIUnit{{\\sieuro}}{{\\mbox{{\\euro}}}}");
_ = texFile.AppendLine($"\\rohead{{{quoteVM.QuoteNumber}}}");
_ = texFile.AppendLine("\\cfoot{Seite \\thepage/\\pageref{LastPage}}\n" + "\\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}" + "\n\\newcommand{\\produkttitel}[1]{\\textsc{#1}}" + "\n\\renewcommand{\\arraystretch}{1.2}\n\\definecolor{AgilentBlau}{HTML}{0085d5}" + "\n\\setlist{noitemsep}\n\\begin{document}" + "\n\\begin{tabular}{p{0.4\\hsize}p{0.5\\hsize}}" + "\n\\multirow{4}{*}{\\includegraphics[width=0.9\\hsize]{agilentLogo.png}}" + "\n&\\normalsize{Agilent Technologies Sales \\& Services GmbH \\& Co.KG}\\\\" + "\n&\\normalsize{Life Sciences \\& Chemical Analysis}\\\\" + $"\n&\\normalsize{{Hewlett-Packard-Str. 8}}\\\\" + "\n&\\normalsize{D-76337 Waldbronn}" + "\n\\end{tabular}" + "\n\\par\n\\begin{flushright}" + "\n\\colorbox{AgilentBlau}{\\textcolor{white}{\\textsc{\\Huge{Angebot}}}}\n\\end{flushright}\n\\begin{tabular}{p{0.4\\hsize}p{0.6\\hsize}}" + "\n&\n\\multirow{4}{*}{" + "\n\\begin{tabular}{|ll|}" + "\n\\hline");
_ = texFile.AppendLine($"\\textbf{{Angebotsnummer:}}&{quoteVM.QuoteNumber}\\\\");
_ = texFile.Append($"Angebotdatum:&\\today\\\\\nAngebotsgültigkeit:&{quoteVM.Validity} Tage\\\\");
_ = texFile.AppendLine($"\\textbf{{Ansprechpartner:}}&{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userName")}\\\\");
_ = texFile.AppendLine($"Telefon: &{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userPhone")}\\\\");
_ = texFile.AppendLine($"Mobil:&{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMobile")}\\\\");
_ = texFile.AppendLine($"E-Mail:&\\href{{mailto:{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMail")}}}{{{RUSettingModel.GetSettingValue(Properties.Settings.Default.userSettingID, "userMail")}}}\\\\");
_ = texFile.AppendLine("\\textbf{Auftragsannahme:}&\\href{mailto:salesservices\\_germany@agilent.com}{salesservices\\_germany@agilent.com}\\\\\n\\hline\n\\end{tabular}\n}\\\\");
if (quoteVM.Recipient != null) _ = texFile.Append(ContactVM.CreateBriefkopf(quoteVM.Recipient, true));
_ = texFile.AppendLine("&\\\\\n&\\\\\n\\end{tabular}\n\\vspace{1cm}\\par ");
//Anrede
_ = quoteVM.Recipient.Gender == (byte)Gender.Male
? texFile.AppendLine($"Sehr geehrter Herr {quoteVM.Recipient.LastName},\\par ")
: texFile.AppendLine($"Sehr geehrte Frau {quoteVM.Recipient.LastName},\\par ");
//Anschreiben
_ = texFile.AppendLine(CreateCoverletter(quoteVM));
//RB-Disclaimer
if (quoteVM.QuoteContainsRB)
{
_ = texFile.AppendLine(CreateRBDisclaimer(quoteVM));
}
//Tabelle
_ = texFile.AppendLine("\\begin{center}");
_ = texFile.AppendLine("\\begin{longtable}");
if (quoteVM.SinglePrices == true)
{
if (quoteVM.ShowDiscounts == false)
{
//mit Einzelpreisen
_ = texFile.AppendLine("{| cp{0.71\\textwidth} cr |} \\hline");
_ = texFile.AppendLine(@"\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Preis}\\ \hline \endhead");
}
else if (quoteVM.ShowDiscounts == true)
{
//mit Einzelpreisen und Discounts
_ = texFile.AppendLine("{| cp{0.595\\textwidth} crr |} \\hline");
_ = texFile.AppendLine(@"\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Discount} & \textbf{Preis}\\ \hline \endhead");
}
}
else
{
//ohne Einzelpreise
_ = texFile.AppendLine("{| cp{0.83\\textwidth} c |} \\hline");
_ = texFile.AppendLine(@"\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge}\\ \hline \endhead");
}
foreach (LineItemVM lI in quoteVM.LineItemsVM)
{
string lineItemTex = "";
if (quoteVM.SinglePrices == true)
{
if (quoteVM.ShowDiscounts == false)
{
//mit Einzelpreisen
lineItemTex = lI.OptionNumber != ""
? $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber}\\#{lI.OptionNumber})\\newline {lI.CustomDescription.DescriptionText}&{lI.Amount}&\\SI{{{lI.CalcTotalNet}}}{{\\sieuro}}\\\\"
: $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber})\\newline {lI.CustomDescription.DescriptionText}&{lI.Amount}&\\SI{{{lI.CalcTotalNet}}}{{\\sieuro}}\\\\";
}
else if (quoteVM.ShowDiscounts == true)
{
//mit Einzelpreisen und Discounts
lineItemTex = lI.OptionNumber != ""
? $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber}\\#{lI.OptionNumber})\\newline {lI.CustomDescription.DescriptionText}\\newline Listenpreis: \\SI{{{lI.ListPrice}}}{{\\sieuro}}&{lI.Amount}&\\SI{{{lI.TotalDiscount}}}{{\\%}}&\\SI{{{lI.CalcTotalNet}}}{{\\sieuro}}\\\\"
: $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber})\\newline {lI.CustomDescription.DescriptionText}\\newline Listenpreis: \\SI{{{lI.ListPrice}}}{{\\sieuro}}&{lI.Amount}&\\SI{{{lI.TotalDiscount}}}{{\\%}}&\\SI{{{lI.CalcTotalNet}}}{{\\sieuro}}\\\\";
}
}
else if (quoteVM.SinglePrices == false)
{
//ohne Einzelpreise
lineItemTex = lI.OptionNumber != ""
? $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber}\\#{lI.OptionNumber})\\newline {lI.CustomDescription.DescriptionText}&{lI.Amount}\\\\"
: $"{lI.Position} &\\textbf{{{lI.CustomDescription.Heading}}} ({lI.ProductNumber})\\newline {lI.CustomDescription.DescriptionText}&{lI.Amount}\\\\";
}
_ = texFile.Append(lineItemTex + "\n");
}
_ = texFile.AppendLine("\\hline\n"
+ "\\end{longtable}\n"
+ "\\end{center}\n");
_ = texFile.AppendLine("\\vspace{-2cm}\n"
+ "\\begin{flushright}\n\n"
+ "\\begin{tabular}{|rr|}\n"
+ "\\hline");
//Gesamtsumme
_ = texFile.AppendLine($"\\textbf{{Gesamtsumme netto}} & \\SI{{{quoteVM.TotalNet}}}{{\\sieuro}}\\\\");
//mit Mehrwertsteuer
if (quoteVM.Brutto == true)
{
_ = texFile.AppendLine($"\\textbf{{Mehrwertsteuer {quoteVM.VAT}\\%}} & \\SI{{{(float)quoteVM.TotalNet * quoteVM.VAT / 100}}}{{\\sieuro}}\\\\");
_ = texFile.AppendLine($"\\textbf{{Gesamtsumme brutto}} & \\SI{{{(float)quoteVM.TotalNet * (1 + (quoteVM.VAT / 100))}}}{{\\sieuro}}\\\\");
}
_ = texFile.Append("\\hline\n\\end{tabular}\n\n\\end{flushright}\n\nDer Betrag versteht sich zzgl. der gesetzlichen Steuern.\\\\\nDiese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\\\\nZahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\\\\nIncoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.\n\\begin{small}\n\n");
_ = texFile.AppendLine($"\\textbf{{Gewährleistung:}}\\\\\nDie Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt {quoteVM.Warranty} Monate.\n");
//3PP-Disclaimer
if (quoteVM.QuoteContains3PP)
{
_ = texFile.AppendLine(Create3PPDisclaimer(quoteVM));
}
_ = texFile.AppendLine("\\textbf{Hinweis:}\\\\ \nFür den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \\emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \\emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.\n\\end{small}\n \n\\begin{scriptsize}\nAgilent Technologies Sales \\& Services GmbH \\& Co. KG, Hewlett-Packard-Str. 8, 76337 Waldbronn\\\\\nSitz der Gesellschaft: Waldbronn, Amtsgericht: Mannheim, HRA 706631, WEEE-Reg.-Nr. DE 86631749\\\\\nPersönlich haftende Gesellschafterin: Agilent Technologies Sales \\& Services Verwaltungs-GmbH, Hewlett-Packard-Str. 8, 76337 Waldbronn\\\\\nSitz der persönlich haftenden Gesellschafterin: Waldbronn, Amtsgericht Mannheim, HRB 723773\\\\\nGeschäftsführer: Armin Jehle\n\\href{www.agilent.com}{www.agilent.com}\n\\end{scriptsize}\n\\end{document}");
return texFile;
}
private static string CreateRBDisclaimer(QuoteVM quoteVM)
{
Random r = new();
string rbDisclaimer = "\\textbf{Wichtiger Hinweis zur Bestellung von überholten Geräten}\\\\\n";
rbDisclaimer += "Bitte beachten Sie, dass in der Regel nur wenige gebrauchte Geräte auf Lager sind und diese ohne die Möglichkeit einer Reservierung auf „first come, first serve“-Basis verkauft werden. Um lange Lieferzeiten zu vermeiden, sollte daher bei konkretem Interesse zunächst der Lagerstand überprüft werden. Die aktuellen Lagerbestände sind:\n";
List<LineItemVM> lineItemsWithRB = quoteVM.LineItemsVM.Where(lI => lI.ProductLine == "RB").ToList();
rbDisclaimer += "\\begin{center}\n\\begin{tabular}{clc}\n";
rbDisclaimer += "\\textbf{Modul} & \\textbf{Beschreibung} &\\textbf{Bestand}\\\\ \\hline \n";
foreach (LineItemVM lineItemWithRB in lineItemsWithRB)
{
int rbcount = r.Next(20) - 5; //Get count of RB?
rbDisclaimer += $"{lineItemWithRB.ProductNumber} & {lineItemWithRB.CustomDescription.Heading} & {rbcount}\\\\ \n";
}
rbDisclaimer += "\\end{tabular}\n\\end{center}\n";
return rbDisclaimer;
}
private static string Create3PPDisclaimer(QuoteVM quoteVM)
{
string dreipp = "\\textbf{Hinweis zu Non-Agilent-Produkten}\\\\ \n"
+ $"Bitte beachten Sie, dass das/die o.g. Produkt/e ";
//List all 3PP product numbers
List<LineItemVM> lineItemsWith3PP = quoteVM.LineItemsVM.Where(lI => lI.ProductLine == "3PP").ToList();
for (int i = 0; i < lineItemsWith3PP.Count; i++)
{
_ = i < lineItemsWith3PP.Count - 1
? dreipp += $"{lineItemsWith3PP[i].ProductNumber}, "
: dreipp += $"{lineItemsWith3PP[i].ProductNumber}";
//Get all 3PP Supplier
//List<Supllier> supllier3PP = lineItemWith3PP.ProductLine.Supplier;
}
dreipp += " nicht von Agilent Technologies hergestellt wird/werden. Agilent Technologies lehnt daher jede Art der Haftung für Leistung, Qualität, Zuverlässigkeitund Lieferung für dieses/r Produkt/e ab.\\\\\n"
+ $"Die Standardgewährleistung, einschließlich Schadensersatz für die Rechtsverletzung von intellektuellem Eigentum, liegt beim Hersteller bzw. Lieferanten des/r Produkt/e, solange nichts anders im Angebot von Agilent Technologies spezifiziert wird.\n";
//dreipp += "\\textbf{Hersteller:}";
//supllier3PP
return dreipp;
}
private static string GetCoverletterRow(LineItemVM lineItemVM)
{
return lineItemVM.CustomDescription.CoverletterText is null or ""
? $"\\item {lineItemVM.CustomDescription.Heading} (\\#{lineItemVM.Position})\n"
: $"\\item {lineItemVM.CustomDescription.CoverletterText} (\\#{lineItemVM.Position})\n";
}
private static string CreateCoverletter(QuoteVM quoteVM)
{
bool subitem = false;
string coverLetter = $"nachfolgend erhalten Sie Ihr gewünschtes Angebot über {quoteVM.QuoteType}.\\\\\n"
+ "Es umfasst im Einzelnen:\n"
+ "\\begin{itemize}\n";
foreach (LineItemVM lineItemVM in quoteVM.LineItemsVM)
{
if (lineItemVM.OptionNumber == "")
{
//Hauptitem
if (subitem)
{
//vorheriges Subitem schließen
coverLetter += "\\end{itemize}\n";
subitem = false;
}
}
else
{
if (!subitem)
{
//neues Subitem
subitem = true;
coverLetter += "\\begin{itemize}\n";
}
}
coverLetter += GetCoverletterRow(lineItemVM);
}
if (subitem)
{
//wenn das letzte Item ein Subitem war
coverLetter += "\\end{itemize}\n";
}
coverLetter += $"\\end{{itemize}}\n"
+ "Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\\par\n"
+ "Mit freundlichen Grüßen\\\\\n"
+ "\\includegraphics[width = 5cm]{signWoitschetzki.png}\n"
+ "\\vspace{1cm} \\\\ \n";
return coverLetter;
}
}
}

@ -2,17 +2,19 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodels="clr-namespace:Gremlin.GremlinUI.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodels:QuoteVM}"
mc:Ignorable="d"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Title="Gremlin QuoteUI" Height="1024" Width="1280">
<Window.Resources>
<viewmodels:QuoteVM x:Key="m" ></viewmodels:QuoteVM>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="96.08"/>
<RowDefinition Height="423.88"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
@ -24,15 +26,26 @@
</Grid.ColumnDefinitions>
<StackPanel x:Name="Contact" Grid.Row="0" Grid.Column="0" Orientation="Vertical">
<TextBox x:Name="tbContactSearch" TextWrapping="NoWrap" Margin="10,5,10,5" TextAlignment="Center" TextChanged="TbContactSearch_TextChanged"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBox x:Name="tbContactSearch" TextWrapping="NoWrap" Margin="10,5,10,5" TextAlignment="Center" TextChanged="TbContactSearch_TextChanged"/>
</StackPanel>
<StackPanel Grid.Column="1">
<Button x:Name="btnReloadContacts" Content="Kontakte laden" Margin="10,5,10,5" Click="BtnReloadContacts_Click" />
</StackPanel>
</Grid>
<DataGrid x:Name="dgFoundContacts"
ItemsSource="{Binding Source=foundContacts}"
AutoGenerateColumns="True"
Height="250"
Margin="10,5,10,5"
AlternatingRowBackground="LightGray" SelectionChanged="DgFoundContacts_SelectionChanged">
</DataGrid>
<TextBlock x:Name="txAnschreiben" Margin="10,5,10,5" Height="70"><Run Language="de-de" Text="Dr. Sascha Woitschetzki"/><LineBreak/><Run Language="de-de" Text="Handelsvertretung Sascha Woitschetzki"/><LineBreak/><Run Language="de-de" Text="Parselvalstr. 58"/><LineBreak/><Run Language="de-de" Text="45470 Mülheim an der Ruhr"/></TextBlock>
AlternatingRowBackground="LightGray" SelectionChanged="DgFoundContacts_SelectionChanged"/>
</StackPanel>
<StackPanel x:Name="Angebotskonfig" Grid.Row="0" Grid.Column="1" Orientation="Vertical">
@ -51,6 +64,7 @@
<StackPanel>
<TextBlock x:Name="lblAngebotsname" Text="Angebotsname" TextAlignment="Right" Margin="10,5,10,5"/>
<TextBlock x:Name="lblAngebotsnummer" Text="Angebotsnummer" TextAlignment="Right" Margin="10,5,10,5"/>
<TextBlock x:Name="lblQuotingPath" Text="Angebotspfad" TextAlignment="Right" Margin="10,5,10,5"/>
<TextBlock x:Name="lblGewaehrleistung" Text="Gewährleistung (Monate)" TextAlignment="Right" Margin="10,5,10,5"/>
<TextBlock x:Name="lblAngebotsgueltigkeit" Text="Angebotsgültigkeit (Tage)" TextAlignment="Right" Margin="10,5,10,5"/>
<TextBlock x:Name="lblOppID" Text="SAP Opp ID" TextAlignment="Right" Margin="10,5,10,5"/>
@ -59,10 +73,11 @@
<Grid Grid.Column="1" Height="Auto">
<StackPanel>
<TextBox x:Name="tbAngebotsname" Text="ein Analysegerät" Margin="10,4,10,4" />
<TextBox x:Name="tbQuotationNumber" Text="DE-83PE89-421-6000" Margin="10,4,10,4" />
<TextBox x:Name="tbGewaehrleistung" Text="12" Margin="10,4,10,4" />
<TextBox x:Name="tbAngebotsgueltigkeit" Text="60" Margin="10,5,10,5" />
<TextBox x:Name="tbQuoteType" Text="{Binding Path=QuoteType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,4,10,4" TextChanged="TbQuoteType_TextChanged" />
<TextBox x:Name="tbQuoteNumber" Text="{Binding Path=QuoteNumber, UpdateSourceTrigger=PropertyChanged}" Margin="10,4,10,4" TextChanged="TbQuoteNumber_TextChanged" />
<TextBox x:Name="tbQuotePath" Text="{Binding Path=QuotePath, UpdateSourceTrigger=PropertyChanged}" Margin="10,4,10,4" TextChanged="TbQuotePath" />
<TextBox x:Name="tbWarranty" Text="{Binding Path=Warranty, UpdateSourceTrigger=PropertyChanged}" Margin="10,4,10,4" TextChanged="TbWarranty_TextChanged" />
<TextBox x:Name="tbValidity" Text="{Binding Path=Validity, UpdateSourceTrigger=PropertyChanged}" Margin="10,5,10,5" TextChanged="TbValidity_TextChanged" />
<TextBox x:Name="tbOppID" Text="3003541659" Margin="10,4,10,4" />
</StackPanel>
</Grid>
@ -70,26 +85,28 @@
</StackPanel>
<StackPanel x:Name="Opp" Grid.Row="0" Grid.Column="2" Orientation="Vertical">
<CheckBox x:Name="cbBrutto" Content="Brutto anzeigen" Margin="10,5,10,5" IsChecked="True" />
<CheckBox x:Name="cbEinzelpreise" Content="keine Einzelpreise" Margin="10,5,10,5" />
<CheckBox x:Name="cbBroschure" Content="Broschüren anhängen" Margin="10,5,10,5" IsChecked="True" />
<CheckBox x:Name="cbDataSheets" Content="Datenblätter anhängen" Margin="10,5,10,5" IsChecked="True" />
<CheckBox x:Name="cbMailTemplate" Content="Mail aus Template erstellen" Margin="10,5,10,5" IsChecked="True" />
<CheckBox d:DataContext="{d:DesignInstance Type=viewmodels:QuoteVM}" x:Name="cbBrutto" Content="Brutto anzeigen" Margin="10,5,10,5" IsChecked="{Binding Path=Brutto, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbBrutto_Click"/>
<CheckBox x:Name="cbSinglePrices" Content="Einzelpreise ausweisen" Margin="10,5,10,5" IsChecked="{Binding Path=SinglePrices, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbSinglePrices_Click" />
<CheckBox x:Name="cbShowDiscounts" Content="Discounts ausweisen" Margin="10,5,10,5" IsChecked="{Binding Path=ShowDiscounts, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbShowDiscounts_Click"/>
<CheckBox x:Name="cbBrochure" Content="Broschüren anhängen" Margin="10,5,10,5" IsChecked="{Binding Path=Brochures, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbBrochure_Click" />
<CheckBox x:Name="cbDataSheets" Content="Datenblätter anhängen" Margin="10,5,10,5" IsChecked="{Binding Path=DataSheets, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbDataSheets_Click" />
<CheckBox x:Name="cbMailTemplate" Content="Mail aus Template erstellen" Margin="10,5,10,5" IsChecked="{Binding Path=MailTemplate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Click="CbMailTemplate_Click" />
</StackPanel>
</Grid>
<Grid Grid.Row="1" Height="Auto" x:Name="gridMitte" Margin="10,5,10,5" Grid.RowSpan="2">
<Grid Grid.Row="1" Height="Auto" x:Name="gridMitte" Margin="10,5,10,5">
<StackPanel>
<Button x:Name="butPasteQuote" Content="Angebot aus PriceSurfer einfügen" Margin="10,5,10,5" Click="ButPasteQuote_Click" />
<TextBlock x:Name="lblQuote" Text="Bitte Angebot einfügen." Height="150" Margin="10,5,10,5"/>
<DataGrid x:Name="dgLineItems" AutoGenerateColumns="True" Height="300" Margin="10,5,10,5" />
<DataGrid x:Name="dgLineItems" AutoGenerateColumns="True" Height="600" Margin="10,5,10,5" >
</DataGrid>
</StackPanel>
</Grid>
<Grid Grid.Row="3" Height="Auto" x:Name="gridUnten" Margin="10,5,10,5">
<StackPanel>
<Button x:Name="butCreateQuote" Content="Angebot als PDF erstellen und öffnen" Margin="10,5,10,5"/>
<Button x:Name="butSendQuote" Content="Angebot als Anhang in Mail-Entwurf erstellen" Margin="10,5,10,5"/>
<StackPanel Orientation="Horizontal" Margin="10,5,10,5" HorizontalAlignment="Center">
<Button x:Name="btnPasteQuote" Content="Angebot aus PriceSurfer erstellen" Margin="10,5,10,5" Click="BtnPasteQuote_Click" MinWidth="280" />
<Button x:Name="btnCreateTex" Content="Tex erstellen" Margin="10,5,10,5" Click="BtnCreateTex_Click" MinWidth="280" />
<Button x:Name="btnCreateQuote" Content="Angebot als PDF erstellen" Margin="10,5,10,5" Click="BtnCreateQuote_Click" MinWidth="280" />
<Button x:Name="btnSendQuote" Content="Angebot als Anhang in Mail-Entwurf erstellen" Margin="10,5,10,5" Click="BtnSendQuote_Click" MinWidth="280" />
</StackPanel>
</Grid>
</Grid>

@ -1,7 +1,10 @@
using Gremlin.GremlinData.EntityClasses;
using Gremlin.GremlinUI.ViewModels;
using Gremlin.Operations;
using Gremlin.ViewModels;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Data;
@ -11,21 +14,25 @@ namespace Gremlin.GremlinUI
{
private static ListCollectionView listCollectionContacts = new(new ObservableCollection<Contact>());
private static ListCollectionView lineItems = new(new ObservableCollection<LineItemVM>());
private QuoteVM quoteVM = new();
private QuoteVM quoteVM = new(ContactVM.GetSalesRepAsContact(1));
public QuoteUI()
{
InitializeComponent();
listCollectionContacts = new(ContactVM.GetContacts());
listCollectionContacts.Filter = ContactVM.SearchContact(listCollectionContacts, tbContactSearch.Text);
dgFoundContacts.ItemsSource = listCollectionContacts;
UpdateUI();
LoadContacts();
}
private void UpdateUI()
{
dgLineItems.ItemsSource = lineItems;
lblQuote.Text = quoteVM.ToString();
}
private void LoadContacts()
{
listCollectionContacts = new(ContactVM.GetAllContactsVM());
listCollectionContacts.Filter = ContactVM.SearchContact(listCollectionContacts, tbContactSearch.Text);
dgFoundContacts.ItemsSource = listCollectionContacts;
UpdateUI();
}
private void TbContactSearch_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
@ -35,16 +42,119 @@ namespace Gremlin.GremlinUI
private void DgFoundContacts_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
txAnschreiben.Text = ContactVM.FilterContacts(dgFoundContacts.SelectedItem);
quoteVM.Recipient = ContactVM.ConvertObjectToContactVM(dgFoundContacts.SelectedItem);
}
private void ButPasteQuote_Click(object sender, RoutedEventArgs e)
private void BtnPasteQuote_Click(object sender, RoutedEventArgs e)
{
quoteVM = QuoteVM.CreateQuoteFromClipboard(tbQuotationNumber.Text, ContactVM.ConvertObjectToContactVM(dgFoundContacts.SelectedItem), ContactVM.GetSalesRepAsContact(-1));
float vat = 19f;
int warranty = int.TryParse(tbWarranty.Text, out int defaultWarranty) ? defaultWarranty : 12;
int validity = int.TryParse(tbValidity.Text, out int defaultValidity) ? defaultValidity : 60;
quoteVM = QuoteVM.CreateQuote(tbQuoteNumber.Text,
ContactVM.ConvertObjectToContactVM(dgFoundContacts.SelectedItem),
quoteVM.SalesRep,
vat,
tbQuoteType.Text,
cbBrutto.IsChecked,
warranty,
cbSinglePrices.IsChecked,
cbBrochure.IsChecked,
cbDataSheets.IsChecked,
cbMailTemplate.IsChecked,
tbQuotePath.Text,
validity,
cbShowDiscounts.IsChecked
);
if (quoteVM == null) return;
ObservableCollection<LineItemVM> lineItemsViewModel = new(quoteVM.LineItemsVM);
lineItems = new(lineItemsViewModel);
UpdateUI();
}
private void BtnCreateTex_Click(object sender, RoutedEventArgs e)
{
if (quoteVM.LineItemsVM == null || quoteVM.LineItemsVM.Count == 0)
{
ErrorHandler.ShowMessageBox("Bitte zuerst ein Angebot einfügen");
}
else
{
TexHandler.CreateTexAndOpen(quoteVM);
}
}
private void BtnCreateQuote_Click(object sender, RoutedEventArgs e)
{
PDFHandler.CreatePDF(quoteVM.QuoteNumber, tbQuotePath.Text);
}
private void BtnSendQuote_Click(object sender, RoutedEventArgs e)
{
//FileIO.SendQuoteViaMail(quoteVM);
}
private void BtnReloadContacts_Click(object sender, RoutedEventArgs e)
{
LoadContacts();
_ = MessageBox.Show($"Es wurden {listCollectionContacts.Count} Kontakte geladen.");
}
private void TbQuoteType_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
quoteVM.OnPropertyChanged("QuoteType");
}
private void TbQuotePath(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
quoteVM.QuotePath = tbQuotePath.Text;
}
private void TbQuoteNumber_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
quoteVM.QuoteNumber = tbQuoteNumber.Text;
}
private void TbWarranty_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
quoteVM.Warranty = int.TryParse(tbWarranty.Text, out int defaultWarranty) ? defaultWarranty : 12;
}
private void TbValidity_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
quoteVM.Validity = int.TryParse(tbValidity.Text, out int defaultValidity) ? defaultValidity : 60;
}
private void CbBrutto_Click(object sender, RoutedEventArgs e)
{
quoteVM.Brutto = cbBrutto.IsChecked;
}
private void CbSinglePrices_Click(object sender, RoutedEventArgs e)
{
quoteVM.SinglePrices = cbSinglePrices.IsChecked;
}
private void CbBrochure_Click(object sender, RoutedEventArgs e)
{
quoteVM.Brochures = cbBrochure.IsChecked;
}
private void CbDataSheets_Click(object sender, RoutedEventArgs e)
{
quoteVM.DataSheets = cbDataSheets.IsChecked;
}
private void CbMailTemplate_Click(object sender, RoutedEventArgs e)
{
quoteVM.MailTemplate = cbMailTemplate.IsChecked;
}
private void CbShowDiscounts_Click(object sender, RoutedEventArgs e)
{
quoteVM.ShowDiscounts = cbMailTemplate.IsChecked;
}
}
}
}

@ -1,6 +1,9 @@
using Gremlin.GremlinData.DBClasses;
using Gremlin.GremlinData.EntityClasses;
using Gremlin.ViewModels;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -8,13 +11,34 @@ namespace Gremlin.GremlinUI.ViewModels
{
internal class AccountVM : BaseVM
{
public static ObservableCollection<Account> GetAccounts()
public uint SAPAccountNumber { get; set; }
public string AccountName { get; set; }
public string Street { get; set; }
public uint ZIP { get; set; }
public string City { get; set; }
public string PhoneNumber { get; set; }
public string FaxNumber { get; set; }
public string Webpage { get; set; }
public string EMail { get; set; }
public ICollection<ContactVM> Contacts { get; set; }
public AccountType AccountType { get; set; }
public SubMarket SubMarket { get; set; }
public static ObservableCollection<AccountVM> GetAllAccountsVM()
{
try
{
using (GremlinContext gremlinContext = new())
{
return new ObservableCollection<Account>(gremlinContext.Accounts.ToList());
List<Account> accounts = gremlinContext.Accounts.Include(account => account.Contacts).ToList();
ObservableCollection<AccountVM> accountsVM = new();
foreach (Account account in accounts)
{
accountsVM.Add(ConvertAccountToVM(account));
}
return accountsVM;
}
}
catch (Exception ex)
@ -24,5 +48,31 @@ namespace Gremlin.GremlinUI.ViewModels
throw;
}
}
public static AccountVM ConvertAccountToVM(Account account)
{
AccountVM accountVM = new();
accountVM.AccountType = account.AccountType;
accountVM.SubMarket = account.SubMarket;
accountVM.AccountName = account.AccountName;
accountVM.Street = account.Street;
accountVM.ZIP = account.ZIP;
accountVM.City = account.City;
accountVM.PhoneNumber = account.PhoneNumber;
accountVM.FaxNumber = account.FaxNumber;
accountVM.Webpage = account.Webpage;
accountVM.EMail = account.EMail;
accountVM.SAPAccountNumber = account.SAPAccountNumber;
List<ContactVM> contactsVM = new();
foreach (Contact contact in account.Contacts)
{
contactsVM.Add(ContactVM.ConvertContactToVM(contact));
}
accountVM.Contacts = contactsVM;
return accountVM;
}
}
}

@ -1,4 +1,6 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Gremlin.GremlinUI.ViewModels
{
@ -6,9 +8,10 @@ namespace Gremlin.GremlinUI.ViewModels
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
public void OnPropertyChanged([CallerMemberName] string propName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
Debug.WriteLine(propName + " changed in INotifyPropertyChanged");
}
}
}

@ -8,7 +8,6 @@ using System.Text;
using System.Windows.Data;
using Microsoft.EntityFrameworkCore;
using Gremlin.GremlinData.EntityClasses;
using System.ComponentModel;
namespace Gremlin.ViewModels
{
@ -45,7 +44,7 @@ namespace Gremlin.ViewModels
{
//TryCast
selectedContact = selectedItem as ContactVM;
//if failed == null
if (selectedContact == null)
{
@ -58,7 +57,7 @@ namespace Gremlin.ViewModels
public override string ToString()
{
return Gender ==(byte)Enums.Gender.Male
return Gender == (byte)Enums.Gender.Male
? $"Herr {FirstName} {LastName}; {AccountName}, {AccountStreet}, {AccountZIP} {AccountCity}"
: $"Frau {FirstName} {LastName}; {AccountName}, {AccountStreet}, {AccountZIP} {AccountCity}";
}
@ -90,51 +89,53 @@ namespace Gremlin.ViewModels
{
case 1:
{
//Get SalesRep as Contact
return null;
ContactVM salesRepWoitschetzki = new((byte)Enums.Gender.Male,
"Sascha",
"Woitschetzki",
"sascha.woitschetzki@non.agilent.com",
"Handelsvertretung Sascha Woitschetzki",
"Parsevalstr. 58",
45470,
"Mülheim an der Ruhr");
return salesRepWoitschetzki;
}
case 2:
{
ContactVM salesRepWelsch = new((byte)Enums.Gender.Male,
"Sebastian",
"Welsch",
"sebastian.welsch@non.agilent.com",
"Handelsvertretung Sebastian Welsch",
"",
0,
"");
return salesRepWelsch;
}
default:
ContactVM salesRep = new((byte)Enums.Gender.Male,
"Sascha",
"Woitschetzki",
"sascha.woitschetzki@non.agilent.com",
"Handelsvertretung Sascha Woitschetzki",
"Parsevalstr. 58",
45470,
"Mülheim an der Ruhr");
return salesRep;
{
ContactVM salesRep = new();
return salesRep;
}
}
}
public static ObservableCollection<ContactVM> GetContacts()
public static ObservableCollection<ContactVM> GetAllContactsVM()
{
try
{
using (GremlinContext gremlinContext = new())
{
List<Contact> contacts = gremlinContext.Contacts.Include(contact => contact.Account).ToList();
ObservableCollection<ContactVM> contactsViewModel = new();
ObservableCollection<ContactVM> contactsVM = new();
foreach (Contact contact in contacts)
{
ContactVM contactViewModel = new();
contactViewModel.Gender = contact.Gender;
contactViewModel.LastName = contact.LastName;
contactViewModel.FirstName = contact.FirstName;
contactViewModel.EMail = contact.EMail;
if (contact.Account != null)
{
contactViewModel.AccountName = contact.Account.AccountName;
contactViewModel.AccountStreet = contact.Account.Street;
contactViewModel.AccountZIP = contact.Account.ZIP;
contactViewModel.AccountCity = contact.Account.City;
}
contactsViewModel.Add(contactViewModel);
contactsVM.Add(ConvertContactToVM(contact));
}
return contactsViewModel;
return contactsVM;
}
}
catch (Exception ex)
@ -145,17 +146,55 @@ namespace Gremlin.ViewModels
}
}
internal static StringBuilder CreateBriefkopf(ContactVM contactViewModel)
public static ContactVM ConvertContactToVM(Contact contact)
{
ContactVM contactVM = new();
contactVM.Gender = contact.Gender;
contactVM.LastName = contact.LastName;
contactVM.FirstName = contact.FirstName;
contactVM.EMail = contact.EMail;
if (contact.Account != null)
{
contactVM.AccountName = contact.Account.AccountName;
contactVM.AccountStreet = contact.Account.Street;
contactVM.AccountZIP = contact.Account.ZIP;
contactVM.AccountCity = contact.Account.City;
}
return contactVM;
}
internal static StringBuilder CreateBriefkopf(ContactVM contactVM, bool tex = false)
{
StringBuilder briefkopf = new();
_ = contactViewModel.Gender == (byte)Enums.Gender.Male
? briefkopf.AppendLine($"Herr {contactViewModel.FirstName} {contactViewModel.LastName}")
: briefkopf.AppendLine($"Frau {contactViewModel.FirstName} {contactViewModel.LastName}");
_ = contactVM.Gender == (byte)Enums.Gender.Male
? briefkopf.AppendLine($"Herr {contactVM.FirstName} {contactVM.LastName}")
: briefkopf.AppendLine($"Frau {contactVM.FirstName} {contactVM.LastName}");
if (tex) _ = briefkopf.AppendLine($"\\\\");
string AccountName = contactVM.AccountName;
//AccountNamen mit "&" im Namen abfangen
if (tex && AccountName.Contains("&"))
{
string[] accountNameSplit = AccountName.Split("&"); //.Replace?
AccountName = "";
for (int i = 0; i < accountNameSplit.Length; i++)
{
AccountName += i < accountNameSplit.Length - 1
? accountNameSplit[i] + "\\&"
: accountNameSplit[i];
}
}
_ = briefkopf.AppendLine($"{AccountName}");
if (tex) { briefkopf.AppendLine($"\\\\"); }
_ = briefkopf.AppendLine($"{contactVM.AccountStreet}");
if (tex) { briefkopf.AppendLine($"\\\\"); }
_ = briefkopf.AppendLine($"{contactViewModel.AccountName}");
_ = briefkopf.AppendLine($"{contactViewModel.AccountStreet}");
_ = briefkopf.AppendLine($"{contactViewModel.AccountZIP} {contactViewModel.AccountCity}");
_ = briefkopf.AppendLine($"{contactVM.AccountZIP} {contactVM.AccountCity}");
if (tex) { briefkopf.AppendLine($"\\\\"); }
return briefkopf;
}

@ -2,45 +2,45 @@
using Gremlin.GremlinData.EntityClasses;
using System.Diagnostics;
using System.Linq;
using System.Windows;
namespace Gremlin.GremlinUI.ViewModels
{
internal class CustomDescriptionVM
{
public string ProductNumber { get; set; }
public string OptionNumber { get; set; }
public string Heading { get; set; }
public string DescriptionText { get; set; }
public string CoverletterText { get; set; }
public string Note { get; set; } //Hinweise, Tipps, Caveats, etc. für Konfiguration, Verwendung, Best Practice usw.
public string Notes { get; set; } //Hinweise, Tipps, Caveats, etc. für Konfiguration, Verwendung, Best Practice usw.
public override string ToString() => $"{Heading}";
internal static CustomDescriptionVM GetCustomDescription(LineItemVM lineItemVM)
{
string tempOptionNumber;
using (GremlinContext gremlinContext = new())
{
CustomDescription customDescription = gremlinContext.CustomDescriptions.Where(cD => cD.ProductNumber == lineItemVM.ProductNumber && cD.OptionNumber == lineItemVM.OptionNumber)
tempOptionNumber = lineItemVM.OptionNumber == "" ? null : lineItemVM.OptionNumber;
CustomDescriptionVM customDescriptionVM = new();
CustomDescription customDescription = gremlinContext.CustomDescriptions.Where(cD => cD.ProductNumber == lineItemVM.ProductNumber && cD.OptionNumber == tempOptionNumber)
.FirstOrDefault();
if (customDescription != null)
{
CustomDescriptionVM customDescriptionVM = new();
customDescriptionVM.ProductNumber = customDescription.ProductNumber;
customDescriptionVM.OptionNumber = customDescription.OptionNumber;
customDescriptionVM.Heading = customDescription.Heading;
customDescriptionVM.DescriptionText = customDescription.DescriptionText;
customDescriptionVM.CoverletterText = customDescription.CoverletterText;
customDescriptionVM.Note = customDescription.Notes;
customDescriptionVM.Notes = customDescription.Notes;
return customDescriptionVM;
}
else
{
Debug.WriteLine($"CustomDescription für {lineItemVM.ProductNumber}#{lineItemVM.OptionNumber} nicht vorhanden!");
return null;
Debug.WriteLine($"CustomDescription für {lineItemVM.ProductNumber}#{lineItemVM.OptionNumber} nicht vorhanden! Verwende (vorläufig) Standardbeschreibung {lineItemVM.SapShortDescription}!");
customDescriptionVM.DescriptionText = lineItemVM.SapShortDescription;
customDescriptionVM.Heading = lineItemVM.SapShortDescription;
}
return customDescriptionVM;
}
}
}

@ -7,27 +7,22 @@ namespace Gremlin.GremlinUI.ViewModels
{
internal class LineItemVM : BaseVM
{
public ushort Position { get; set; }
public ushort Amount { get; set; }
public string ProductNumber { get; set; }
public string OptionNumber { get; set; }
public string SapShortDescription { get; set; }
public CustomDescriptionVM CustomDescription { get; set; }
public string ProductLine { get; set; }
public decimal TotalDiscount { get; set; }
//public decimal SalesDiscount { get; set; }
//public decimal PromotionalDiscount { get; set; }
//public decimal ContractualDiscount { get; set; }
//public decimal DemoDiscount { get; set; }
public decimal ListPrice { get; set; }
public decimal NetPrice { get; set; }
public decimal TotalNet { get; set; }
internal static ObservableCollection<LineItemVM> ReadLineItemsFromClipboard()
public ushort Position { get; private set; }
public ushort Amount { get; internal set; }
public string ProductNumber { get; private set; }
public string OptionNumber { get; private set; }
public string SapShortDescription { get; private set; }
public CustomDescriptionVM CustomDescription { get; private set; }
public string ProductLine { get; private set; }
public decimal TotalDiscount { get; internal set; }
public decimal CalcNetPrice { get; private set; }
public decimal CalcTotalNet { get; private set; }
public decimal ListPrice { get; private set; }
internal static ObservableCollection<LineItemVM> ReadLineItemsFromClipboard ()
{
if (Clipboard.GetText() != "")
{
//Clipboard einlesen
{
string clipboard = Clipboard.GetText();
//Zeilen aufteilen
@ -81,8 +76,16 @@ namespace Gremlin.GremlinUI.ViewModels
lineItem.Amount = ushort.Parse(lineItemString[5]);
lineItem.ListPrice = decimal.Parse(lineItemString[6]);
lineItem.TotalDiscount = decimal.Parse(lineItemString[9]);
lineItem.NetPrice = decimal.Parse(lineItemString[10]);
lineItem.TotalNet = decimal.Parse(lineItemString[11]);
//Preise einlesen
//lineItem.NetPrice = decimal.Parse(lineItemString[10]);
//lineItem.TotalNet = decimal.Parse(lineItemString[11]);
//Preise selbst berechnen
lineItem.CalcNetPrice = lineItem.ListPrice * (100 - lineItem.TotalDiscount) / 100;
lineItem.CalcTotalNet = lineItem.CalcNetPrice * lineItem.Amount;
//nicht genutzte Felder
//lineItem.SalesDiscount = decimal.Parse(lineItemString[12]);
//lineItem.ContractualDiscount = decimal.Parse(lineItemString[13]);
//lineItem.PromotionalDiscount = decimal.Parse(lineItemString[14]);
@ -107,7 +110,7 @@ namespace Gremlin.GremlinUI.ViewModels
decimal totalNet = 0;
foreach (LineItemVM lineItem in lineItems)
{
totalNet += lineItem.NetPrice;
totalNet += lineItem.CalcNetPrice;
}
return totalNet;
}

@ -2,77 +2,139 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Gremlin.GremlinUI.ViewModels
{
internal class QuoteVM : BaseVM
{
public string QuotationNumber { get; set; }
public ContactVM Recipient { get; set; }
public ContactVM SalesRep { get; set; }
public List<LineItemVM> LineItemsVM { get; set; }
public decimal TotalListprice { get; set; }
public decimal AverageDiscount { get; set; }
public decimal TotalNet { get; set; }
public float VAT { get; set; }
public bool QuoteContains3PP { get; set; }
public bool QuoteContainsRB { get; set; }
internal QuoteVM() { }
internal QuoteVM(string quotationNumber, ContactVM recipient, ContactVM salesRep, decimal totalListprice, decimal totalDiscount, decimal totalNet, float vAT, bool quoteContains3PP, bool quoteContainsRB)
private string _quoteType = "ein Analysegerät";
private string _quotePath;
private List<LineItemVM> _lineItemsVM;
private ContactVM _recipient;
private ContactVM _salesRep;
private string _quoteNumber;
private int _warranty = 12;
private int _validity = 60;
private decimal _totalListprice;
private decimal _averageDiscount;
private decimal _totalNet;
private float _vAT = 19f;
private bool? _brutto = true;
private bool? _singlePrices = true;
private bool? _brochures = false;
private bool? _dataSheets = false;
private bool? _mailTemplate = false;
private bool _quoteContains3PP = false;
private bool _quoteContainsRB = false;
private bool? _showDiscounts = true;
public string QuoteType
{
QuotationNumber = quotationNumber ?? throw new ArgumentNullException(nameof(quotationNumber));
Recipient = recipient ?? throw new ArgumentNullException(nameof(recipient));
SalesRep = salesRep ?? throw new ArgumentNullException(nameof(salesRep));
TotalListprice = totalListprice;
AverageDiscount = totalDiscount;
TotalNet = totalNet;
VAT = vAT;
QuoteContains3PP = quoteContains3PP;
QuoteContainsRB = quoteContainsRB;
get => _quoteType;
internal set
{
_quoteType = value;
OnPropertyChanged("QuoteType");
}
}
public string QuotePath
{
get => _quotePath;
internal set
{
_quotePath = value;
OnPropertyChanged("QuotePath");
}
}
public override string ToString()
public List<LineItemVM> LineItemsVM { get => _lineItemsVM; private set => _lineItemsVM = value; }
public ContactVM Recipient { get => _recipient; internal set => _recipient = value; }
public ContactVM SalesRep { get => _salesRep; private set => _salesRep = value; }
public string QuoteNumber { get => _quoteNumber; internal set => _quoteNumber = value; }
public int Warranty { get => _warranty; internal set => _warranty = value; }
public int Validity { get => _validity; internal set => _validity = value; }
public decimal TotalListprice { get => _totalListprice; private set => _totalListprice = value; }
public decimal AverageDiscount { get => _averageDiscount; private set => _averageDiscount = value; }
public decimal TotalNet { get => _totalNet; private set => _totalNet = value; }
public float VAT { get => _vAT; private set => _vAT = value; }
public bool? Brutto { get => _brutto; internal set => _brutto = value; }
public bool? SinglePrices { get => _singlePrices; internal set => _singlePrices = value; }
public bool? Brochures { get => _brochures; internal set => _brochures = value; }
public bool? DataSheets { get => _dataSheets; internal set => _dataSheets = value; }
public bool? MailTemplate { get => _mailTemplate; internal set => _mailTemplate = value; }
public bool QuoteContains3PP { get => _quoteContains3PP; private set => _quoteContains3PP = value; }
public bool QuoteContainsRB { get => _quoteContainsRB; private set => _quoteContainsRB = value; }
public bool? ShowDiscounts { get => _showDiscounts; internal set => _showDiscounts = value; }
internal QuoteVM(ContactVM salesRep)
{
return $"QuotationNumber: {QuotationNumber}\n"
+ $"Recipient: {Recipient}\n"
+ $"SalesRep: {SalesRep}\n"
+ $"TotalListprice: {TotalListprice}\n"
+ $"AverageDiscount: {AverageDiscount}\n"
+ $"TotalNet: {TotalNet}\n"
+ $"VAT: {VAT}\n"
+ $"QuoteContains3PP: {QuoteContains3PP}\n"
+ $"QuoteContainsRB: {QuoteContainsRB}";
Random random = new();
SalesRep = salesRep;
QuoteNumber = SalesRep.LastName switch
{
"Woitschetzki" => $"DE-83PE89-{DateTime.Now:My}-{random.Next(999999)}",
"Welsch" => $"DE-83RE32-{DateTime.Now:My}-{random.Next(999999)}",
_ => $"DE-XXYYXX-{DateTime.Now:My}-{random.Next(999999)}",
};
}
internal static QuoteVM CreateQuoteFromClipboard(string quotationNumber, ContactVM recipient, ContactVM salesRep, float vAT = 19f)
public QuoteVM() { }
private QuoteVM(string quoteNumber, string quoteType, ContactVM recipient, ContactVM salesRep, bool? brutto, float vAT, int warranty, int validity, string quotePath, bool? singlePrices, bool? brochures, bool? dataSheets, bool? mailTemplate, bool? showDiscounts)
{
QuoteVM quoteVM = new();
QuoteNumber = quoteNumber;
QuoteType = quoteType;
Recipient = recipient;
SalesRep = salesRep;
Brutto = brutto;
VAT = vAT;
Warranty = warranty;
Validity = validity;
QuotePath = quotePath;
SinglePrices = singlePrices;
Brochures = brochures;
DataSheets = dataSheets;
MailTemplate = mailTemplate;
ShowDiscounts = showDiscounts;
}
decimal totalListprice = 0, totalDiscount = 0, totalNet = 0;
internal static QuoteVM CreateQuote(string quoteNumber, ContactVM recipient, ContactVM salesRep, float vAT = 19f, string quoteType = "ein Analysegerät", bool? brutto = true, int warranty = 12, bool? singlePrices = true, bool? brochures = true, bool? dataSheets = true, bool? mailTemplate = true, string quotePath = "", int validity = 60, bool? showDiscounts = true)
{
decimal totalListprice = 0, totalDiscount = 0, calcTotalNet = 0;
bool quoteContains3PP = false, quoteContainsRB = false;
quoteVM.QuotationNumber = quotationNumber;
quoteVM.Recipient = recipient;
quoteVM.SalesRep = salesRep;
quoteVM.VAT = vAT;
QuoteVM quoteVM = new(quoteNumber, quoteType, recipient, salesRep, brutto, vAT, warranty, validity, quotePath, singlePrices, brochures, dataSheets, mailTemplate, showDiscounts);
quoteVM.LineItemsVM = new();
ObservableCollection<LineItemVM> lineItemsVM = LineItemVM.ReadLineItemsFromClipboard();
if (lineItemsVM == null) return null;
foreach (LineItemVM lineItemVM in lineItemsVM)
{
totalListprice += lineItemVM.ListPrice;
totalDiscount += lineItemVM.TotalDiscount;
totalNet += lineItemVM.TotalNet;
calcTotalNet += lineItemVM.CalcTotalNet; //Use calculated values
if (lineItemVM.ProductLine == "RB")
switch (lineItemVM.ProductLine)
{
quoteContainsRB = true;
case "RB":
if (lineItemVM.ProductNumber != "R2005A") //kein wirkliches RB-Produkt
{
quoteContainsRB = true;
}
break;
case "3PP":
quoteContains3PP = true; break;
}
else if (lineItemVM.ProductLine == "3PP")
if (lineItemVM.OptionNumber.StartsWith("8D"))
{
quoteContains3PP = true;
quoteVM.Warranty = int.Parse(lineItemVM.OptionNumber.Last().ToString()) * 12;
}
quoteVM.LineItemsVM.Add(lineItemVM);
@ -83,7 +145,7 @@ namespace Gremlin.GremlinUI.ViewModels
{
quoteVM.AverageDiscount = (decimal)MathF.Round((float)(totalDiscount / lineItemsVM.Count), 3);
}
quoteVM.TotalNet = totalNet;
quoteVM.TotalNet = calcTotalNet; //Use calculated values
quoteVM.QuoteContainsRB = quoteContainsRB;
quoteVM.QuoteContains3PP = quoteContains3PP;

@ -1,41 +0,0 @@
using Microsoft.Win32;
using System.IO;
using System.Text;
namespace Gremlin.GremlinUtilities
{
internal class FileHelper
{
private static readonly Encoding defaultEncodingIfNoBom = Encoding.UTF8;
public static string GetFilepathFromUser(string filter = "Delimited Data File|*.csv; *.txt; *.tsv")
{
string _filepath = "";
OpenFileDialog dlg = new()
{
Multiselect = false,
Filter = filter
};
if (dlg.ShowDialog() == true)
{
//Auswahl dlg.filenames an passende Objekte übergeben
_filepath = dlg.FileName;
}
return _filepath;
}
public static Encoding GetEncoding(string fileName)
{
if (fileName == "" || fileName == null) return null;
Stream fileStream = File.OpenRead(fileName);
using (var reader = new StreamReader(fileStream, defaultEncodingIfNoBom, true))
{
_ = reader.Peek();
Encoding encoding = reader.CurrentEncoding;
return encoding;
}
}
}
}

@ -1,15 +1,20 @@
using System.Linq;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.Win32;
using System.Diagnostics;
using System;
//using Outlook = Microsoft.Office.Interop.Outlook;
namespace Gremlin.GremlinUtilities
{
public static class FileIO
internal static class FileIO
{
public static string ReadResource(string name)
//Source and credit to: https://stackoverflow.com/questions/3314140/how-to-read-embedded-resource-text-file
{
var assembly = Assembly.GetExecutingAssembly();
Assembly assembly = Assembly.GetExecutingAssembly();
// Format: "{Namespace}.{Folder}.{filename}.{Extension}"
string resourcePath = name.StartsWith(nameof(Gremlin))
@ -22,5 +27,67 @@ namespace Gremlin.GremlinUtilities
return reader.ReadToEnd();
}
}
private static readonly Encoding defaultEncodingIfNoBom = Encoding.UTF8;
public static string GetFilepathFromUser(string filter = "Delimited Data File|*.csv; *.txt; *.tsv")
{
string _filepath = "";
OpenFileDialog dlg = new()
{
Multiselect = false,
Filter = filter
};
if (dlg.ShowDialog() == true)
{
//Auswahl dlg.filenames an passende Objekte übergeben
_filepath = dlg.FileName;
}
return _filepath;
}
internal static void OpenFile(string path, string file, string type)
{
try
{
_ = Process.Start(path == "" ? $"{file}.{type}" : $"{path}\\{file}.{type}");
}
catch (Exception ex)
{
ErrorHandler.ShowErrorInMessageBox(ex);
}
}
public static Encoding GetEncoding(string fileName)
{
Stream fileStream = File.OpenRead(fileName);
using (StreamReader reader = new(fileStream, defaultEncodingIfNoBom, true))
{
_ = reader.Peek();
Encoding encoding = reader.CurrentEncoding;
return encoding;
}
}
//public static void SendQuoteViaMail(QuoteVM quoteVM)
//{
// Outlook.Application application = new();
// Outlook.MailItem mail = application.CreateItem(Outlook.OlItemType.olMailItem) as Outlook.MailItem;
// mail.Subject = quoteVM.QuoteNumber;
// Outlook.AddressEntry currentUser = application.Session.CurrentUser.AddressEntry;
// if (currentUser.Type == "EX")
// {
// Outlook.ExchangeUser manager = currentUser.GetExchangeUser().GetExchangeUserManager();
// // Add recipient using display name, alias, or smtp address
// mail.Recipients.Add(manager.PrimarySmtpAddress);
// mail.Recipients.ResolveAll();
// mail.Attachments.Add($"{quoteVM.QuoteNumber}.pdf", Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);
// mail.Send();
// }
//}
}
}
}

@ -0,0 +1,93 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Gremlin.Properties {
using System;
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Gremlin.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] agilentLogo {
get {
object obj = ResourceManager.GetObject("agilentLogo", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] gremlin {
get {
object obj = ResourceManager.GetObject("gremlin", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Byte[].
/// </summary>
internal static byte[] signWoitschetzki {
get {
object obj = ResourceManager.GetObject("signWoitschetzki", resourceCulture);
return ((byte[])(obj));
}
}
}
}

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="agilentLogo" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\agilentlogo.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="gremlin" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\gremlin.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="signWoitschetzki" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\signwoitschetzki.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

@ -142,5 +142,17 @@ namespace Gremlin.Properties {
this["MwSt"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string QuotePath {
get {
return ((string)(this["QuotePath"]));
}
set {
this["QuotePath"] = value;
}
}
}
}

@ -24,7 +24,7 @@
<Value Profile="(Default)" />
</Setting>
<Setting Name="texRand" Type="System.Single" Scope="User">
<Value Profile="(Default)">2.5</Value>
<Value Profile="(Default)">2</Value>
</Setting>
<Setting Name="userSettingID" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">1</Value>
@ -32,5 +32,8 @@
<Setting Name="MwSt" Type="System.Int32" Scope="User">
<Value Profile="(Default)">19</Value>
</Setting>
<Setting Name="QuotePath" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB