Product Import

pull/3/head
Sascha Woitschetzki 2023-08-22 17:05:54 +07:00
parent d70383bbc1
commit f2deb16b3d
27 changed files with 1033 additions and 133 deletions

@ -52,53 +52,56 @@ public class Account : IMetadata
//IBase //IBase
//tbd //tbd
//public bool Equals(Account other) public bool Equals(Account? other) {
//{ if (other is null) return false;
// if (other == null) return false; return SapAccountNumber == other.SapAccountNumber
// if (this == null) return false; && AccountName == other.AccountName
// if (this.SAPAccountNumber == other.SAPAccountNumber) return true; && City == other.City
// else return false; && EMail == other.EMail
//} && PhoneNumber == other.PhoneNumber
&& Street == other.Street
//public static bool operator ==(Account account1, Account account2) && Zip == other.Zip;
//{ }
// if (account1.SAPAccountNumber == 0) return false;
// if (account2.SAPAccountNumber == 0) return false; public static bool operator ==(Account? account1, Account? account2) {
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return true; if (account1 is null || account2 is null) return false;
// else return false; if (account1.SapAccountNumber == 0) return false;
//} if (account2.SapAccountNumber == 0) return false;
return account1.SapAccountNumber == account2.SapAccountNumber
//public static bool operator !=(Account account1, Account account2) && account1.AccountName == account2.AccountName
//{ && account1.City == account2.City
// if (account1.SAPAccountNumber == 0) return false; && account1.EMail == account2.EMail
// if (account2.SAPAccountNumber == 0) return false; && account1.PhoneNumber == account2.PhoneNumber
// if (account1.SAPAccountNumber == account2.SAPAccountNumber) return false; && account1.Street == account2.Street
// else return true; && account1.Zip == account2.Zip;
//} }
//public override bool Equals(object obj) public static bool operator !=(Account? account1, Account? account2)
//{ {
// if (obj == null) return false; if (account1 is null || account2 is null) return false;
if (account1.SapAccountNumber == 0) return false;
// Account accountObj = obj as Account; if (account2.SapAccountNumber == 0) return false;
// if (accountObj == null) return false; return account1.SapAccountNumber != account2.SapAccountNumber
// else return base.Equals(obj); && account1.AccountName != account2.AccountName
//} && account1.City != account2.City
&& account1.EMail != account2.EMail
//public override int GetHashCode() && account1.PhoneNumber != account2.PhoneNumber
/////<summary> && account1.Street != account2.Street
/////Returns the (unique) SAP Account ID. && account1.Zip != account2.Zip;
/////</summary>
//{ }
// return Convert.ToInt32(this.SAPAccountNumber);
//} // public override bool Equals(object? obj) {
// if (obj == null) return false;
// return obj is Account && base.Equals(obj);
// }
//
// public override int GetHashCode() => $"{AccountName}:{City}:{Street}:{Zip}".GetHashCode();
public List<Account> AddIfUniqueTo(List<Account> accounts) { public List<Account> AddIfUniqueTo(List<Account> accounts) {
if (accounts.Count > 0 && SapAccountNumber == accounts[^1].SapAccountNumber) return accounts; if (accounts.Count > 0 && SapAccountNumber == accounts[^1].SapAccountNumber) return accounts;
foreach (Account account in accounts) if (accounts.Any(account => SapAccountNumber == account.SapAccountNumber)) return accounts;
if (SapAccountNumber == account.SapAccountNumber)
return accounts;
accounts.Add(this); accounts.Add(this);
return accounts; return accounts;

@ -1,4 +1,6 @@
namespace Gremlin_BlazorServer.Data.EntityClasses; using Newtonsoft.Json;
namespace Gremlin_BlazorServer.Data.EntityClasses;
public class Contact : IMetadata { public class Contact : IMetadata {
//primary key: //primary key:
@ -51,4 +53,49 @@ public class Contact : IMetadata {
public uint DataVersionNumber { get; set; } = 1; public uint DataVersionNumber { get; set; } = 1;
public string? DataVersionComment { get; set; } public string? DataVersionComment { get; set; }
public string DataStatus { get; set; } = "Active"; public string DataStatus { get; set; } = "Active";
public bool Equals(Contact? other) {
if (other is null) return false;
return SapContactNumber == other.SapContactNumber
&& LastName == other.LastName
&& FirstName == other.FirstName
&& EMail == other.EMail
&& PhoneNumber == other.PhoneNumber
&& EmailBounced == other.EmailBounced
&& OptInStatus == other.OptInStatus;
}
public static bool operator ==(Contact? contact1, Contact? contact2) {
if (contact1 is null || contact2 is null) return false;
if (contact1.SapContactNumber == 0) return false;
if (contact2.SapContactNumber == 0) return false;
return contact1.SapContactNumber == contact2.SapContactNumber
&& contact1.LastName == contact2.LastName
&& contact1.FirstName == contact2.FirstName
&& contact1.EMail == contact2.EMail
&& contact1.PhoneNumber == contact2.PhoneNumber
&& contact1.EmailBounced == contact2.EmailBounced
&& contact1.OptInStatus == contact2.OptInStatus;
}
public static bool operator !=(Contact? contact1, Contact? contact2)
{
if (contact1 is null || contact2 is null) return false;
if (contact1.SapContactNumber == 0) return false;
if (contact2.SapContactNumber == 0) return false;
return contact1.SapContactNumber != contact2.SapContactNumber
&& contact1.LastName != contact2.LastName
&& contact1.FirstName != contact2.FirstName
&& contact1.EMail != contact2.EMail
&& contact1.PhoneNumber != contact2.PhoneNumber
&& contact1.EmailBounced != contact2.EmailBounced
&& contact1.OptInStatus != contact2.OptInStatus;
}
// public override bool Equals(object? obj) {
// if (obj == null) return false;
// return obj is Account && base.Equals(obj);
// }
//
// public override int GetHashCode() => $"{LastName}:{FirstName}:{EMail}".GetHashCode();
} }

@ -9,4 +9,5 @@ public interface IMetadata {
DateTime DataValidUntil { get; set; } DateTime DataValidUntil { get; set; }
string? DataVersionComment { get; set; } string? DataVersionComment { get; set; }
uint DataVersionNumber { get; set; } uint DataVersionNumber { get; set; }
int HashCode => GetHashCode();
} }

@ -36,4 +36,40 @@ public class Product : IMetadata {
public uint DataVersionNumber { get; set; } public uint DataVersionNumber { get; set; }
public string? DataVersionComment { get; set; } public string? DataVersionComment { get; set; }
public string DataStatus { get; set; } = "Active"; public string DataStatus { get; set; } = "Active";
public bool Equals(Product? other) {
if (other is null) return false;
return ProductNumber == other.ProductNumber
&& OptionNumber == other.OptionNumber
&& SapShortDescription == other.SapShortDescription
&& SapLongDescription == other.SapLongDescription
&& ListPrice == other.ListPrice;
}
public static bool operator ==(Product? product1, Product? product2) {
if (product1 is null || product2 is null) return false;
return product1.ProductNumber == product2.ProductNumber
&& product1.OptionNumber == product2.OptionNumber
&& product1.SapShortDescription == product2.SapShortDescription
&& product1.SapLongDescription == product2.SapLongDescription
&& product1.ListPrice == product2.ListPrice;
}
public static bool operator !=(Product? product1, Product? product2)
{
if (product1 is null || product2 is null) return false;
return product1.ProductNumber != product2.ProductNumber
&& product1.OptionNumber != product2.OptionNumber
&& product1.SapShortDescription != product2.SapShortDescription
&& product1.SapLongDescription != product2.SapLongDescription
&& product1.ListPrice != product2.ListPrice;
}
// public override bool Equals(object? obj) {
// if (obj == null) return false;
// return obj is Account && base.Equals(obj);
// }
// public override int GetHashCode() => $"{ProductNumber}:{OptionNumber}".GetHashCode();
} }

@ -103,17 +103,16 @@
Padding="Padding.Is3" Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40"> style="box-shadow: 10px 10px #343A40">
<Paragraph>
<Button Color="Color.Primary" Clicked="@OnRemoveDublicates">Remove Dublicates</Button>
</Paragraph>
<Heading Size="HeadingSize.Is6">Import Accounts from CSV</Heading> <Heading Size="HeadingSize.Is6">Import Accounts from CSV</Heading>
<Paragraph> <Paragraph>
<Field> <Field>
<FileEdit Filter=".csv" Changed="@OnImportAccounts"/> <FileEdit Filter=".csv" Changed="@OnImportAccounts"/>
</Field> </Field>
</Paragraph> </Paragraph>
<Paragraph>
<Progress>
<ProgressBar Animated Value="@ImportProgress"/>
</Progress>
</Paragraph>
</Div> </Div>
</Authorized> </Authorized>

@ -84,4 +84,9 @@ public partial class Accounts {
// newAccount.SubMarket = await GenericController.GetAsync<SubMarket>(sM => sM.SubMarketCode.Equals("VEN")); // newAccount.SubMarket = await GenericController.GetAsync<SubMarket>(sM => sM.SubMarketCode.Equals("VEN"));
return newAccount; return newAccount;
} }
private static async Task OnRemoveDublicates() {
int i = await GenericController.RemoveDublicatesAsync<Account>();
Console.WriteLine($"Removed {i} dublicates from Accounts.");
}
} }

