diff --git a/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs b/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs index 4e13a77..cc94148 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs @@ -532,7 +532,7 @@ namespace Gremlin_BlazorServer.Data.DBClasses } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex); + Debug.WriteLine(ex); } } } @@ -1906,7 +1906,7 @@ namespace Gremlin_BlazorServer.Data.DBClasses } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex, "Unhandled Error in Function 'ParseWeight'"); + Debug.WriteLine(ex, "Unhandled Error in Function 'ParseWeight'"); return 0; } diff --git a/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs b/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs index 96a7c00..4c9b3b5 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/GenericImporter.cs @@ -600,7 +600,7 @@ namespace Gremlin_BlazorServer.Data.DBClasses } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex, "Unhandled Error in Function 'ParseWeight'"); + Debug.WriteLine(ex, "Unhandled Error in Function 'ParseWeight'"); return 0; } diff --git a/Gremlin_BlazorServer/Data/DBClasses/GremlinContext.cs b/Gremlin_BlazorServer/Data/DBClasses/GremlinContext.cs index c8cbdfb..55f3950 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/GremlinContext.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/GremlinContext.cs @@ -1,6 +1,7 @@ using Gremlin_BlazorServer.Data.EntityClasses; using Gremlin_BlazorServer.Utilities; using Microsoft.EntityFrameworkCore; +using System.Diagnostics; namespace Gremlin_BlazorServer.Data.DBClasses { @@ -46,7 +47,7 @@ namespace Gremlin_BlazorServer.Data.DBClasses } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex); + Debug.WriteLine(ex); //ChooseDB chooseDB = new(); //_ = chooseDB.ShowDialog(); OnConfiguring(optionsBuilder); diff --git a/Gremlin_BlazorServer/Data/EntityClasses/Quote.cs b/Gremlin_BlazorServer/Data/EntityClasses/Quote.cs index 2481d34..9947cb3 100644 --- a/Gremlin_BlazorServer/Data/EntityClasses/Quote.cs +++ b/Gremlin_BlazorServer/Data/EntityClasses/Quote.cs @@ -1,6 +1,4 @@ -using System.Data; - -namespace Gremlin_BlazorServer.Data.EntityClasses +namespace Gremlin_BlazorServer.Data.EntityClasses { public class Quote : IMetadata { @@ -28,6 +26,13 @@ namespace Gremlin_BlazorServer.Data.EntityClasses public bool QuoteContains3PP { get; set; } public bool QuoteContainsRB { get; set; } public string QuoteTemplate { get; set; } = string.Empty; + + //new properties + public int Warranty { get; set; } = 12; + public decimal TotalFreightOnly { get; set; } + public decimal TotalFreight { get; set; } + public decimal TotalVAT { get; set; } + public decimal Freight { get; set; } //metadata: public DateTime DataCreationDate { get; set; } = DateTime.Now; @@ -38,5 +43,23 @@ namespace Gremlin_BlazorServer.Data.EntityClasses public DateTime DataValidUntil { get; set; } = DateTime.MaxValue; public string DataVersionComment { get; set; } = string.Empty; public uint DataVersionNumber { get; set; } + + //constructor + public Quote() { } + + public Quote(Contact salesRep, bool randomQuoteNumber = true) + { + SalesRep = salesRep; + if (randomQuoteNumber) + { + Random random = new(); + QuotationNumber = 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)}", + }; + } + } } } diff --git a/Gremlin_BlazorServer/Gremlin_BlazorServer.csproj b/Gremlin_BlazorServer/Gremlin_BlazorServer.csproj index c871fac..a483cd8 100644 --- a/Gremlin_BlazorServer/Gremlin_BlazorServer.csproj +++ b/Gremlin_BlazorServer/Gremlin_BlazorServer.csproj @@ -10,6 +10,7 @@ + diff --git a/Gremlin_BlazorServer/Pages/Quotes/Add.razor b/Gremlin_BlazorServer/Pages/Quotes/Add.razor new file mode 100644 index 0000000..82dc05a --- /dev/null +++ b/Gremlin_BlazorServer/Pages/Quotes/Add.razor @@ -0,0 +1,141 @@ +@page "/Quotes/Add" + +@using Blazorise.Components +@using Gremlin_BlazorServer.Data.EntityClasses; +@using Gremlin_BlazorServer.Services; +@using System.Diagnostics; + +@inject QuoteService quoteService +@inject ContactService contactService +@inject AccountService accountService +@inject NavigationManager navigationManager +@inject ClipboardService clipboardService + +

