pull/1/head
Sascha 2023-03-27 18:28:14 +07:00
parent ab95729a4a
commit 8ab7f52b24
5 changed files with 321 additions and 369 deletions

@ -5,101 +5,103 @@ namespace Gremlin_BlazorServer.Data.EntityClasses;
public class Account : IMetadata
//: IEquatable<Account>
{
//primary key:
[Required] public uint AccountId { get; set; }
//foreign keys:
// [Required] public uint ParentAccountId { get; set; }
public string AccountTypeCode { get; set; }
public string SubMarketCode { get; set; }
//navigation properties:
public AccountType? AccountType { get; set; }
public SubMarket? SubMarket { get; set; }
public IList<Contact>? Contacts { get; set; }
// public IList<CustomDescription>? CustomDescriptions { get; set; }
//class properties:
[Required] public string? AccountName { get; set; }
public string Notes { get; set; } = string.Empty;
[Required] public string? Street { get; set; }
[Required] public uint Zip { get; set; }
[Required] public string? City { get; set; }
public string FloorOrBuilding { get; set; } = string.Empty;
public float Longitude { get; set; }
public float Latitude { get; set; }
public string PhoneNumber { get; set; } = string.Empty;
public string FaxNumber { get; set; } = string.Empty;
public string Webpage { get; set; } = string.Empty;
public string EMail { get; set; } = string.Empty;
//Agilent-specific Properties:
public uint SapAccountNumber { get; set; }
public DateTime AccountCreatedInSapOn { get; set; }
//metadata:
public DateTime DataCreationDate { get; set; } = DateTime.Now;
public DateTime DataModificationDate { get; set; } = DateTime.Now;
public DateTime DataValidFrom { get; set; } = DateTime.Now;
public DateTime DataValidUntil { get; set; } = DateTime.MaxValue;
public string DataModificationByUser { get; set; } = "Gremlin_BlazorServer";
public uint DataVersionNumber { get; set; }
public string? DataVersionComment { get; set; } = string.Empty;
public string DataStatus { get; set; } = "Active";
//IBase
//tbd
//public bool Equals(Account other)
//{
// if (other == null) return false;
// if (this == null) return false;
// if (this.SAPAccountNumber == other.SAPAccountNumber) return true;
// else return false;
//}
//public static bool operator ==(Account account1, Account account2)
//{
// if (account1.SAPAccountNumber == 0) return false;
// if (account2.SAPAccountNumber == 0) return false;
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return true;
// else return false;
//}
//public static bool operator !=(Account account1, Account account2)
//{
// if (account1.SAPAccountNumber == 0) return false;
// if (account2.SAPAccountNumber == 0) return false;
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return false;
// else return true;
//}
//public override bool Equals(object obj)
//{
// if (obj == null) return false;
// Account accountObj = obj as Account;
// if (accountObj == null) return false;
// else return base.Equals(obj);
//}
//public override int GetHashCode()
/////<summary>
/////Returns the (unique) SAP Account ID.
/////</summary>
//{
// return Convert.ToInt32(this.SAPAccountNumber);
//}
public List<Account> AddIfUniqueTo(List<Account> accounts)
//primary key:
[Required] public uint AccountId { get; set; }
//foreign keys:
// [Required] public uint ParentAccountId { get; set; }
public string? AccountTypeCode { get; set; }
public string? SubMarketCode { get; set; }
//navigation properties:
public AccountType? AccountType { get; set; }
public SubMarket? SubMarket { get; set; }
public IList<Contact>? Contacts { get; set; }
// public IList<CustomDescription>? CustomDescriptions { get; set; }
//class properties:
[Required] public string? AccountName { get; set; }
public string Notes { get; set; } = string.Empty;
[Required] public string? Street { get; set; }
[Required] public uint Zip { get; set; }
[Required] public string? City { get; set; }
public string FloorOrBuilding { get; set; } = string.Empty;
public float Longitude { get; set; }
public float Latitude { get; set; }
public string PhoneNumber { get; set; } = string.Empty;
public string FaxNumber { get; set; } = string.Empty;
public string Webpage { get; set; } = string.Empty;
public string EMail { get; set; } = string.Empty;
//Agilent-specific Properties:
public uint SapAccountNumber { get; set; }
public DateTime AccountCreatedInSapOn { get; set; }
//metadata:
public DateTime DataCreationDate { get; set; } = DateTime.Now;
public DateTime DataModificationDate { get; set; } = DateTime.Now;
public DateTime DataValidFrom { get; set; } = DateTime.Now;
public DateTime DataValidUntil { get; set; } = DateTime.MaxValue;
public string DataModificationByUser { get; set; } = "Gremlin_BlazorServer";
public uint DataVersionNumber { get; set; }
public string? DataVersionComment { get; set; } = string.Empty;
public string DataStatus { get; set; } = "Active";
//IBase
//tbd
//public bool Equals(Account other)
//{
// if (other == null) return false;
// if (this == null) return false;
// if (this.SAPAccountNumber == other.SAPAccountNumber) return true;
// else return false;
//}
//public static bool operator ==(Account account1, Account account2)
//{
// if (account1.SAPAccountNumber == 0) return false;
// if (account2.SAPAccountNumber == 0) return false;
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return true;
// else return false;
//}
//public static bool operator !=(Account account1, Account account2)
//{
// if (account1.SAPAccountNumber == 0) return false;
// if (account2.SAPAccountNumber == 0) return false;
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return false;
// else return true;
//}
//public override bool Equals(object obj)
//{
// if (obj == null) return false;
// Account accountObj = obj as Account;
// if (accountObj == null) return false;
// else return base.Equals(obj);
//}
//public override int GetHashCode()
/////<summary>
/////Returns the (unique) SAP Account ID.
/////</summary>
//{
// return Convert.ToInt32(this.SAPAccountNumber);
//}
public List<Account> AddIfUniqueTo(List<Account> accounts)
{
if (accounts.Count > 0 && SapAccountNumber == accounts[^1].SapAccountNumber) return accounts;
foreach (Account account in accounts)
{
if (accounts.Count > 0 && SapAccountNumber == accounts[^1].SapAccountNumber) return accounts;
foreach (Account account in accounts)
if (SapAccountNumber == account.SapAccountNumber)
return accounts;
accounts.Add(this);
if (SapAccountNumber == account.SapAccountNumber)
return accounts;
}
accounts.Add(this);
return accounts;
}
}

