237 lines
13 KiB
C#
237 lines
13 KiB
C#
using Blazorise;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using Gremlin_BlazorServer.Data.EntityClasses;
|
|
using MySqlX.XDevAPI.Common;
|
|
|
|
namespace Gremlin_BlazorServer.Services;
|
|
|
|
public class GenericImporter {
|
|
|
|
public static async void ImportCsv<T>(string fileContent) where T : class, IMetadata, new() {
|
|
Console.WriteLine($"GENERIC IMPORTER: Importing {typeof(T)} from csv...");
|
|
List<string[]> splitLines = SplitLines(fileContent);
|
|
Console.WriteLine($"Found {splitLines.Count} potential {typeof(T)} in csv.");
|
|
int i = await ParseLinesToResultSet<T>(splitLines);
|
|
Console.WriteLine(i > 0 ? $"GENERIC IMPORTER: wrote {i} to db." : "GENERIC IMPORTER: Error by writing to db!");
|
|
}
|
|
|
|
private static List<string[]> SplitLines(string fileContent) {
|
|
IEnumerable<string> fileLines = fileContent.Split(Environment.NewLine.ToCharArray());
|
|
List<string[]> fileList = (from clipboardLine in fileLines where clipboardLine != "" select clipboardLine.Split('\t')).ToList();
|
|
return fileList;
|
|
}
|
|
|
|
private static void DrawTextProgressBar(int progress, int total)
|
|
{
|
|
//draw empty progress bar
|
|
Console.CursorLeft = 0;
|
|
Console.Write("["); //start
|
|
Console.CursorLeft = 64;
|
|
Console.Write("]"); //end
|
|
Console.CursorLeft = 1;
|
|
float onechunk = 62.0f / total;
|
|
|
|
//draw filled part
|
|
int position = 1;
|
|
for (int i = 0; i < onechunk * progress; i++)
|
|
{
|
|
Console.BackgroundColor = ConsoleColor.Green;
|
|
Console.CursorLeft = position++;
|
|
Console.Write(" ");
|
|
}
|
|
|
|
//draw unfilled part
|
|
for (int i = position; i <= 63 ; i++)
|
|
{
|
|
Console.BackgroundColor = ConsoleColor.Gray;
|
|
Console.CursorLeft = position++;
|
|
Console.Write(" ");
|
|
}
|
|
|
|
//draw totals
|
|
Console.CursorLeft = 66;
|
|
Console.BackgroundColor = ConsoleColor.Black;
|
|
int percent = (int)MathF.Round((float)progress / total * 100, 0);
|
|
Console.Write($"{progress} of {total} [{percent}%] "); //blanks at the end remove any excess
|
|
}
|
|
private static async Task<int> ParseLinesToResultSet<T>(IEnumerable<string[]> lineList) where T : class, IMetadata, new() {
|
|
Tuple<T?, T?>? resultSet = new(new(), new());
|
|
List<T> updatedItems = new();
|
|
List<T> newItems = new();
|
|
IList<T>? allItems = await GenericController.GetAllAsync<T>();
|
|
if (allItems is null) return 0;
|
|
int i = 0;
|
|
|
|
IEnumerable<string[]> lines = lineList.Select(strings => strings.Select(x => x.Replace("\"", string.Empty)).ToArray()).ToList();
|
|
foreach (string[] line in lines) {
|
|
i++;
|
|
if (typeof(T) == typeof(Account)) resultSet = ParseToAccount(line, allItems as IList<Account>) as Tuple<T?, T?>;
|
|
if (typeof(T) == typeof(Contact)) resultSet = ParseToContact(line, allItems as IList<Contact>) as Tuple<T?, T?>;
|
|
if (typeof(T) == typeof(Product)) resultSet = ParseToProduct(line, allItems as IList<Product>) as Tuple<T?, T?>;
|
|
|
|
if (resultSet is null) continue;
|
|
if (resultSet.Item1 is not null) updatedItems.Add(resultSet.Item1);
|
|
if (resultSet.Item2 is not null) newItems.Add(resultSet.Item2);
|
|
DrawTextProgressBar(i, lines.Count());
|
|
}
|
|
|
|
int success = 0;
|
|
Console.WriteLine();
|
|
Console.WriteLine($"Found {updatedItems.Count} updates and {newItems.Count} new items.");
|
|
if (updatedItems.Count > 0) success += GenericController.Update(updatedItems);
|
|
if (newItems.Count > 0) success += GenericController.Insert(newItems);
|
|
|
|
return success;
|
|
}
|
|
|
|
private static Tuple<Account?, Account?>? ParseToAccount(IReadOnlyList<string> line, IEnumerable<Account> allAccounts) {
|
|
// "ID" "Name" "Street" "City" "BP Role" "Postal Code" "Customer Type" "Market Indicator" "Phone" "E-Mail" "Market code"
|
|
|
|
Account? updatedAccount = null;
|
|
Account? newAccount = null;
|
|
|
|
if (line[0].Contains("ID")) return null; //HACK: skip first row if header
|
|
if (!uint.TryParse(line[0], out uint sapAccountNumber)) return null; //HACK: skip wrong SapAccountNumbers
|
|
if (!uint.TryParse(line[5], out uint zip)) return null; //HACK: skip wrong ZIPs
|
|
|
|
string accountTypeCode = line[6];
|
|
if (accountTypeCode is "" or null) accountTypeCode = "FPC"; // standard AccountType
|
|
|
|
string subMarketCode = line[7];
|
|
if (subMarketCode is "" or null) subMarketCode = "CCH"; // standard AccountType
|
|
|
|
Account readAccount = new(){
|
|
SapAccountNumber = sapAccountNumber,
|
|
AccountName = line[1], //System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[1].ToLower())
|
|
Street = line[2],
|
|
City = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[3].ToLower()), //line[3].ToUpper().First() + line[3][1..].ToLower(),
|
|
Zip = zip,
|
|
PhoneNumber = line[8],
|
|
EMail = line[9],
|
|
DataModificationByUser = "Gremlin Generic Importer",
|
|
AccountTypeCode = accountTypeCode,
|
|
SubMarketCode = subMarketCode
|
|
};
|
|
|
|
Account? existingAccount = allAccounts.FirstOrDefault(a => a.SapAccountNumber == sapAccountNumber || a.AccountName == readAccount.AccountName);
|
|
if (existingAccount is not null) {
|
|
if (existingAccount.Equals(readAccount)) return null;
|
|
existingAccount.DataModificationDate = DateTime.Now;
|
|
existingAccount.DataVersionNumber++;
|
|
existingAccount.DataModificationByUser = "Updated by Gremlin Generic Importer";
|
|
if (existingAccount.AccountName != readAccount.AccountName) existingAccount.AccountName = readAccount.AccountName;
|
|
if (existingAccount.City != readAccount.City) existingAccount.City = readAccount.City;
|
|
if (existingAccount.EMail != readAccount.EMail) existingAccount.EMail = readAccount.EMail;
|
|
if (existingAccount.PhoneNumber != readAccount.PhoneNumber) existingAccount.PhoneNumber = readAccount.PhoneNumber;
|
|
if (existingAccount.Street != readAccount.Street) existingAccount.Street = readAccount.Street;
|
|
if (existingAccount.Zip != readAccount.Zip) existingAccount.Zip = readAccount.Zip;
|
|
// Console.WriteLine($"Update in Contact {existingAccount.SapAccountNumber}:{existingAccount.AccountName}");
|
|
updatedAccount = existingAccount;
|
|
}
|
|
else {
|
|
// Console.WriteLine($"Account {readAccount.SapAccountNumber}:{readAccount.AccountName} ist neu!");
|
|
newAccount = readAccount;
|
|
}
|
|
|
|
return new(updatedAccount, newAccount);
|
|
}
|
|
|
|
private static Tuple<Contact?, Contact?>? ParseToContact(IReadOnlyList<string> line, IEnumerable<Contact> allContacts) {
|
|
//"Contact ID" "Account ID" "Last Name" "First Name" "Acct Name 1 and 2" "Street - Work Address" "Postal Code - Work Address" "City - Work Address" "E-Mail" "Phone" "E-Mail Opt"
|
|
|
|
Contact? updatedContact = null;
|
|
Contact? newContact = null;
|
|
|
|
if (line[0].Contains("ID")) return null; //HACK: skip first row if header
|
|
if (!uint.TryParse(line[0], out uint sapContactNumber)) return null; //HACK: skip wrong SapContactNumbers
|
|
if (!uint.TryParse(line[1], out uint sapAccountNumber)) return null; //HACK: skip wrong SapAccountNumbers
|
|
Account? account = GenericController.Get<Account>(a => a.SapAccountNumber.Equals(sapAccountNumber));
|
|
if (account is null)
|
|
// Console.WriteLine($"Account with SapAccountNumber {sapAccountNumber} is not existing!!");
|
|
return null; //HACK: skip empty Accounts
|
|
|
|
Contact readContact = new(){
|
|
SapContactNumber = sapContactNumber,
|
|
AccountId = account.AccountId,
|
|
LastName = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[2].ToLower()),
|
|
FirstName = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[3].ToLower()),
|
|
EMail = line[8].ToLower(),
|
|
EmailBounced = line[8].Contains("bounced"),
|
|
PhoneNumber = line[9],
|
|
OptInStatus = line[10] == "Opt In",
|
|
DataModificationByUser = "Gremlin Generic Importer",
|
|
Gender = 0
|
|
};
|
|
|
|
Contact? existingContact = allContacts.FirstOrDefault(a => a.SapContactNumber == sapContactNumber || (a.LastName == readContact.LastName && a.FirstName == readContact.FirstName));
|
|
if (existingContact is not null) {
|
|
if (existingContact.Equals(readContact)) return null;
|
|
existingContact.DataModificationDate = DateTime.Now;
|
|
existingContact.DataVersionNumber++;
|
|
existingContact.DataModificationByUser = "Updated by Gremlin Generic Importer";
|
|
existingContact.AccountId = readContact.AccountId;
|
|
existingContact.LastName = readContact.LastName;
|
|
existingContact.EMail = readContact.EMail;
|
|
existingContact.PhoneNumber = readContact.PhoneNumber;
|
|
existingContact.EmailBounced = readContact.EmailBounced;
|
|
existingContact.OptInStatus = readContact.OptInStatus;
|
|
// Console.WriteLine($"Update in Contact {existingContact.SapContactNumber}:{existingContact.FirstName} {existingContact.LastName}");
|
|
updatedContact = existingContact;
|
|
}
|
|
else {
|
|
// Console.WriteLine($"Contact {readContact.SapContactNumber}:{readContact.FirstName} {readContact.LastName} ist neu!");
|
|
newContact = readContact;
|
|
}
|
|
|
|
return new(updatedContact, newContact);
|
|
}
|
|
|
|
private static Tuple<Product?, Product?>? ParseToProduct(IReadOnlyList<string> line, IEnumerable<Product> allProducts) {
|
|
//Position Partnumber Option Description Current Month Price(EUR) Previous Month Price(EUR) Breaks Range From Breaks Range To Warranty Productline PH Code PH Description Status Air Packaged Unit Air Packaged Weight Country of Manufacturing ECCL M41 First Supplier code Harmonized Tarif Schedule Hazardous Goods Flag Order Instructions Introduction Date End of Production Date End of Support Date Long Description
|
|
Product? updatedProduct = null;
|
|
Product? newProduct = null;
|
|
|
|
if (line[0].Contains("Position")) return null; //HACK: skip first row if header
|
|
if (!decimal.TryParse(line[4], out decimal listPrice)) return null; //ListPrice not convertable
|
|
if (!int.TryParse(line[6], out int breakRangeFrom)) breakRangeFrom = 0; //no BreakRangeFrom
|
|
if (!int.TryParse(line[7], out int breakRangeTo)) breakRangeTo = 0; //no BreakRangeTo
|
|
if (!float.TryParse(line[13], out float weight)) weight = 0; //no Weight
|
|
|
|
Product readProduct = new(){
|
|
ProductNumber = line[1],
|
|
OptionNumber = line[2].Replace("'", ""),
|
|
SapShortDescription = line[3],
|
|
ListPrice = listPrice,
|
|
ProductLineCode = line[9],
|
|
SapLongDescription = line[25],
|
|
BreakRangeFrom = breakRangeFrom,
|
|
BreakRangeTo = breakRangeTo,
|
|
Weight = weight,
|
|
DataModificationByUser = "Gremlin Generic Importer",
|
|
};
|
|
|
|
Product? existingProduct = allProducts.FirstOrDefault(p => p.ProductNumber.Equals(readProduct.ProductNumber) && p.OptionNumber.Equals(readProduct.OptionNumber));
|
|
if (existingProduct is not null) {
|
|
if (existingProduct.Equals(readProduct)) return null;
|
|
existingProduct.DataModificationDate = DateTime.Now;
|
|
existingProduct.DataVersionNumber++;
|
|
existingProduct.DataModificationByUser = "Updated by Gremlin Generic Importer";
|
|
existingProduct.SapShortDescription = readProduct.SapShortDescription;
|
|
existingProduct.ListPrice = readProduct.ListPrice;
|
|
existingProduct.ProductLine = readProduct.ProductLine;
|
|
existingProduct.SapLongDescription = readProduct.SapLongDescription;
|
|
existingProduct.BreakRangeFrom = readProduct.BreakRangeFrom;
|
|
existingProduct.BreakRangeTo = readProduct.BreakRangeTo;
|
|
existingProduct.HasBreakPrices = existingProduct.BreakRangeFrom != 0 || existingProduct.BreakRangeTo != 0;
|
|
existingProduct.Weight = readProduct.Weight;
|
|
// Console.WriteLine($"Update in Product {existingProduct.ProductNumber}#{existingProduct.OptionNumber}:{existingProduct.SapShortDescription}");
|
|
updatedProduct = existingProduct;
|
|
}
|
|
else {
|
|
// Console.WriteLine($"Product {readProduct.ProductNumber}#{readProduct.OptionNumber}:{readProduct.SapShortDescription} ist neu!");
|
|
newProduct = readProduct;
|
|
}
|
|
|
|
return new(updatedProduct, newProduct);
|
|
}
|
|
} |