Clipboard paste

pull/1/head
DJh2o2 2023-04-13 11:32:37 +07:00
parent aec5c8fcae
commit 717a42f63b
6 changed files with 376 additions and 391 deletions

@ -20,27 +20,28 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazorise.Bootstrap" Version="1.2.1" />
<PackageReference Include="Blazorise.Components" Version="1.2.1" />
<PackageReference Include="Blazorise.DataGrid" Version="1.2.1" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.2.1" />
<PackageReference Include="Blazorise.Icons.Material" Version="1.2.1" />
<PackageReference Include="Blazorise.LoadingIndicator" Version="1.2.1" />
<PackageReference Include="Blazorise.Material" Version="1.2.1" />
<PackageReference Include="Blazorise.Bootstrap" Version="1.2.2" />
<PackageReference Include="Blazorise.Components" Version="1.2.2" />
<PackageReference Include="Blazorise.DataGrid" Version="1.2.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.2.2" />
<PackageReference Include="Blazorise.Icons.Material" Version="1.2.2" />
<PackageReference Include="Blazorise.LoadingIndicator" Version="1.2.2" />
<PackageReference Include="Blazorise.Material" Version="1.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0-preview.3.23177.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.1.23112.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.0-preview.1.23112.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.1.23111.4">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.3.23177.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.0-preview.3.23177.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0-preview.3.23174.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.1.23111.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.1.23111.4">
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.3.23174.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.3.23174.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0-preview.1.23117.2" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0-preview.3.23206.5" />
<PackageReference Include="MySql.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="MySqlConnector" Version="2.3.0-beta.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />

@ -2,14 +2,13 @@
@using Gremlin_BlazorServer.Services
@using Gremlin_BlazorServer.Data.EntityClasses
@using System.Globalization
@using System.Security.Claims;
@using System.Text;
@inject GenericController GenericController
@inject NavigationManager NavigationManager
@inject QuoteHandling QuoteHandling
@inject HostingService HostingService
@inject IJSRuntime JsRuntime
@inject ILoadingIndicatorService ApplicationLoadingIndicatorService
@* @inject ILoadingIndicatorService ApplicationLoadingIndicatorService *@
<AuthorizeView>
<Authorized>
@ -23,7 +22,7 @@
<DataGrid TItem="Contact"
Data="@contacts"
SelectedRow="@selectedContact"
SelectedRowChanged="@OnSelectedContactChanged"
SelectedRowChanged="@SelectedContactChanged"
Bordered Hoverable Filterable Striped ShowPager Responsive Sortable>
<DataGridCommandColumn/>
@ -39,8 +38,6 @@
</Paragraph>
</Div>
@if (quote is not null)
{
<Div Margin="Margin.Is3"
Border="Border.Dark.OnAll"
Padding="Padding.Is3"
@ -94,8 +91,7 @@
</Field>
</Column>
@if (quote?.Recipient?.Account is not null)
{
@if (quote?.Recipient?.Account is not null) {
<Column ColumnSize="ColumnSize.Is3">
<Field>
<FieldLabel ColumnSize="ColumnSize.Is4">FirstName</FieldLabel>
@ -172,7 +168,7 @@
</Row>
</Paragraph>
</Div>
}
@if (quote?.LineItems is not null) {
<Div Margin="Margin.Is3"
@ -239,7 +235,10 @@
Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40">
<Heading Size="HeadingSize.Is5">Upload PriceSurfer Quote </Heading>
<Heading Size="HeadingSize.Is5">Load PriceSurfer quote</Heading>
<Paragraph>
<Button Color="Color.Primary" Clicked="@GetClipboardTextAsync">Paste from Clipboard</Button>
</Paragraph>
<Paragraph>
<Field>
<FieldLabel>Please select PriceSurfer quote as TSV</FieldLabel>
@ -304,4 +303,3 @@
</Div>
</NotAuthorized>
</AuthorizeView>

@ -1,4 +1,3 @@
using System.Diagnostics;
using System.Globalization;
using System.Security.Claims;
using System.Text;
@ -19,18 +18,18 @@ public partial class QuoteAdd {
private bool isCreatingPdf;
private bool isCreatingTex;
private bool lineItemsNotReady = true;
private CustomDescription? newCustomDescription;
private static CustomDescription newCustomDescription;
private bool pdfNotReady = true;
private Quote quote = new();
private Contact? selectedContact;
private LineItem? selectedLineItem;
private List<CustomDescription>? suggestedCustomDescriptions;
// private List<CustomDescription>? suggestedCustomDescriptions;
private bool texNotReady = true;
private string? url;
[CascadingParameter] private Task<AuthenticationState>? AuthenticationStateTask { get; set; }
[Inject] public IModalService? ModalService { get; set; }
[Inject] public static IModalService ModalService { get; set; }
public Task ShowCustomDescriptionModal() {
public static Task ShowCustomDescriptionModal(List<CustomDescription> suggestedCustomDescriptions) {
return ModalService.Show<CustomDescriptionModal>(builder => {
builder.Add(parameter => parameter.CustomDescription, newCustomDescription);
builder.Add(parameter => parameter.SuggestedCustomDescriptions, suggestedCustomDescriptions);
@ -44,7 +43,7 @@ public partial class QuoteAdd {
contacts = await GenericController.GetAllAsync<Contact>();
selectedContact = contacts?.FirstOrDefault();
if (selectedContact is not null)
await OnSelectedContactChanged(selectedContact);
await SelectedContactChanged(selectedContact);
SalesRep newSalesRep = new() {
LastName = "Woitschetzki",
@ -83,37 +82,8 @@ public partial class QuoteAdd {
_ = 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 is not null) quote = HostingService.SetPath(quote);
if (quote.LineItems is null) return;
FileService.WriteQuoteToTsv(fileContent, quote);
//TODO Load all relevant CustomDescriptions upfront
foreach (LineItem lineItem in quote.LineItems) {
newCustomDescription = await GenericController.GetAsync<CustomDescription>(newCustomDescription => newCustomDescription.ProductNumber.Equals(lineItem.ProductNumber, StringComparison.Ordinal) && newCustomDescription.OptionNumber.Equals(lineItem.OptionNumber, StringComparison.Ordinal));
if (newCustomDescription is null) {
Console.WriteLine($"Keine CustomDescription für {lineItem.ProductNumber}#{lineItem.OptionNumber} verfügbar!");
suggestedCustomDescriptions = await SuggestCustomDescriptions(lineItem);
newCustomDescription = new() { ProductNumber = lineItem.ProductNumber, OptionNumber = lineItem.OptionNumber, Heading = lineItem.SapShortDescription, DescriptionText = lineItem.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);
}
lineItem.Product = await GenericController.GetAsync<Product>(p => p.ProductNumber.Equals(lineItem.ProductNumber) && p.OptionNumber.Equals(lineItem.OptionNumber));
if (lineItem.Product is null) return;
lineItem.ProductId = lineItem.Product.ProductId;
lineItem.Product.CustomDescription = await GenericController.GetAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber) && cD.OptionNumber.Equals(lineItem.OptionNumber));
lineItem.Quote = quote;
lineItem.QuoteId = lineItem.Quote.QuoteId;
}
quote = await QuoteHandling.GenerateQuoteFromString(quote, fileContent);
}
}
catch (Exception exc) {
@ -125,18 +95,6 @@ public partial class QuoteAdd {
}
}
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)}");
}
@ -145,7 +103,7 @@ public partial class QuoteAdd {
Console.WriteLine($"File: {e.File.Name} Progress: {e.Percentage}");
}
private async Task OnSelectedContactChanged(Contact newSelectedContact) {
private async Task SelectedContactChanged(Contact newSelectedContact) {
quote.Recipient = await GenericController.GetAsync<Contact>(c => c.ContactId.Equals(newSelectedContact.ContactId));
if (quote.Recipient is not null)
@ -193,55 +151,31 @@ public partial class QuoteAdd {
}
}
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 newSelectedLineItem) => selectedLineItem = newSelectedLineItem;
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");
private async Task<string> GetClipboardTextAsync() => await JsRuntime.InvokeAsync<string>("navigator.clipboard.readText");
}

@ -22,6 +22,7 @@ builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<GremlinDb>();
builder.Services.AddScoped<GenericController>();
builder.Services.AddScoped<HostingService>();
builder.Services.AddScoped<QuoteHandling>();
builder.Services.AddScoped<GenericImporter>();
builder.Services.AddBlazorise(options => { options.Immediate = true; })

@ -2,14 +2,11 @@ using Gremlin_BlazorServer.Data.EntityClasses;
namespace Gremlin_BlazorServer.Services;
internal class HostingService
public class HostingService
{
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly IWebHostEnvironment hostingEnvironment;
public HostingService(IWebHostEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public HostingService(IWebHostEnvironment newHostingEnvironment) => hostingEnvironment = newHostingEnvironment;
public Quote SetPath(Quote quote)
{
@ -23,7 +20,7 @@ internal class HostingService
string description = quote.Description.Replace(" ", "_");
quote.Path = Path.Combine(
_hostingEnvironment.WebRootPath,
hostingEnvironment.WebRootPath,
"quotes",
accountName,
$"{DateTime.Today.Year}-{DateTime.Today.Month}-{quote.Recipient?.LastName}-{description}"

@ -1,12 +1,14 @@
using System.Globalization;
using System.Text;
using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Pages.Quotes;
namespace Gremlin_BlazorServer.Services;
public static class QuoteHandling {
private static GenericController genericController = new();
public class QuoteHandling {
private static readonly GenericController genericController = new();
private readonly IWebHostEnvironment hostingEnvironment;
public static async Task<StringBuilder?> CreateTexAsync(Quote quote) {
StringBuilder? texString = await TexService.CreateTex(quote);
if (texString is null) return null;
@ -17,7 +19,7 @@ public static class QuoteHandling {
//FromWindowsGremlin
public static Quote ReadLineItems(Quote quote, string clipboard) {
private Quote ReadLineItems(Quote quote, string clipboard) {
try {
quote = ResetTotals(quote);
@ -53,7 +55,7 @@ public static class QuoteHandling {
}
}
private static List<LineItem> ReadLineItemsFromClipboard(string clipboard) {
private List<LineItem> ReadLineItemsFromClipboard(string clipboard) {
//Zeilen aufteilen
IEnumerable<string> clipboardLines = clipboard.Split(Environment.NewLine.ToCharArray());
List<string[]> clipboardList = (from clipboardLine in clipboardLines where clipboardLine != "" select clipboardLine.Split('\t')).ToList();
@ -61,7 +63,7 @@ public static class QuoteHandling {
return lineItems;
}
private static async Task<List<LineItem>> ParseClipboardList(List<string[]> lineItemStrings) {
private async Task<List<LineItem>> ParseClipboardList(List<string[]> lineItemStrings) {
List<LineItem> lineItems = new();
CultureInfo cultureInfoUs = new("en-US");
@ -98,16 +100,16 @@ public static class QuoteHandling {
return lineItems;
}
private static byte CheckForAcademic(Account account) {
private byte CheckForAcademic(Account account) {
return account.AccountType?.AccountTypeCode == null ? (byte)60 : (byte)(account.AccountType.AccountTypeCode.StartsWith("N") ? 90 : 60);
}
private static decimal GetFreight(decimal net, decimal freight) {
private decimal GetFreight(decimal net, decimal freight) {
decimal freightNet = net * freight / 100;
return freightNet < 3000 ? freightNet : 3000;
}
private static Quote CalculateTotals(Quote quote) {
private 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;
@ -115,7 +117,7 @@ public static class QuoteHandling {
return quote;
}
private static Quote ResetTotals(Quote quote) {
private Quote ResetTotals(Quote quote) {
quote.TotalListprice = 0;
quote.TotalNet = 0;
quote.TotalFreightOnly = 0;
@ -126,7 +128,7 @@ public static class QuoteHandling {
return quote;
}
private static decimal GetTotal(Quote quote, string type) {
private decimal GetTotal(Quote quote, string type) {
decimal total = 0;
if (quote.LineItems == null) return total;
@ -148,8 +150,8 @@ public static class QuoteHandling {
return total;
}
private static bool DoesContains(Quote quote, string type) {
if (quote.LineItems == null) return false;
private bool DoesContains(Quote quote, string type) {
if (quote.LineItems is null) return false;
foreach (LineItem lineItem in quote.LineItems)
switch (type) {
@ -171,4 +173,56 @@ public static class QuoteHandling {
return false;
}
public async Task<Quote> GenerateQuoteFromString(Quote quote, string fileContent) {
HostingService hostingService = new(hostingEnvironment);
quote = ReadLineItems(quote, fileContent);
if (quote.Recipient?.Account?.AccountName is not null) quote = hostingService.SetPath(quote);
if (quote.LineItems is null) return quote;
FileService.WriteQuoteToTsv(fileContent, quote);
//TODO Load all relevant CustomDescriptions upfront
foreach (LineItem lineItem in quote.LineItems) {
CustomDescription newCustomDescription = await genericController.GetAsync<CustomDescription>(newCustomDescription => newCustomDescription.ProductNumber.Equals(lineItem.ProductNumber, StringComparison.Ordinal) && newCustomDescription.OptionNumber.Equals(lineItem.OptionNumber, StringComparison.Ordinal));
if (newCustomDescription is null) {
Console.WriteLine($"Keine CustomDescription für {lineItem.ProductNumber}#{lineItem.OptionNumber} verfügbar!");
List<CustomDescription>? suggestedCustomDescriptions = await SuggestCustomDescriptions(lineItem);
newCustomDescription = new() {
ProductNumber = lineItem.ProductNumber,
OptionNumber = lineItem.OptionNumber,
Heading = lineItem.SapShortDescription,
DescriptionText = lineItem.SapLongDescription
};
//Show windows to edit new cD
await QuoteAdd.ShowCustomDescriptionModal(suggestedCustomDescriptions);
//TODO need to wait for modal!
//Insert new CustomDescription to db
newCustomDescription.AccountId = 1;
_ = await genericController.InsertAsync(newCustomDescription);
}
lineItem.Product = await genericController.GetAsync<Product>(p => p.ProductNumber.Equals(lineItem.ProductNumber) && p.OptionNumber.Equals(lineItem.OptionNumber));
if (lineItem.Product is null) return quote;
lineItem.ProductId = lineItem.Product.ProductId;
lineItem.Product.CustomDescription = await genericController.GetAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber) && cD.OptionNumber.Equals(lineItem.OptionNumber));
lineItem.Quote = quote;
lineItem.QuoteId = lineItem.Quote.QuoteId;
}
return quote;
}
private static 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();
}
}