@ -1,262 +1,262 @@
using System.Diagnostics;
using System.Globalization;
using System.Security.Claims;
using System.Text;
using Blazorise;
using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.JSInterop;
using System.Diagnostics;
using System.Globalization;
using System.Security.Claims;
using System.Text;
namespace Gremlin_BlazorServer.Pages.Quotes;
public partial class QuoteAdd
{
[CascadingParameter] private Task<AuthenticationState>? AuthenticationStateTask { get; set; }
[Inject] public IModalService? ModalService { get; set; }
private IList<Contact>? contacts;
private Quote? quote;
private Contact? selectedContact;
private readonly CultureInfo cultureInfo = new("de-DE");
private bool pdfNotReady = true;
private bool isCreatingPdf;
private bool texNotReady = true;
private bool isCreatingTex;
private bool lineItemsNotReady = true;
private string? url;
private readonly bool debug;
private List<CustomDescription>? suggestedCustomDescriptions;
private CustomDescription? newCustomDescription;
private LineItem? selectedLineItem;
public Task ShowCustomDescriptionModal() =>
ModalService.Show<CustomDescriptionModal>(builder =>
{
builder.Add(parameter => parameter.CustomDescription, newCustomDescription);
builder.Add(parameter => parameter.SuggestedCustomDescriptions, suggestedCustomDescriptions);
});
protected override async Task OnParametersSetAsync()
[CascadingParameter] private Task<AuthenticationState>? AuthenticationStateTask { get; set; }
[Inject] public IModalService? ModalService { get; set; }
private IList<Contact>? contacts;
private Quote? quote;
private Contact? selectedContact;
private readonly CultureInfo cultureInfo = new("de-DE");
private bool pdfNotReady = true;
private bool isCreatingPdf;
private bool texNotReady = true;
private bool isCreatingTex;
private bool lineItemsNotReady = true;
private string? url;
private readonly bool debug;
private List<CustomDescription>? suggestedCustomDescriptions;
private CustomDescription? newCustomDescription;
private LineItem? selectedLineItem;
public Task ShowCustomDescriptionModal() =>
ModalService.Show<CustomDescriptionModal>(builder =>
{
builder.Add(parameter => parameter.CustomDescription, newCustomDescription);
builder.Add(parameter => parameter.SuggestedCustomDescriptions, suggestedCustomDescriptions);
});
protected override async Task OnParametersSetAsync()
{
if (AuthenticationStateTask != null)
{
if (AuthenticationStateTask != null)
{
ClaimsPrincipal user = (await AuthenticationStateTask).User;
if (user.Identity is { IsAuthenticated: true })
{
await ApplicationLoadingIndicatorService.Show();
contacts = await genericController.GetAllAsync<Contact>();
await ApplicationLoadingIndicatorService.Hide();
quote = await GenerateNewQuote(quote, "Woitschetzki");
}
}
await base.OnInitializedAsync();
ClaimsPrincipal user = (await AuthenticationStateTask).User;
if (user.Identity is { IsAuthenticated: true })
{
await ApplicationLoadingIndicatorService.Show();
contacts = await genericController.GetAllAsync<Contact>();
await ApplicationLoadingIndicatorService.Hide();
quote = await GenerateNewQuote(quote, "Woitschetzki");
}
}
private async Task<Quote> GenerateNewQuote(Quote? _quote, string salesRepLastName)
{
if (_quote == null)
{
_quote = new();
await genericController.InsertAsync(_quote);
}
_quote.SalesRep = await genericController.GetAsync<Contact>(c => c.LastName == salesRepLastName);
if (_quote.SalesRep != null)
//Read Account seperatly to avoid new creation of account
_quote.SalesRep.Account = await genericController.GetAsync<Account>(a => a.AccountId == _quote.SalesRep.AccountId);
Quote? lastQuote = genericController.GetLast<Quote>();
_quote.QuoteId = lastQuote != null ? lastQuote.QuoteId + 1 : 1;
await base.OnInitializedAsync();
}
_quote.QuotationNumber = _quote.SalesRep != null ? _quote.SalesRep.LastName switch
{
"Woitschetzki" => $"DE-83PE89-{DateTime.Now:My}-{_quote.QuoteId}",
"Welsch" => $"DE-83RE32-{DateTime.Now:My}-{_quote.QuoteId}",
_ => $"DE-XXYYXX-{DateTime.Now:My}-{_quote.QuoteId}"
}
: $"DE-XXYYXX-{DateTime.Now:My}-{_quote.QuoteId}";
private async Task<Quote> GenerateNewQuote(Quote? _quote, string salesRepLastName)
{
if (_quote == null)
{
_quote = new();
_ = await genericController.InsertAsync(_quote);
}
_quote.Description = "Gerät";
TryToSaveQuote(_quote);
_quote.SalesRep = await genericController.GetAsync<Contact>(c => c.LastName == salesRepLastName);
if (_quote.SalesRep != null)
//Read Account seperatly to avoid new creation of account
_quote.SalesRep.Account = await genericController.GetAsync<Account>(a => a.AccountId == _quote.SalesRep.AccountId);
return _quote;
}
Quote? lastQuote = genericController.GetLast<Quote>();
_quote.QuoteId = lastQuote != null ? lastQuote.QuoteId + 1 : 1;
private async Task TryToSaveQuote(Quote _quote)
_quote.QuotationNumber = _quote.SalesRep != null ? _quote.SalesRep.LastName switch
{
if (await genericController.UpdateAsync(_quote) > 0)
Debug.WriteLine("Speichern der Quote erfolgreich.");
else
Debug.WriteLine("Fehler beim Speichern der Quote!");
return;
"Woitschetzki" => $"DE-83PE89-{DateTime.Now:My}-{_quote.QuoteId}",
"Welsch" => $"DE-83RE32-{DateTime.Now:My}-{_quote.QuoteId}",
_ => $"DE-XXYYXX-{DateTime.Now:My}-{_quote.QuoteId}"
}
private async Task SelectQuoteOnChanged(FileChangedEventArgs e)
: $"DE-XXYYXX-{DateTime.Now:My}-{_quote.QuoteId}";
_quote.Description = "Gerät";
_ = TryToSaveQuote(_quote);
return _quote;
}
private async Task TryToSaveQuote(Quote _quote)
{
if (await genericController.UpdateAsync(_quote) > 0)
Debug.WriteLine("Speichern der Quote erfolgreich.");
else
Debug.WriteLine("Fehler beim Speichern der Quote!");
return;
}
private async Task SelectQuoteOnChanged(FileChangedEventArgs e)
{
try
{
try
foreach (IFileEntry? file in e.Files)
{
using MemoryStream stream = new();
await file.WriteToStreamAsync(stream);
_ = stream.Seek(0, SeekOrigin.Begin);
using StreamReader reader = new(stream);
string fileContent = await reader.ReadToEndAsync();
quote = QuoteHandling.ReadLineItems(quote, fileContent);
if (quote.Recipient?.Account?.AccountName != null) quote = hostingService.SetPath(quote);
if (quote.LineItems == null) return;
FileService.WriteQuoteToTsv(fileContent, quote);
//TODO Load all relevant CustomDescriptions upfront
for (int i = 0; i < quote.LineItems.Count; i++)
{
foreach (IFileEntry? file in e.Files)
newCustomDescription = await genericController.GetAsync<CustomDescription>(
newCustomDescription => newCustomDescription.ProductNumber.Equals(quote.LineItems[i].ProductNumber, StringComparison.Ordinal)
&& newCustomDescription.OptionNumber.Equals(quote.LineItems[i].OptionNumber, StringComparison.Ordinal)
);
if (newCustomDescription == null)
{
Console.WriteLine($"Keine CustomDescription für {quote.LineItems[i].ProductNumber}#{quote.LineItems[i].OptionNumber} verfügbar!");
suggestedCustomDescriptions = await SuggestCustomDescriptions(quote.LineItems[i]);
newCustomDescription = new()
{
using MemoryStream stream = new();
await file.WriteToStreamAsync(stream);
_ = stream.Seek(0, SeekOrigin.Begin);
using StreamReader reader = new(stream);
string fileContent = await reader.ReadToEndAsync();
quote = QuoteHandling.ReadLineItems(quote, fileContent);
if (quote.Recipient?.Account?.AccountName != null) quote = hostingService.SetPath(quote);
if (quote.LineItems == null) return;
FileService.WriteQuoteToTsv(fileContent, quote);
//TODO Load all relevant CustomDescriptions upfront
for (int i = 0; i < quote.LineItems.Count; i++)
{
newCustomDescription = await genericController.GetAsync<CustomDescription>(
newCustomDescription => newCustomDescription.ProductNumber.Equals(quote.LineItems[i].ProductNumber, StringComparison.Ordinal)
&& newCustomDescription.OptionNumber.Equals(quote.LineItems[i].OptionNumber, StringComparison.Ordinal)
);
if (newCustomDescription == null)
{
Console.WriteLine($"Keine CustomDescription für {quote.LineItems[i].ProductNumber}#{quote.LineItems[i].OptionNumber} verfügbar!");
suggestedCustomDescriptions = await SuggestCustomDescriptions(quote.LineItems[i]);
newCustomDescription = new()
{
ProductNumber = quote.LineItems[i].ProductNumber,
OptionNumber = quote.LineItems[i].OptionNumber,
Heading = quote.LineItems[i].SapShortDescription,
DescriptionText = quote.LineItems[i].SapLongDescription
};
//Show windows to edit new cD
await ShowCustomDescriptionModal();
//TODO need to wait for modal!
//Insert new CustomDescription to db
newCustomDescription.AccountId = 1;
_ = await genericController.InsertAsync(newCustomDescription);
}
//read cD form db to cleanup ChangeTracker
quote.LineItems[i].CustomDescription = await genericController.GetAsync<CustomDescription>(cD => cD.CustomDescriptionId.Equals(newCustomDescription.CustomDescriptionId));
}
}
ProductNumber = quote.LineItems[i].ProductNumber,
OptionNumber = quote.LineItems[i].OptionNumber,
Heading = quote.LineItems[i].SapShortDescription,
DescriptionText = quote.LineItems[i].SapLongDescription
};
//Show windows to edit new cD
await ShowCustomDescriptionModal();
//TODO need to wait for modal!
//Insert new CustomDescription to db
newCustomDescription.AccountId = 1;
_ = await genericController.InsertAsync(newCustomDescription);
}
//read cD form db to cleanup ChangeTracker
quote.LineItems[i].CustomDescription = await genericController.GetAsync<CustomDescription>(cD => cD.CustomDescriptionId.Equals(newCustomDescription.CustomDescriptionId));
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
finally
{
if (quote.Recipient != null) lineItemsNotReady = false;
StateHasChanged();
}
}
private async Task<List<CustomDescription>?> SuggestCustomDescriptions(LineItem lineItem)
{
//IList<CustomDescription>? fromProductNumber = await genericController.GetAllAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber, StringComparison.Ordinal));
IList<CustomDescription>? fromOptionNumber = new List<CustomDescription>();
if (lineItem.OptionNumber != "")
fromOptionNumber = await genericController.GetAllAsync<CustomDescription>(cD => cD.OptionNumber.Equals(lineItem.OptionNumber, StringComparison.Ordinal));
//if (fromOptionNumber == null && fromProductNumber == null) return null;
//if (fromOptionNumber == null) return fromProductNumber.ToList();
//if (fromProductNumber == null) return fromOptionNumber.ToList();
//return fromProductNumber.Union(fromOptionNumber).ToList();
return fromOptionNumber.ToList();
}
private static void SelectQuoteOnWritten(FileWrittenEventArgs e) =>
Console.WriteLine($"File: {e.File.Name} Position: {e.Position} Data: {Convert.ToBase64String(e.Data)}");
private static void SelectQuoteOnProgressed(FileProgressedEventArgs e) =>
Console.WriteLine($"File: {e.File.Name} Progress: {e.Percentage}");
private async Task OnSelectedContactChanged(Contact _selectedContact)
{
if (quote == null) return;
quote.Recipient = await genericController.GetAsync<Contact>(c => c.ContactId.Equals(_selectedContact.ContactId));
if (quote.Recipient != null)
//Read account seperatly to avoid new generation
quote.Recipient.Account = await genericController.GetAsync<Account>(a => a.AccountId.Equals(quote.Recipient.AccountId));
if (quote.LineItems != null && quote.Recipient != null && quote.Recipient.Account != null) lineItemsNotReady = false;
selectedContact = _selectedContact;
}
}
private async Task OnSave()
catch (Exception exc)
{
//HACK Try to avoid new generation of FKs
// quote.Recipient = new() { Account = new() };
// quote.SalesRep = new() { Account = new() };
// quote.LineItems = null;
if (await genericController.UpdateAsync(quote) > 0)
navigationManager.NavigateTo("Quotes/QuoteIndex");
Console.WriteLine(exc.Message);
}
private async Task OnCreateTex()
finally
{
isCreatingTex = true;
StringBuilder? tex = await QuoteHandling.CreateTexAsync(quote);
if (tex == null) return;
quote.Tex = tex.ToString();
if (quote.Tex == null) return;
await FileService.WriteTexFile(quote);
isCreatingTex = false;
Console.WriteLine("Tex file succesfull created.");
texNotReady = false;
if (quote.Recipient != null) lineItemsNotReady = false;
StateHasChanged();
}
private async Task OnCreatePdf()
}
private async Task<List<CustomDescription>?> SuggestCustomDescriptions(LineItem lineItem)
{
//IList<CustomDescription>? fromProductNumber = await genericController.GetAllAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber, StringComparison.Ordinal));
IList<CustomDescription>? fromOptionNumber = new List<CustomDescription>();
if (lineItem.OptionNumber != "")
fromOptionNumber = await genericController.GetAllAsync<CustomDescription>(cD => cD.OptionNumber.Equals(lineItem.OptionNumber, StringComparison.Ordinal));
//if (fromOptionNumber == null && fromProductNumber == null) return null;
//if (fromOptionNumber == null) return fromProductNumber.ToList();
//if (fromProductNumber == null) return fromOptionNumber.ToList();
//return fromProductNumber.Union(fromOptionNumber).ToList();
return fromOptionNumber.ToList();
}
private static void SelectQuoteOnWritten(FileWrittenEventArgs e) =>
Console.WriteLine($"File: {e.File.Name} Position: {e.Position} Data: {Convert.ToBase64String(e.Data)}");
private static void SelectQuoteOnProgressed(FileProgressedEventArgs e) =>
Console.WriteLine($"File: {e.File.Name} Progress: {e.Percentage}");
private async Task OnSelectedContactChanged(Contact _selectedContact)
{
if (quote == null) return;
quote.Recipient = await genericController.GetAsync<Contact>(c => c.ContactId.Equals(_selectedContact.ContactId));
if (quote.Recipient != null)
//Read account seperatly to avoid new generation
quote.Recipient.Account = await genericController.GetAsync<Account>(a => a.AccountId.Equals(quote.Recipient.AccountId));
if (quote.LineItems != null && quote.Recipient != null && quote.Recipient.Account != null) lineItemsNotReady = false;
selectedContact = _selectedContact;
}
private async Task OnSave()
{
//HACK Try to avoid new generation of FKs
// quote.Recipient = new() { Account = new() };
// quote.SalesRep = new() { Account = new() };
// quote.LineItems = null;
if (await genericController.UpdateAsync(quote) > 0)
navigationManager.NavigateTo("Quotes/QuoteIndex");
}
private async Task OnCreateTex()
{
isCreatingTex = true;
StringBuilder? tex = await QuoteHandling.CreateTexAsync(quote);
if (tex == null) return;
quote.Tex = tex.ToString();
if (quote.Tex == null) return;
await FileService.WriteTexFile(quote);
isCreatingTex = false;
Console.WriteLine("Tex file succesfull created.");
texNotReady = false;
}
private async Task OnCreatePdf()
{
isCreatingPdf = true;
if (await PdfService.CreatePdf(quote))
{
isCreatingPdf = true;
if (await PdfService.CreatePdf(quote))
{
pdfNotReady = false;
isCreatingPdf = false;
Console.WriteLine("PDF successfull written.");
url = HostingService.GetPdfUrl(quote);
Console.WriteLine($"URL to PDF is {url}");
}
pdfNotReady = false;
isCreatingPdf = false;
Console.WriteLine("PDF successfull written.");
url = HostingService.GetPdfUrl(quote);
Console.WriteLine($"URL to PDF is {url}");
}
}
private void OnOpenPdfApp() => PdfService.OpenPdfWithOkular(quote);
private void OnOpenPdfApp() => PdfService.OpenPdfWithOkular(quote);
private async Task OnOpenPdfNewTab() =>
await jSRuntime.InvokeVoidAsync("OpenNewTab", $"quotes/{url}");
private async Task OnOpenPdfNewTab() =>
await jSRuntime.InvokeVoidAsync("OpenNewTab", $"quotes/{url}");
private void OnDescriptionChanged(string description) => quote.Description = description;
private void OnDescriptionChanged(string description) => quote.Description = description;
private void OnQuotationNumberChanged(string quotationNumber) =>
quote.QuotationNumber = quotationNumber;
private void OnQuotationNumberChanged(string quotationNumber) =>
quote.QuotationNumber = quotationNumber;
private void OnValidForChanged(string validFor) => quote.ValidFor = byte.Parse(validFor);
private void OnValidForChanged(string validFor) => quote.ValidFor = byte.Parse(validFor);
private void OnVATChanged(string vat) => quote.Vat = float.Parse(vat);
private void OnVATChanged(string vat) => quote.Vat = float.Parse(vat);
private void OnIsPriceInformationChanged(bool isPriceInformation) =>
quote.IsPriceInformation = isPriceInformation;
private void OnIsPriceInformationChanged(bool isPriceInformation) =>
quote.IsPriceInformation = isPriceInformation;
private void OnShowBruttoChanged(bool onShowBrutto) => quote.ShowBrutto = onShowBrutto;
private void OnShowBruttoChanged(bool onShowBrutto) => quote.ShowBrutto = onShowBrutto;
private void OnShowDiscountsChanged(bool showDiscount) => quote.ShowDiscounts = showDiscount;
private void OnShowDiscountsChanged(bool showDiscount) => quote.ShowDiscounts = showDiscount;
private void OnShowSinglePricesChanged(bool showSinglePrices) =>
quote.ShowSinglePrices = showSinglePrices;
private void OnShowSinglePricesChanged(bool showSinglePrices) =>
quote.ShowSinglePrices = showSinglePrices;
private void OnSelectedLineItemChanged(LineItem _selectedLineItem) =>
selectedLineItem = _selectedLineItem;
private void OnSelectedLineItemChanged(LineItem _selectedLineItem) =>
selectedLineItem = _selectedLineItem;
private void OnWarrantyChanged(string warranty) => quote.Warranty = int.Parse(warranty);
private void OnWarrantyChanged(string warranty) => quote.Warranty = int.Parse(warranty);
private void OnCancel() => navigationManager.NavigateTo("Quotes/QuoteIndex");
private void OnCancel() => navigationManager.NavigateTo("Quotes/QuoteIndex");
}

