Gremlin/Gremlin_BlazorServer/Data/DBClasses/DbHelper.cs

1969 lines
66 KiB
C#

using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualBasic.FileIO;
using MySqlConnector;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using static Gremlin_BlazorServer.Data.EntityClasses.Enums;
namespace Gremlin_BlazorServer.Data.DBClasses
{
internal static class DbHelper
{
private static readonly DateTime farInTheFuture = DateTime.Parse("2050-12-31t00:00:00.000000z", CultureInfo.CurrentCulture);
private static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = Environment.ProcessorCount * 4 };
private static readonly Random random = new();
public static bool CheckDatabaseConnection(string connectionString)
{
bool isConnected = false;
MySqlConnection mySqlConnection = new();
try
{
mySqlConnection = new(connectionString);
mySqlConnection.Open();
isConnected = true;
}
catch (ArgumentException aEx)
{
Debug.WriteLine(aEx.Message);
}
catch (MySqlException ex)
{
Debug.WriteLine($"Message: {ex.Message}\nSource: {ex.Source}\nNumber: {ex.Number}", "Connection Failed!");
isConnected = false;
switch (ex.Number)
{
case 1042: // Unable to connect to any of the specified MySQL hosts (Check Server,Port)
break;
case 0: // Access denied (Check DB name,username,password)
break;
}
}
finally
{
if (mySqlConnection.State == ConnectionState.Open) { mySqlConnection.Close(); }
}
return isConnected;
}
public static void DbBuilder()
{
using GremlinContext gremlinContext = new();
_ = gremlinContext.Database.EnsureDeleted();
_ = gremlinContext.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" };
string[] productLineDescriptions = { "Cell Analysis", "Lab Management", "Flow Cell", "Remarketed Instruments", " Genomics",
"Drugs of Abuse", "Sample Prep/Resins", "Research Products", "Dissolution & Other", "Vacuum Products",
"AA/OES", "Drugs of Abuse", "Micro GC", "Common PL & Default", "Expedite Freight",
"Automation", "Particle Analysis", "Molecular Spectroscopy", "Software and Informatics", "GC Columns",
"Mid Range GC", "GC-MS", "LC Columns", "Gas Phase Analysis", "ICPMS",
"Bio-consumables", "Instrument Supplies", "Analytical Parts Sup", "LC-MS", "Support Services",
"Software and Professinal Services", "Other Local Products", "Competitive Supplies", "LC", "Amplicon Sequencing",
"Reagent Partnership", "Companion Diagnostic", "Pathology", "Biotek", "Raman Spectroscopy",
"Genomics Systems", "Parallel CE Instrument", "Bio Reagents", "Flexible Spending", "Nucleic Acid",
"Microfluidics", "3rd Party Product" };
int length = productLineAbbreviations.Length;
for (int i = 0; i < length; i++)
{
ProductLine plToBeAdded = new()
{
ProductLineCode = productLineAbbreviations[i],
ProductLineDescription = productLineDescriptions[i],
DataStatus = Status.Active.ToString(),
DataModificationByUser = "DbInitializer"
};
using GremlinContext gremlinContext = new();
_ = gremlinContext.ProductLines.Add(plToBeAdded);
_ = gremlinContext.SaveChanges();
}
//ACCOUNT TYPES
string[] accountTypes = { "FPC", "FPR", "FPT", "FPV", "FPN",
"FPP", "FPG", "FPX", "FPS", "FPA",
"FPO", "FPW", "NPU", "NPR", "NPG",
"NPH", "SUP" };
string[] accountTypeDescription = { "Commercial", "Contract Manufacturing CRM & Contract Research CRO", "Contract Testing", "Instrument IDR Distributor", "Instrument National Distributor",
"Foreign Trade Org", "Genomics/CSD Distributor", "Regulated Distributor", "Transactional Reseller", "Value Added Reseller",
"OEM", "Occasional resellers", "University", "Research Institute", "Government",
"Hospital", "Supplier" };
length = accountTypes.Length;
for (int i = 0; i < length; i++)
{
AccountType aTtoBeAdded = new()
{
AccountTypeCode = accountTypes[i],
AccountTypeDescription = accountTypeDescription[i],
DataStatus = Status.Active.ToString(),
};
_ = MetaDataSetter.ForImport(aTtoBeAdded, "DbInitializer", "DbInitializer");
using GremlinContext db = new();
_ = db.AccountTypes.Add(aTtoBeAdded);
_ = db.SaveChanges();
}
//SubMarkets
string[] subMarketCodes = { "CCH", "CEN", "CFD", "CFR", "CMS",
"COT", "LPH", "LBT", "LGN", "LRS",
"DCT", "LOT", "VEN", "VAR", "SUP" };
string[] subMarketDescriptions = { "Chemical & Energy", "Environmental", "Food", "Forensics", "Material Science",
"CA Other", "Pharmaceutical", "BioPharma", "Generics", "LS Research",
"Clinical Testing", "LS Other", "Analytical Instrument Vendor", "VAR-Partner", "Supplier" };
length = subMarketCodes.Length;
for (int i = 0; i < length; i++)
{
SubMarket sMtoBeAdded = new()
{
SubMarketCode = subMarketCodes[i],
SubMarketDescription = subMarketDescriptions[i],
DataStatus = Status.Active.ToString(),
};
_ = MetaDataSetter.ForImport(sMtoBeAdded, "DbInitializer", "DbInitializer");
using GremlinContext db = new();
_ = db.SubMarkets.Add(sMtoBeAdded);
_ = db.SaveChanges();
}
List<Account> seedAccounts = new();
Account agilent = new()
{
AccountName = "Agilent Technologies",
AccountId = 1,
Street = "Hewlett-Packard-Str. 8",
Zip = 76337,
City = "Waldbronn",
SapAccountNumber = 00000001,
EMail = "CustomerCare_Germany@agilent.com",
PhoneNumber = "+49 800 6031000",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "VEN"
}
};
seedAccounts.Add(agilent);
Account bios = new()
{
AccountName = "Bios Analytical Ltd.",
AccountId = 2,
Street = "7 South Fens Enterprise Park//Fenton Way//PE16 6WA Chatteris//Cambridgeshire//United Kingdom",
Zip = 0,
City = "Chatteris, UNITED KINGDOM",
SapAccountNumber = 00000002,
EMail = "",
PhoneNumber = "+44 1354 694377",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "VAR"
}
};
seedAccounts.Add(bios);
Account huber = new()
{
AccountName = "Huber Kältemaschinenbau AG",
AccountId = 3,
Street = "Werner-von-Siemens-Straße 1",
Zip = 77656,
City = "Offenburg",
SapAccountNumber = 00000003,
EMail = "sales@huber-online.com",
PhoneNumber = "0781 96030",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(huber);
Account vanderheijden = new()
{
AccountName = "Van der Heijden Labortechnik GmbH",
AccountId = 4,
Street = "Tramsmeiers Berg 2",
Zip = 32694,
City = "Dörentrup",
SapAccountNumber = 00000004,
EMail = "info@vdh-online.com",
PhoneNumber = "05265 945520",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(vanderheijden);
Account ole = new()
{
AccountName = "OLE DICH Instrumentmakers ApS",
AccountId = 5,
Street = "18, Taarnfalkevej",
Zip = 2650,
City = "Hvidovre, DENMARK",
SapAccountNumber = 00000005,
EMail = "",
PhoneNumber = "+45 36 78 41 85",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(ole);
Account sprep = new()
{
AccountName = "S-prep GmbH",
AccountId = 6,
Street = "Im Amann 7",
Zip = 88662,
City = "Überlingen",
SapAccountNumber = 00000006,
EMail = "info@s-prep.com",
PhoneNumber = "07551 932696",
FaxNumber = "07551-932699",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(sprep);
Account hellma = new()
{
AccountName = "Hellma GmbH & Co. KG",
AccountId = 7,
Street = "Klosterrunsstraße 5",
Zip = 79379,
City = "Müllheim",
SapAccountNumber = 00000007,
EMail = "info.de@hellma.com",
PhoneNumber = "07631 1820",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(hellma);
Account glasside = new()
{
AccountName = "Glasside Technologies",
AccountId = 8,
Street = "Albany House, 82 Avenue Road//PE19 1LH St Neots, Cambridgeshire//United Kingdom",
Zip = 0,
City = "St Neots, Cambridgesgire, UNITED KINGDOM",
SapAccountNumber = 00000008,
EMail = "",
PhoneNumber = "-/-",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(glasside);
Account pike = new()
{
AccountName = "PIKE Technologies Inc.",
AccountId = 9,
Street = "6125 Cottonwood Drive//Madison, WI 53719//USA",
Zip = 53719,
City = "Madison (WI), USA",
SapAccountNumber = 00000009,
EMail = "info@piketech.com",
PhoneNumber = "+1 608 274 2721",
FaxNumber = "+1 608 274 0103",
DataStatus = Status.Active.ToString(),
AccountType = new()
{
AccountTypeCode = "SUP"
},
SubMarket = new()
{
SubMarketCode = "SUP"
}
};
seedAccounts.Add(pike);
using (GremlinContext db = new())
{
foreach (Account account in seedAccounts)
{
// AccountType und SubMarket aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
account.AccountType = ResolveAccountType(db, account.AccountType.AccountTypeCode);
account.SubMarket = ResolveSubmarket(db, account.SubMarket.SubMarketCode);
_ = MetaDataSetter.ForImport(account, "DbInitializer", "Function DbHelper.DbInitializer");
}
db.Accounts.AddRange(seedAccounts);
_ = db.SaveChanges();
}
//RegisteredUser
List<RegisteredUser> seedRegisteredUser = new();
RegisteredUser userSascha = new()
{
UserName = "woitsche",
PasswordHash = "527646"
};
seedRegisteredUser.Add(userSascha);
RegisteredUser userSebastian = new()
{
UserName = "sewelsch",
PasswordHash = "123456"
};
seedRegisteredUser.Add(userSebastian);
using (GremlinContext db = new())
{
foreach (RegisteredUser userSetting in seedRegisteredUser)
{
userSetting.DataCreationDate = DateTime.Now;
userSetting.DataModificationByUser = "DB Initializer";
userSetting.DataModificationDate = DateTime.Now;
}
db.RegisteredUser.AddRange(seedRegisteredUser);
_ = db.SaveChanges();
}
//RUSetting
List<RuSettings> seedRuSettings = new();
using (RuSettings saschaName = new())
{
saschaName.RegisteredUserId = 1;
saschaName.SettingKey = "userName";
saschaName.SettingValue = "Dr. Sascha Woitschetzki";
seedRuSettings.Add(saschaName);
}
using (RuSettings saschaPhone = new())
{
saschaPhone.RegisteredUserId = 1;
saschaPhone.SettingKey = "userPhone";
saschaPhone.SettingValue = "+49 208 74129134";
seedRuSettings.Add(saschaPhone);
}
using (RuSettings saschaMobile = new())
{
saschaMobile.RegisteredUserId = 1;
saschaMobile.SettingKey = "userMobile";
saschaMobile.SettingValue = "+49 176 22285334";
seedRuSettings.Add(saschaMobile);
}
using (RuSettings saschaMail = new())
{
saschaMail.RegisteredUserId = 1;
saschaMail.SettingKey = "userMail";
saschaMail.SettingValue = "sascha.woitschetzki@non.agilent.com";
seedRuSettings.Add(saschaMail);
}
using (RuSettings saschaTerritoryId = new())
{
saschaTerritoryId.RegisteredUserId = 1;
saschaTerritoryId.SettingKey = "userTerritoryID";
saschaTerritoryId.SettingValue = "83PE89";
seedRuSettings.Add(saschaTerritoryId);
}
using (RuSettings saschaTexRand = new())
{
saschaTexRand.RegisteredUserId = 1;
saschaTexRand.SettingKey = "texRand";
saschaTexRand.SettingValue = "2";
seedRuSettings.Add(saschaTexRand);
}
using (GremlinContext db = new())
{
foreach (RuSettings userSetting in seedRuSettings)
{
userSetting.DataCreationDate = DateTime.Now;
userSetting.DataModificationByUser = "DB Initializer";
userSetting.DataModificationDate = DateTime.Now;
}
db.RuSettings.AddRange(seedRuSettings);
_ = db.SaveChanges();
}
}
public static void InsertTestDataIntoDb()
{
using GremlinContext db = new();
Random r = new();
Contact newContact = new()
{
AcademicTitle = RandomString(9),
FirstName = RandomString(5),
LastName = RandomString(20),
Gender = (byte)Gender.Male,
Department = RandomString(9),
};
_ = MetaDataSetter.ForImport(newContact, "Tester", "Seeding data for testing.");
Account newAccount = new()
{
AccountName = RandomString(20),
Street = RandomString(9),
Zip = Convert.ToUInt32(r.Next(11111, 99999)),
City = RandomString(9),
SapAccountNumber = Convert.ToUInt32(r.Next(1111111, 99999999)),
EMail = RandomString(9) + "@" + RandomString(9) + "." + RandomString(3),
PhoneNumber = "+49 " + r.Next().ToString(),
};
_ = MetaDataSetter.ForImport(newAccount, "Tester", "Seeding data for testing.");
AccountType accountType = db.AccountTypes.FirstOrDefault(a => a.AccountTypeCode == "FPC")!;
newAccount.AccountType = accountType;
SubMarket subMarket = db.SubMarkets
.First(a => a.SubMarketCode == "CEN");
newAccount.SubMarket = subMarket;
newContact.Account = newAccount;
try
{
_ = db.Add(newAccount);
_ = db.SaveChanges();
_ = db.Add(newContact);
_ = db.SaveChanges();
Debug.WriteLine($"Account {newAccount.AccountName} mit Contact {newContact.LastName} wurde angelegt.");
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
private static bool ImportContactsFromCsv(string filepath = "", string separator = ";", bool dataHasHeading = true)
{
//Pfad abfragen über Dtei-Öffnen-Dialog:
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser();
}
//Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch:
if (filepath == "")
{
return false;
}
List<Contact> contactsReadFromFile = new(8000);
// ENCODING CHANGED TO UNICODE. TO BE TESTED!
using (TextFieldParser csvParser = new(filepath, FileService.GetEncoding(filepath)))
{
//Parser configuration:
csvParser.Delimiters = new[] { separator };
csvParser.CommentTokens = new[] { "#" };
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names:
if (dataHasHeading)
{
_ = csvParser.ReadLine();
}
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
Contact importedContact = new();
string[] fields = csvParser.ReadFields()!;
//No conversion
importedContact.AcademicTitle = fields[1];
importedContact.FirstName = fields[3];
importedContact.LastName = fields[4];
importedContact.EMail = fields[6];
importedContact.Department = fields[7];
importedContact.Room = fields[8];
importedContact.PhoneNumber = fields[9];
importedContact.Function = fields[10];
importedContact.MobileNumber = fields[13];
//Convert Gender
importedContact.Gender = fields[2] == "M" ? (byte)Gender.Male : fields[2] == "F" ? (byte)Gender.Female : (byte)Gender.Unknown;
//Convert OptIn Status
importedContact.OptInStatus = fields[5] switch
{
"Opt In" => true,
"Opt Out" => false,
_ => false,
};
//Convert "SAP Contact Number"
try
{
importedContact.SapContactNumber = Convert.ToInt32(fields[11], CultureInfo.CurrentCulture);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[11];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[11];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
//Convert "Account Created on"
int year = Convert.ToInt32(fields[12].Substring(0, 4), CultureInfo.CurrentCulture);
int month = Convert.ToInt32(fields[12].Substring(4, 2), CultureInfo.CurrentCulture);
int day = Convert.ToInt32(fields[12].Substring(6, 2), CultureInfo.CurrentCulture);
importedContact.SapContactCreationDate = new(year, month, day);
//Convert "No Phone Calls"
if (fields[26] == "1")
{
importedContact.NoPhoneCalls = true;
}
//Convert "No Hardcopy Mailing"
if (fields[27] == "1")
{
importedContact.NoHardcopyMailing = true;
}
//SAPAccountID in Contact.Notes speichern, um den entsprechenden Account aus DB heraussuchen zu können:
importedContact.Notes = fields[0];
contactsReadFromFile.Add(importedContact);
}
//Eingelesenen Account in DB schreiben:
using (GremlinContext db = new())
{
foreach (Contact contact in contactsReadFromFile)
{
// AccountID aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
contact.Account = ResolveAccountById(db, uint.Parse(contact.Notes));
contact.Notes = "";
_ = MetaDataSetter.ForImport(contact, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportContactsFromCSV)");
}
db.Contacts.AddRange(contactsReadFromFile);
_ = db.SaveChanges();
}
}
//Bestätigung senden
Debug.WriteLine($"Es wurden {contactsReadFromFile.Count} Contacts erfolgreich der Datenbank hinzugefügt.");
return true;
}
private static bool ImportAccountsFromCsv(string filepath = "", string separator = ";", bool dataHasHeading = true)
{
//Pfad abfragen über Dtei-Öffnen-Dialog:
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser();
}
//Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch:
if (filepath == "")
{
return false;
}
List<Account> accountsReadFromFile = new(1000);
//ENCODING CHANGED TO ISO-8859-1 AS EQUIVALENT (?) TO "LATIN1" (unavailable). TO BE TESTED!
//ich würde mir ja UTF8 als universelles Encoding wünschen
using TextFieldParser csvParser = new(filepath, FileService.GetEncoding(filepath));
//Parser configuration:
csvParser.Delimiters = new[] { separator };
csvParser.CommentTokens = new[] { "#" };
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names:
if (dataHasHeading)
{
_ = csvParser.ReadLine();
}
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
Account importedAccount = new();
{
importedAccount.SubMarket = new();
importedAccount.AccountType = new();
importedAccount.Contacts = new List<Contact>();
}
string[] fields = csvParser.ReadFields()!;
//Konvertierung erforderlich:
try
{
importedAccount.SapAccountNumber = Convert.ToUInt32(fields[0]);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[0];
if (errorRaiser == "")
{
errorRaiser = "No Account Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[0];
if (errorRaiser == "")
{
errorRaiser = "No Account Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
try
{
importedAccount.Zip = Convert.ToUInt32(fields[2]);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[0];
if (errorRaiser == "")
{
errorRaiser = "No ZIP in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[0];
if (errorRaiser == "")
{
errorRaiser = "No ZIP in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
//Konvertierung für AccountCreatedinSAPOn Property:
//Test auf Inhalt
if (fields[8].Length != 0)
{
int year = Convert.ToInt32(fields[8].Substring(0, 4));
int month = Convert.ToInt32(fields[8].Substring(4, 2));
int day = Convert.ToInt32(fields[8].Substring(6, 2));
importedAccount.AccountCreatedInSapOn = new(year, month, day);
}
//keine Konvertierung nötig:
importedAccount.AccountName = fields[1];
importedAccount.City = fields[3];
importedAccount.Street = fields[4];
importedAccount.SubMarket.SubMarketCode = fields[5];
importedAccount.SubMarket.DataStatus = Status.Active.ToString();
importedAccount.AccountType.AccountTypeCode = fields[6];
importedAccount.AccountType.DataStatus = Status.Active.ToString();
importedAccount.PhoneNumber = fields[7];
importedAccount.DataStatus = Status.Active.ToString();
accountsReadFromFile.Add(importedAccount);
}
//Eingelesenen Account in DB schreiben:
using GremlinContext db = new();
foreach (Account account in accountsReadFromFile)
{
// AccountType aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
if (account.AccountType.AccountTypeCode != "")
{
AccountType accountType = db.AccountTypes
.First(a => a.AccountTypeCode == account.AccountType.AccountTypeCode);
account.AccountType = accountType;
account.AccountType = ResolveAccountType(db, account.AccountType.AccountTypeCode);
}
else
{
//Default
account.AccountType = ResolveAccountType(db, "FPC");
}
if (account.SubMarket.SubMarketCode != "")
{
SubMarket subMarket = db.SubMarkets
.First(a => a.SubMarketCode == account.SubMarket.SubMarketCode);
account.SubMarket = subMarket;
//Warum gelöscht? account.AccountType = ResolveAccountType(db, account.AccountType.AccountTypeCode);
account.SubMarket = ResolveSubmarket(db, account.SubMarket.SubMarketCode);
}
else
{
//Default
account.SubMarket = ResolveSubmarket(db, "COT");
}
_ = MetaDataSetter.ForImport(account, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportAccountsFromCSV)");
}
db.Accounts.AddRange(accountsReadFromFile);
_ = db.SaveChanges();
//Bestätigung senden
Debug.WriteLine($"Es wurden {accountsReadFromFile.Count} Accounts erfolgreich der Datenbank hinzugefügt.");
return true;
}
private static bool ImportLsagContactListToolData(string filepath = "", string separator = ";")
{
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser(); //Pfad abfragen über Dtei-Öffnen-Dialog:
}
if (filepath == "")
{
return false; //Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch:
}
List<Contact> contactsReadFromFile = new(8000);
List<Account> accountsReadFromFile = new(1000);
using TextFieldParser csvParser = new(filepath, FileService.GetEncoding(filepath));
//Parser configuration:
csvParser.Delimiters = new[] { separator };
csvParser.CommentTokens = new[] { "#" };
csvParser.HasFieldsEnclosedInQuotes = true;
//dynamische Spaltenzuordnung in Dictonary speichern
string[] fields = csvParser.ReadFields()!;
Dictionary<string, int> columnNumberOf = ColumnMapping(fields);
//Daten einlesen und Accounts und Kontakte in eigene List<Account> bzw List<Contact> extrahieren.
while (!csvParser.EndOfData)
{
bool dataHasError = false;
Account importedAccount = new();
Contact importedContact = new();
importedAccount.SubMarket = new();
importedAccount.AccountType = new();
importedContact.Account = new();
fields = csvParser.ReadFields()!; // Read current line fields, pointer moves to the next line.
if (fields[0] == "")
{
break; // Am Ende hängt eine leere Zeile, die im Parser einen Fehler auslösen würde.
}
//Account-Daten
// Konvertierung erforderlich:
try
{
importedAccount.SapAccountNumber = Convert.ToUInt32(fields[columnNumberOf["SAPAccountNumber"]]);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
try
{
importedAccount.Zip = Convert.ToUInt32(fields[columnNumberOf["ZIP"]]);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
// Konvertierung für AccountCreatedinSAPOn Property:
int year = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(0, 4));
int month = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(4, 2));
int day = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(6, 2));
importedAccount.AccountCreatedInSapOn = new(year, month, day);
//Convert City von Großschreibung zu Normalschreibung
importedAccount.City = fields[columnNumberOf["City"]].First().ToString() + fields[columnNumberOf["City"]].Substring(1).ToLower();
// keine Konvertierung nötig:
importedAccount.AccountName = fields[columnNumberOf["AccountName"]];
importedAccount.Street = fields[columnNumberOf["Street"]];
importedAccount.SubMarket.SubMarketCode = fields[columnNumberOf["SubMarketCode"]];
importedAccount.SubMarket.DataStatus = Status.Active.ToString();
importedAccount.AccountType.AccountTypeCode = fields[columnNumberOf["AccountTypeCode"]];
importedAccount.AccountType.DataStatus = Status.Active.ToString();
importedAccount.PhoneNumber = fields[columnNumberOf["AccountPhoneNumber"]];
importedAccount.DataStatus = Status.Active.ToString();
//Validierungen:
if (importedAccount.AccountName == ""
|| importedAccount.City == ""
|| importedAccount.Street == ""
|| importedAccount.SubMarket.SubMarketCode == ""
|| importedAccount.AccountType.AccountTypeCode == ""
|| importedAccount.SapAccountNumber == 0)
{
dataHasError = true;
}
//Validierten Account der Liste hinzufügen:
if (dataHasError == false)
{
_ = importedAccount.AddIfUniqueTo(accountsReadFromFile);
}
//Contact-Daten
// Ohne Konvertierung
importedContact.AcademicTitle = fields[columnNumberOf["AcademicTitle"]];
importedContact.FirstName = fields[columnNumberOf["FirstName"]];
importedContact.LastName = fields[columnNumberOf["LastName"]];
importedContact.EMail = fields[columnNumberOf["EMail"]];
importedContact.Department = fields[columnNumberOf["Department"]];
importedContact.Room = fields[columnNumberOf["Room"]];
importedContact.PhoneNumber = fields[columnNumberOf["PhoneNumber"]];
importedContact.Function = fields[columnNumberOf["Function"]];
importedContact.MobileNumber = fields[columnNumberOf["MobileNumber"]];
//Convert Gender
importedContact.Gender = fields[columnNumberOf["Gender"]] == "M"
? (byte)Gender.Male
: fields[columnNumberOf["Gender"]] == "F" ? (byte)Gender.Female : (byte)Gender.Unknown;
//Convert OptIn Status
importedContact.OptInStatus = fields[columnNumberOf["OptInStatus"]] == "Opt In" || (fields[columnNumberOf["OptInStatus"]] == "Opt Out" && false);
//Convert "SAP Contact Number"
try
{
importedContact.SapContactNumber = Convert.ToInt32(fields[columnNumberOf["SAPContactNumber"]]);
}
catch (FormatException ex)
{
//errorhandling here: "Unable to parse input: " + field[0] + " as uint"
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
catch (OverflowException ex)
{
//errorhandling here: "Number cannot fit in an Uint."
//Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben
string errorRaiser = fields[columnNumberOf["SAPContactNumber"]];
if (errorRaiser == "")
{
errorRaiser = "No Contact Number in this row!";
}
DisplayErrorDetails(ex, errorRaiser);
return false;
}
//Convert "Account Created on"
year = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(0, 4));
month = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(4, 2));
day = Convert.ToInt32(fields[columnNumberOf["AccountCreatedInSAPOn"]].Substring(6, 2));
importedContact.SapContactCreationDate = new(year, month, day);
//Convert "No Phone Calls"
if (fields[columnNumberOf["NoPhoneCalls"]] == "1")
{
importedContact.NoPhoneCalls = true;
}
//Convert "No Hardcopy Mailing"
if (fields[columnNumberOf["NoHardcopyMailing"]] == "1")
{
importedContact.NoHardcopyMailing = true;
}
//SAPAccountID in Contact.Notes speichern, um den entsprechenden Account aus DB heraussuchen zu können:
importedContact.Notes = fields[columnNumberOf["SAPAccountNumber"]];
//Validierungen:
if (importedContact.EmailBounced || importedContact.LastName == "" || importedContact.SapContactNumber == 0)
{
dataHasError = true;
}
if (importedContact.EMail.Length > 7 && importedContact.EMail.Substring(0, 8) == "bounced-")
{
dataHasError = true;
}
//Validierte Kontakte zu Liste hinzufügen:
if (dataHasError == false)
{
contactsReadFromFile.Add(importedContact);
}
}
//Eingelesenen Account in DB schreiben:
using (GremlinContext db = new())
{
foreach (Account account in accountsReadFromFile)
{
// AccountType aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
AccountType accountType = db.AccountTypes
.First(a => a.AccountTypeCode == account.AccountType.AccountTypeCode);
account.AccountType = accountType;
SubMarket subMarket = db.SubMarkets
.First(a => a.SubMarketCode == account.SubMarket.SubMarketCode);
account.SubMarket = subMarket;
account.AccountType = ResolveAccountType(db, account.AccountType.AccountTypeCode);
account.SubMarket = ResolveSubmarket(db, account.SubMarket.SubMarketCode);
_ = MetaDataSetter.ForImport(account, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportAccountsFromCSV)");
}
db.Accounts.AddRange(accountsReadFromFile);
_ = db.SaveChanges();
}
//Eingelesenen Contacts in DB schreiben:
using (GremlinContext db = new())
{
foreach (Contact contact in contactsReadFromFile)
{
contact.Account = ResolveAccountById(db, uint.Parse(contact.Notes));
contact.Notes = "";
_ = MetaDataSetter.ForImport(contact, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportContactsFromCSV)");
}
db.Contacts.AddRange(contactsReadFromFile);
_ = db.SaveChanges();
}
return true;
}
private static bool ImportProductsFromCsv(string filepath = "", string separator = "|", bool dataHasHeading = true)
{
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser(); //Pfad abfragen über Dtei-Öffnen-Dialog.
}
if (filepath == "")
{
return false; //Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch.
}
List<Product> productsReadFromFile = new(ParseProductFile(filepath, separator, dataHasHeading));
return InsertProducts(productsReadFromFile);
}
private static bool ImportCustomDescriptionsFromCsv(string filepath = "", string separator = "|", bool dataHasHeading = true)
{
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser("Delimited Data File|*.csv; *.txt; *.tsv"); //Pfad abfragen über Dtei-Öffnen-Dialog:
}
if (filepath == "")
{
return false; //Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch:
}
List<CustomDescription> cDsReadFromFile = new(2500);
using TextFieldParser csvParser = new(filepath, Encoding.GetEncoding("UTF-8"));
//Parser configuration:
csvParser.Delimiters = new[] { separator };
csvParser.CommentTokens = new[] { "#" };
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names:
if (dataHasHeading)
{
_ = csvParser.ReadLine();
}
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
string[] fields = csvParser.ReadFields()!;
//string productNumber = fields[0];
//string? optionNumber = fields[1] == "" ? null : fields[1];
CustomDescription importedCd = new()
{
ProductNumber = fields[0],
OptionNumber = fields[1],
Heading = fields[3],
DescriptionText = fields[4],
CoverletterText = fields[5],
Notes = fields[6],
Products = new List<Product>(),
Supplier = new()
{
AccountName = fields[2] is "" or "RB" ? "Agilent Technologies" : fields[2]
}
};
_ = MetaDataSetter.ForImport(importedCd, "Importer", "Initial Importer by CD-ImporterFomCsv");
cDsReadFromFile.Add(importedCd);
}
//Eingelesenen Custum Desciptions in DB schreiben:
//Check for 3PPs that not yet in the db.
//Step 1a: Add 3PP-Products from CD-List to list
//Step 1b: Add list to db.
//Step 2: Add CDs to products.
using GremlinContext db = new();
//Step 1a
List<Product> thirdPartyProductsFromImportedCDs = new();
foreach (CustomDescription cd in cDsReadFromFile)
{
if (cd.Supplier.AccountName != "Agilent Technologies")
{
Product new3PpProduct = new()
{
CustomDescription = cd,
HasBreakPrices = false,
ListPrice = 0,
ProductNumber = cd.ProductNumber,
OptionNumber = cd.OptionNumber,
ProductStatus = Status.Active.ToString(),
SapLongDescription = "",
SapShortDescription = "",
Weight = 0,
IntroductionDate = DateTime.Now.Date,
BreakRangeFrom = 0,
BreakRangeTo = 0,
ProductLine = ResolveProductLine(db, "3P")
};
new3PpProduct.CustomDescription.Supplier = ResolveAccountByName(db, new3PpProduct.CustomDescription.Supplier.AccountName);
_ = MetaDataSetter.ForImport(new3PpProduct, "Custom Desciption Importer", "Created at import from Custom Descriptions.");
thirdPartyProductsFromImportedCDs.Add(new3PpProduct);
}
}
//Step 1b
db.Products.AddRange(thirdPartyProductsFromImportedCDs); //Imports both the products and the associated custom descriptions!
_ = db.SaveChanges();
// Produkt aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
//Step 2
List<CustomDescription> importedCDsWithEfReferences = new(100000);
List<CustomDescription> cDsWithoutEfReferences = new(1000); //nur zur Kontrolle, wird nicht verwendet.
List<Product> productsInDb = db.Products.ToList();
Account agilent = db.Accounts.Single(a => a.AccountName == "Agilent Technologies");
foreach (CustomDescription cd in cDsReadFromFile.Where(cd => !thirdPartyProductsFromImportedCDs.Intersect(cd.Products).Any()))
{
//Establish EF Reference. If no PN/Opt found, then skip this custom description.
//CD.Product = GetProduct(db, CD.ProductNumber, CD.OptionNumber); //ResolveXY-functions return null, if no match is found in db.
cd.Products = productsInDb.Where(product => product.ProductNumber == cd.ProductNumber && product.OptionNumber == cd.OptionNumber).ToList();
//Establish EF Reference: If no Supplier-Account found, then skip this custom description (shouldn't happen), else set Supplier to Agilent (3PP-Supplier have been processed before and their products should not be part of this list).
cd.Supplier = agilent;
_ = MetaDataSetter.ForImport(cd, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportCustomDescriptionsFromCsv)");
//add to final list of CDs, that will go into the db.
importedCDsWithEfReferences.Add(cd);
}
db.CustomDescriptions.AddRange(importedCDsWithEfReferences);
_ = db.SaveChanges();
//Bestätigung senden
Debug.WriteLine($"Es wurden {importedCDsWithEfReferences.Count} eigene Beschreibungen erfolgreich der Datenbank hinzugefügt.{Environment.NewLine}Es wurden {thirdPartyProductsFromImportedCDs.Count} 3PP-Produkte neu angelegt.");
return true;
}
//public static bool ImportCustomDescriptionsFromDocx(string filepath = "")
/////Importiert Eigene Beschreibungen aus Word-Dokument
/////Abkürzung: CD = CustomDescriptions
//{
// if (filepath == "")
// {
// //filepath = FileIO.GetFilepathFromUser("Word-Dokument|*.doc; *.docx; *.docm"); //Pfad abfragen über Dtei-Öffnen-Dialog:
// }
// if (filepath == "")
// {
// return false; //Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch:
// }
// List<CustomDescription> importedCDs = new(2500);
// WordprocessingDocument CDDoc = WordprocessingDocument.Open(filepath, false); //alternativ in using-Block packen, dann kann man nicht das Document.Close vergessen.
// Table CDTable = CDDoc.MainDocumentPart.Document.Body.Elements<Table>().ElementAt(0); //alternativ: Elements<Table>.First()
// if (CDTable != null)
// {
// CDTable.Descendants<TableRow>().First().Remove(); //Überschriften löschen
// foreach (TableRow aRow in CDTable.Descendants<TableRow>()) //einmal durch den XML-Baum wandern: WordprocessingDocument -> MainDocumentPart -> Document -> Body -> Table -> TableRow -> TableCell -> Paragraph -> Run -> Text
// {
// CustomDescription importedCD = new();
// List<string> stringsRead = new();
// foreach (TableCell aCell in aRow.Descendants<TableCell>())
// {
// StringBuilder sb = new();
// StringBuilder note = new();
// foreach (Paragraph aParagraph in aCell.Descendants<Paragraph>())
// {
// foreach (Run aRun in aParagraph.Descendants<Run>())
// {
// foreach (Text aText in aRun.Descendants<Text>())
// {
// string temp = "";
// temp = aParagraph.Descendants<Run>()
// .Where(r => r.RunProperties.Vanish != null)
// .Aggregate("", (text, r) => text += r.InnerText); //nach hidden Text filtern, diesen in "note" speichern
// _ = note.Append(temp);
// if (temp == "") //true, falls kein hidden Text => normalen Text verketten...
// {
// _ = sb.Append(aText.InnerText);
// }
// }
// }
// }
// stringsRead.Add(sb.ToString()); //...und zellenweise abspeichern
// if (note.ToString() != "")
// {
// importedCD.Notes = note.ToString();
// }
// }
// importedCD.ProductNumber = stringsRead.ElementAt(0);
// importedCD.OptionNumber = stringsRead.ElementAt(1) == "" ? null : stringsRead.ElementAt(1);
// importedCD.Supplier.AccountName = stringsRead.ElementAt(2) is "" or "RB" ? "Agilent Technologies" : stringsRead.ElementAt(2);
// importedCD.Heading = stringsRead.ElementAt(3);
// importedCD.DescriptionText = stringsRead.ElementAt(4);
// importedCD.CoverletterText = stringsRead.ElementAt(5);
// importedCDs.Add(importedCD);
// //if (importedCDs.Count >= 1200 && importedCDs.Count % 100 == 0) MessageBox.Show(importedCDs.Count.ToString()); //DEBUGGING
// }
// }
// //MessageBox.Show(importedCDs.Count.ToString()); //DEBUGGING
// CDDoc.Close();
// //Eingelesenen Custum Desciptions in DB schreiben:
// //Check for 3PPs that not yet in the db.
// //Step 1a: Add 3PP-Products from CD-List to list
// //Step 1b: Add list to db.
// //Step 2: Add CDs to products.
// using (GremlinContext db = new())
// {
// //Step 1a
// List<Product> thirdPartyProductsFromImportedCDs = new();
// foreach (CustomDescription CD in importedCDs)
// {
// if (CD.Supplier.AccountName != "Agilent Technologies")
// {
// Product new3PPProduct = new()
// {
// CustomDescription = CD,
// HasBreakPrices = false,
// ListPrice = 0,
// ProductNumber = CD.ProductNumber,
// OptionNumber = CD.OptionNumber,
// ProductStatus = Status.Active.ToString(),
// SapLongDescription = null,
// SapShortDescription = null,
// Weight = 0,
// IntroductionDate = DateTime.Now.Date,
// BreakRangeFrom = 0,
// BreakRangeTo = 0,
// ProductLine = ResolveProductLine(db, "3P")
// };
// new3PPProduct.CustomDescription.Supplier = ResolveAccountByName(db, new3PPProduct.CustomDescription.Supplier.AccountName);
// _ = MetaDataSetter.ForImport(new3PPProduct, "Custom Desciption Importer", "Created at import from Custom Descriptions.");
// thirdPartyProductsFromImportedCDs.Add(new3PPProduct);
// }
// };
// //Step 1b
// db.Products.AddRange(thirdPartyProductsFromImportedCDs); //Imports both the products and the associated custom descriptions!
// _ = db.SaveChanges();
// // Produkt aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen.
// //Step 2
// List<CustomDescription> importedCDsWithEFReferences = new();
// List<CustomDescription> CDsWithoutEFReferences = new(); //nur zur Kontrolle, wird nicht verwendet.
// foreach (CustomDescription CD in importedCDs)
// {
// //Skip Desciptions, if it has been already imported above (as part from 3PP)
// if (thirdPartyProductsFromImportedCDs.Intersect(CD.Products).Any())
// {
// continue;
// }
// //Establish EF Reference. If no PN/Opt found, then skip this custom description.
// CD.Products.Add(ResolveProduct(db, CD.ProductNumber, CD.OptionNumber)); //ResolveXY-functions return null, if no match is found in db.
// if (CD.Products == null)
// {
// CDsWithoutEFReferences.Add(CD);
// continue;
// }
// //Establish EF Reference. If no Supplier-Account found, then skip this custom description.
// CD.Supplier = ResolveAccountByName(db, CD.Supplier.AccountName); //ResolveXY-functions return null, if no match is found in db.
// if (CD.Supplier == null)
// {
// CDsWithoutEFReferences.Add(CD);
// continue;
// }
// _ = MetaDataSetter.ForImport(CD, "DocxImporter", "Initial import by CSV Importer (Function DbHelper.ImportCustomDescriptionsFromDocx)");
// //add to final list of CDs, that will go into the db.
// importedCDsWithEFReferences.Add(CD);
// }
// db.CustomDescriptions.AddRange(importedCDsWithEFReferences);
// _ = db.SaveChanges();
// }
// return true;
//}
private static bool UpdateProductsFromCsv(string filepath = "", string separator = "|", bool dataHasHeading = true)
{
if (filepath == "")
{
//filepath = FileIO.GetFilepathFromUser(); //Pfad abfragen über Dtei-Öffnen-Dialog.
}
string fileName = ExtractFileName(filepath);
if (filepath == "")
{
return false; //Wenn keine Datei ausgewählt (Cancel geklickt), dann Abbruch.
}
//Aktiven Produkte aus DB laden
using GremlinContext db = new();
//Aktive Produkte aus der DB einlesen
List<Product> activeProductsInDb = db.Products
.Where(p => p.DataStatus == Status.Active.ToString())
.Include(p => p.ProductLine)
.ToList();
//Neue CPL einlesen...
List<Product> productsReadFromFile = new(ParseProductFile(filepath, separator, dataHasHeading));
//...Aufteilung in neue und nicht-neue Produkte...
List<Product> newProducts = new(FilterNewProducts(productsReadFromFile));
List<Product> nonNewProducts = new(productsReadFromFile.Except(newProducts));
//...Letztere wiederum in obsolete Produkte...
ProductEqualityComparer proEc = new();
List<Product> obsoleteProducts = new(activeProductsInDb.Except(nonNewProducts, proEc));
DateTime now = DateTime.Now;
//Obsolete Produkte prozessieren
foreach (Product product in obsoleteProducts)
{
product.ProductStatus = Status.Obsolete.ToString();
product.DataStatus = Status.Archived.ToString();
product.DataValidUntil = now;
product.DataModificationDate = now;
product.DataModificationByUser = "CPL Updater";
}
//den Rest prozessieren
List<Product> updatedProducts = new(10000);
string dataVersionComment = "Data as per " + fileName;
////Counter für Debugging
//int x = 0;
//Multithreading
_ = Parallel.ForEach(nonNewProducts, parallelOptions, (product) =>
{
////Debugging-Counter
//x++;
Product? existingProduct = activeProductsInDb
.FirstOrDefault(p => p.ProductNumber == product.ProductNumber
&& p.OptionNumber == product.OptionNumber
&& p.BreakRangeFrom == product.BreakRangeFrom
&& p.BreakRangeTo == product.BreakRangeTo);
//Checkpoint, falls ein neues Produkt versehentlich auf "Active" statt auf "New" gesetzt ist.
if (existingProduct == null)
{
newProducts.Add(product);
return;
}
//Wenn keine Änderung, dann nur DataVersionComment mit CPL-Dateinamen aktualisieren...
if (product.ListPrice == existingProduct.ListPrice
&& product.ProductStatus == existingProduct.ProductStatus
&& Normalize(product.SapLongDescription) == Normalize(existingProduct.SapLongDescription)
&& Normalize(product.SapShortDescription) == Normalize(existingProduct.SapShortDescription))
{
existingProduct.DataVersionComment = dataVersionComment;
}
//andernfalls die vorhandene Datensatz-Version archivieren und neue Version anlegen
else //alten Datensatz archivieren:
{
existingProduct.DataStatus = Status.Archived.ToString();
existingProduct.DataModificationByUser = "CPL Updater";
existingProduct.DataModificationDate = now;
existingProduct.DataValidUntil = now;
existingProduct.DataVersionComment = "Archived b/c of data update";
if (product.ListPrice != existingProduct.ListPrice)
{
existingProduct.ProductStatus = Status.PriceUpdated.ToString();
}
else if (Normalize(product.SapLongDescription) != Normalize(existingProduct.SapLongDescription)
|| Normalize(product.SapShortDescription) != Normalize(existingProduct.SapShortDescription))
{
existingProduct.ProductStatus = Status.DescriptionUpdated.ToString();
}
else if (product.ProductStatus == existingProduct.ProductStatus)
{
existingProduct.ProductStatus = Status.StatusUpdated.ToString();
}
//neuen Datensatz anlegen
Product updatedProduct = new()
{
ProductNumber = product.ProductNumber,
OptionNumber = product.OptionNumber,
BreakRangeFrom = product.BreakRangeFrom,
BreakRangeTo = product.BreakRangeTo,
HasBreakPrices = product.HasBreakPrices,
IntroductionDate = existingProduct.IntroductionDate,
ListPrice = product.ListPrice,
ProductStatus = Status.Active.ToString(),
SapLongDescription = product.SapLongDescription,
SapShortDescription = product.SapShortDescription,
Weight = product.Weight,
};
_ = MetaDataSetter.ForImport(updatedProduct, "CPL Updater", "Product Update");
updatedProduct.ProductLine = product.ProductLine.ProductLineCode == existingProduct.ProductLine.ProductLineCode
? product.ProductLine
: ResolveProductLine(db, product.ProductLine.ProductLineCode);
newProducts.Add(updatedProduct);
}
updatedProducts.Add(existingProduct);
});
_ = InsertProducts(newProducts); //enthält auch ResolvePL()
db.Products.UpdateRange(obsoleteProducts);
db.Products.UpdateRange(updatedProducts);
int changes = db.SaveChanges();
Debug.WriteLine($"Es wurden {changes} Änderungen an der Datenbank durchgeführt.\n Davon waren {updatedProducts.Count} UpdatedProducts, {obsoleteProducts.Count} ObsoleteProducts und {newProducts.Count} NewProducts");
return true;
}
private static Dictionary<string, int> ColumnMapping(string[] fields)
{
Dictionary<string, int> result = new();
for (int i = 0; i < fields.Length; i++)
{
switch (fields[i]
.ToLower(CultureInfo.CurrentCulture)
.Trim()
.Replace(" ", "")
.Replace("-", "")
.Replace("_", "")
.Replace(".", "")
.Replace(":", "")
)
{
case "accountid" or "fcustomercd" or "sapaccountnumber":
result.Add("SAPAccountNumber", i);
break;
case "accountname" or "fcustomername":
result.Add("AccountName", i);
break;
case "postalcode" or "fpostcode" or "zip":
result.Add("ZIP", i);
break;
case "city" or "ftown":
result.Add("City", i);
break;
case "street" or "faddress":
result.Add("Street", i);
break;
case "title" or "ftitletext1":
result.Add("AcademicTitle", i);
break;
case "gender" or "fgender1":
result.Add("Gender", i);
break;
case "firstname" or "ffirstname":
result.Add("FirstName", i);
break;
case "lastname" or "flastname":
result.Add("LastName", i);
break;
case "optin" or "femailopt":
result.Add("OptInStatus", i);
break;
case "workemail" or "fworkemail1" or "accountemail":
result.Add("EMail", i);
break;
case "bouncedbackemail" or "fbouncedback":
result.Add("EmailBounced", i);
break;
case "department" or "fdepartment1":
result.Add("Department", i);
break;
case "roomnumber" or "froomnumber":
result.Add("Room", i);
break;
case "workphone" or "fworkphone1" or "phonenumber":
result.Add("PhoneNumber", i);
break;
case "faxnumber":
result.Add("FaxNumber", i);
break;
case "function" or "ffunction":
result.Add("Function", i);
break;
case "contactid" or "fcontactcd":
result.Add("SAPContactNumber", i);
break;
case "contactcreationdate" or "fcreationdate":
result.Add("SAPContactCreationDate", i);
break;
case "mobilephone" or "fmobilephone":
result.Add("MobileNumber", i);
break;
case "jobfunction" or "fjobfunction":
result.Add("MA_JobFunctions", i);
break;
case "productinterest" or "fproductinterest":
result.Add("MA_ProductInterests", i);
break;
case "applicationinterest" or "fapplicationinterest":
result.Add("MA_ApplicationInterests", i);
break;
case "joblevel" or "fjoblevel":
result.Add("MA_JobLevel", i);
break;
case "contactindustry" or "fcontactindustry":
result.Add("MA_ContactIndustry", i);
break;
case "competitiveibase" or "fcompetativeibase":
result.Add("MA_CompetitiveIBase", i);
break;
case "submarketcode" or "fsubmarketcd":
result.Add("SubMarketCode", i);
break;
case "customertypecode" or "fcustomertypecd" or "accounttype" or "accounttypecode":
result.Add("AccountTypeCode", i);
break;
case "phone" or "fphone":
result.Add("AccountPhoneNumber", i);
break;
case "accountcreatedon" or "fcreatedon":
result.Add("AccountCreatedInSAPOn", i);
break;
case "contactchangedon" or "fcontactchangedon":
result.Add("SAPContactModifiedDate", i);
break;
case "notelephonecalls" or "fnotelephonecalls":
result.Add("NoPhoneCalls", i);
break;
case "nohardcopymailing" or "fnohardcopymailing":
result.Add("NoHardcopyMailing", i);
break;
case "accounttypedescription":
result.Add("AccountTypeDescription", i);
break;
case "submarketdescription":
result.Add("SubMarketDescription", i);
break;
case "productlineabbreviation":
result.Add("ProductLineAbbreviation", i);
break;
case "productlinedescription":
result.Add("ProductLineDescription", i);
break;
}
}
return result;
}
private static List<Product> ParseProductFile(string filepath, string separator, bool dataHasHeading)
{
List<Product> results = new(100000);
//ENCODING CHANGED TO ISO-8859-1 AS EQUIVALENT (?) TO "LATIN1" (unavailable). TO BE TESTED!
using TextFieldParser csvParser = new(filepath, FileService.GetEncoding(filepath));
//Parser configuration:
csvParser.Delimiters = new[] { separator };
csvParser.CommentTokens = new[] { "#" };
csvParser.HasFieldsEnclosedInQuotes = true;
// Skip the row with the column names:
if (dataHasHeading)
{
_ = csvParser.ReadLine();
}
//decimal oldListPrice;
//bool listpriceHasChanged;
while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
Product importedProduct = new()
{
ProductLine = new()
};
string[] fields = csvParser.ReadFields() ?? new string[1];
//Kontrolle, ob Trennzeichen in Daten vorhanden ist:
if (fields.Length > 27) //27 ist der Normalfall
{
//Sonderfall "EnVisionTM G|2":
//suche fields[x], das auf " G" endet und auf das field[x+1] mit "2 " beginnt, kombiniere die beiden und schiebe alle darauffolgenden Felder in diesem Datensatz eins nach links.
for (int i = 0; i < fields.Length - 1; i++) //fields.Length - 1, um durch i+1 nicht aus dem Index zu laufen
{
if (fields[i].EndsWith(" G"))
{
if (fields[i + 1].StartsWith("2 "))
{
fields[i] = fields[i] + fields[i + 1];
for (int j = i + 2; j < fields.Length; j++)
{
fields[j - 1] = fields[j];
}
}
}
}
}
//NICHT ausgewertete Felder:
//fields[0] = Position [in CPL]
//fields[8] = Warranty
//fields[10] = PH Code
//fields[11] = PH Description
//fields[15] = Country of Manufacturing
//fields[16] = ECCL
//fields[17] = M41
//fields[18] = First Supplier Code
//fields[19] = Harmonized Tarif Schedule
//fields[20] = Hazardous Good Flag
//fields[21] = Order instructions
//fields[23] = End of Production Date
//fields[24] = End of Support Date
importedProduct.ProductNumber = fields[1];
if (fields[2].Length == 4) //Optionsnummer mit führendem Apostroph
{
importedProduct.OptionNumber = fields[2].Substring(1); //schneidet erstes Zeichen/Apostroph weg
}
else if (fields[2].Length == 3) //3-stellige Optionsnummer übernehmen; keine Aktion bei leerem Feld (keine Optionsnummer)
{
importedProduct.OptionNumber = fields[2];
}
importedProduct.SapShortDescription = fields[3];
importedProduct.ListPrice = decimal.Parse(fields[4], new CultureInfo("de-de")); //parsing! compare with old value (either from CSV or from DB) -> price change?
//if (fields[5] != "")
// if (decimal.Parse(fields[5], new CultureInfo("de-de")) != ImportedProduct.ListPrice)
// listpriceHasChanged = true;
importedProduct.BreakRangeFrom = fields[6] == "" ? 0 : Convert.ToInt32(fields[6]);
importedProduct.HasBreakPrices = importedProduct.BreakRangeFrom > 0;
importedProduct.BreakRangeTo = fields[7] is "" or "+" ? 0 : Convert.ToInt32(fields[7]); //erfasst sowohl Produkte ohne Break-Preise ("") als auch "+" bei Mengenangaben a la "100+" (= von 100 bis unendlich)
importedProduct.ProductLine.ProductLineCode = fields[9];
switch (fields[12])
{
case "Active":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.Active.ToString();
break;
case "New Product" or "New":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.New.ToString();
break;
case "Price Changed":
importedProduct.DataStatus = Status.Active.ToString();
importedProduct.ProductStatus = Status.PriceUpdated.ToString();
break;
default:
importedProduct.DataStatus = Status.StatusUpdated.ToString();
break;
}
importedProduct.Weight = ParseWeight(fields[14]);
if (fields[13] == "G")
{
importedProduct.Weight /= 1000; //Umrechnung g in kg
}
if (fields[22] != "")
{
importedProduct.IntroductionDate = DateTime.Parse(fields[22]);
}
importedProduct.SapLongDescription = fields[25];
results.Add(importedProduct);
}
return results;
}
private static float ParseWeight(string input)
{
StringBuilder sb = new();
if (input.StartsWith("."))
{
input = sb.Append(0).Append(input).ToString();
}
try
{
return float.Parse(input, new CultureInfo("en-us"));
}
catch (Exception ex)
{
Debug.WriteLine(ex, "Unhandled Error in Function 'ParseWeight'");
return 0;
}
}
private static bool InsertProducts(List<Product> products)
{
using GremlinContext db = new();
List<ProductLine> productLines = db.ProductLines.ToList();
foreach (Product product in products)
{
product.ProductLine = productLines.Find(x => x.ProductLineCode == product.ProductLine.ProductLineCode) ?? new ProductLine();
_ = MetaDataSetter.ForImport(product, "CSVImporter", "Initial import by CSV Importer (Function DbHelper.ImportProductsFromCSV)");
}
db.Products.AddRange(products);
_ = db.SaveChanges();
//Bestätigung senden
// Debug.WriteLine($"Es wurden {products.Count} Produkte erfolgreich der Datenbank hinzugefügt.");
Debug.WriteLine($"Es wurden {products.Count} Produkte erfolgreich der Datenbank hinzugefügt.");
return true;
}
public static void DisplayErrorDetails(Exception ex, string errorRaiser)
{
string errorMessage = $"Source: {ex.Source}{Environment.NewLine}" + $"Message: {ex.Message}{Environment.NewLine}{Environment.NewLine}" + $"Contact: {errorRaiser}";
Debug.WriteLine(errorMessage);
// Debug.WriteLine(errorMessage);
}
private static string ExtractFileName(string filepath, bool withExtension = false)
{
string[] fields = filepath.Split(@"\");
string[] filename = fields[^1].Split(".");
return withExtension ? fields[^1] : filename[0];
}
private static string Normalize(string input)
{
return input.ToLower()
.Trim()
.Replace(" ", "")
.Replace("-", "")
.Replace("_", "")
.Replace(".", "");
}
private static List<Product> FilterNewProducts(List<Product> products)
{
DateTime now = DateTime.Now;
List<Product> results = new(5000);
foreach (Product product in products)
{
if (product.ProductStatus == Status.New.ToString())
{
product.DataCreationDate = now;
product.DataModificationDate = now;
product.DataValidFrom = now;
product.DataValidUntil = farInTheFuture;
product.DataModificationByUser = "CPL Updater";
product.DataVersionNumber = 1;
product.DataVersionComment = "Initial Import by CSV Importer/Updater";
results.Add(product);
}
}
return results;
}
//private static List<Product> FilterProductsWithPriceChange(List<Product> products)
//{
// List<Product> results = new();
// foreach (var product in products)
// {
// if (product.ProductStatus == Status.PriceUpdated.ToString())
// {
// results.Add(product);
// }
// }
// return results;
//}
public static async Task<bool> ImportLsagContactListToolDataAsync() => await Task.Run(() => ImportLsagContactListToolData());
public static async Task<bool> ImportAccountsFromCsvAsync() => await Task.Run(() => ImportAccountsFromCsv());
public static async Task<bool> ImportContactsFromCsvAsync() => await Task.Run(() => ImportContactsFromCsv());
public static async Task<bool> ImportProductsFromCsvAsync() => await Task.Run(() => ImportProductsFromCsv());
//public static async Task<bool> ImportCustomDescriptionsFromDocxAsync() => await Task.Run(() => ImportCustomDescriptionsFromDocx());
public static async Task<bool> ImportCustomDescriptionsFromCsvAsync() => await Task.Run(() => ImportCustomDescriptionsFromCsv());
public static async Task<bool> UpdateProductsFromCsvAsync() => await Task.Run(() => UpdateProductsFromCsv());
public static Account ResolveAccountByName(GremlinContext context, string accountName)
{
try { return context.Accounts.Include(account => account.AccountType).Include(account => account.SubMarket).First(account => account.AccountName == accountName); }
catch { return new(); }
}
public static Account ResolveAccountById(GremlinContext context, uint accountId)
{
try { return context.Accounts.First(account => account.SapAccountNumber == accountId); }
catch { return new(); }
}
public static Account ResolveAccountById(GremlinContext context, string accountId)
{
try { return context.Accounts.First(account => account.SapAccountNumber == Convert.ToUInt32(accountId)); }
catch { return new(); }
}
public static AccountType ResolveAccountType(GremlinContext context, string accountTypeCode)
{
try { return context.AccountTypes.First(account => account.AccountTypeCode == accountTypeCode); }
catch { return new(); }
}
public static SubMarket ResolveSubmarket(GremlinContext context, string subMarketCode)
{
try { return context.SubMarkets.First(submarket => submarket.SubMarketCode == subMarketCode); }
catch { return new(); }
}
public static Product GetProduct(GremlinContext context, string productNumber, string optionNumber)
{
try { return context.Products.Include(product => product.ProductLine).Include(product => product.CustomDescription).First(product => product.ProductNumber == productNumber && product.OptionNumber == optionNumber); }
catch { return new(); }
}
public static Product ResolveProduct(GremlinContext context, string productNumber, string option)
{
try { return context.Products.First(product => product.ProductNumber == productNumber && product.OptionNumber == option); }
catch { return new(); }
}
public static ProductLine ResolveProductLine(GremlinContext context, string productLineCode)
{
try { return context.ProductLines.First(productline => productline.ProductLineCode == productLineCode); }
catch { return new(); }
}
private static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return new(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray());
}
public static Contact GetContact(GremlinContext context, string lastName)
{
try { return context.Contacts.First(contact => contact.LastName == lastName); }
catch { return new(); }
}
public static Contact GetContact(GremlinContext context, uint contactId)
{
try { return context.Contacts.First(contact => contact.ContactId == contactId); }
catch { return new(); }
}
internal static List<Contact> GetAllContacts(GremlinContext context)
{
try { return context.Contacts.Where(contact => contact.LastName != "").ToList(); }
catch { return new(); }
}
}
}