From c0cf03adb374e9352ec8234b7eff35b12355cf36 Mon Sep 17 00:00:00 2001 From: Sascha Woitschetzki Date: Wed, 13 Sep 2023 10:37:10 +0200 Subject: [PATCH] add Opportunities --- .../Data/DBClasses/DbHelper.cs | 9 +- .../Data/DBClasses/EntityConfiguration.cs | 30 +++++ .../Data/DBClasses/GremlinDb.cs | 10 +- .../Data/EntityClasses/Opportunity.cs | 112 ++++++++++++++++++ Gremlin_BlazorServer/Pages/Index.razor | 21 +++- .../Pages/Opportunities.razor | 106 +++++++++++++++++ .../Pages/Opportunities.razor.cs | 86 ++++++++++++++ .../Services/GenericController.cs | 11 ++ .../Shared/BlazoriseNavMenu.razor | 7 ++ 9 files changed, 382 insertions(+), 10 deletions(-) create mode 100644 Gremlin_BlazorServer/Data/EntityClasses/Opportunity.cs create mode 100644 Gremlin_BlazorServer/Pages/Opportunities.razor create mode 100644 Gremlin_BlazorServer/Pages/Opportunities.razor.cs diff --git a/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs b/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs index c34c569..66c098a 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs @@ -51,12 +51,11 @@ internal static class DbHelper { } public static void DbBuilder() { - using (GremlinDb gremlinDb = new()) { - _ = gremlinDb.Database.EnsureDeleted(); - _ = gremlinDb.Database.EnsureCreated(); - } + using GremlinDb gremlinDb = new(); + _ = gremlinDb.Database.EnsureDeleted(); + _ = gremlinDb.Database.EnsureCreated(); } - + public static void DbInitializer() { //PRODUCT LINES string[] productLineAbbreviations = { "XF", "LM", "XA", "RB", "GE", "9Z", "9P", "9M", "9K", "9H", "9F", "9E", "9C", "ZZ", "ZF", "V1", "MB", "MA", "LI", "JW", "CA", "BZ", "BC", "AZ", "AJ", "AB", "AA", "8P", "89", "74", "6P", "6G", "58", "29", "AM", "RP", "CD", "PT", "XB", "RM", "GS", "AT", "SR", "FS", "BD", "UF", "3P" }; diff --git a/Gremlin_BlazorServer/Data/DBClasses/EntityConfiguration.cs b/Gremlin_BlazorServer/Data/DBClasses/EntityConfiguration.cs index 8bc3000..ea4cb2a 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/EntityConfiguration.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/EntityConfiguration.cs @@ -301,4 +301,34 @@ public class RuSettingsConfiguration : IEntityTypeConfiguration { rUSettingsEntitity.Property(e => e.DataModificationDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP").ValueGeneratedOnAddOrUpdate().IsConcurrencyToken(); rUSettingsEntitity.Property(e => e.DataModificationByUser).HasColumnType("TINYTEXT").HasDefaultValueSql("ON INSERT CURRENT_USER() ON UPDATE CURRENT_USER()"); } +} + +public class OpportunityConfiguration : IEntityTypeConfiguration { + public void Configure(EntityTypeBuilder opportunityEntity) { + opportunityEntity.HasKey(e => e.OpportunityId); + + opportunityEntity.Property(e => e.AccountName); + opportunityEntity.Property(e => e.ContactName); + opportunityEntity.Property(e => e.OppProductLineCode); + opportunityEntity.Property(e => e.MainCompetitorName); + opportunityEntity.Property(e => e.Description); + opportunityEntity.Property(e => e.ForecastDate); + opportunityEntity.Property(e => e.FollowUpDate); + opportunityEntity.Property(e => e.ExpSalesVolume); + opportunityEntity.Property(e => e.WinProbability); + opportunityEntity.Property(e => e.Notes); + opportunityEntity.Property(e => e.PrimaryCampaignId); + opportunityEntity.Property(e => e.SecondaryCampaignId); + opportunityEntity.Property(e => e.SapOpportunityNumber); + opportunityEntity.Property(e => e.OpportunityStartDate); + + opportunityEntity.Property(e => e.DataCreationDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP").ValueGeneratedOnAdd(); + opportunityEntity.Property(e => e.DataValidFrom).HasColumnType("DATETIME").HasDefaultValueSql("CURRENT_TIMESTAMP").ValueGeneratedOnAdd(); + opportunityEntity.Property(e => e.DataValidUntil).HasColumnType("DATETIME").HasDefaultValueSql("'9999-12-31 23:59:59.000000'").ValueGeneratedOnAdd(); + opportunityEntity.Property(e => e.DataVersionNumber).HasDefaultValue(1).IsRequired(); + opportunityEntity.Property(e => e.DataVersionComment).HasDefaultValue(""); + opportunityEntity.Property(e => e.DataStatus).IsRequired().HasDefaultValue("Active"); + opportunityEntity.Property(e => e.DataModificationDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP").ValueGeneratedOnAddOrUpdate().IsConcurrencyToken(); + opportunityEntity.Property(e => e.DataModificationByUser).HasColumnType("TINYTEXT").IsRequired().HasDefaultValueSql("ON INSERT CURRENT_USER() ON UPDATE CURRENT_USER()"); + } } \ No newline at end of file diff --git a/Gremlin_BlazorServer/Data/DBClasses/GremlinDb.cs b/Gremlin_BlazorServer/Data/DBClasses/GremlinDb.cs index 3283598..9d86f52 100644 --- a/Gremlin_BlazorServer/Data/DBClasses/GremlinDb.cs +++ b/Gremlin_BlazorServer/Data/DBClasses/GremlinDb.cs @@ -16,12 +16,11 @@ public class GremlinDb : DbContext { public DbSet? SubMarkets { get; set; } public DbSet? RuSettings { get; set; } public DbSet? RegisteredUser { get; set; } + public DbSet? Opportunities { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - // const string connectionString = "server=192.168.177.2;port=3306;database=regulus;user=root;password=lungretter1;SslMode=;SslCa="; - // const string connectionString = "server=server0;port=3306;database=regulus;user=sascha;password=mgltoJtmmDnKJ86LltsGdw"; const string connectionString = "server=woitschetzki.de;port=3308;database=regulus;user=sascha;password=mgltoJtmmDnKJ86LltsGdw;SslMode=;SslCa="; - // optionsBuilder.LogTo(Console.WriteLine); + optionsBuilder.LogTo(Console.WriteLine); try { optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => options.EnableRetryOnFailure()).EnableSensitiveDataLogging().EnableDetailedErrors(); @@ -50,12 +49,15 @@ public class GremlinDb : DbContext { // } protected override void OnModelCreating(ModelBuilder modelBuilder) { - //wozu dient die folgende Zeile? base.OnModelCreating(modelBuilder); ////alle Fluent-Konfigurationen aufrufen: //TO BE TESTED! _ = modelBuilder.ApplyConfigurationsFromAssembly(typeof(GremlinDb).Assembly); + + //Create missing tables + modelBuilder.Entity().ToTable("Opportunites"); + //AutoInclude all NavigationParameters in Entities // _ = modelBuilder.Entity().Navigation(db => db.Contacts).AutoInclude(); diff --git a/Gremlin_BlazorServer/Data/EntityClasses/Opportunity.cs b/Gremlin_BlazorServer/Data/EntityClasses/Opportunity.cs new file mode 100644 index 0000000..e762c69 --- /dev/null +++ b/Gremlin_BlazorServer/Data/EntityClasses/Opportunity.cs @@ -0,0 +1,112 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Gremlin_BlazorServer.Data.EntityClasses; + +public class Opportunity : IMetadata +{ + //primary key: + [Required] public uint OpportunityId { get; set; } + + //foreign keys: + public string? AccountName { get; set; } + public string? ContactName { get; set; } + public string? OppProductLineCode { get; set; } + public string? MainCompetitorName { get; set; } + + //navigation properties: + public Account? Account { get; set; } + public Contact? Contact { get; set; } + public ProductLine? OppProductLine { get; set; } + public Account? MainCompetitor { get; set; } + + //class properties: + public string? Description { get; set; } + public DateTime ForecastDate { get; set; } + public DateTime FollowUpDate { get; set; } + public double ExpSalesVolume { get; set; } + public int WinProbability { get; set; } + public string? Notes { get; set; } + public string? PrimaryCampaignId { get; set; } + public string? SecondaryCampaignId { get; set; } + + //Agilent-specific Properties: + public uint SapOpportunityNumber { get; set; } + public DateTime OpportunityStartDate { 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(Opportunity? other) { + if (other is null) return false; + return SapOpportunityNumber == other.SapOpportunityNumber + && Description == other.Description + && AccountName == other.AccountName + && ContactName == other.ContactName + && ExpSalesVolume == other.ExpSalesVolume + && WinProbability == other.WinProbability + && OppProductLineCode == other.OppProductLineCode + && ForecastDate == other.ForecastDate + && FollowUpDate == other.FollowUpDate + && MainCompetitorName == other.MainCompetitorName + && PrimaryCampaignId == other.PrimaryCampaignId + && SecondaryCampaignId == other.SecondaryCampaignId; + } + + public static bool operator ==(Opportunity? Opportunity1, Opportunity? Opportunity2) { + if (Opportunity1 is null || Opportunity2 is null) return false; + if (Opportunity1.SapOpportunityNumber == 0) return false; + if (Opportunity2.SapOpportunityNumber == 0) return false; + return Opportunity1.SapOpportunityNumber == Opportunity2.SapOpportunityNumber + && Opportunity1.Description == Opportunity2.Description + && Opportunity1.AccountName == Opportunity2.AccountName + && Opportunity1.ContactName == Opportunity2.ContactName + && Opportunity1.ExpSalesVolume == Opportunity2.ExpSalesVolume + && Opportunity1.WinProbability == Opportunity2.WinProbability + && Opportunity1.OppProductLineCode == Opportunity2.OppProductLineCode + && Opportunity1.ForecastDate == Opportunity2.ForecastDate + && Opportunity1.FollowUpDate == Opportunity2.FollowUpDate + && Opportunity1.MainCompetitorName == Opportunity2.MainCompetitorName + && Opportunity1.PrimaryCampaignId == Opportunity2.PrimaryCampaignId + && Opportunity1.SecondaryCampaignId == Opportunity2.SecondaryCampaignId; + } + + public static bool operator !=(Opportunity? Opportunity1, Opportunity? Opportunity2) + { + if (Opportunity1 is null || Opportunity2 is null) return false; + if (Opportunity1.SapOpportunityNumber == 0) return false; + if (Opportunity2.SapOpportunityNumber == 0) return false; + return Opportunity1.SapOpportunityNumber != Opportunity2.SapOpportunityNumber + && Opportunity1.Description != Opportunity2.Description + && Opportunity1.AccountName != Opportunity2.AccountName + && Opportunity1.ContactName != Opportunity2.ContactName + && Opportunity1.ExpSalesVolume != Opportunity2.ExpSalesVolume + && Opportunity1.WinProbability != Opportunity2.WinProbability + && Opportunity1.OppProductLineCode != Opportunity2.OppProductLineCode + && Opportunity1.ForecastDate != Opportunity2.ForecastDate + && Opportunity1.FollowUpDate != Opportunity2.FollowUpDate + && Opportunity1.MainCompetitorName != Opportunity2.MainCompetitorName + && Opportunity1.PrimaryCampaignId != Opportunity2.PrimaryCampaignId + && Opportunity1.SecondaryCampaignId != Opportunity2.SecondaryCampaignId; + + } + + public List AddIfUniqueTo(List Oppertunities) { + if (Oppertunities.Count > 0 && SapOpportunityNumber == Oppertunities[^1].SapOpportunityNumber) return Oppertunities; + + if (Oppertunities.Any(Opportunity => SapOpportunityNumber == Opportunity.SapOpportunityNumber)) return Oppertunities; + + Oppertunities.Add(this); + return Oppertunities; + } +} \ No newline at end of file diff --git a/Gremlin_BlazorServer/Pages/Index.razor b/Gremlin_BlazorServer/Pages/Index.razor index 2db6ac7..3ab1ea7 100644 --- a/Gremlin_BlazorServer/Pages/Index.razor +++ b/Gremlin_BlazorServer/Pages/Index.razor @@ -1,4 +1,5 @@ @page "/" +@using Gremlin_BlazorServer.Services Gremlin BlazorServer
@if (context.User.Identity != null) { You are logged in as @context.User.Identity.Name + + + + } @@ -21,4 +26,18 @@ -
\ No newline at end of file + + +@code { + + private static Task OnEnsureCreated() { + Task dbGenerated = GenericController.EnsureCreatedAsync(); + Console.WriteLine(dbGenerated.Result ? "Datenbank wurde verändert!" : "Datenbank bereits korrekt."); + return dbGenerated; + } + + private static async Task OnMigrate() { + await GenericController.Migrate(); + Console.WriteLine("Migration done."); + } +} diff --git a/Gremlin_BlazorServer/Pages/Opportunities.razor b/Gremlin_BlazorServer/Pages/Opportunities.razor new file mode 100644 index 0000000..eae1db8 --- /dev/null +++ b/Gremlin_BlazorServer/Pages/Opportunities.razor @@ -0,0 +1,106 @@ +@page "/Opportunities" +@using Gremlin_BlazorServer.Data.EntityClasses +@inherits LayoutComponentBase + +@inject ILoadingIndicatorService ApplicationLoadingIndicatorService + + + +
+ + Opportunities + + + + + + + + + + + + + + + + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + @* *@ + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + Import Opportunities from TSV + + + + + +
+
+ + +
+ + Authentication Failure! + You're not signed in. Please click on the upper right to either register or log in. +
+
+
\ No newline at end of file diff --git a/Gremlin_BlazorServer/Pages/Opportunities.razor.cs b/Gremlin_BlazorServer/Pages/Opportunities.razor.cs new file mode 100644 index 0000000..4ef8972 --- /dev/null +++ b/Gremlin_BlazorServer/Pages/Opportunities.razor.cs @@ -0,0 +1,86 @@ +using System.Security.Claims; +using Blazorise; +using Blazorise.DataGrid; +using Gremlin_BlazorServer.Data.EntityClasses; +using Gremlin_BlazorServer.Services; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using NuGet.Packaging; + +namespace Gremlin_BlazorServer.Pages; + +public partial class Opportunities { + private readonly IList opportunities = new List(); + private Opportunity? selectedOpportunity; + private static int ImportProgress { get; set; } + + [CascadingParameter] private Task? AuthenticationStateTask { get; set; } + + protected override async Task OnInitializedAsync() { + if (AuthenticationStateTask != null) { + ClaimsPrincipal user = (await AuthenticationStateTask).User; + if (user.Identity is { IsAuthenticated: true }) { + ImportProgress = 0; + await ApplicationLoadingIndicatorService.Show(); + opportunities.AddRange(await GenericController.GetAllAsync()); + selectedOpportunity = opportunities.FirstOrDefault(); + await OnSelectedOpportunityChanged(selectedOpportunity); + await ApplicationLoadingIndicatorService.Hide(); + } + await base.OnInitializedAsync(); + } + } + + public Task OnSelectedOpportunityChanged(Opportunity? newSelectedOpportunity) => Task.FromResult(selectedOpportunity = newSelectedOpportunity); + + private async Task OnImportOpportunities(FileChangedEventArgs fileChangedEventArgs) { + try { + foreach (IFileEntry? file in fileChangedEventArgs.Files) { + using MemoryStream stream = new(); + await file.WriteToStreamAsync(stream); + _ = stream.Seek(0, SeekOrigin.Begin); + using StreamReader reader = new(stream); + string fileContent = await reader.ReadToEndAsync(); + GenericImporter.ImportCsv(fileContent); + } + } + catch (Exception exception) { + Console.WriteLine(exception.Message); + } + + StateHasChanged(); + } + + private static Task OnRowInsertedAsync(SavedRowItem> opportunity) { + Opportunity newOpportunity = ResolveOpportunityAsync(opportunity.Item); + // if (newOpportunity.OpportunityType is null || newOpportunity.SubMarket is null) return; + int count = GenericController.Insert(newOpportunity); + Console.WriteLine($"Inserted {count} properties for new Opportunity {newOpportunity.OpportunityId}: {newOpportunity.Description}."); + return Task.CompletedTask; + } + + private static Task OnRowUpdatedAsync(SavedRowItem> opportunity) { + Opportunity newOpportunity = ResolveOpportunityAsync(opportunity.Item); + int count = GenericController.Update(opportunity.Item); + Console.WriteLine($"Updated {count} properties in Opportunity {newOpportunity.OpportunityId}: {newOpportunity.Description}."); + return Task.CompletedTask; + } + + private static async Task OnRowRemovedAsync(Opportunity opportunity) { + int count = await GenericController.RemoveAsync(opportunity); + Console.WriteLine($"Removed {count} properties and Opportunity {opportunity.OpportunityId}: {opportunity.Description}."); + } + + private static Opportunity ResolveOpportunityAsync(Opportunity newOpportunity) { + newOpportunity.DataModificationByUser = "Gremlin Blazor Server GUI"; + newOpportunity.DataVersionNumber++; + // newOpportunity.OpportunityType = await GenericController.GetAsync(aT => aT.OpportunityTypeCode.Equals("SUP")); + // newOpportunity.SubMarket = await GenericController.GetAsync(sM => sM.SubMarketCode.Equals("VEN")); + return newOpportunity; + } + + private static async Task OnRemoveDublicates() { + int i = await GenericController.RemoveDublicatesAsync(); + Console.WriteLine($"Removed {i} dublicates from Opportunities."); + } +} \ No newline at end of file diff --git a/Gremlin_BlazorServer/Services/GenericController.cs b/Gremlin_BlazorServer/Services/GenericController.cs index 0dd8ec0..d409cbf 100644 --- a/Gremlin_BlazorServer/Services/GenericController.cs +++ b/Gremlin_BlazorServer/Services/GenericController.cs @@ -195,6 +195,17 @@ public class GenericController { return 0; } } + + public static async Task EnsureCreatedAsync() { + bool b = await gremlinDb.Database.EnsureCreatedAsync(); + await gremlinDb.SaveChangesAsync(); + return b; + } + + public static async Task Migrate() { + await gremlinDb.Database.MigrateAsync(); + await gremlinDb.SaveChangesAsync(); + } public static async Task RemoveAsync(T entity) where T : class, IMetadata { try { diff --git a/Gremlin_BlazorServer/Shared/BlazoriseNavMenu.razor b/Gremlin_BlazorServer/Shared/BlazoriseNavMenu.razor index 292c1c0..698717a 100644 --- a/Gremlin_BlazorServer/Shared/BlazoriseNavMenu.razor +++ b/Gremlin_BlazorServer/Shared/BlazoriseNavMenu.razor @@ -100,6 +100,13 @@ Debug + + + + + Opportunities + +