@ -69,29 +69,7 @@ Sehr geehrter Herr Franke,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) Gerät.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item Isokratische Pumpe (\#1)
\begin{itemize}
\item Werkzeugsatz (\#2)
\item Säule (\#3)
\end{itemize}
\item Säulenthermostat (\#4)
\item Vialsampler (\#5)
\begin{itemize}
\item Probenteller für 6x11 2,0 ml Vials (\#6)
\end{itemize}
\item RI-Detektor (\#7)
\item UV/VIS-Detektor (\#8)
\begin{itemize}
\item Standardflusszelle (\#9)
\end{itemize}
\item Agilent GPC/SEC-Software (\#10)
\item PLgel 10um 500A 300 x 7.5 mm (\#11)
\item PLgel 10um 100A 300 x 7.5 mm (\#12)
\item 1260 Infinity II HPLC-System (\#13)
\begin{itemize}
\item Einweisung und First Run Assist (\#14)
\item Silber-Vertrag (Laufzeit: 1 Jahr) (\#15)
\end{itemize}
\item 8890 GC (\#1)
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
@ -102,21 +80,7 @@ Mit freundlichen Grüßen\\
\begin{longtable}
{| cp{0.595\textwidth} crr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Discount} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{Isokratische Pumpe} (G7110B)\newline 1260 Infinity II isokratische Pumpe, Maximaldruck 600 bar.Inkl. vergünstigter Säule, Verbindungskapillaren, Lösemittelwanne und CAN-Kabel.\newline Listenpreis: \SI{8908}{\sieuro}&1&\SI{45}{\%}&\SI{4899,4}{\sieuro}\\
2 &\textbf{Werkzeugsatz} (G7110B\#001)\newline HPLC System Tool Kit, für Agilent 1260/1290 Infinity II LC.\newline Listenpreis: \SI{400}{\sieuro}&1&\SI{45}{\%}&\SI{220}{\sieuro}\\
3 &\textbf{Säule} (G7110B\#094)\newline InfinityLab Poroshell 120 EC-C18 3.0 x 150 mm, 2.7 µm.\newline Listenpreis: \SI{1}{\sieuro}&1&\SI{45}{\%}&\SI{0,55}{\sieuro}\\
4 &\textbf{Säulenthermostat} (G7116A)\newline 1260 Infinity II Thermostat für bis zu vier 30 cm Säulen, Temperaturbereich: 10° unter Raumtemperatur (min. 4 °C) bis max. 85 °C, inkl. Säulenidentifikations-Kit. Ventilantrieb optional.\newline Listenpreis: \SI{6494}{\sieuro}&1&\SI{45}{\%}&\SI{3571,7}{\sieuro}\\
5 &\textbf{Vialsampler} (G7129A)\newline 1260 Infinity II automatischer Flüssigprobengeber zur Verwendung bei bis zu 600 bar. Mit integriertem Nadelspülanschluss zur Minimierung der Verschleppung, 100 µl Dosiereinheit und 100 µl Probenschleife. Inklusive Gerätetreiber für ein LC-System (2D-UV).\newline Listenpreis: \SI{19905}{\sieuro}&1&\SI{45}{\%}&\SI{10947,75}{\sieuro}\\
6 &\textbf{Probenteller für 6x11 2,0 ml Vials} (G7129A\#010)\newline \newline Listenpreis: \SI{375}{\sieuro}&1&\SI{45}{\%}&\SI{206,25}{\sieuro}\\
7 &\textbf{RI-Detektor} (G7162A)\newline Agilent 1260 Infinity II Brechungsindexdetektor. \newline Datenrate bis zu 72 Hz, mit integrierter 8 µl Flusszelle.\newline Listenpreis: \SI{13989}{\sieuro}&1&\SI{45}{\%}&\SI{7693,95}{\sieuro}\\
8 &\textbf{UV/VIS-Detektor} (G7114A)\newline 1260 Infinity II variabler Wellenlängendetektor (190 600 nm). Für schnelle programmierbare Einzel- (bis zu 120 Hz) und Doppelwellenlängen-Detektion. RFID-Tags für Durchflusszellen und UV-Lampe.\newline Listenpreis: \SI{9307}{\sieuro}&1&\SI{45}{\%}&\SI{5118,85}{\sieuro}\\
9 &\textbf{Standardflusszelle} (G7114A\#018)\newline Standarddurchflusszelle VWD aus Edelstahl mit RFID-Tag zur Identifizierung, 10 mm, 14 µl.\newline Listenpreis: \SI{1612}{\sieuro}&1&\SI{45}{\%}&\SI{886,6}{\sieuro}\\
10 &\textbf{Agilent GPC/SEC-Software} (G7850AA)\newline Agilent GPC/SEC-Software für GPC-Berechnungen. Nur Datenanalyse. Kann mit Gerätesteuerung (G7854AA) und Multi-Detektor-GPC (G7852AA) aufgerüstet werden.\newline Listenpreis: \SI{14789}{\sieuro}&1&\SI{45}{\%}&\SI{8133,95}{\sieuro}\\
11 &\textbf{PLgel 10um 500A 300 x 7.5 mm} (PL1110-6125)\newline \newline Listenpreis: \SI{1806}{\sieuro}&2&\SI{45}{\%}&\SI{1986,6}{\sieuro}\\
12 &\textbf{PLgel 10um 100A 300 x 7.5 mm} (PL1110-6120)\newline \newline Listenpreis: \SI{1806}{\sieuro}&2&\SI{45}{\%}&\SI{1986,6}{\sieuro}\\
13 &\textbf{1260 Infinity II HPLC-System} (SYS-LC-1260II)\newline \newline Listenpreis: \SI{0}{\sieuro}&1&\SI{20}{\%}&\SI{0}{\sieuro}\\
14 &\textbf{Einweisung und First Run Assist} (SYS-LC-1260II\#2C9)\newline Einweisung für neue Anwender und zusätzlich Unterstützung bei der Einrichtung der ersten Methode (erfordert Prüfung der Methode vorab durch Agilent).\newline Listenpreis: \SI{1034}{\sieuro}&1&\SI{20}{\%}&\SI{827,2}{\sieuro}\\
15 &\textbf{Silber-Wartungsvertrag} (SYS-LC-1260II\#8R1)\newline Laufzeit: 1 Jahr.\newline Listenpreis: \SI{2688}{\sieuro}&1&\SI{20}{\%}&\SI{2150,4}{\sieuro}\\
1 &\textbf{8890 GC} (G3560A)\newline Wärmeleitfähigkeitsdetektor mit EPC zum Einbau in ein bestehendes 8890 GC.\newline Listenpreis: \SI{6994}{\sieuro}&1&\SI{20}{\%}&\SI{5595.2}{\sieuro}\\
\hline
\end{longtable}
\end{center}
@ -126,11 +90,11 @@ Mit freundlichen Grüßen\\
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{48629,80}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{1458,894}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{50088,694}{\sieuro}\\
\textbf{Umsatzsteuer (19\%)} & \SI{9516,85186}{\sieuro}\\
\textbf{Gesamtsumme brutto} & \SI{59605,54586}{\sieuro}\\
\textbf{Summe netto} & \SI{5595.2}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{167.856}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{5763.056}{\sieuro}\\
\textbf{Umsatzsteuer (19\%)} & \SI{1094.98064}{\sieuro}\\
\textbf{Gesamtsumme brutto} & \SI{6858.03664}{\sieuro}\\
\hline
\end{tabular}

@ -1,16 +1,2 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G7110B 29 1260 Infinity II isokratische Pumpe 1 8908 0 0 45 4899.4 4899.4 45 0 0 0 ISL100P1 Pumps
2 G7110B 001 29 HPLC System Tool Kit 1260 Infinity II 1 400 0 0 45 220 220 45 0 0 0
3 G7110B 094 29 Poroshell 120 EC-C18 3,0x150mm, 2,7um 1 1 0 0 45 0.55 0.55 45 0 0 0
4 G7116A 29 1260 Infinity II Therm. f. mehr. Saeulen 1 6494 0 0 45 3571.7 3571.7 45 0 0 0 ISL100LC1 LC Hardware
5 G7129A 29 1260 Inf. II Fluessigprobengeber 1 19905 0 0 45 10947.75 10947.75 45 0 0 0 ISL100A1 Autosamplers
6 G7129A 010 29 Standard-Schublade (6x11 Probenflaschen) 1 375 0 0 45 206.25 206.25 45 0 0 0
7 G7162A 29 1260 Infinity II Brechungsindexdetektor 1 13989 0 0 45 7693.95 7693.95 45 0 0 0 ISL100D1 Detectors
8 G7114A 29 1260 Infinity II VW-Detektor 1 9307 0 0 45 5118.85 5118.85 45 0 0 0 ISL100D1 Detectors
9 G7114A 018 29 Standarddurchflusszelle VWD 1 1612 0 0 45 886.6 886.6 45 0 0 0
10 G7850AA 29 Agilent GPC/SEC-Software 1 14789 0 0 45 8133.95 8133.95 45 0 0 0 ISL230L230 Special LC
11 PL1110-6125 BC PLgel 10um 500A 300 x 7.5 mm 2 1806 0 0 45 993.3 1986.6 45 0 0 0 CSCV27C04L GPC/SEC - PLgel
12 PL1110-6120 BC PLgel 10um 100A 300 x 7.5 mm 2 1806 0 0 45 993.3 1986.6 45 0 0 0 CSCV27C04L GPC/SEC - PLgel
13 SYS-LC-1260II 74 LC 1260 Infinity II System 1 0 0 0 20 0 0 20 0 0 0 TSSYS0SYLC Service Systems - Liquid Chromatography
14 SYS-LC-1260II 2C9 74 Einweisung zum ersten Methodenlauf 1 1034 0 0 20 827.2 827.2 20 0 0 0 TSSTRN Training Services
15 SYS-LC-1260II 8R1 74 CrossLab Silver - 1J, kompl. 1 2688 0 0 20 2150.4 2150.4 20 0 0 0 TSSYS1 Serviced As Systems - 1 YR
1 G3560A AZ Waermeleitfaehigkeitsdetektor mit EPC 1 6994 0 0 20 5595.2 5595.2 20 0 0 0 ISG100G190 GC Accessories

1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G7110B G3560A 29 AZ 1260 Infinity II isokratische Pumpe Waermeleitfaehigkeitsdetektor mit EPC 1 8908 6994 0 0 45 20 4899.4 5595.2 4899.4 5595.2 45 20 0 0 0 ISL100P1 ISG100G190 Pumps GC Accessories
2 G7110B 001 29 HPLC System Tool Kit 1260 Infinity II 1 400 0 0 45 220 220 45 0 0 0
3 G7110B 094 29 Poroshell 120 EC-C18 3,0x150mm, 2,7um 1 1 0 0 45 0.55 0.55 45 0 0 0
4 G7116A 29 1260 Infinity II Therm. f. mehr. Saeulen 1 6494 0 0 45 3571.7 3571.7 45 0 0 0 ISL100LC1 LC Hardware
5 G7129A 29 1260 Inf. II Fluessigprobengeber 1 19905 0 0 45 10947.75 10947.75 45 0 0 0 ISL100A1 Autosamplers
6 G7129A 010 29 Standard-Schublade (6x11 Probenflaschen) 1 375 0 0 45 206.25 206.25 45 0 0 0
7 G7162A 29 1260 Infinity II Brechungsindexdetektor 1 13989 0 0 45 7693.95 7693.95 45 0 0 0 ISL100D1 Detectors
8 G7114A 29 1260 Infinity II VW-Detektor 1 9307 0 0 45 5118.85 5118.85 45 0 0 0 ISL100D1 Detectors
9 G7114A 018 29 Standarddurchflusszelle VWD 1 1612 0 0 45 886.6 886.6 45 0 0 0
10 G7850AA 29 Agilent GPC/SEC-Software 1 14789 0 0 45 8133.95 8133.95 45 0 0 0 ISL230L230 Special LC
11 PL1110-6125 BC PLgel 10um 500A 300 x 7.5 mm 2 1806 0 0 45 993.3 1986.6 45 0 0 0 CSCV27C04L GPC/SEC - PLgel
12 PL1110-6120 BC PLgel 10um 100A 300 x 7.5 mm 2 1806 0 0 45 993.3 1986.6 45 0 0 0 CSCV27C04L GPC/SEC - PLgel
13 SYS-LC-1260II 74 LC 1260 Infinity II System 1 0 0 0 20 0 0 20 0 0 0 TSSYS0SYLC Service Systems - Liquid Chromatography
14 SYS-LC-1260II 2C9 74 Einweisung zum ersten Methodenlauf 1 1034 0 0 20 827.2 827.2 20 0 0 0 TSSTRN Training Services
15 SYS-LC-1260II 8R1 74 CrossLab Silver - 1J, kompl. 1 2688 0 0 20 2150.4 2150.4 20 0 0 0 TSSYS1 Serviced As Systems - 1 YR