@ -144,6 +144,10 @@
Padding="Padding.Is3" Padding="Padding.Is3"
style="box-shadow: 10px 10px #343A40"> style="box-shadow: 10px 10px #343A40">
<Paragraph>
<Button Color="Color.Primary" Clicked="@OnRemoveDublicates">Remove Dublicates</Button>
</Paragraph>
<Heading Size="HeadingSize.Is6">Import Contacts from CSV</Heading> <Heading Size="HeadingSize.Is6">Import Contacts from CSV</Heading>
<Paragraph> <Paragraph>
<Field> <Field>

@ -89,4 +89,9 @@ public partial class Contacts {
StateHasChanged(); StateHasChanged();
} }
private static async Task OnRemoveDublicates() {
int i = await GenericController.RemoveDublicatesAsync<Contact>();
Console.WriteLine($"Removed {i} dublicates from Contacts.");
}
} }

@ -98,7 +98,23 @@
</Paragraph> </Paragraph>
</Div> </Div>
} *@ } *@
</Authorized> <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 Products from TSV</Heading>
<Paragraph>
<Field>
<FileEdit Filter=".tsv" Changed="@OnImportProducts"/>
</Field>
</Paragraph>
</Div>
</Authorized>
<NotAuthorized> <NotAuthorized>
<Div Margin="Margin.Is3" <Div Margin="Margin.Is3"
Border="Border.Dark.OnAll" Border="Border.Dark.OnAll"

@ -1,4 +1,5 @@
using System.Security.Claims; using System.Security.Claims;
using Blazorise;
using Gremlin_BlazorServer.Data.EntityClasses; using Gremlin_BlazorServer.Data.EntityClasses;
using Gremlin_BlazorServer.Services; using Gremlin_BlazorServer.Services;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -29,4 +30,27 @@ public partial class Products {
selectedProduct = newSelectedProduct; selectedProduct = newSelectedProduct;
selectedProduct.CustomDescription = GenericController.Get<CustomDescription>(cD => cD.ProductNumber.Equals(selectedProduct.ProductNumber) && cD.OptionNumber.Equals(selectedProduct.OptionNumber)); selectedProduct.CustomDescription = GenericController.Get<CustomDescription>(cD => cD.ProductNumber.Equals(selectedProduct.ProductNumber) && cD.OptionNumber.Equals(selectedProduct.OptionNumber));
} }
private static async Task OnRemoveDublicates() {
int i = await GenericController.RemoveDublicatesAsync<Product>();
Console.WriteLine($"Removed {i} dublicates from Products.");
}
private async Task OnImportProducts(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<Product>(fileContent);
}
}
catch (Exception exception) {
Console.WriteLine(exception.Message);
}
StateHasChanged();
}
} }

