add Opportunities

pull/3/head
Sascha Woitschetzki 2023-09-13 10:37:10 +07:00
parent 3af421b189
commit c0cf03adb3
9 changed files with 382 additions and 10 deletions

@ -51,11 +51,10 @@ internal static class DbHelper {
}
public static void DbBuilder() {
using (GremlinDb gremlinDb = new()) {
using GremlinDb gremlinDb = new();
_ = gremlinDb.Database.EnsureDeleted();
_ = gremlinDb.Database.EnsureCreated();
}
}
public static void DbInitializer() {
//PRODUCT LINES

@ -302,3 +302,33 @@ public class RuSettingsConfiguration : IEntityTypeConfiguration<RuSettings> {
rUSettingsEntitity.Property(e => e.DataModificationByUser).HasColumnType("TINYTEXT").HasDefaultValueSql("ON INSERT CURRENT_USER() ON UPDATE CURRENT_USER()");
}
}
public class OpportunityConfiguration : IEntityTypeConfiguration<Opportunity> {
public void Configure(EntityTypeBuilder<Opportunity> 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()");
}
}

@ -16,12 +16,11 @@ public class GremlinDb : DbContext {
public DbSet<SubMarket>? SubMarkets { get; set; }
public DbSet<RuSettings>? RuSettings { get; set; }
public DbSet<RegisteredUser>? RegisteredUser { get; set; }
public DbSet<Opportunity>? 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,13 +49,16 @@ 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<Opportunity>().ToTable("Opportunites");
//AutoInclude all NavigationParameters in Entities
// _ = modelBuilder.Entity<Account>().Navigation(db => db.Contacts).AutoInclude();
// _ = modelBuilder.Entity<Account>().Navigation(db => db.AccountType).AutoInclude();

@ -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<Opportunity> AddIfUniqueTo(List<Opportunity> 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;
}
}

@ -1,4 +1,5 @@
@page "/"
@using Gremlin_BlazorServer.Services
<PageTitle>Gremlin BlazorServer</PageTitle>
<Div Margin="Margin.Is3"
@ -13,6 +14,10 @@
<Authorized>
@if (context.User.Identity != null) {
<Heading Size="HeadingSize.Is6">You are logged in as @context.User.Identity.Name</Heading>
<Paragraph>
<Button Color="Color.Primary" Clicked="@OnEnsureCreated">Ensure Created Database</Button>
<Button Color="Color.Secondary" Clicked="@OnMigrate">Migrate Database</Button>
</Paragraph>
}
</Authorized>
<NotAuthorized>
@ -22,3 +27,17 @@
</AuthorizeView>
</Paragraph>
</Div>
@code {
private static Task OnEnsureCreated() {
Task<bool> 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.");
}
}