Create New Quote

+
+ +

Quote Details

+ + + SalesRep: + @quote.SalesRep.LastName + + + QuotationDate: + @quote.QuotationDate + + + QuotationNumber: + + + + ValidFor: + + + + +

Recipient

+ + + + + + + + + + + + @selectedRecipient.FirstName @selectedRecipient.LastName + @correspondingAccount.AccountName + @correspondingAccount.Street + @correspondingAccount.ZIP @correspondingAccount.City + + +

Line Items

+@if (quote.LineItems.Count != 0) +{ + + + + + + + + + +} + + + + + +@code { + IEnumerable contacts = new List(); + Quote quote = new(); + Contact selectedRecipient = new(); + Account correspondingAccount = new(); + + protected override async Task OnInitializedAsync() + { + contacts = await contactService.GetAllContactsAsync(); + await OnSelectedRowChanged(contacts.FirstOrDefault() ?? new()); + + Contact salesRep = await contactService.GetContactAsync("Woitschetzki"); + quote = new(salesRep, true); + + await base.OnInitializedAsync(); + } + + protected async Task OnSelectedRowChanged(Contact contact) + { + selectedRecipient = contact; + quote.Recipient = contact; + correspondingAccount = await accountService.GetAccountAsync(contact.AccountId) ?? new Account(); + } + + protected async void CreateQuote() + { + quote.DataModificationByUser = "Gremlin_BlazorServer"; + + if (await quoteService.InsertQuoteAsync(quote)) + { + navigationManager.NavigateTo("Quotes/Index"); + } + } + + private async Task ImportLineItems() + { + try + { + string clipboard = await clipboardService.ReadTextAsync(); + + if (clipboard != "") + { + quote = quoteService.ReadLineItems(quote, clipboard); + } + } + catch + { + Debug.WriteLine("Cannot read from clipboard"); + } + } + + void Cancel() => navigationManager.NavigateTo("Quotes/Index"); +} \ No newline at end of file diff --git a/Gremlin_BlazorServer/Pages/Quotes/Index.razor b/Gremlin_BlazorServer/Pages/Quotes/Index.razor index b196e33..c4ea540 100644 --- a/Gremlin_BlazorServer/Pages/Quotes/Index.razor +++ b/Gremlin_BlazorServer/Pages/Quotes/Index.razor @@ -9,61 +9,52 @@

Quotes

-@if (quotes is null) -{ -

Loading... !

-} -else -{ -

Es wurden @quotes.Count Quotes gefunden.

- @if (quotes != null) - { - - - - - - - - - - - + + + -

LineItems in Quote @selectedQuote.QuotationNumber

- - - - - - - - - - - - } -} + + + + + + + + + + + + +

@selectedQuote.QuotationNumber