@ -20,7 +20,7 @@ public class GenericController {
} }
} }
public IList<TResult>? GetAll<TResult>(Predicate<TResult> search) where TResult : class, IMetadata { public static IList<TResult>? GetAll<TResult>(Predicate<TResult> search) where TResult : class, IMetadata {
ArgumentNullException.ThrowIfNull(search); ArgumentNullException.ThrowIfNull(search);
try { try {
return gremlinDb.Set<TResult>().AsEnumerable().Where(t => search(t)).ToList(); return gremlinDb.Set<TResult>().AsEnumerable().Where(t => search(t)).ToList();
@ -210,9 +210,9 @@ public class GenericController {
private static void HandleConcurrencyExceptions(DbUpdateException ex) { private static void HandleConcurrencyExceptions(DbUpdateException ex) {
Console.WriteLine($"!!! HandleConcurrencyException: {ex.Message}"); Console.WriteLine($"!!! HandleConcurrencyException: {ex.Message}");
foreach (EntityEntry entry in ex.Entries) { foreach (EntityEntry entry in ex.Entries)
switch (entry.Entity) { switch (entry.Entity) {
case Quote or CustomDescription or Account or Contact: { case Quote or CustomDescription or Account or Contact or Product: {
PropertyValues proposedValues = entry.CurrentValues; PropertyValues proposedValues = entry.CurrentValues;
PropertyValues? databaseValues = entry.GetDatabaseValues(); PropertyValues? databaseValues = entry.GetDatabaseValues();
@ -230,6 +230,18 @@ public class GenericController {
default: default:
throw new NotSupportedException("Don't know how to handle concurrency conflicts for " + entry.Metadata.Name); throw new NotSupportedException("Don't know how to handle concurrency conflicts for " + entry.Metadata.Name);
} }
}
public static async Task<int> RemoveDublicatesAsync<T>() where T : class, IMetadata {
try {
List<T> entities = gremlinDb.Set<T>().AsEnumerable().GroupBy(e => new { e.HashCode}).SelectMany(grp => grp.Skip(1)).ToList();
gremlinDb.Set<T>().RemoveRange(entities);
return await gremlinDb.SaveChangesAsync();
}
catch (Exception exception) {
Console.WriteLine(exception.InnerException);
return 0;
} }
} }
} }

@ -7,11 +7,12 @@ namespace Gremlin_BlazorServer.Services;
public class GenericImporter { public class GenericImporter {
public static void ImportCsv<T>(string fileContent) where T : class, IMetadata, new() { public static async void ImportCsv<T>(string fileContent) where T : class, IMetadata, new() {
Console.WriteLine($"GENERIC IMPORTER: Importing {typeof(T)} from csv..."); Console.WriteLine($"GENERIC IMPORTER: Importing {typeof(T)} from csv...");
List<string[]> splitLines = SplitLines(fileContent); List<string[]> splitLines = SplitLines(fileContent);
Console.WriteLine($"Found {splitLines.Count} potential {typeof(T)} in csv."); Console.WriteLine($"Found {splitLines.Count} potential {typeof(T)} in csv.");
Console.WriteLine(ParseLinesToResultSet<T>(splitLines) ? $"GENERIC IMPORTER: Ready." : $"GENERIC IMPORTER: Error by writing to db!"); 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) { private static List<string[]> SplitLines(string fileContent) {
@ -20,29 +21,71 @@ public class GenericImporter {
return fileList; return fileList;
} }
private static bool ParseLinesToResultSet<T>(IEnumerable<string[]> lineList) where T : class, IMetadata, new() { 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()); Tuple<T?, T?>? resultSet = new(new(), new());
List<T> updatedItems = new(); List<T> updatedItems = new();
List<T> newItems = new(); List<T> newItems = new();
IList<T>? allItems = await GenericController.GetAllAsync<T>();
if (allItems is null) return 0;
int i = 0;
foreach (string[] line in lineList.Select(strings => strings.Select(x => x.Replace("\"", string.Empty)).ToArray())) { // Delete all "" IEnumerable<string[]> lines = lineList.Select(strings => strings.Select(x => x.Replace("\"", string.Empty)).ToArray()).ToList();
if (typeof(T) == typeof(Account)) resultSet = ParseToAccount(line) as Tuple<T?, T?>; foreach (string[] line in lines) {
if (typeof(T) == typeof(Contact)) resultSet = ParseToContact(line) as Tuple<T?, T?>; 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 is null) continue;
if (resultSet.Item1 is not null) updatedItems.Add(resultSet.Item1); if (resultSet.Item1 is not null) updatedItems.Add(resultSet.Item1);
if (resultSet.Item2 is not null) newItems.Add(resultSet.Item2); if (resultSet.Item2 is not null) newItems.Add(resultSet.Item2);
DrawTextProgressBar(i, lines.Count());
} }
int success = 0; int success = 0;
Console.WriteLine();
Console.WriteLine($"Found {updatedItems.Count} updates and {newItems.Count} new items."); Console.WriteLine($"Found {updatedItems.Count} updates and {newItems.Count} new items.");
if (updatedItems.Count > 0) success += GenericController.Update(updatedItems); if (updatedItems.Count > 0) success += GenericController.Update(updatedItems);
if (newItems.Count > 0) success += GenericController.Insert(newItems); if (newItems.Count > 0) success += GenericController.Insert(newItems);
return success > 0; return success;
} }
private static Tuple<Account?, Account?>? ParseToAccount(IReadOnlyList<string> line) { // "ID" "Name" "Street" "City" "BP Role" "Postal Code" "Customer Type" "Market Indicator" "Phone" "E-Mail" "Market code" 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? updatedAccount = null;
Account? newAccount = null; Account? newAccount = null;
@ -57,7 +100,7 @@ public class GenericImporter {
string subMarketCode = line[7]; string subMarketCode = line[7];
if (subMarketCode is "" or null) subMarketCode = "CCH"; // standard AccountType if (subMarketCode is "" or null) subMarketCode = "CCH"; // standard AccountType
Account readAccount = new() { Account readAccount = new(){
SapAccountNumber = sapAccountNumber, SapAccountNumber = sapAccountNumber,
AccountName = line[1], //System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[1].ToLower()) AccountName = line[1], //System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[1].ToLower())
Street = line[2], Street = line[2],
@ -70,30 +113,31 @@ public class GenericImporter {
SubMarketCode = subMarketCode SubMarketCode = subMarketCode
}; };
Account? existingAccount = GenericController.Get<Account>(a => a.SapAccountNumber == sapAccountNumber || a.AccountName == readAccount.AccountName); Account? existingAccount = allAccounts.FirstOrDefault(a => a.SapAccountNumber == sapAccountNumber || a.AccountName == readAccount.AccountName);
if (existingAccount is not null) { if (existingAccount is not null) {
if (IsEqualAccount(readAccount, existingAccount)) return null; if (existingAccount.Equals(readAccount)) return null;
existingAccount.DataModificationDate = DateTime.Now; existingAccount.DataModificationDate = DateTime.Now;
existingAccount.DataVersionNumber++; existingAccount.DataVersionNumber++;
existingAccount.DataModificationByUser = "Updated by Gremlin Generic Importer"; existingAccount.DataModificationByUser = "Updated by Gremlin Generic Importer";
existingAccount.AccountName = readAccount.AccountName; if (existingAccount.AccountName != readAccount.AccountName) existingAccount.AccountName = readAccount.AccountName;
existingAccount.City = readAccount.City; if (existingAccount.City != readAccount.City) existingAccount.City = readAccount.City;
existingAccount.EMail = readAccount.EMail; if (existingAccount.EMail != readAccount.EMail) existingAccount.EMail = readAccount.EMail;
existingAccount.PhoneNumber = readAccount.PhoneNumber; if (existingAccount.PhoneNumber != readAccount.PhoneNumber) existingAccount.PhoneNumber = readAccount.PhoneNumber;
existingAccount.Street = readAccount.Street; if (existingAccount.Street != readAccount.Street) existingAccount.Street = readAccount.Street;
existingAccount.Zip = readAccount.Zip; if (existingAccount.Zip != readAccount.Zip) existingAccount.Zip = readAccount.Zip;
// Console.WriteLine($"Update in Contact {existingAccount.SapAccountNumber}:{existingAccount.AccountName}");
updatedAccount = existingAccount; updatedAccount = existingAccount;
} }
else { else {
Console.WriteLine($"Account {readAccount.SapAccountNumber}:{readAccount.AccountName} ist neu!"); // Console.WriteLine($"Account {readAccount.SapAccountNumber}:{readAccount.AccountName} ist neu!");
newAccount = readAccount; newAccount = readAccount;
} }
return new(updatedAccount, newAccount); return new(updatedAccount, newAccount);
} }
private static Tuple<Contact?, Contact?>? ParseToContact(IReadOnlyList<string> line) { //"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" 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? updatedContact = null;
Contact? newContact = null; Contact? newContact = null;
@ -102,12 +146,11 @@ public class GenericImporter {
if (!uint.TryParse(line[0], out uint sapContactNumber)) return null; //HACK: skip wrong SapContactNumbers 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 if (!uint.TryParse(line[1], out uint sapAccountNumber)) return null; //HACK: skip wrong SapAccountNumbers
Account? account = GenericController.Get<Account>(a => a.SapAccountNumber.Equals(sapAccountNumber)); Account? account = GenericController.Get<Account>(a => a.SapAccountNumber.Equals(sapAccountNumber));
if (account is null) { if (account is null)
Console.WriteLine($"Account with SapAccountNumber {sapAccountNumber} is not existing!!"); // Console.WriteLine($"Account with SapAccountNumber {sapAccountNumber} is not existing!!");
return null; //HACK: skip empty Accounts return null; //HACK: skip empty Accounts
}
Contact readContact = new() { Contact readContact = new(){
SapContactNumber = sapContactNumber, SapContactNumber = sapContactNumber,
AccountId = account.AccountId, AccountId = account.AccountId,
LastName = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[2].ToLower()), LastName = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(line[2].ToLower()),
@ -120,14 +163,9 @@ public class GenericImporter {
Gender = 0 Gender = 0
}; };
Contact? existingContact = GenericController.Get<Contact>(a => a.SapContactNumber == sapContactNumber || (a.LastName == readContact.LastName && a.FirstName == readContact.FirstName)); Contact? existingContact = allContacts.FirstOrDefault(a => a.SapContactNumber == sapContactNumber || (a.LastName == readContact.LastName && a.FirstName == readContact.FirstName));
if (existingContact is not null) { if (existingContact is not null) {
if (existingContact.Equals(readContact)) return null;
if (IsEqualContact(readContact, existingContact)) {
// Console.WriteLine($"---> The contact {readContact.SapContactNumber}:{readContact.FirstName} {readContact.LastName} ist aktuell.");
return null;
}
existingContact.DataModificationDate = DateTime.Now; existingContact.DataModificationDate = DateTime.Now;
existingContact.DataVersionNumber++; existingContact.DataVersionNumber++;
existingContact.DataModificationByUser = "Updated by Gremlin Generic Importer"; existingContact.DataModificationByUser = "Updated by Gremlin Generic Importer";
@ -137,57 +175,63 @@ public class GenericImporter {
existingContact.PhoneNumber = readContact.PhoneNumber; existingContact.PhoneNumber = readContact.PhoneNumber;
existingContact.EmailBounced = readContact.EmailBounced; existingContact.EmailBounced = readContact.EmailBounced;
existingContact.OptInStatus = readContact.OptInStatus; existingContact.OptInStatus = readContact.OptInStatus;
Console.WriteLine($"Update in Contact {existingContact.SapContactNumber}:{existingContact.FirstName} {existingContact.LastName}"); // Console.WriteLine($"Update in Contact {existingContact.SapContactNumber}:{existingContact.FirstName} {existingContact.LastName}");
updatedContact = existingContact; updatedContact = existingContact;
} }
else { else {
Console.WriteLine($"Contact {readContact.SapContactNumber}:{readContact.FirstName} {readContact.LastName} ist neu!"); // Console.WriteLine($"Contact {readContact.SapContactNumber}:{readContact.FirstName} {readContact.LastName} ist neu!");
newContact = readContact; newContact = readContact;
} }
return new(updatedContact, newContact); return new(updatedContact, newContact);
} }
private static bool IsEqualAccount(Account readAccount, Account existingAccount) { private static Tuple<Product?, Product?>? ParseToProduct(IReadOnlyList<string> line, IEnumerable<Product> allProducts) {
bool equalAccount = true; //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
if (readAccount.AccountName != existingAccount.AccountName) { Product? updatedProduct = null;
equalAccount = false; Product? newProduct = null;
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---");
Console.WriteLine($"{readAccount.AccountName} | {existingAccount.AccountName}"); 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 (readAccount.City != existingAccount.City) { if (!int.TryParse(line[6], out int breakRangeFrom)) breakRangeFrom = 0; //no BreakRangeFrom
equalAccount = false; if (!int.TryParse(line[7], out int breakRangeTo)) breakRangeTo = 0; //no BreakRangeTo
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---"); if (!float.TryParse(line[13], out float weight)) weight = 0; //no Weight
Console.WriteLine($"{readAccount.City} | {existingAccount.City}");
} Product readProduct = new(){
if (readAccount.EMail != existingAccount.EMail) { ProductNumber = line[1],
equalAccount = false; OptionNumber = line[2].Replace("'", ""),
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---"); SapShortDescription = line[3],
Console.WriteLine($"{readAccount.EMail} | {existingAccount.EMail}"); ListPrice = listPrice,
} ProductLineCode = line[9],
if (readAccount.PhoneNumber != existingAccount.PhoneNumber) { SapLongDescription = line[25],
equalAccount = false; BreakRangeFrom = breakRangeFrom,
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---"); BreakRangeTo = breakRangeTo,
Console.WriteLine($"{readAccount.PhoneNumber} | {existingAccount.PhoneNumber}"); Weight = weight,
} DataModificationByUser = "Gremlin Generic Importer",
if (readAccount.Street != existingAccount.Street) { };
equalAccount = false;
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---"); Product? existingProduct = allProducts.FirstOrDefault(p => p.ProductNumber.Equals(readProduct.ProductNumber) && p.OptionNumber.Equals(readProduct.OptionNumber));
Console.WriteLine($"{readAccount.Street} | {existingAccount.Street}"); 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;
} }
if (readAccount.Zip != existingAccount.Zip) { else {
equalAccount = false; // Console.WriteLine($"Product {readProduct.ProductNumber}#{readProduct.OptionNumber}:{readProduct.SapShortDescription} ist neu!");
Console.WriteLine($"--- UPDATE in Account {readAccount.SapAccountNumber} ---"); newProduct = readProduct;
Console.WriteLine($"{readAccount.Zip} | {existingAccount.Zip}");
} }
return equalAccount;
}
private static bool IsEqualContact(Contact readContact, Contact existingContact) return new(updatedProduct, newProduct);
=> readContact.LastName == existingContact.LastName }
&& readContact.FirstName == existingContact.FirstName
&& readContact.EMail == existingContact.EMail
&& readContact.PhoneNumber == existingContact.PhoneNumber
&& readContact.EmailBounced == existingContact.EmailBounced
&& readContact.OptInStatus == existingContact.OptInStatus;
} }

@ -0,0 +1,137 @@
\documentclass[a4paper,ngerman,parskip,10pt]{scrlttr2}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[hidelinks]{hyperref}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage[right]{{eurosym}}
\usepackage[locale=DE]{{siunitx}}
\usepackage{{scrlayer-scrpage}}
\usepackage{{lastpage}}
\usepackage{{graphicx}}
\usepackage{{multirow}}
\usepackage{{longtable}}
\usepackage{{enumitem}}
\usepackage{{fp, xstring, spreadtab, numprint}}
\DeclareSIUnit{{\sieuro}}{{\mbox{{\euro}}}}
\rohead{DE-83PE89-823-271}
\cfoot{Seite \thepage/\pageref{LastPage}}
\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}
\newcommand{\produkttitel}[1]{\textsc{#1}}
\renewcommand{\arraystretch}{1.2}
\definecolor{AgilentBlau}{HTML}{0085d5}
\setlist{noitemsep}
\begin{document}
\begin{tabular}{p{0.4\hsize}p{0.5\hsize}}
\multirow{4}{*}{\includegraphics[width=0.9\hsize]{agilentLogo.png}}
&\normalsize{Agilent Technologies Deutschland GmbH}\\
&\normalsize{Life Sciences \& Chemical Analysis}\\
&\normalsize{Hewlett-Packard-Str. 8}\\
&\normalsize{D-76337 Waldbronn}
\end{tabular}
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
&
\multirow{4}{*}{
\begin{tabular}{|ll|}
\hline
\textbf{Angebotsnummer:}&DE-83PE89-823-271\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 163 9681131\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline
\end{tabular}
}\\
Herr Marcel Battenberg
\\
Advanced Accelerator Applications Germany GmbH
\\
Saime Genc Ring 18
\\
53121 Bonn
\\
&\\
&\\
\end{tabular}
\vspace{1cm}\par
Sehr geehrter Herr Battenberg,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) 8890GC.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item 8890 Gaschromatograph (\#1)
\begin{itemize}
\item S/SL-Einlass (\#2)
\item Flammenionisationsdetektor (\#3)
\end{itemize}
\item DB-624 UI (\#4)
\item 8890 GC (\#5)
\begin{itemize}
\item Installation and Operational Qualification (\#6)
\end{itemize}
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
\includegraphics[width = 5cm]{signWoitschetzki.png}
\vspace{1cm} \\
\begin{center}
\begin{longtable}
{| cp{0.71\textwidth} cr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{8890 Gaschromatograph} (G3540A)\newline Agilent 8890 GC\newline Ofen bis 450 °C, Kühlen von 450 auf 50 °C in unter 4 Min., bis zu 2 Inlets und 4 Detektoren. Elektronische Druck- und Flusskontrolle (EPC; 0-99 psi, 0.001 psi Genauigkeit), retention time locking (RTL).&1&\SI{8569.13}{\sieuro}\\
2 &\textbf{S/SL-Einlass} (G3540A\#112)\newline Split/Splitless-Einlasssystem für Kapillarsäulen mit elektronischer Drucksteuerung (EPC), max. 100 psi.&1&\SI{2175.91}{\sieuro}\\
3 &\textbf{Flammenionisationsdetektor} (G3540A\#211)\newline Flammenionisationsdetektor mit elektronischer Fluss- /Drucksteuerung zur digitalen Steuerung aller Detektorgase. Optimiert für Kapillarsäulen.&1&\SI{2167.45}{\sieuro}\\
4 &\textbf{DB-624 UI} (123-1334UI)\newline 30m, 0.32 mm, 1.80 µm&2&\SI{761.4}{\sieuro}\\
5 &\textbf{8890 GC} (SYS-GC-8890)\newline &1&\SI{0}{\sieuro}\\
6 &\textbf{Installation and Operational Qualification} (SYS-GC-8890\#6H9)\newline Gerätequalifizierung (IQ/OQ) im Rahmen der Installation.&1&\SI{4088.7}{\sieuro}\\
\hline
\end{longtable}
\end{center}
\vspace{-2cm}
\begin{flushright}
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{17762.59}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{532.8777}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{18295.4677}{\sieuro}\\
\hline
\end{tabular}
\end{flushright}
Der Betrag versteht sich zzgl. der gesetzlichen Steuern.\\
Diese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\
Zahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\
Incoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.
\begin{small}
\textbf{Gewährleistung:}\\
Die Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt 12 Monate.
\textbf{Hinweis:}\\
Für den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.
\end{small}
\begin{scriptsize}
Agilent Technologies Deutschland GmbH, Hewlett-Packard-Str. 8, D-76337 Waldbronn\\
Telefon +49 (0)7243-602-0\\
USt.-IdNr.: DE812729296, WEEE-Reg.-Nr. DE 86631749\\
Sitz der Gesellschaft: Waldbronn Amtsgericht Mannheim, HRB 723782\\
Geschäftsführer: Dr. Andreas Kistner (Vorsitzender der Geschäftsführung), Armin Jehle, Norbert Sabatzki, Dr. Knut Wintergerst\\
\href{www.agilent.com}{www.agilent.com}
\end{scriptsize}
\end{document}

@ -0,0 +1,7 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G3540A AZ Agilent 8890 GC-System Kundenspezifisch 1 20258 0 0 57.7 8569.13 8569.13 57.7 0 0 0 ISG100G111 6890 GC system
2 G3540A 112 AZ 8890 100 psi Split/Splitless-Einlasszub. 1 5144 0 0 57.7 2175.91 2175.91 57.7 0 0 0
3 G3540A 211 AZ 8890 FID Zubeh. optimiert f. Kapillare 1 5124 0 0 57.7 2167.45 2167.45 57.7 0 0 0
4 123-1334UI JW DB-624 UI 30m, 0.32mm, 1.80u 2 900 0 0 57.7 380.7 761.4 57.7 0 0 0 CSCG25C25V SpecApps Volatile
5 SYS-GC-8890 74 8890 GC-System 1 0 0 0 30 0 0 30 0 0 0 TSSYS0SYGC Service Systems - Gas Chromatography > 29
6 SYS-GC-8890 6H9 74 Analysegeraet-Qualifizierung-auf Wunsch 1 5841 0 0 30 4088.7 4088.7 30 0 0 0 TSSYS1 Serviced As Systems - 1 YR > 29
1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G3540A AZ Agilent 8890 GC-System Kundenspezifisch 1 20258 0 0 57.7 8569.13 8569.13 57.7 0 0 0 ISG100G111 6890 GC system
3 2 G3540A 112 AZ 8890 100 psi Split/Splitless-Einlasszub. 1 5144 0 0 57.7 2175.91 2175.91 57.7 0 0 0
4 3 G3540A 211 AZ 8890 FID Zubeh. optimiert f. Kapillare 1 5124 0 0 57.7 2167.45 2167.45 57.7 0 0 0
5 4 123-1334UI JW DB-624 UI 30m, 0.32mm, 1.80u 2 900 0 0 57.7 380.7 761.4 57.7 0 0 0 CSCG25C25V SpecApps Volatile
6 5 SYS-GC-8890 74 8890 GC-System 1 0 0 0 30 0 0 30 0 0 0 TSSYS0SYGC Service Systems - Gas Chromatography > 29
7 6 SYS-GC-8890 6H9 74 Analysegeraet-Qualifizierung-auf Wunsch 1 5841 0 0 30 4088.7 4088.7 30 0 0 0 TSSYS1 Serviced As Systems - 1 YR > 29

@ -0,0 +1,129 @@
\documentclass[a4paper,ngerman,parskip,10pt]{scrlttr2}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[hidelinks]{hyperref}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage[right]{{eurosym}}
\usepackage[locale=DE]{{siunitx}}
\usepackage{{scrlayer-scrpage}}
\usepackage{{lastpage}}
\usepackage{{graphicx}}
\usepackage{{multirow}}
\usepackage{{longtable}}
\usepackage{{enumitem}}
\usepackage{{fp, xstring, spreadtab, numprint}}
\DeclareSIUnit{{\sieuro}}{{\mbox{{\euro}}}}
\rohead{DE-83PE89-823-269}
\cfoot{Seite \thepage/\pageref{LastPage}}
\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}
\newcommand{\produkttitel}[1]{\textsc{#1}}
\renewcommand{\arraystretch}{1.2}
\definecolor{AgilentBlau}{HTML}{0085d5}
\setlist{noitemsep}
\begin{document}
\begin{tabular}{p{0.4\hsize}p{0.5\hsize}}
\multirow{4}{*}{\includegraphics[width=0.9\hsize]{agilentLogo.png}}
&\normalsize{Agilent Technologies Deutschland GmbH}\\
&\normalsize{Life Sciences \& Chemical Analysis}\\
&\normalsize{Hewlett-Packard-Str. 8}\\
&\normalsize{D-76337 Waldbronn}
\end{tabular}
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
&
\multirow{4}{*}{
\begin{tabular}{|ll|}
\hline
\textbf{Angebotsnummer:}&DE-83PE89-823-269\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 163 9681131\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline
\end{tabular}
}\\
Frau Stefanie Ritter
\\
Grünenthal GmbH
\\
Zieglerstr. 6
\\
52224 Stolberg
\\
&\\
&\\
\end{tabular}
\vspace{1cm}\par
Sehr geehrte Frau Ritter,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) GCMSD.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item Automatischer Probengeber 7650A (\#1)
\item 5977C MSD-Paket mit 8860 GC (\#2)
\begin{itemize}
\item Konfiguration mit Turbopumpe (\#3)
\end{itemize}
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
\includegraphics[width = 5cm]{signWoitschetzki.png}
\vspace{1cm} \\
\begin{center}
\begin{longtable}
{| cp{0.595\textwidth} crr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Discount} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{Automatischer Probengeber 7650A} (G4567A)\newline Automatischer Flüssigprobengeber mit 50 Probenplätzen.\newline Listenpreis: \SI{16952}{\sieuro}&1&\SI{25}{\%}&\SI{12714}{\sieuro}\\
2 &\textbf{5977C MSD-Paket mit 8860 GC} (G7082CW)\newline 8860 GC mit S/SL-Inlet und MSD-Interface, 5977C MSD mit Edelstahl-Quelle, Masshunter-Workstation inkl. PC\newline Listenpreis: \SI{83099}{\sieuro}&1&\SI{25}{\%}&\SI{62324.25}{\sieuro}\\
3 &\textbf{Konfiguration mit Turbopumpe} (G7082CW\#002)\newline \newline Listenpreis: \SI{11884}{\sieuro}&1&\SI{25}{\%}&\SI{8913}{\sieuro}\\
\hline
\end{longtable}
\end{center}
\vspace{-2cm}
\begin{flushright}
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{83951.25}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{2518.5375}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{86469.7875}{\sieuro}\\
\hline
\end{tabular}
\end{flushright}
Der Betrag versteht sich zzgl. der gesetzlichen Steuern.\\
Diese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\
Zahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\
Incoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.
\begin{small}
\textbf{Gewährleistung:}\\
Die Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt 12 Monate.
\textbf{Hinweis:}\\
Für den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.
\end{small}
\begin{scriptsize}
Agilent Technologies Deutschland GmbH, Hewlett-Packard-Str. 8, D-76337 Waldbronn\\
Telefon +49 (0)7243-602-0\\
USt.-IdNr.: DE812729296, WEEE-Reg.-Nr. DE 86631749\\
Sitz der Gesellschaft: Waldbronn Amtsgericht Mannheim, HRB 723782\\
Geschäftsführer: Dr. Andreas Kistner (Vorsitzender der Geschäftsführung), Armin Jehle, Norbert Sabatzki, Dr. Knut Wintergerst\\
\href{www.agilent.com}{www.agilent.com}
\end{scriptsize}
\end{document}

@ -0,0 +1,4 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G4567A AZ 7650 automatischer Fluessigprobengeber 1 16952 0 0 25 12714 12714 25 0 0 0 ISG100G140 Autosampler
2 G7082CW BZ 5977C MSD-Paket mit 8860 GC 1 83099 0 0 25 62324.25 62324.25 25 0 0 0 ISG310G310 GCMS SQ Bundle
3 G7082CW 002 BZ TURBOPUMPE 1 11884 0 0 25 8913 8913 25 0 0 0
1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G4567A AZ 7650 automatischer Fluessigprobengeber 1 16952 0 0 25 12714 12714 25 0 0 0 ISG100G140 Autosampler
3 2 G7082CW BZ 5977C MSD-Paket mit 8860 GC 1 83099 0 0 25 62324.25 62324.25 25 0 0 0 ISG310G310 GCMS SQ Bundle
4 3 G7082CW 002 BZ TURBOPUMPE 1 11884 0 0 25 8913 8913 25 0 0 0

@ -0,0 +1,131 @@
\documentclass[a4paper,ngerman,parskip,10pt]{scrlttr2}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[hidelinks]{hyperref}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage[right]{{eurosym}}
\usepackage[locale=DE]{{siunitx}}
\usepackage{{scrlayer-scrpage}}
\usepackage{{lastpage}}
\usepackage{{graphicx}}
\usepackage{{multirow}}
\usepackage{{longtable}}
\usepackage{{enumitem}}
\usepackage{{fp, xstring, spreadtab, numprint}}
\DeclareSIUnit{{\sieuro}}{{\mbox{{\euro}}}}
\rohead{DE-83PE89-823-270}
\cfoot{Seite \thepage/\pageref{LastPage}}
\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}
\newcommand{\produkttitel}[1]{\textsc{#1}}
\renewcommand{\arraystretch}{1.2}
\definecolor{AgilentBlau}{HTML}{0085d5}
\setlist{noitemsep}
\begin{document}
\begin{tabular}{p{0.4\hsize}p{0.5\hsize}}
\multirow{4}{*}{\includegraphics[width=0.9\hsize]{agilentLogo.png}}
&\normalsize{Agilent Technologies Deutschland GmbH}\\
&\normalsize{Life Sciences \& Chemical Analysis}\\
&\normalsize{Hewlett-Packard-Str. 8}\\
&\normalsize{D-76337 Waldbronn}
\end{tabular}
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
&
\multirow{4}{*}{
\begin{tabular}{|ll|}
\hline
\textbf{Angebotsnummer:}&DE-83PE89-823-270\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 163 9681131\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline
\end{tabular}
}\\
Frau Stefanie Ritter
\\
Grünenthal GmbH
\\
Zieglerstr. 6
\\
52224 Stolberg
\\
&\\
&\\
\end{tabular}
\vspace{1cm}\par
Sehr geehrte Frau Ritter,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) GCMSD.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item Autosampler mit 16 Probenplätzen (\#1)
\item Autosamplererweiterung auf 150 Probenplätze (\#2)
\item 5977C MSD-Paket mit 8860 GC (\#3)
\begin{itemize}
\item Konfiguration mit Turbopumpe (\#4)
\end{itemize}
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
\includegraphics[width = 5cm]{signWoitschetzki.png}
\vspace{1cm} \\
\begin{center}
\begin{longtable}
{| cp{0.595\textwidth} crr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Discount} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{Automatischer Probengeber 7693A} (G4513A)\newline Zur Injektion flüssiger Proben, 16 Probenplätze, Befestigungsstift, Parkhalterung für den GC, 10 µl-Spritze und Lösemittelflaschen.\newline Listenpreis: \SI{9476}{\sieuro}&1&\SI{25}{\%}&\SI{7107}{\sieuro}\\
2 &\textbf{Autosampler-Erweiterung auf 150 Probenplätze} (G4514A)\newline Erweiterung für den 7693A Autosampler, 150 Probenplätze auf drei herausnehmbaren Probenträgern. Optional mit kühl-/heizbaren Probenträgern, Barcodeleser, Probenvorbereitungsplätze (Heizen, Mischen) und Sample Prep Software.\newline Listenpreis: \SI{11092}{\sieuro}&1&\SI{25}{\%}&\SI{8319}{\sieuro}\\
3 &\textbf{5977C MSD-Paket mit 8860 GC} (G7082CW)\newline 8860 GC mit S/SL-Inlet und MSD-Interface, 5977C MSD mit Edelstahl-Quelle, Masshunter-Workstation inkl. PC\newline Listenpreis: \SI{83099}{\sieuro}&1&\SI{25}{\%}&\SI{62324.25}{\sieuro}\\
4 &\textbf{Konfiguration mit Turbopumpe} (G7082CW\#002)\newline \newline Listenpreis: \SI{11884}{\sieuro}&1&\SI{25}{\%}&\SI{8913}{\sieuro}\\
\hline
\end{longtable}
\end{center}
\vspace{-2cm}
\begin{flushright}
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{86663.25}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{2599.8975}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{89263.1475}{\sieuro}\\
\hline
\end{tabular}
\end{flushright}
Der Betrag versteht sich zzgl. der gesetzlichen Steuern.\\
Diese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\
Zahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\
Incoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.
\begin{small}
\textbf{Gewährleistung:}\\
Die Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt 12 Monate.
\textbf{Hinweis:}\\
Für den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.
\end{small}
\begin{scriptsize}
Agilent Technologies Deutschland GmbH, Hewlett-Packard-Str. 8, D-76337 Waldbronn\\
Telefon +49 (0)7243-602-0\\
USt.-IdNr.: DE812729296, WEEE-Reg.-Nr. DE 86631749\\
Sitz der Gesellschaft: Waldbronn Amtsgericht Mannheim, HRB 723782\\
Geschäftsführer: Dr. Andreas Kistner (Vorsitzender der Geschäftsführung), Armin Jehle, Norbert Sabatzki, Dr. Knut Wintergerst\\
\href{www.agilent.com}{www.agilent.com}
\end{scriptsize}
\end{document}

@ -0,0 +1,5 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G4513A AZ 7693A automatischer Probengeber 1 9476 0 0 25 7107 7107 25 0 0 0 ISG100G141 Autosampler
2 G4514A AZ 7693A Probenteller, 150 Probenflaschen 1 11092 0 0 25 8319 8319 25 0 0 0 ISG100G141 Autosampler
3 G7082CW BZ 5977C MSD-Paket mit 8860 GC 1 83099 0 0 25 62324.25 62324.25 25 0 0 0 ISG310G310 GCMS SQ Bundle
4 G7082CW 002 BZ TURBOPUMPE 1 11884 0 0 25 8913 8913 25 0 0 0
1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G4513A AZ 7693A automatischer Probengeber 1 9476 0 0 25 7107 7107 25 0 0 0 ISG100G141 Autosampler
3 2 G4514A AZ 7693A Probenteller, 150 Probenflaschen 1 11092 0 0 25 8319 8319 25 0 0 0 ISG100G141 Autosampler
4 3 G7082CW BZ 5977C MSD-Paket mit 8860 GC 1 83099 0 0 25 62324.25 62324.25 25 0 0 0 ISG310G310 GCMS SQ Bundle
5 4 G7082CW 002 BZ TURBOPUMPE 1 11884 0 0 25 8913 8913 25 0 0 0

@ -0,0 +1,130 @@
\documentclass[a4paper,ngerman,parskip,10pt]{scrlttr2}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[hidelinks]{hyperref}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage[right]{{eurosym}}
\usepackage[locale=DE]{{siunitx}}
\usepackage{{scrlayer-scrpage}}
\usepackage{{lastpage}}
\usepackage{{graphicx}}
\usepackage{{multirow}}
\usepackage{{longtable}}
\usepackage{{enumitem}}
\usepackage{{fp, xstring, spreadtab, numprint}}
\DeclareSIUnit{{\sieuro}}{{\mbox{{\euro}}}}
\rohead{DE-83PE89-823-268}
\cfoot{Seite \thepage/\pageref{LastPage}}
\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}
\newcommand{\produkttitel}[1]{\textsc{#1}}
\renewcommand{\arraystretch}{1.2}
\definecolor{AgilentBlau}{HTML}{0085d5}
\setlist{noitemsep}
\begin{document}
\begin{tabular}{p{0.4\hsize}p{0.5\hsize}}
\multirow{4}{*}{\includegraphics[width=0.9\hsize]{agilentLogo.png}}
&\normalsize{Agilent Technologies Deutschland GmbH}\\
&\normalsize{Life Sciences \& Chemical Analysis}\\
&\normalsize{Hewlett-Packard-Str. 8}\\
&\normalsize{D-76337 Waldbronn}
\end{tabular}
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
&
\multirow{4}{*}{
\begin{tabular}{|ll|}
\hline
\textbf{Angebotsnummer:}&DE-83PE89-823-268\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 163 9681131\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline
\end{tabular}
}\\
Herr Peter Boeker
\\
HyperChrom Deutschland GmbH
\\
Endenicher Allee 11-13
\\
53115 Bonn
\\
&\\
&\\
\end{tabular}
\vspace{1cm}\par
Sehr geehrter Herr Boeker,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) 7000D.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item 7000D refurbished (\#1)
\begin{itemize}
\item Konfiguration für 8890 GC (\#2)
\end{itemize}
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
\includegraphics[width = 5cm]{signWoitschetzki.png}
\vspace{1cm} \\
\textbf{Wichtiger Hinweis zur Bestellung von überholten Geräten}\\
Bitte beachten Sie, dass in der Regel nur wenige gebrauchte Geräte auf Lager sind und diese ohne die Möglichkeit einer Reservierung auf „first come, first serve“-Basis verkauft werden. Um lange Lieferzeiten zu vermeiden, sollte daher bei konkretem Interesse zunächst der Lagerstand überprüft werden. Die aktuellen Lagerbestände sind:
\begin{center}
\begin{longtable}
{| cp{0.71\textwidth} cr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{7000D refurbished} (G7010BAR)\newline Agilent zertifiziertes und gebrauchtes GC/QQQ 7000D mit EI-Quelle.&1&\SI{69890.7}{\sieuro}\\
2 &\textbf{Konfiguration für 8890 GC} (G7010BAR\#245)\newline Anschlüsse passend für 8890 GC.&1&\SI{-745.2}{\sieuro}\\
\hline
\end{longtable}
\end{center}
\vspace{-2cm}
\begin{flushright}
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{69145.5}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{2074.365}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{71219.865}{\sieuro}\\
\hline
\end{tabular}
\end{flushright}
Der Betrag versteht sich zzgl. der gesetzlichen Steuern.\\
Diese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\
Zahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\
Incoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.
\begin{small}
\textbf{Gewährleistung:}\\
Die Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt 12 Monate.
\textbf{Hinweis:}\\
Für den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.
\end{small}
\begin{scriptsize}
Agilent Technologies Deutschland GmbH, Hewlett-Packard-Str. 8, D-76337 Waldbronn\\
Telefon +49 (0)7243-602-0\\
USt.-IdNr.: DE812729296, WEEE-Reg.-Nr. DE 86631749\\
Sitz der Gesellschaft: Waldbronn Amtsgericht Mannheim, HRB 723782\\
Geschäftsführer: Dr. Andreas Kistner (Vorsitzender der Geschäftsführung), Armin Jehle, Norbert Sabatzki, Dr. Knut Wintergerst\\
\href{www.agilent.com}{www.agilent.com}
\end{scriptsize}
\end{document}

@ -0,0 +1,3 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G7010BAR RB Agilent zert. gebr. 7000D MS/MS 1 232969 0 0 70 69890.7 69890.7 70 0 0 0 ISRG40RB10 Remarketed GC/MS/MS
2 G7010BAR 245 RB MS fuer 8890 GC 1 -2484 0 0 70 -745.2 -745.2 70 0 0 0
1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G7010BAR RB Agilent zert. gebr. 7000D MS/MS 1 232969 0 0 70 69890.7 69890.7 70 0 0 0 ISRG40RB10 Remarketed GC/MS/MS
3 2 G7010BAR 245 RB MS fuer 8890 GC 1 -2484 0 0 70 -745.2 -745.2 70 0 0 0

@ -0,0 +1,147 @@
\documentclass[a4paper,ngerman,parskip,10pt]{scrlttr2}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}
\usepackage[hidelinks]{hyperref}
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
\usepackage[table]{xcolor}
\usepackage[right]{{eurosym}}
\usepackage[locale=DE]{{siunitx}}
\usepackage{{scrlayer-scrpage}}
\usepackage{{lastpage}}
\usepackage{{graphicx}}
\usepackage{{multirow}}
\usepackage{{longtable}}
\usepackage{{enumitem}}
\usepackage{{fp, xstring, spreadtab, numprint}}
\DeclareSIUnit{{\sieuro}}{{\mbox{{\euro}}}}
\rohead{DE-83PE89-823-269}
\cfoot{Seite \thepage/\pageref{LastPage}}
\sisetup{round-integer-to-decimal,round-precision=2,round-mode=places}
\newcommand{\produkttitel}[1]{\textsc{#1}}
\renewcommand{\arraystretch}{1.2}
\definecolor{AgilentBlau}{HTML}{0085d5}
\setlist{noitemsep}
\begin{document}
\begin{tabular}{p{0.4\hsize}p{0.5\hsize}}
\multirow{4}{*}{\includegraphics[width=0.9\hsize]{agilentLogo.png}}
&\normalsize{Agilent Technologies Deutschland GmbH}\\
&\normalsize{Life Sciences \& Chemical Analysis}\\
&\normalsize{Hewlett-Packard-Str. 8}\\
&\normalsize{D-76337 Waldbronn}
\end{tabular}
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
&
\multirow{4}{*}{
\begin{tabular}{|ll|}
\hline
\textbf{Angebotsnummer:}&DE-83PE89-823-269\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 163 9681131\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline
\end{tabular}
}\\
Herr Janko Potzkei
\\
SenseUp GmbH
\\
Wilhelm-Johnen-Str.
\\
52428 Jülich
\\
&\\
&\\
\end{tabular}
\vspace{1cm}\par
Sehr geehrter Herr Potzkei,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) GC.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item 8890 Gaschromatograph (\#1)
\begin{itemize}
\item S/SL-Einlass (\#2)
\item Anschluss für MSD (\#3)
\item Flammenionisationsdetektor (\#4)
\end{itemize}
\item Automatischer Probengeber 7650A (\#5)
\item OpenLAB CDS 2 Workstation mit Software und Lizenzen, PC, Softwarewartungsvertrag: alle Updates und Upgrades, bevorzugter telefonischer Support. Laufzeit: 1 Jahr (\#6)
\begin{itemize}
\item Inkludierte Gerätelizenz (\#7)
\end{itemize}
\item DB-5 30m, 0,25 mm, 0,25 um (\#8)
\item 8890 GC (\#9)
\begin{itemize}
\item Wartungsvertrag (Laufzeit: 1 Jahr) (\#10)
\end{itemize}
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
\includegraphics[width = 5cm]{signWoitschetzki.png}
\vspace{1cm} \\
\begin{center}
\begin{longtable}
{| cp{0.71\textwidth} cr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{8890 Gaschromatograph} (G3540A)\newline Agilent 8890 GC\newline Ofen bis 450 °C, Kühlen von 450 auf 50 °C in unter 4 Min., bis zu 2 Inlets und 4 Detektoren. Elektronische Druck- und Flusskontrolle (EPC; 0-99 psi, 0.001 psi Genauigkeit), retention time locking (RTL).&1&\SI{9928.45}{\sieuro}\\
2 &\textbf{S/SL-Einlass} (G3540A\#112)\newline Split/Splitless-Einlasssystem für Kapillarsäulen mit elektronischer Drucksteuerung (EPC), max. 100 psi.&1&\SI{2521.07}{\sieuro}\\
3 &\textbf{Anschluss für MSD} (G3540A\#201)\newline Linksseitig, Frontposition.&1&\SI{1222.31}{\sieuro}\\
4 &\textbf{Flammenionisationsdetektor} (G3540A\#211)\newline Flammenionisationsdetektor mit elektronischer Fluss- /Drucksteuerung zur digitalen Steuerung aller Detektorgase. Optimiert für Kapillarsäulen.&1&\SI{2511.27}{\sieuro}\\
5 &\textbf{Automatischer Probengeber 7650A} (G4567A)\newline Automatischer Flüssigprobengeber mit 50 Probenplätzen.&1&\SI{8308.18}{\sieuro}\\
6 &\textbf{OpenLab CDS 2 Workstation Bundle} (M8414AA)\newline OpenLab CDS Workstation-Software, dateibasiert, \newline inklusive 2 Geräteverbindungslizenzen, 1 Jahr Software-Wartungsvertrag sowie telefonischem Support und Workstation-PC.&1&\SI{8673.3}{\sieuro}\\
7 &\textbf{Inkludierte Gerätelizenz} (M8414AA\#002)\newline Für ein Agilent GC-System.&1&\SI{0}{\sieuro}\\
8 &\textbf{DB-5 30m, 0,25 mm, 0,25 um} (122-5032)\newline &1&\SI{333.76}{\sieuro}\\
9 &\textbf{8890 GC} (SYS-GC-8890)\newline &1&\SI{0}{\sieuro}\\
10 &\textbf{Wartungsvertrag (Laufzeit: 1 Jahr)} (SYS-GC-8890\#0L1)\newline 1 jährliche Wartung inklusive aller turnusmäßig zu wechselnden Ersatzteilen, inklusive Fahrt- und Arbeitszeitkosten.&1&\SI{1218.17}{\sieuro}\\
\hline
\end{longtable}
\end{center}
\vspace{-2cm}
\begin{flushright}
\begin{tabular}{|rr|}
\hline
\textbf{Summe netto} & \SI{34716.51}{\sieuro}\\
\textbf{Versand und Bereitstellungskosten (3\%)} & \SI{1041.4953}{\sieuro}\\
\textbf{Gesamtsumme netto} & \SI{35758.0053}{\sieuro}\\
\hline
\end{tabular}
\end{flushright}
Der Betrag versteht sich zzgl. der gesetzlichen Steuern.\\
Diese werden im Rechnungszeitraum auf der Rechnung gesondert ausgewiesen.\\
Zahlungsbedingungen: 30 Tage netto ab Rechnungsdatum.\\
Incoterm (2010) für Lieferungen innerhalb Deutschlands: DDP.
\begin{small}
\textbf{Gewährleistung:}\\
Die Gewährleistung für Zubehör und Ersatzteilprodukte und für Analytik-Hardwareprodukte beträgt 12 Monate.
\textbf{Hinweis:}\\
Für den Verkauf der in diesem Angebot aufgeführten Standard-Produkte und -Services gelten die aktuellen \emph{Agilent Geschäftsbedingungen} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Soweit Produkte oder Services nach speziellen Kundenanforderungen hergestellt, konfiguriert oder angepasst werden, gelten für den Verkauf aller in diesem Angebot aufgeführten Produkte und Services die aktuellen \emph{Agilent Geschäftsbedingungen für kundenspezifische Produkte} und alle sonstigen anwendbaren Zusatzbedingungen sowie zusätzliche Bedingungen, soweit darauf hier Bezug genommen wird. Eine Kopie der maßgeblichen Bedingungen ist entweder beigefügt oder wurde Ihnen bereits zur Verfügung gestellt. Sollten Sie keine Kopie erhalten haben oder eine weitere Kopie benötigen, setzen Sie sich bitte mit uns in Verbindung. Soweit Sie mit Agilent eine gesonderte Vereinbarung getroffen haben, die den Verkauf der in diesem Angebot aufgeführten Produkte und Services umfasst, sind die Bestimmungen dieser Vereinbarung anwendbar. Abweichende oder ergänzende Vereinbarungen, insbesondere widersprechende Geschäftsbedingungen, sind nur gültig, wenn sie ausdrücklich schriftlich vereinbart worden sind. Die angegebenen Daten zur Verfügbarkeit von Produkten und Services sind vorläufig. Die tatsächlichen Lieferzeiten bzw. Lieferperioden werden Ihnen bei Auftragsbestätigung mitgeteilt. Waren, Technologien oder Software, die aus den Vereinigten Staaten von Amerika (\emph{USA}) oder anderen exportierenden Ländern ausgeführt werden, unterliegen den Ausfuhrbestimmungen der USA sowie anderer Rechtsordnungen. Bei Ausfuhr ist der Kunde dafür verantwortlich, dass die anwendbaren Ausfuhrbestimmungen eingehalten werden.
\end{small}
\begin{scriptsize}
Agilent Technologies Deutschland GmbH, Hewlett-Packard-Str. 8, D-76337 Waldbronn\\
Telefon +49 (0)7243-602-0\\
USt.-IdNr.: DE812729296, WEEE-Reg.-Nr. DE 86631749\\
Sitz der Gesellschaft: Waldbronn Amtsgericht Mannheim, HRB 723782\\
Geschäftsführer: Dr. Andreas Kistner (Vorsitzender der Geschäftsführung), Armin Jehle, Norbert Sabatzki, Dr. Knut Wintergerst\\
\href{www.agilent.com}{www.agilent.com}
\end{scriptsize}
\end{document}

@ -0,0 +1,11 @@
# Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
1 G3540A AZ Agilent 8890 GC-System Kundenspezifisch 1 20258 0 0 50.99 9928.45 9928.45 50.99 0 0 0 ISG100G111 6890 GC system
2 G3540A 112 AZ 8890 100 psi Split/Splitless-Einlasszub. 1 5144 0 0 50.99 2521.07 2521.07 50.99 0 0 0
3 G3540A 201 AZ MSD-Interface 1 2494 0 0 50.99 1222.31 1222.31 50.99 0 0 0
4 G3540A 211 AZ 8890 FID Zubeh. optimiert f. Kapillare 1 5124 0 0 50.99 2511.27 2511.27 50.99 0 0 0
5 G4567A AZ 7650 automatischer Fluessigprobengeber 1 16952 0 0 50.99 8308.18 8308.18 50.99 0 0 0 ISG100G140 Autosampler
6 M8414AA LI OpenLab CDS Workstation PC-Paket 1 17697 0 0 50.99 8673.3 8673.3 50.99 0 0 0 ISF300F110 OpenLAB CDS w/Hardware
7 M8414AA 002 LI GC Geraeteverbindung 1 0 0 0 50.99 0 0 50.99 0 0 0
8 122-5032 JW DB-5 30m, 0,25 mm, 0,25 um 1 681 0 0 50.99 333.76 333.76 50.99 0 0 0 CSCG10C10B CapCol 5-type
9 SYS-GC-8890 74 8890 GC-System 1 0 0 0 29.99 0 0 29.99 0 0 0 TSSYS0SYGC Service Systems - Gas Chromatography > 29
10 SYS-GC-8890 0L1 74 CrossLab Vorb. Wartung - 1J, kompl. 1 1740 0 0 29.99 1218.17 1218.17 29.99 0 0 0 TSSYS1 Serviced As Systems - 1 YR > 29
1 # Part Number Opt PL Description Qty Price EUR Breaks EUR Uplift % Total Discount % Net EUR Total EUR Sales Discount YA9% Contractual Discount Y99% Promotion Discount Y07% Demo Discount Y04% PH Code PH Description YMax
2 1 G3540A AZ Agilent 8890 GC-System Kundenspezifisch 1 20258 0 0 50.99 9928.45 9928.45 50.99 0 0 0 ISG100G111 6890 GC system
3 2 G3540A 112 AZ 8890 100 psi Split/Splitless-Einlasszub. 1 5144 0 0 50.99 2521.07 2521.07 50.99 0 0 0
4 3 G3540A 201 AZ MSD-Interface 1 2494 0 0 50.99 1222.31 1222.31 50.99 0 0 0
5 4 G3540A 211 AZ 8890 FID Zubeh. optimiert f. Kapillare 1 5124 0 0 50.99 2511.27 2511.27 50.99 0 0 0
6 5 G4567A AZ 7650 automatischer Fluessigprobengeber 1 16952 0 0 50.99 8308.18 8308.18 50.99 0 0 0 ISG100G140 Autosampler
7 6 M8414AA LI OpenLab CDS Workstation PC-Paket 1 17697 0 0 50.99 8673.3 8673.3 50.99 0 0 0 ISF300F110 OpenLAB CDS w/Hardware
8 7 M8414AA 002 LI GC Geraeteverbindung 1 0 0 0 50.99 0 0 50.99 0 0 0
9 8 122-5032 JW DB-5 30m, 0,25 mm, 0,25 um 1 681 0 0 50.99 333.76 333.76 50.99 0 0 0 CSCG10C10B CapCol 5-type
10 9 SYS-GC-8890 74 8890 GC-System 1 0 0 0 29.99 0 0 29.99 0 0 0 TSSYS0SYGC Service Systems - Gas Chromatography > 29
11 10 SYS-GC-8890 0L1 74 CrossLab Vorb. Wartung - 1J, kompl. 1 1740 0 0 29.99 1218.17 1218.17 29.99 0 0 0 TSSYS1 Serviced As Systems - 1 YR > 29