@ -0,0 +1,106 @@
@page "/Opportunities"
@using Gremlin_BlazorServer.Data.EntityClasses
@inherits LayoutComponentBase
@inject ILoadingIndicatorService ApplicationLoadingIndicatorService
<AuthorizeView>
<Authorized Context="_">
<Div Margin="Margin.Is3"
Border="Border.Dark.OnAll"
Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40">
<Heading Size="HeadingSize.Is4">Opportunities</Heading>
<Paragraph>
<DataGrid TItem="Opportunity"
Data="@opportunities"
SelectedRow="@selectedOpportunity"
SelectedRowChanged="@OnSelectedOpportunityChanged"
RowInserted="@OnRowInsertedAsync"
RowUpdated="@OnRowUpdatedAsync"
RowRemoved="@OnRowRemovedAsync"
CommandMode="DataGridCommandMode.ButtonRow"
EditMode="DataGridEditMode.Popup"
UseValidation Narrow Editable ShowPager Hoverable Sortable Filterable Striped Responsive>
<DataGridColumns>
<DataGridCommandColumn NewCommandAllowed="false" EditCommandAllowed="false" DeleteCommandAllowed="false">
<SaveCommandTemplate>
<Button ElementId="btnSave" PreventDefaultOnSubmit Color="Color.Primary" Clicked="context.Clicked">@context.LocalizationString</Button>
</SaveCommandTemplate>
<CancelCommandTemplate>
<Button ElementId="btnCancel" Color="Color.Secondary" Clicked="context.Clicked">@context.LocalizationString</Button>
</CancelCommandTemplate>
</DataGridCommandColumn>
<DataGridColumn Field="@nameof(Opportunity.OpportunityId)" Caption="OpportunityId" Filterable Sortable/>
@* <DataGridColumn Field="@nameof(Opportunity.OpportunityName)" Caption="OpportunityName" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.Street)" Caption="Street" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.Zip)" Caption="Zip" Filterable Sortable Editable> *@
@* <EditTemplate> *@
@* <NumericEdit TValue="uint" Value="Convert.ToUInt32(context.CellValue)" ValueChanged="v => context.CellValue = v"/> *@
@* </EditTemplate> *@
@* </DataGridColumn> *@
@* <DataGridColumn Field="@nameof(Opportunity.City)" Caption="City" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.PhoneNumber)" Caption="PhoneNumber" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.EMail)" Caption="EMail" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.FaxNumber)" Caption="FaxNumber" Filterable Sortable Editable/> *@
@* *@
@* <DataGridColumn Field="@nameof(Opportunity.Webpage)" Caption="Webpage" Filterable Sortable Editable/> *@
<DataGridColumn Field="@nameof(Opportunity.SapOpportunityNumber)" Caption="SapOpportunityNumber" Filterable Sortable Editable>
<EditTemplate>
<NumericEdit TValue="uint" Value="Convert.ToUInt32(context.CellValue)" ValueChanged="v => context.CellValue = v"/>
</EditTemplate>
</DataGridColumn>
<DataGridColumn Field="@nameof(Opportunity.DataModificationDate)" Caption="DataModificationDate" Filterable Sortable/>
</DataGridColumns>
<ButtonRowTemplate>
<Button Color="Color.Primary" Clicked="context.NewCommand.Clicked">New</Button>
<Button Color="Color.Warning" Disabled="selectedOpportunity is null" Clicked="context.EditCommand.Clicked">Edit</Button>
<Button Color="Color.Danger" Disabled="selectedOpportunity is null" Clicked="context.DeleteCommand.Clicked">Delete</Button>
<Button Color="Color.Secondary" Clicked="context.ClearFilterCommand.Clicked">Clear Filter</Button>
</ButtonRowTemplate>
</DataGrid>
</Paragraph>
</Div>
<Div Margin="Margin.Is3"
Border="Border.Dark.OnAll"
Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40">
<Paragraph>
<Button Color="Color.Primary" Clicked="@OnRemoveDublicates">Remove Dublicates</Button>
</Paragraph>
<Heading Size="HeadingSize.Is6">Import Opportunities from TSV</Heading>
<Paragraph>
<Field>
<FileEdit Filter=".csv" Changed="@OnImportOpportunities"/>
</Field>
</Paragraph>
</Div>
</Authorized>
<NotAuthorized>
<Div Margin="Margin.Is3"
Border="Border.Dark.OnAll"
Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40">
<Heading Size="HeadingSize.Is3">Authentication Failure!</Heading>
<Paragraph>You're not signed in. Please click on the upper right to either register or log in.</Paragraph>
</Div>
</NotAuthorized>
</AuthorizeView>

@ -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<Opportunity> opportunities = new List<Opportunity>();
private Opportunity? selectedOpportunity;
private static int ImportProgress { get; set; }
[CascadingParameter] private Task<AuthenticationState>? 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<Opportunity>());
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<Opportunity>(fileContent);
}
}
catch (Exception exception) {
Console.WriteLine(exception.Message);
}
StateHasChanged();
}
private static Task OnRowInsertedAsync(SavedRowItem<Opportunity, Dictionary<string, object>> 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, Dictionary<string, object>> 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<OpportunityType>(aT => aT.OpportunityTypeCode.Equals("SUP"));
// newOpportunity.SubMarket = await GenericController.GetAsync<SubMarket>(sM => sM.SubMarketCode.Equals("VEN"));
return newOpportunity;
}
private static async Task OnRemoveDublicates() {
int i = await GenericController.RemoveDublicatesAsync<Opportunity>();
Console.WriteLine($"Removed {i} dublicates from Opportunities.");
}
}

@ -196,6 +196,17 @@ public class GenericController {
}
}
public static async Task<bool> 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<int> RemoveAsync<T>(T entity) where T : class, IMetadata {
try {
await Task.Run(() => gremlinDb.Set<T>().Remove(entity));

@ -101,6 +101,13 @@
</BarLink>
</BarItem>
<BarItem>
<BarLink To="/Opportunities">
<BarIcon IconName="IconName.ShoppingCart"/>
Opportunities
</BarLink>
</BarItem>
</BarStart>
</BarMenu>
</Bar>