+ + + + + + + + + + + @code { public string searchQuote = ""; @@ -74,36 +65,17 @@ else List quotes = new(); - protected override async Task OnInitializedAsync() { quotes = await Task.Run(() => quoteService.GetAllQuotesAsync()); - selectedQuote = quotes.LastOrDefault()!; - await Task.Run(() => SelectedRowChanged()); - } - - private async Task SelectedRowChanged() => lineItemsInSelectedQuote = await quoteService.GetLineItemsAsync(selectedQuote); + await OnSelectedQuoteChanged(quotes.LastOrDefault() ?? new()); - private int totalQuotes; + await base.OnInitializedAsync(); + } - private async Task OnReadData(DataGridReadDataEventArgs eventArgs) + private async Task OnSelectedQuoteChanged(Quote selectedQuote) { - if (!eventArgs.CancellationToken.IsCancellationRequested) - { - List? response = null; - - if (eventArgs.ReadDataMode is DataGridReadDataMode.Virtualize) - response = (await quoteService.GetAllQuotesAsync()).Skip(eventArgs.VirtualizeOffset).Take(eventArgs.VirtualizeCount).ToList(); - else if (eventArgs.ReadDataMode is DataGridReadDataMode.Paging) - response = (await quoteService.GetAllQuotesAsync()).Skip((eventArgs.Page - 1) * eventArgs.PageSize).Take(eventArgs.PageSize).ToList(); - else - throw new Exception("Unhandled ReadDataMode"); - - if (!eventArgs.CancellationToken.IsCancellationRequested) - { - totalQuotes = (await quoteService.GetAllQuotesAsync()).Count; - quotes = new List(response); // an actual data for the current page - } - } + lineItemsInSelectedQuote = await quoteService.GetLineItemsAsync(selectedQuote); + this.selectedQuote = selectedQuote; } } \ No newline at end of file diff --git a/Gremlin_BlazorServer/Program.cs b/Gremlin_BlazorServer/Program.cs index 8ef55bc..d576d86 100644 --- a/Gremlin_BlazorServer/Program.cs +++ b/Gremlin_BlazorServer/Program.cs @@ -20,6 +20,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddBlazorise(options => { options.Immediate = true; }).AddBootstrapProviders().AddFontAwesomeIcons(); builder.Services.AddOptions(); diff --git a/Gremlin_BlazorServer/Services/ClipboardService.cs b/Gremlin_BlazorServer/Services/ClipboardService.cs new file mode 100644 index 0000000..64777b2 --- /dev/null +++ b/Gremlin_BlazorServer/Services/ClipboardService.cs @@ -0,0 +1,16 @@ +namespace Gremlin_BlazorServer.Services +{ + using System.Threading.Tasks; + using Microsoft.JSInterop; + + public sealed class ClipboardService + { + private readonly IJSRuntime jsRuntime; + + public ClipboardService(IJSRuntime jsRuntime) => this.jsRuntime = jsRuntime; + + public ValueTask ReadTextAsync() => jsRuntime.InvokeAsync("navigator.clipboard.readText"); + + public ValueTask WriteTextAsync(string text) => jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text); + } +} diff --git a/Gremlin_BlazorServer/Services/ContactService.cs b/Gremlin_BlazorServer/Services/ContactService.cs index 293ca18..478b41e 100644 --- a/Gremlin_BlazorServer/Services/ContactService.cs +++ b/Gremlin_BlazorServer/Services/ContactService.cs @@ -18,11 +18,11 @@ namespace Gremlin_BlazorServer.Services public async Task> GetAllContactsAsync() => await gremlinContext.Contacts.ToListAsync(); - public async Task InsertContactAsync(Contact Contact) + public async Task InsertContactAsync(Contact contact) { try { - _ = await gremlinContext.Contacts.AddAsync(Contact); + _ = await gremlinContext.Contacts.AddAsync(contact); _ = await gremlinContext.SaveChangesAsync(); return true; } @@ -35,8 +35,14 @@ namespace Gremlin_BlazorServer.Services public async Task GetContactAsync(uint contactId) { - Contact? contact = await gremlinContext.Contacts.FirstOrDefaultAsync(Contact => Contact.ContactId.Equals(contactId)); - return contact == null ? new Contact() : contact; + Contact? contact = await gremlinContext.Contacts.FirstOrDefaultAsync(c => c.ContactId.Equals(contactId)); + return contact ?? new Contact(); + } + + public async Task GetContactAsync(string lastName) + { + Contact? contact = await gremlinContext.Contacts.FirstOrDefaultAsync(c => c.LastName.Equals(lastName)); + return contact ?? new Contact(); } public async Task UpdateContactAsync(Contact contact) diff --git a/Gremlin_BlazorServer/Services/QuoteService.cs b/Gremlin_BlazorServer/Services/QuoteService.cs index 51b2a60..0d09a4c 100644 --- a/Gremlin_BlazorServer/Services/QuoteService.cs +++ b/Gremlin_BlazorServer/Services/QuoteService.cs @@ -1,6 +1,8 @@ using Gremlin_BlazorServer.Data.DBClasses; using Gremlin_BlazorServer.Data.EntityClasses; +using Gremlin_BlazorServer.Utilities; using Microsoft.EntityFrameworkCore; +using System.Collections.ObjectModel; using System.Diagnostics; namespace Gremlin_BlazorServer.Services @@ -50,5 +52,205 @@ namespace Gremlin_BlazorServer.Services _ = await gremlinContext.SaveChangesAsync(); return true; } + + //FromWindowsGremlin + + public Quote ReadLineItems(Quote quote, string clipboard) + { + quote = ResetTotals(quote); + + quote.LineItems = ReadLineItemsFromClipboard(clipboard); + + quote.QuoteContains3PP = DoesContains(quote, "3PP"); + quote.QuoteContainsRB = DoesContains(quote, "RB"); + + quote.TotalListprice = GetTotal(quote, "TotalListprice"); + quote.TotalDiscount = GetTotal(quote, "AverageDiscount"); + quote.TotalNet = GetTotal(quote, "TotalNet"); + + quote.ValidFor = CheckForAcademic(quote.Recipient); + + foreach (LineItem lineItem in quote.LineItems) + { + if (lineItem.OptionNumber != null) + { + // normale Gewährleistungsverlängerung + if (lineItem.OptionNumber.StartsWith("8D")) + { + quote.Warranty = int.Parse(lineItem.OptionNumber.Last().ToString()) * 12; + } + + //24 Monate Gewährleistung für Akademia + if (lineItem.OptionNumber == "9EC") + { + quote.Warranty = 24; + } + + //36 Monate Gewährleistung für Akademia + if (lineItem.OptionNumber == "9CC") + { + quote.Warranty = 36; + } + } + + //AddToTotal + quote.TotalListprice += lineItem.ListPrice; + quote.TotalNet += lineItem.Total; + } + + quote = CalculateTotals(quote); + + return quote; + } + + private static List ReadLineItemsFromClipboard(string clipboard) + { + //Zeilen aufteilen + IEnumerable clipboardLines = clipboard.Split(Environment.NewLine.ToCharArray()); + List clipboardList = new(); + + foreach (string clipboardLine in clipboardLines) + { + if (clipboardLine != "") + { + //Nach Trennzeichen trennen + string[] clipboardLineSplit = clipboardLine.Split('\t'); + clipboardList.Add(clipboardLineSplit); + } + } + + return ParseClipboardList(clipboardList); + } + + private static List ParseClipboardList(List lineItemStrings) + { + List lineItems = new(); + int countError = 0; + int countEmpty = 0; + + foreach (string[] lineItemString in lineItemStrings) + { + //Anzahl an Spalten entspricht Clipboard + if (lineItemString.Length == 19) + { + //Header ignorieren + if (lineItemString[0] == "#") + { + countEmpty++; + continue; + } + + //Dateiinhalt in Klasse schreiben + LineItem lineItem = new() + { + Position = ushort.Parse(lineItemString[0]), + ProductNumber = lineItemString[1], + OptionNumber = lineItemString[2], + ProductLine = lineItemString[3], + SapShortDescription = lineItemString[4], + Amount = ushort.Parse(lineItemString[5]), + ListPrice = decimal.Parse(lineItemString[6]), + TotalDiscount = decimal.Parse(lineItemString[9]), + NetPrice = decimal.Parse(lineItemString[10]), + Total = decimal.Parse(lineItemString[11]), + SalesDiscount = decimal.Parse(lineItemString[12]), + ContractualDiscount = decimal.Parse(lineItemString[13]), + PromotionalDiscount = decimal.Parse(lineItemString[14]), + DemoDiscount = decimal.Parse(lineItemString[15]) + }; + + lineItems.Add(lineItem); + } + else + { + Debug.WriteLine("Angebot konnte nicht eingelesen werden!"); + countError++; + continue; + } + } + return lineItems; + } + + private static byte CheckForAcademic(Contact recipient) + { + return (byte)(recipient == null || recipient.Account.AccountType.AccountTypeCode == null ? 0 : recipient.Account.AccountType.AccountTypeCode.StartsWith("N") ? 90 : 60); + } + + private static decimal GetFreight(decimal net, decimal freight) + { + decimal freightNet = net * freight / 100; + return freightNet < 3000 ? freightNet : 3000; + } + + private static Quote CalculateTotals(Quote quote) + { + quote.TotalFreightOnly = GetFreight(quote.TotalNet, quote.Freight); + quote.TotalFreight = quote.TotalNet + quote.TotalFreightOnly; + quote.TotalVAT = quote.TotalFreight * Convert.ToDecimal(quote.VAT) / 100; + quote.TotalGross = quote.TotalFreight * (100 + Convert.ToDecimal(quote.VAT)) / 100; + return quote; + } + + private static Quote ResetTotals(Quote quote) + { + quote.TotalListprice = 0; + quote.TotalNet = 0; + quote.TotalFreightOnly = 0; + quote.TotalFreight = 0; + quote.TotalVAT = 0; + quote.TotalGross = 0; + + return quote; + } + + private static decimal GetTotal(Quote quote, string type) + { + decimal total = 0; + + foreach (LineItem lineItem in quote.LineItems) + { + switch (type) + { + case "TotalListprice": + total += lineItem.ListPrice; + break; + case "TotalNet": + total += lineItem.NetPrice; + break; + case "AverageDiscount": + total += lineItem.TotalDiscount; + break; + } + } + + if (type == "AverageDiscount" & quote.LineItems.Count != 0) { total /= quote.LineItems.Count; } + + return total; + } + + private static bool DoesContains(Quote quote, string type) + { + foreach (LineItem lineItem in quote.LineItems) + { + switch (type) + { + case "3PP": + if (lineItem.ProductLine == "3PP") + { + Debug.WriteLine($"Quote containts 3PP with ProductNumber {lineItem.ProductNumber}"); + return true; + } + break; + case "RB": + if (lineItem.ProductLine == "RB" & lineItem.ProductNumber != "R2005A") + { + Debug.WriteLine($"Quote containts RB with ProductNumber {lineItem.ProductNumber}"); + return true; + } + break; + } + } + return false; + } } -} +} \ No newline at end of file diff --git a/Gremlin_BlazorServer/Utilities/ErrorHandler.cs b/Gremlin_BlazorServer/Utilities/ErrorHandler.cs deleted file mode 100644 index 77aa2a2..0000000 --- a/Gremlin_BlazorServer/Utilities/ErrorHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Diagnostics; - -namespace Gremlin_BlazorServer.Utilities -{ - internal class ErrorHandler - { - public static void ShowErrorInMessageBox(Exception ex) - { - Debug.WriteLine(ex.Message); - } - - public static void ShowErrorInMessageBox(Exception ex, string title) - { - Debug.WriteLine(message: $"{title}: {ex.Message}"); - } - - public static void ShowErrorInDebug(Exception ex) - { - Debug.WriteLine(ex.Message); - } - - public static void ShowErrorMessage(string message) - { - Debug.WriteLine(message, "Error"); - } - - public static void ShowInfoMessage(string message) - { - Debug.WriteLine(message, "Info"); - } - } -} diff --git a/Gremlin_BlazorServer/Utilities/FileIO.cs b/Gremlin_BlazorServer/Utilities/FileIO.cs index 829cdda..c0fb1f0 100644 --- a/Gremlin_BlazorServer/Utilities/FileIO.cs +++ b/Gremlin_BlazorServer/Utilities/FileIO.cs @@ -53,7 +53,7 @@ namespace Gremlin_BlazorServer.Utilities } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex); + Debug.WriteLine(ex); } } diff --git a/Gremlin_BlazorServer/Utilities/PDFHandler.cs b/Gremlin_BlazorServer/Utilities/PDFHandler.cs index 44707b0..1537eec 100644 --- a/Gremlin_BlazorServer/Utilities/PDFHandler.cs +++ b/Gremlin_BlazorServer/Utilities/PDFHandler.cs @@ -1,4 +1,6 @@ -namespace Gremlin_BlazorServer.Utilities +using System.Diagnostics; + +namespace Gremlin_BlazorServer.Utilities { internal class PDFHandler { @@ -25,7 +27,7 @@ // } // catch (Exception ex) // { - // ErrorHandler.ShowErrorInMessageBox(ex); + // Debug.WriteLine(ex); // return false; // } // } @@ -45,7 +47,7 @@ } catch (Exception ex) { - ErrorHandler.ShowErrorInMessageBox(ex); + Debug.WriteLine(ex); } } } @@ -68,7 +70,7 @@ // } // catch (Exception ex) // { - // ErrorHandler.ShowErrorInMessageBox(ex); + // Debug.WriteLine(ex); // } // process.WaitForExit(); @@ -89,7 +91,7 @@ // } // catch (Exception ex) // { - // ErrorHandler.ShowErrorInMessageBox(ex); + // Debug.WriteLine(ex); // return false; // } // }