Feature repricing min max (#10)

amazon settlement import/export improvements
This commit is contained in:
2020-05-01 09:08:23 +01:00
committed by GitHub
parent 56647c7648
commit 43d61c2ef8
118 changed files with 7930 additions and 3021 deletions

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class GetAccountCodeInfo
{
private string sqlConnectionString;
private Data.Database.Account.ReadAccountCode readAccountCode;
private Dictionary<int, Model.Account.AccountCode> cache;
public GetAccountCodeInfo(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
readAccountCode = new Data.Database.Account.ReadAccountCode(sqlConnectionString);
cache = new Dictionary<int, Model.Account.AccountCode>();
}
public void CacheClear()
{
cache.Clear();
}
public void CacheFill()
{
CacheClear();
var resultList = readAccountCode.All();
foreach (var result in resultList)
{
cache.Add(result.AccountCodeId, result);
}
}
public void CacheFill(List<int> accountCodeList, bool forceDbRead = false)
{
if (accountCodeList == null || !accountCodeList.Any())
{
return;
}
var accountCodeQueryList = new List<int>();
foreach (var code in accountCodeList.Distinct().ToList())
{
if (forceDbRead)
{
cache.Remove(code);
accountCodeQueryList.Add(code);
}
else if (!cache.ContainsKey(code))
{
accountCodeQueryList.Add(code);
}
}
// get db list
var dbList = readAccountCode.ByAccountCode(accountCodeQueryList);
// add to cache
foreach (var item in dbList)
{
cache.Add(item.AccountCodeId, item);
}
}
public Model.Account.AccountCode ByAccountCode(int accountCode, bool forceDbRead = false)
{
CacheFill(new List<int> { accountCode }, forceDbRead);
if (cache.ContainsKey(accountCode))
{
return cache[accountCode];
}
else
{
return null;
}
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class GetInvoiceLineItem
{
string sqlConnectionString;
private Dictionary<string, Model.Account.InvoiceLineItem> cache;
private Data.Database.Account.ReadInvoiceLineItem dbRead;
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
private Logic.Account.GetTaxCodeInfo getTaxCode;
private Logic.Account.GetAccountCodeInfo getAccountCode;
public GetInvoiceLineItem(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
CacheInnit();
dbRead = new Data.Database.Account.ReadInvoiceLineItem(sqlConnectionString);
getAccountCode = new GetAccountCodeInfo(sqlConnectionString);
getTaxCode = new GetTaxCodeInfo(sqlConnectionString);
}
/// <summary>
/// Create new 'default' line item code when a requested item code is not found.
/// </summary>
public bool InsertNewOnNoMatch { get; set; } = false;
public void CacheInnit()
{
cache = new Dictionary<string, Model.Account.InvoiceLineItem>();
}
/// <summary>
/// Prefill cache in one SQL call, saves multiple SQL calls.
/// </summary>
/// <param name="itemCodeList">List of item codes to lookup from database</param>
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
public void CacheFill(List<string> itemCodeList, bool forceDbRead = false)
{
if (itemCodeList == null || !itemCodeList.Any())
{
return;
}
var itemCodeQueryList = new List<string>();
foreach (var itemCode in itemCodeList)
{
if (forceDbRead)
{
cache.Remove(itemCode);
itemCodeQueryList.Add(itemCode);
}
else if (!cache.ContainsKey(itemCode))
{
itemCodeQueryList.Add(itemCode);
}
}
// query database
var resultList = dbRead.ByItemCode(itemCodeQueryList);
// fill account & tax codes cache
getAccountCode.CacheFill(dbRead.AccountCodeList.Values.ToList(), forceDbRead);
getTaxCode.CacheFill(dbRead.TaxCodeList.Values.ToList(), forceDbRead);
// build final itemcode object and add to cache
foreach (var result in resultList.Values)
{
if (dbRead.AccountCodeList.ContainsKey(result.ItemCode))
{
result.DefaultAccountCode = getAccountCode.ByAccountCode(dbRead.AccountCodeList[result.ItemCode]);
}
if (dbRead.TaxCodeList.ContainsKey(result.ItemCode))
{
result.DefaultTaxCode = getTaxCode.GetByTaxCode(dbRead.TaxCodeList[result.ItemCode]);
}
cache.Add(result.ItemCode, result);
}
}
/// <summary>
/// Creates new 'default' item code entry. The item code and title are set the same and will require updating.
/// </summary>
/// <param name="itemCode">Item code string</param>
/// <returns></returns>
public Model.Account.InvoiceLineItem CreateDefault(string itemCode)
{
new Data.Database.Account.CreateInvoiceLineItem(sqlConnectionString).CreateDefault(itemCode);
var item = dbRead.ByItemCode(itemCode);
cache.Add(item.ItemCode, item);
return item;
}
public Model.Account.InvoiceLineItem ByItemCode(string itemCode, bool forceDbRead = false)
{
if (string.IsNullOrWhiteSpace(itemCode))
{
return null;
}
CacheFill(new List<string> { itemCode }, forceDbRead);
Model.Account.InvoiceLineItem item = null;
if (cache.ContainsKey(itemCode))
{
item = cache[itemCode];
// check title
if (!item.IsNewReviewRequired && item.Name == item.ItemCode)
{
throw new Exception
("ItemCode found with the incomplete title. Update title and then try again.");
}
return item;
}
if (!InsertNewOnNoMatch)
{
return null;
}
else
{
return CreateDefault(itemCode);
}
}
/// <summary>
/// Tests if ItemCode has Invoice line entry enabled
/// </summary>
/// <param name="itemCode">Item code</param>
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
/// <returns></returns>
public bool InvoiceLineEntryEnabled(string itemCode, bool forceDbRead = false)
{
var item = ByItemCode(itemCode, forceDbRead);
if (item == null)
throw new Exception("Invalid item code");
return item.InvoiceLineEntryEnabled;
}
/// <summary>
/// Tests if ItemCode is new (default) and a review id required
/// </summary>
/// <param name="itemCode">Item code</param>
/// <param name="forceDbRead">Forces a database read (does not read cache)</param>
/// <returns></returns>
public bool IsNewReviewRequired(string itemCode, bool forceDbRead = false)
{
var item = ByItemCode(itemCode, forceDbRead);
if (item == null)
{
throw new Exception("Invalid item code");
}
return item.IsNewReviewRequired;
}
}
}

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class GetTaxCodeInfo
{
string sqlConnectionString;
private bool allRetrived;
private Dictionary<string, Model.Account.TaxCodeInfo> cache;
private Dictionary<string, Model.Account.TaxCodeInfo> cacheInvoiceItem;
private Data.Database.Account.ReadTaxCode dbRead;
public GetTaxCodeInfo(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
allRetrived = false;
CacheClear();
dbRead = new Data.Database.Account.ReadTaxCode(sqlConnectionString);
}
private void CacheClear()
{
cache = new Dictionary<string, Model.Account.TaxCodeInfo>();
cacheInvoiceItem = new Dictionary<string, Model.Account.TaxCodeInfo>();
}
public void CacheFill()
{
CacheClear();
var list = dbRead.GetAllActive();
foreach (var item in list)
{
cache.Add(item.TaxCode, item);
}
}
public void CacheFill(List<string> taxCodeList, bool forceDbRead = false)
{
if (taxCodeList == null || !taxCodeList.Any())
{
return;
}
var taxCodeQueryList = new List<string>();
foreach (var code in taxCodeList.Distinct().ToList())
{
if (forceDbRead)
{
cache.Remove(code);
taxCodeQueryList.Add(code);
}
else if (!cache.ContainsKey(code))
{
taxCodeQueryList.Add(code);
}
}
// get db list
var dbList = dbRead.GetByTaxCode(taxCodeQueryList);
// add to cache
foreach (var item in dbList)
{
cache.Add(item.TaxCode, item);
}
}
public List<Model.Account.TaxCodeInfo> GetAllActive()
{
CacheFill();
return cache.Values.ToList();
}
public Model.Account.TaxCodeInfo GetByTaxCode(string taxCode, bool forceDbRead = false)
{
if (string.IsNullOrWhiteSpace(taxCode))
{
return null;
}
CacheFill(new List<string> { taxCode }, forceDbRead);
if (cache.ContainsKey(taxCode))
{
return cache[taxCode];
}
else
{
return null;
}
}
/// <summary>
/// Gets list of Tax Code Info for a given list of Sku Numbers
/// </summary>
/// <param name="skuNumberList">List of SKU numbers</param>
/// <returns>Dictionary, key is SkuNumber and value is Tax Code Info</returns>
public Dictionary<string, Model.Account.TaxCodeInfo> GetBySkuNumber(List<string> skuNumberList)
{
var returnList = new Dictionary<string, Model.Account.TaxCodeInfo>();
if (skuNumberList == null || !skuNumberList.Any())
{
return returnList;
}
// remove any duplicates
var cleanSkuList = skuNumberList.Distinct().ToList();
// get db list
var dbList = dbRead.GetTaxCodeBySkuNumber(skuNumberList);
if (!dbList.Any()) { return returnList; }
// charge the cache
dbRead.GetAllActive();
// build dictionary
foreach (var item in dbList)
{
var taxInfo = GetByTaxCode(item.Value);
if (taxInfo != null)
{
returnList.Add(item.Key, taxInfo);
}
}
return returnList;
}
public Dictionary<string, Model.Account.TaxCodeInfo> Get(List<Model.Account.InvoiceLineItem> invoiceLineList)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class TaxCalculation
{
public decimal GetGrossAmount(decimal netAmount, decimal taxPercent)
{
return netAmount * (1 + (taxPercent / 100));
}
public decimal GetGrossAmount(decimal netAmount, Model.Account.TaxCodeInfo taxCodeInfo)
{
return GetGrossAmount(netAmount, taxCodeInfo.TaxRate);
}
public decimal GetNetAmount(decimal grossAmount, decimal taxPercent)
{
return grossAmount / (1 + (taxPercent / 100));
}
public decimal GetNetAmount(decimal grossAmount, Model.Account.TaxCodeInfo taxCodeInfo)
{
return GetNetAmount(grossAmount, taxCodeInfo.TaxRate);
}
public decimal GetTaxAmount(decimal amount, bool amountIsTaxInclusive, decimal taxPercent)
{
if (amountIsTaxInclusive)
{
return amount - GetNetAmount(amount, taxPercent);
}
else
{
return amount * (taxPercent / 100);
}
}
public decimal GetTaxAmount(decimal amount, bool amountIsTaxInclusive, Model.Account.TaxCodeInfo taxCodeInfo)
{
return GetTaxAmount(amount, amountIsTaxInclusive, taxCodeInfo.TaxRate);
}
public decimal GetMarginTaxRate(DateTime transactionDate)
{
decimal vatRate = 0;
if (transactionDate >= new DateTime(2011, 01, 04))
{
vatRate = 20;
}
else
{
// more coding required
throw new Exception("Transaction is outside the current margin scheme date scope");
}
return vatRate;
}
public decimal GetMarginTaxAmount(decimal marginAmount, DateTime transactionDate)
{
decimal vatRate = GetMarginTaxRate(transactionDate);
return GetTaxAmount(marginAmount, true, vatRate);
}
public decimal GetMarginTaxAmount(decimal purchasePrice, decimal salePrice, DateTime transactionDate)
{
return GetMarginTaxAmount(salePrice - purchasePrice, transactionDate);
}
/// <summary>
/// Returns a value to multiply margin (sale - purchase price) by to find margin scheme tax amount
/// </summary>
/// <param name="transactionDate">Date of transaction</param>
/// <returns>Value between 0 and 1</returns>
public decimal GetMarginMultiplier(DateTime transactionDate)
{
decimal vatRate = GetMarginTaxRate(transactionDate);
return vatRate / (vatRate + 100);
}
}
}

View File

@@ -1,267 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public abstract class ValidateInvoice : Validate
{
protected Account.ValidateAccountCode validateAccountCode = new Account.ValidateAccountCode();
protected Account.ValidateCurrencyCode validateCurrencyCode = new Account.ValidateCurrencyCode();
protected Account.ValidateTaxCode validateTaxCode = new ValidateTaxCode();
protected Logic.Utilities.StringCheck stringCheck = new Logic.Utilities.StringCheck();
public bool InvoiceNumberIsRequired { get; set; } = true;
public bool InvoiceLineDescriptionIsRequired { get; set; } = true;
public new void Innit()
{
base.Innit();
validateAccountCode.Innit();
validateCurrencyCode.Innit();
validateTaxCode.Innit();
stringCheck.Innit();
}
protected bool IsValidInvoice(Model.Account.IInvoice invoice)
{
return IsValidInvoice(new List<Model.Account.IInvoice> { invoice });
}
protected bool IsValidInvoice(List<Model.Account.IInvoice> invoiceList)
{
Innit();
if (invoiceList == null || !invoiceList.Any())
{
ErrorListAdd("Invoice list is null or empty.");
return false;
}
var tempErrorList = new List<string>();
for (int i = 0; i < invoiceList.Count(); i++)
{
Innit();
// check header info
if (invoiceList[i].ContactNameIsSet) { IsValidInvoiceContact(invoiceList[i].ContactName); }
else { ErrorListAdd("'Contact Name' is a required value"); }
if (invoiceList[i].InvoiceAmountIsSet)
{
if (!invoiceList[i].IsCreditNoteIsSet)
{ ErrorListAdd("'Is Credit Note' is a required value"); }
else
{
if (invoiceList[i].IsCreditNote && invoiceList[i].InvoiceAmount > 0)
{ ErrorListAdd("Credit Note amount cannot be greater than zero"); }
else if (!invoiceList[i].IsCreditNote && invoiceList[i].InvoiceAmount < 0)
{ ErrorListAdd("Invoice amount cannot be less than zero"); }
}
}
else { ErrorListAdd("'Invoice Amount' is a required value"); }
if (invoiceList[i].InvoiceCurrencyCodeIsSet) { IsValidInvoiceCurrency(invoiceList[i].InvoiceCurrencyCode); }
else { ErrorListAdd("'Invoice Currency Code' is a required value"); }
if (invoiceList[i].InvoiceDateIsSet) { IsValidInvoiceDate(invoiceList[i].InvoiceDate); }
else { ErrorListAdd("'Invoice Date' is a required value"); }
if (!invoiceList[i].InvoiceDateKindIsSet) { ErrorListAdd("'Invoice Date Kind' is a required value"); }
if (invoiceList[i].InvoiceDueDateIsSet) { IsValidInvoiceDueDate(invoiceList[i].InvoiceDueDate); }
if (invoiceList[i].InvoiceNumberIsSet)
{ IsValidInvoiceNumber(invoiceList[i].InvoiceNumber); }
else
{
if (InvoiceNumberIsRequired) { ErrorListAdd("'Invoice Number' is a required value"); }
}
if (invoiceList[i].InvoiceReferenceIsSet) { IsValidInvoiceReference(invoiceList[i].InvoiceReference); }
else { ErrorListAdd("'Invoice Reference' is a required value"); }
if (!invoiceList[i].IsCreditNoteIsSet) { ErrorListAdd("'Invoice Reference' is a required value"); }
// loop though lines and check and sum totals
if (!invoiceList[i].InvoiceLineListIsSet)
{ ErrorListAdd("Invoice is required to have lines."); }
else
{
decimal lineTotal = 0;
for (int j = 0; j < invoiceList[i].InvoiceLineList.Count(); j++)
{
if (!invoiceList[i].InvoiceLineList[j].AccountCodeIsSet) { ErrorListAdd("Line 'Account Code' is a required value"); }
else { IsValidInvoiceLineAccount(invoiceList[i].InvoiceLineList[j].AccountCode); }
if (!invoiceList[i].InvoiceLineList[j].DescriptionIsSet)
{
if (InvoiceLineDescriptionIsRequired) { ErrorListAdd("Line 'Description' is a required value"); }
}
else { IsValidInvoiceLineDescription(invoiceList[i].InvoiceLineList[j].Description); }
if (!invoiceList[i].InvoiceLineList[j].GrossTotalAmountIsSet) { ErrorListAdd("Line 'Gross Total Amount' is a required value"); }
if (!invoiceList[i].InvoiceLineList[j].ItemCodeIsSet) { ErrorListAdd("Line 'Item Code' is a required value"); }
else { IsValidLineItemCode(invoiceList[i].InvoiceLineList[j].ItemCode); }
if (!invoiceList[i].InvoiceLineList[j].QuantityIsSet) { ErrorListAdd("Line 'Quantity' is a required value"); }
if (!invoiceList[i].InvoiceLineList[j].TaxAmountIsSet) { ErrorListAdd("Line 'Tax Amount' is a required value"); }
if (!invoiceList[i].InvoiceLineList[j].TaxCodeIsSet) { ErrorListAdd("Line 'Tax Code' is a required value"); }
else { IsValidLineTaxCode(invoiceList[i].InvoiceLineList[j].TaxCode); }
if (!invoiceList[i].InvoiceLineList[j].TotalNetAmountIsSet) { ErrorListAdd("Line 'Total Net Amount' is a required value"); }
if ((invoiceList[i].InvoiceLineList[j].TaxAmount
+ invoiceList[i].InvoiceLineList[j].TaxAmountAdjust
+ invoiceList[i].InvoiceLineList[j].TotalNetAmount) != invoiceList[i].InvoiceLineList[j].GrossTotalAmount)
{ ErrorListAdd("Incorrect invoice line total (gross) amount."); }
lineTotal = lineTotal + invoiceList[i].InvoiceLineList[j].GrossTotalAmount;
}
// check totals
if (invoiceList[i].InvoiceAmountIsSet && (invoiceList[i].InvoiceAmount != lineTotal))
{ ErrorListAdd("Invoice line total does not match invoice total amount."); }
}
tempErrorList.AddRange(ErrorList.Select(x => "[Inv" + i + "] " + x).ToList());
}
Innit();
ErrorListAdd(tempErrorList);
if (ErrorListIsSet) { return false; }
else { return true; }
}
protected bool IsValidInvoiceLineAccount(int accountCode)
{
validateAccountCode.Innit();
if (!validateAccountCode.IsValidAccountCodeId(accountCode))
{
ErrorListAdd(validateAccountCode.ErrorList.Select(x => "Invalid invoice line account code: " + x).ToList());
return false;
}
else
{ return true; }
}
protected bool IsValidInvoiceLineDescription(string lineDescription)
{
int maxLength = 150;
stringCheck.Innit();
if (!stringCheck.MaxLength(lineDescription, maxLength))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid invoice line description: " + x).ToList());
return false;
}
return true;
}
protected bool IsValidInvoiceContact(string contactName)
{
int maxlength = 150;
stringCheck.Innit();
if (!stringCheck.MaxLength(contactName, maxlength))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid invoice nontact name: " + x).ToList());
return false;
}
return true;
}
protected bool IsValidInvoiceDate(DateTime invoiceDate)
{
if (invoiceDate.Kind != DateTimeKind.Utc)
{
ErrorListAdd(@"Invalid date/time, UTC kind required.");
return false;
}
else if (invoiceDate == default(DateTime))
{
ErrorListAdd("Date and time is default value.");
return false;
}
return true;
}
protected bool IsValidInvoiceDueDate(DateTime invoiceDueDate)
{
if (invoiceDueDate.Kind != DateTimeKind.Utc)
{
ErrorListAdd(@"Invalid date/time, UTC kind required.");
return false;
}
else if (invoiceDueDate == default(DateTime))
{
ErrorListAdd("Date and time is default value.");
return false;
}
return true;
}
protected bool IsValidInvoiceCurrency(string currencyCode)
{
validateCurrencyCode.Innit();
if (!validateCurrencyCode.IsValidCurrencyCode(currencyCode))
{
ErrorListAdd(validateCurrencyCode.ErrorList.Select(x => "Invalid invoice currency code: " + x).ToList());
return false;
}
else { return true; }
}
protected bool IsValidInvoiceNumber(string invoiceNumber)
{
int maxlength = 64;
stringCheck.Innit();
if (!stringCheck.MaxLength(invoiceNumber, maxlength))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid invoice number: " + x).ToList());
return false;
}
return true;
}
protected bool IsValidInvoiceReference(string invoiceReference)
{
int maxlength = 50;
stringCheck.Innit();
if (!stringCheck.MaxLength(invoiceReference, maxlength))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid invoice reference: " + x).ToList());
return false;
}
return true;
}
protected bool IsValidLineItemCode(string itemCode)
{
int maxlength = 250;
stringCheck.Innit();
if (!stringCheck.MaxLength(itemCode, maxlength))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid invoice line item code: " + x).ToList());
return false;
}
return true;
}
protected bool IsValidLineTaxCode(string taxCode)
{
validateTaxCode.Innit();
if (validateTaxCode.IsValidTaxCodeId(taxCode))
{ return true; }
else
{
ErrorListAdd(validateTaxCode.ErrorList.Select(x => "Invalid invoice line " + x).ToList());
return false;
}
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class ValidateSalesInvoice : ValidateInvoice
{
public ValidateSalesInvoice()
{
int propertyCount = new Model.Account.SalesInvoice().GetType().GetProperties().Count();
if (propertyCount != 20)
{ throw new Exception("Model.Account.SalesInvoice property count has altered. Validate class requires an update."); }
propertyCount = new Model.Account.SalesInvoice.InvoiceLine().GetType().GetProperties().Count();
if (propertyCount != 18)
{ throw new Exception("Model.Account.SalesInvoice property count has altered. Validate class requires an update."); }
}
public bool IsValidInvoice(Model.Account.SalesInvoice invoice)
{
return IsValidInvoice(new List<Model.Account.SalesInvoice> { invoice });
}
public bool IsValidInvoice(List<Model.Account.SalesInvoice> invoiceList)
{
var interfaceList = invoiceList.Cast<Model.Account.IInvoice>().ToList();
return IsValidInvoice(interfaceList);
}
}
}

View File

@@ -1,145 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
{
public class ValidateTaxCode : Validate
{
private Logic.Utilities.StringCheck stringCheck = new Logic.Utilities.StringCheck();
public new void Innit()
{
base.Innit();
stringCheck.Innit();
}
public bool IsValidGrossAmountMultiplier(decimal multiplier)
{
if (multiplier >= 0 && multiplier <= 1)
{
return true;
}
else
{
ErrorListAdd( "Gross multiplier must be equal to, or between, 0 and 1.");
return false;
}
}
public bool IsValidNetAmountMultiplier(decimal multiplier)
{
if (multiplier >= 0 && multiplier <= 1)
{
return true;
}
else
{
ErrorListAdd("Net multiplier must not be less than 0 or greater than 1.");
return false;
}
}
public bool IsValidOnExpense(Model.Account.TaxCode taxInfo)
{
if (taxInfo.IsValidOnExpense || taxInfo.IsSetIsValidOnIncome)
{
if (taxInfo.IsSetIsValidOnExpense)
{
return true;
}
else
{
ErrorListAdd("Is Valid On Expense has not been set.");
return false;
}
}
else
{
ErrorListAdd("Either 'IsValidOnExpense' or 'IsSetIsValidOnSale' must be set to true.");
return false;
}
}
public bool IsValidOnIncome(Model.Account.TaxCode taxInfo)
{
if (taxInfo.IsValidOnExpense || taxInfo.IsSetIsValidOnIncome)
{
if (taxInfo.IsSetIsValidOnIncome)
{
return true;
}
else
{
ErrorListAdd("Is Valid On Income has not been set.");
return false;
}
}
else
{
ErrorListAdd("Either 'IsValidOnPurchase' or 'IsSetIsValidOnSale' must be set to true.");
return false;
}
}
public bool IsValidTaxCodeId(string taxCodeId)
{
if (!stringCheck.IsAlphaNumeric(taxCodeId, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid Tax Code: " + x).ToList());
return false;
}
if (taxCodeId.Length != 4)
{
ErrorListAdd("Invalid Tax Code: Length does not equal 4 charaters.");
return false;
}
return true;
}
public bool IsValidTaxRateDescription(string description)
{
if (stringCheck.MaxLength(description, 250, true))
{
return true;
}
else
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid Tax Rate Description: " + x).ToList());
return false;
}
}
public bool IsValidTaxRateTitle(string title)
{
if (stringCheck.MaxLength(title, 50, false))
{
return true;
}
else
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid Tax Rate Title: " + x).ToList());
return false;
}
}
public bool IsValidTaxRateTitleShort(string title)
{
if (stringCheck.MaxLength(title, 50, false))
{
return true;
}
else
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid Tax Rate Title Short: " + x).ToList());
return false;
}
}
public bool IsValidTaxType(string taxType)
{
if (stringCheck.MaxLength(taxType, 50, false))
{
return true;
}
else
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid Tax Rate Type: " + x).ToList());
return false;
}
}
}
}

View File

@@ -36,33 +36,32 @@ namespace bnhtrade.Core.Logic.AmazonFBAInbound
shipmentRequest.LastUpdatedAfter = dateTimeAfter.AddDays(-14);
shipmentRequest.LastUpdatedBefore = dateTimeBefore;
List<Model.AmazonFBAInbound.ShipmentInfo> shipmentInfoList = shipmentRequest.GetShipmentInfo();
List<Model.AmazonFba.ShipmentInfo> shipmentInfoList = shipmentRequest.GetShipmentInfo();
// build list of shipments returned from mws
var dicShipExistsInDb = new Dictionary<string, bool>();
foreach (var item in shipmentInfoList)
{
dicShipExistsInDb.Add(item.AmazonShipmentId, false);
dicShipExistsInDb.Add(item.FbaShipmentId, false);
}
// build list of shipmentId that do not exist in database
int complete = 0;
using (TransactionScope scope = new TransactionScope())
{
List<Model.AmazonFBAInbound.ShipmentInfo> newShipmentInfoList = null;
List<Model.AmazonFba.ShipmentInfo> newShipmentInfoList = null;
if (dicShipExistsInDb.Any())
{
var newShipmentId = new List<string>();
// query db for shipment header info
var requestHeader = new Data.Database.FBAInbound.GetShipmentHeaderInfo(sqlConnectionString);
requestHeader.ShipmentIdList = dicShipExistsInDb.Keys.ToList();
var resultHeader = requestHeader.Execute();
var resultHeader = new Data.Database.AmazonShipment.ReadShipmentInfo(sqlConnectionString)
.HeaderByFbaShipmentId(dicShipExistsInDb.Keys.ToList());
// compare db and mws result
foreach (var item in resultHeader)
{
dicShipExistsInDb[item.AmazonShipmentId] = true;
dicShipExistsInDb[item.FbaShipmentId] = true;
}
foreach (var item in dicShipExistsInDb)
{
@@ -81,7 +80,7 @@ namespace bnhtrade.Core.Logic.AmazonFBAInbound
foreach (var item in newShipmentInfoList)
{
var shipmentItemInfoRequest = new Data.AmazonMWS.FBAInbound.ListInboundShipmentItems();
item.ShipmentItemInfoList = shipmentItemInfoRequest.GetByAmazonShipmentId(item.AmazonShipmentId);
item.ShipmentItemInfoList = shipmentItemInfoRequest.GetByAmazonShipmentId(item.FbaShipmentId);
}
}
@@ -91,10 +90,10 @@ namespace bnhtrade.Core.Logic.AmazonFBAInbound
foreach (var item in newShipmentInfoList)
{
// add the update date
item.LastUpdatedUtc = dateTimeBefore;
item.LastUpdated = dateTimeBefore;
// write to db
var dbWrite = new Data.Database.FBAInbound.SetShipmentInfo(sqlConnectionString);
var dbWrite = new Data.Database.AmazonShipment.SetShipmentInfo(sqlConnectionString);
dbWrite.Excecute(item);
complete = complete + 1;
}

View File

@@ -11,7 +11,8 @@ namespace bnhtrade.Core.Logic.Export
public class AmazonSettlement
{
private string sqlConnectionString;
private Dictionary<string, Model.Account.TaxCode> taxCodeBySkuNumer;
private Logic.Log.LogEvent log = new Logic.Log.LogEvent();
private List<string> lineItemCodeList = new List<string>();
public AmazonSettlement(string sqlConnectionString)
{
@@ -21,12 +22,11 @@ namespace bnhtrade.Core.Logic.Export
public void ToInvoice()
{
var console = new UI.Console.Update();
var log = new Logic.Log.LogEvent();
log.LogInformation("Starting processing of Amazon settlement data into export invoice table...");
// check settlement reports consistancy
var consistencyCheck = new Data.Database.Consistency.ImportAmazonSettlement(sqlConnectionString).PeriodDateGaps();
if (consistencyCheck == false )
if (consistencyCheck == false)
{ return; }
// get list of unprocssed settlement reports to export
@@ -65,38 +65,34 @@ namespace bnhtrade.Core.Logic.Export
}
// validate settlelments
var validate = new Logic.Import.ValidateAmazonSettlement();
var validate = new Logic.Validate.AmazonSettlement();
for (int i = 0; i < settlementList.Count(); i++)
{
if (!validate.IsValid(settlementList[i]))
{
log.LogError("Error procesing Amazon Settlement data for export.", validate.ErrorListToString());
log.LogError("Error procesing Amazon Settlement data for export.", validate.ValidationResultListToString());
}
}
if (validate.ErrorListIsSet) { return; }
if (validate.IsValidResult == false) { return; }
// get dictionary of sku-number to taxcodeId
Console.Write("\rBuilding SKU list... ");
var dicSkuNumberToTaxCodeId = new Dictionary<string, string>();
for (int i = 0; i < settlementList.Count(); i++)
var skuList = new List<string>();
foreach (var settlement in settlementList)
{
if (settlementList[i].SettlementLineListIsSet)
if (settlement.SettlementLineListIsSet)
{
for (int j = 0; j < settlementList[i].SettlementLineList.Count(); j++)
foreach (var line in settlement.SettlementLineList)
{
if (settlementList[i].SettlementLineList[j].SkuIsSet
&& !string.IsNullOrWhiteSpace(settlementList[i].SettlementLineList[j].Sku))
if (line.SkuIsSet
&& !string.IsNullOrWhiteSpace(line.Sku))
{
if (!dicSkuNumberToTaxCodeId.ContainsKey(settlementList[i].SettlementLineList[j].Sku))
{
dicSkuNumberToTaxCodeId.Add(settlementList[i].SettlementLineList[j].Sku, null);
}
skuList.Add(line.Sku);
}
}
}
}
var readTaxCode = new Data.Database.Account.ReadTaxCode(sqlConnectionString);
taxCodeBySkuNumer = readTaxCode.BySkuNumber(dicSkuNumberToTaxCodeId.Keys.ToList());
var taxCodeBySkuNumer = new Logic.Account.GetTaxCodeInfo(sqlConnectionString).GetBySkuNumber(skuList);
// loop through each settlement and build list of invoices to export
Console.Write("\rBuilding invoices to export... ");
@@ -117,7 +113,7 @@ namespace bnhtrade.Core.Logic.Export
var itemCodeTotal = new Dictionary<string, decimal>();
foreach (var line in month)
{
string itemCode = BuildLineItemCode(line.Sku, line.TransactionType, line.AmountType, line.AmountDescription);
string itemCode = BuildLineItemCode(taxCodeBySkuNumer, line.Sku, line.TransactionType, line.AmountType, line.AmountDescription);
if (itemCodeTotal.ContainsKey(itemCode))
{
itemCodeTotal[itemCode] += line.Amount;
@@ -137,13 +133,12 @@ namespace bnhtrade.Core.Logic.Export
decimal lineTaxTotal = 0m;
foreach (var item in itemCodeTotal)
{
var line = new Model.Account.SalesInvoice.InvoiceLine();
var line = new Model.Account.SalesInvoice.InvoiceLine(invoice.UnitAmountIsTaxExclusive);
line.ItemCode = item.Key;
line.TotalNetAmount = item.Value;
lineNetTotal += item.Value;
line.TaxAmount = 0;
lineTaxTotal += 0;
line.Quantity = 1;
line.UnitAmount = item.Value;
lineNetTotal += item.Value;
lineTaxTotal += 0;
invoice.InvoiceLineList.Add(line);
}
@@ -153,11 +148,10 @@ namespace bnhtrade.Core.Logic.Export
{ invoice.InvoiceDate = settlementList[i].EndDate; }
else
{ invoice.InvoiceDate = new DateTime(month.Key.Year, month.Key.Month, 1, 0, 0, 0, DateTimeKind.Utc).AddMonths(1).AddDays(-1); }
invoice.InvoiceDateKind = DateTimeKind.Utc;
invoice.InvoiceDueDate = settlementList[i].DepositDate;
invoice.InvoiceReference = settlementList[i].SettlementId;
invoice.InvoiceAmount = lineNetTotal + lineTaxTotal;
if (invoice.InvoiceAmount < 0) { invoice.IsCreditNote = true; }
invoice.InvoiceTotalAmount = lineNetTotal + lineTaxTotal;
if (invoice.InvoiceTotalAmount < 0) { invoice.IsCreditNote = true; }
else { invoice.IsCreditNote = false; }
// invoice complete, add to list
@@ -174,11 +168,11 @@ namespace bnhtrade.Core.Logic.Export
{
if (invoiceTotal.ContainsKey(invoiceList[i].InvoiceReference))
{
invoiceTotal[invoiceList[i].InvoiceReference] += invoiceList[i].InvoiceAmount;
invoiceTotal[invoiceList[i].InvoiceReference] += invoiceList[i].InvoiceTotalAmount.GetValueOrDefault();
}
else
{
invoiceTotal.Add(invoiceList[i].InvoiceReference, invoiceList[i].InvoiceAmount);
invoiceTotal.Add(invoiceList[i].InvoiceReference, invoiceList[i].InvoiceTotalAmount.GetValueOrDefault());
}
}
for (int i = 0; i < settlementList.Count(); i++)
@@ -194,8 +188,65 @@ namespace bnhtrade.Core.Logic.Export
return;
}
// postfix invoices spanning multiple months with -n
if (invoiceList.Count() > 1)
// add invoice item code data to lines
// also clean invoices of any disabled lines (remove lines and possibly invoices)
var getLineItemInfo = new Logic.Account.GetInvoiceLineItem(sqlConnectionString);
getLineItemInfo.InsertNewOnNoMatch = true;
getLineItemInfo.CacheFill(lineItemCodeList);
bool newTypeFound = false;
for (int i = 0; i < invoiceList.Count(); i++)
{
for (int j = 0; j < invoiceList[i].InvoiceLineList.Count(); j++)
{
var itemCode = getLineItemInfo.ByItemCode(invoiceList[i].InvoiceLineList[j].ItemCode);
// error! itemCode should never be null
if (itemCode == null)
{
throw new Exception("Item code is null");
}
// flag new type and throw exception further on
else if (itemCode.IsNewReviewRequired)
{
newTypeFound = true;
}
// clean invoices of any disabled lines (remove lines and possibly invoices)
else if (itemCode.InvoiceLineEntryEnabled == false)
{
// remove line
invoiceList[i].InvoiceTotalAmount = invoiceList[i].InvoiceTotalAmount - invoiceList[i].InvoiceLineList[j].LineTotalAmount;
invoiceList[i].InvoiceLineList.RemoveAt(j);
j = j - 1;
// remove invoice?
if (invoiceList[i].InvoiceLineList.Count == 0)
{
invoiceList.RemoveAt(i);
i = i - 1;
}
}
// get here add info to lines
else
{
invoiceList[i].InvoiceLineList[j].AccountCode = itemCode.DefaultAccountCode;
invoiceList[i].InvoiceLineList[j].Description = itemCode.Name;
invoiceList[i].InvoiceLineList[j].ItemCode = itemCode.ItemCode;
invoiceList[i].InvoiceLineList[j].TaxCode = itemCode.DefaultTaxCode;
}
}
}
if (newTypeFound)
{
if (newTypeFound)
{
throw new Exception("New line ItemCode found. Add item code default values and then try again.");
}
return;
}
// postfix invoices references that span multiple months with -n
if (invoiceList.Count > 1)
{
string lastRef = invoiceList[0].InvoiceReference;
int countRef = 1;
@@ -215,7 +266,7 @@ namespace bnhtrade.Core.Logic.Export
if (countRef > 2)
{
log.LogError(
countRef + " total numner of export invoices created from Amazon Settlement Id" + lastRef + "."
countRef + " invoices where created from Amazon Settlement Id" + lastRef + "."
, "Settlement period appears to span more 3 months or more. Whilst this is possible, it is unsual. Confirm his is correct.");
}
@@ -230,8 +281,12 @@ namespace bnhtrade.Core.Logic.Export
{
try
{
var saveInv = new Logic.Export.SalesInvoice(sqlConnectionString);
// add temp invoice numbers
saveInv.AddTempInvoiceNumber(invoiceList, true);
// write to the database (gets validated there)
new Data.Database.Export.CreateSalesInvoice(sqlConnectionString).SaveInvoice(invoiceList);
saveInv.SaveSalesInvoice(invoiceList);
// set settlements to isprocessed
new Data.Database.Import.UpdateAmazonSettlement(sqlConnectionString).SetIsProcessedTrue(settlementIdList);
@@ -241,15 +296,16 @@ namespace bnhtrade.Core.Logic.Export
catch (Exception ex)
{
scope.Dispose();
log.LogError(ex.Message);
log.LogError("Exeception caught while writing Amazon settlement invoices to DB. Changes were rolled back."
, ex.Message);
return;
}
}
Console.Write("\r");
log.LogInformation("\rFinished processing of Amazon settlement data. " + invoiceList.Count() + " invoices created from "+ settlementIdList.Count() + " Amazon settlement reports.");
log.LogInformation("\rFinished processing of Amazon settlement data. " + invoiceList.Count() + " invoices created from " + settlementIdList.Count() + " Amazon settlement reports.");
}
private string BuildLineItemCode(string skuNumber, string transactionType, string amountType, string amountDescription)
private string BuildLineItemCode(Dictionary<string, Model.Account.TaxCodeInfo> taxCodeBySkuNumer, string skuNumber, string transactionType, string amountType, string amountDescription)
{
// build the match string
// NB special case for global accounting sale and refunds (also note Goodlwill is included) and sku's where tax is included
@@ -264,13 +320,18 @@ namespace bnhtrade.Core.Logic.Export
{
if (taxCodeBySkuNumer.ContainsKey(skuNumber))
{
matchString = matchString + "<TaxCode=" + taxCodeBySkuNumer[skuNumber].TaxCodeId + ">";
matchString = matchString + "<TaxCode=" + taxCodeBySkuNumer[skuNumber].TaxCode + ">";
}
else
{
throw new Exception("Sku#" + skuNumber + " tax info not found in dictionary list.");
}
}
// add to list of generated line item codes
lineItemCodeList.Add(matchString);
// return value
return matchString;
}
}

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Export
{
public class AmazonSubmitFile
{
private string sqlConnectionString;
private Logic.Utilities.StringCheck stringCheck = new Logic.Utilities.StringCheck();
private Log.LogEvent log = new Log.LogEvent();
public AmazonSubmitFile(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
public void SubmitInventoryLoader(MemoryStream stream)
{
Model.Export.AmazonFeedSubmission result;
int queueId = QueueInventoryLoader(stream, out result);
SubmitQueuedFeedSubmission(queueId, result);
}
public void SubmitInventoryLoader(MemoryStream stream, out Model.Export.AmazonFeedSubmission result)
{
int queueId = QueueInventoryLoader(stream, out result);
SubmitQueuedFeedSubmission(queueId, result);
}
public int QueueInventoryLoader(MemoryStream stream)
{
Model.Export.AmazonFeedSubmission result;
return QueueFeedSubbmission("_POST_FLAT_FILE_INVLOADER_DATA_", "txt", stream, out result);
}
public int QueueInventoryLoader(MemoryStream stream, out Model.Export.AmazonFeedSubmission result)
{
// write file database
return QueueFeedSubbmission("_POST_FLAT_FILE_INVLOADER_DATA_", "txt", stream, out result);
}
private int QueueFeedSubbmission(string feedType, string fileExtention, MemoryStream stream, out Model.Export.AmazonFeedSubmission result)
{
// construct file object
var fileInfo = new Model.Data.DatabaseFileStream();
fileInfo.FileExtention = fileExtention;
fileInfo.FileData = stream;
fileInfo.FileMD5base64 = new Logic.Utilities.CalculateMD5().Base64(stream);
long longSize = stream.Length;
if (longSize > 2147483647)
throw new Exception("File size over 2.15 GB");
fileInfo.FileSize = (int)longSize;
// save to database
int queueId = 0;
using (var scope = new TransactionScope())
{
queueId = new Data.Database.Export.CreateAmazonFeedSubmission(sqlConnectionString).Execute(feedType, fileInfo);
// validate the result
var validateResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(
fileInfo,
new ValidationContext(fileInfo, null, null),
validateResults);
if (isValid == false)
{
string error = "Create Database Amazon Feed Submission cancelled. Object validation failed.";
new Log.LogEvent().LogError(error, string.Join(Environment.NewLine, validateResults));
scope.Dispose();
throw new Exception(error);
}
scope.Complete();
}
// create feed object
result = new Model.Export.AmazonFeedSubmission();
result.FeedType = feedType;
result.File = fileInfo;
return queueId;
}
// private, only to be used where you're sure feedSubmission matches DB and you don't want to call info from DB (large file)
private void SubmitQueuedFeedSubmission(int queueId, Model.Export.AmazonFeedSubmission feedSubmission)
{
// upload to mws
var feedSubmitt = new Data.AmazonMWS.Feeds.SubmitFeed(feedSubmission);
if (!feedSubmitt.FeedSubmissionRecived)
{
// failed do something here
throw new NotImplementedException();
}
// set the amazon feed Id
var dbUpdate = new Data.Database.Export.UpdateAmazonFeedSubmission(sqlConnectionString);
dbUpdate.AddAmazonFeedId(queueId, feedSubmission.FeedSubmissionId);
// update progress info
dbUpdate.UpdateStatusInfo(feedSubmission);
}
private void SubmitQueuedFeedSubmission(int queueId)
{
throw new NotImplementedException();
}
private void SubmitQueuedFeedSubmission()
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Export
{
public class AmazonSubmitFileStatus
{
private string sqlConnectionString;
private bnhtrade.Core.Logic.Log.LogEvent log = new Log.LogEvent();
public AmazonSubmitFileStatus(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
public void UpdateStatusInfo()
{
var openList = new Data.Database.Export.ReadAmazonFeedSubmission(sqlConnectionString).GetOpenFeeds();
if (openList.Count == 0)
return;
var getStatus = new Data.AmazonMWS.Feeds.GetFeedSubmissions();
getStatus.FeedSubmissionIdList = openList.Select(x => x.FeedSubmissionId).ToList();
var currentStatus = getStatus.Invoke();
for (int i = 0; i < openList.Count; i++)
{
bool found = false;
for (int j = 0; j < currentStatus.Count; j++)
{
if (openList[i].FeedSubmissionId == currentStatus[j].FeedSubmissionId)
{
found = true;
openList[i].FeedProcessingStatus = currentStatus[j].FeedProcessingStatus;
if (currentStatus[j].IsSetCompletedProcessingDate())
openList[i].CompletedProcessingDate = currentStatus[j].CompletedProcessingDate;
if (currentStatus[j].IsSetStartedProcessingDate())
openList[i].StartedProcessingDate = currentStatus[j].StartedProcessingDate;
if (currentStatus[j].IsSetSubmittedDate())
openList[i].SubmittedDate = DateTime.Parse(currentStatus[j].SubmittedDate);
}
}
if (found == false)
{
log.LogError("FeedSubmissionId '" + openList[i].FeedSubmissionId + "' exists in database but was not returned by Amazon MWS when queried.");
openList.RemoveAt(i);
i = i - 1;
}
}
// update the database
new Data.Database.Export.UpdateAmazonFeedSubmission(sqlConnectionString).UpdateStatusInfo(openList);
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Export
{
public class SalesInvoice
{
private string sqlConnectionString;
private Logic.Log.LogEvent log = new Log.LogEvent();
public SalesInvoice(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
public string GetNextTempInvoiceNumber()
{
var sequence = new Data.Database.Programmability.Sequence(sqlConnectionString);
return "_tmp" + sequence.GetNext("ExportTempInvoiceNumber").ToString("00000000");
}
public void AddTempInvoiceNumber(IEnumerable<Model.Account.IInvoice> invoiceList, bool overwriteExisting)
{
for (int i = 0; i < invoiceList.Count(); i++)
{
if (invoiceList.ElementAt(i).InvoiceNumber != null && overwriteExisting == false)
{
continue;
}
invoiceList.ElementAt(i).InvoiceNumber = GetNextTempInvoiceNumber();
}
}
public void SaveSalesInvoice(List<Model.Account.SalesInvoice> invoiceList)
{
// validate the list of invoices
var validateInvoice = new Logic.Validate.SalesInvoice();
validateInvoice.IsValidExportInvoice(invoiceList);
if (validateInvoice.IsValidResult == false)
{
log.LogError("Invalid Sales invoice(s) found. See extended info.", validateInvoice.ValidationResultListToString());
return;
}
validateInvoice = null;
// save to database
new Data.Database.Export.CreateSalesInvoice(sqlConnectionString).Execute(invoiceList);
}
}
}

View File

@@ -1,134 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Export
{
class ValidateSalesInvoice : Logic.Account.ValidateInvoice
{
//public bool IsValidInvoice(Model.Export.SetSalesInvoice invoice)
//{
// ErrorMessage = null;
// var baseInvoice = CopyInvoiceToBase(invoice);
// if (!base.IsValidInvoice(baseInvoice))
// {
// return false;
// }
// // check each match string only appears once
// var dicLookup = new Dictionary<string, string>();
// foreach (var line in invoice.InvoiceLineList)
// {
// if (dicLookup.ContainsKey(line.LineTypeMatchString))
// {
// ErrorMessage = "'Line type match string' duplication.";
// return false;
// }
// else
// {
// dicLookup.Add(line.LineTypeMatchString, null);
// }
// }
// return true;
//}
//public bool IsValidInvoiceHeader(Model.Export.SetSalesInvoice invoice)
//{
// ErrorMessage = null;
// var baseInvoice = CopyInvoiceHeaderToBase(invoice);
// if (base.IsValidInvoiceHeader(baseInvoice))
// {
// return true;
// }
// else
// {
// return false;
// }
//}
//public bool IsValidInvoiceLine(Model.Export.SetSalesInvoiceLine invoiceLine)
//{
// ErrorMessage = null;
// var baseLine = CopyInvoiceLineToBase(invoiceLine);
// if (!IsValidLineTypeMatchString(invoiceLine.LineTypeMatchString)
// || !base.IsValidInvoiceLine(baseLine))
// {
// return false;
// }
// return true;
//}
//public bool IsValidLineTypeMatchString(string matchString)
//{
// ErrorMessage = null;
// var stringCheck = new Logic.Utilities.StringCheck();
// if (!stringCheck.MaxLength(matchString, 250))
// {
// ErrorMessage = "Invalid Line Type Match String: " + stringCheck.ErrorMessage;
// return false;
// }
// return true;
//}
//private Model.Account.SalesInvoice CopyInvoiceToBase(Model.Export.SetSalesInvoice invoice)
//{
// var baseInvoice = CopyInvoiceHeaderToBase(invoice);
// if (invoice.IsSetInvoiceLineList)
// {
// var baseLineList = new List<Model.Account.SalesInvoiceLine>();
// foreach (var line in invoice.InvoiceLineList)
// {
// baseLineList.Add(CopyInvoiceLineToBase(line));
// }
// baseInvoice.InvoiceLineList = baseLineList;
// }
// return baseInvoice;
//}
//private Model.Account.SalesInvoice CopyInvoiceHeaderToBase(Model.Export.SetSalesInvoice invoice)
//{
// var baseInvoice = new Model.Account.SalesInvoice();
// if (invoice.IsSetContactName)
// { baseInvoice.ContactName = invoice.ContactName; }
// if (invoice.IsSetInvoiceAmount)
// { baseInvoice.InvoiceAmount = invoice.InvoiceAmount; }
// if (invoice.IsSetInvoiceCurrencyCode)
// { baseInvoice.InvoiceCurrencyCode = invoice.InvoiceCurrencyCode; }
// if (invoice.IsSetInvoiceDate)
// { baseInvoice.InvoiceDate = invoice.InvoiceDate; }
// if (invoice.IsSetInvoiceNumber)
// { baseInvoice.InvoiceNumber = invoice.InvoiceNumber; }
// if (invoice.IsSetInvoiceReference)
// { baseInvoice.InvoiceReference = invoice.InvoiceReference; }
// if (invoice.IsSetIsComplete)
// { baseInvoice.IsComplete = invoice.IsComplete; }
// return baseInvoice;
//}
//private Model.Account.SalesInvoiceLine CopyInvoiceLineToBase(Model.Export.SetSalesInvoiceLine invoiceLine)
//{
// var baseLine = new Model.Account.SalesInvoiceLine();
// if (invoiceLine.IsSetAccountCode)
// { baseLine.AccountCode = invoiceLine.AccountCode; }
// // something missing here ??????
// if (invoiceLine.IsSetDescription)
// { baseLine.Description = invoiceLine.Description; }
// if (invoiceLine.IsSetNetAmount)
// { baseLine.TotalNetAmount = invoiceLine.NetAmount; }
// // tax adjustment to set ??????????
// if (invoiceLine.IsSetTaxCode)
// { baseLine.TaxCode = invoiceLine.TaxCode; }
// return baseLine;
//}
}
}

View File

@@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Product
{
public class GetCompetitivePrice
{
private Dictionary<string, Model.Product.CompetitivePrice> competitivePrices;
private int newConditionId = Data.Database.Constants.GetProductConditionIdNew();
private string sqlConnectionString;
private Data.Database.Product.ReadCompetitivePrice read;
private List<string> productIdList;
private List<string> conditionIdList;
private Data.Database.Product.ReadCompetitivePrice dbRead;
private Dictionary<int, decimal> newConditionMultipier;
public GetCompetitivePrice(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
dbRead = new Data.Database.Product.ReadCompetitivePrice(sqlConnectionString);
newConditionMultipier = new Dictionary<int, decimal>();
}
/// <summary>
/// If a competitive price for a used condition is not found, return an estimated figure.
/// </summary>
public bool EstimatePrice { get; set; } = false;
public int DistinctRequestCount { get; private set; } = 0;
public int DistinctResponceCount { get; private set; } = 0;
public void Innit()
{
DistinctRequestCount = 0;
DistinctResponceCount = 0;
}
public Dictionary<string, Model.Product.CompetitivePrice> Execute(List<Model.Sku.Price.SkuPriceParameter> skuParamList)
{
var requestList = new List<(int ProductId, int ConditionId)>();
for (int i = 0; i < skuParamList.Count; i++)
{
requestList.Add((skuParamList[i].ProductId, skuParamList[i].ConditionId));
}
var result = Execute(requestList);
// build return list
var returnList = new Dictionary<string, Model.Product.CompetitivePrice>();
for (int i = 0; i < skuParamList.Count; i++)
{
// skip?
if (returnList.ContainsKey(skuParamList[i].SkuNumber))
{
continue;
}
// make a match
for (int j = 0; j < result.Count; j++)
{
if (skuParamList[i].ProductId == result[j].ProductId && skuParamList[i].ConditionId == result[j].ConditionId)
{
returnList.Add(skuParamList[i].SkuNumber, result[j].Clone());
break;
}
}
}
return returnList;
}
public List<Model.Product.CompetitivePrice> Execute(List<(int ProductId, int ConditionId)> requestList)
{
Innit();
var result = new List<Model.Product.CompetitivePrice>();
if (requestList == null || !requestList.Any())
{
return result;
}
// build distinct list
var distinctRequestList = new List<(int productId, int conditionId)>();
for (int i = 0; i < requestList.Count; i++)
{
// filter values already requested
bool distinct = true;
for (int j = 0; j < distinctRequestList.Count; j++)
{
if (requestList[i].ProductId == distinctRequestList[j].productId
&& requestList[i].ConditionId == distinctRequestList[j].conditionId)
{
distinct = false;
break;
}
}
if (distinct)
{ distinctRequestList.Add((requestList[i].ProductId, requestList[i].ConditionId)); }
}
DistinctRequestCount = distinctRequestList.Count();
var dbResult = dbRead.Execute(distinctRequestList);
// only contiue if estimated values are retured
if (DistinctRequestCount == dbResult.Count || !EstimatePrice)
{
DistinctResponceCount = dbResult.Count;
return result;
}
// build estimated list
var estimatedRequestList = new List<(int productId, int conditionId)>();
//var conditionMultipier = new Dictionary<int, decimal>();
for (int i = 0; i < distinctRequestList.Count; i++)
{
bool request = true;
// check if db returned record
for (int j = 0; j < dbResult.Count; j++)
{
if (distinctRequestList[i].productId == dbResult[j].ProductId
&& distinctRequestList[i].conditionId == dbResult[j].ConditionId)
{
request = false;
break;
}
}
// check if 'new' condition price has already been retrived
if (request)
{
for (int j = 0; j < dbResult.Count; j++)
{
if (distinctRequestList[i].productId == dbResult[j].ProductId
&& dbResult[j].ConditionId == newConditionId)
{
request = false;
break;
}
}
}
// add to request lists
if (request)
{
estimatedRequestList.Add((distinctRequestList[i].productId, newConditionId));
if (!newConditionMultipier.ContainsKey(distinctRequestList[i].conditionId))
{
newConditionMultipier.Add(distinctRequestList[i].conditionId, -1);
}
}
}
dbResult.AddRange(dbRead.Execute(estimatedRequestList));
UpdateConditionMultipers();
// build a new return list
var newResult = new List<Model.Product.CompetitivePrice>();
var noMatchList = new List<(int productId, int conditionId)>();
for (int i = 0; i < distinctRequestList.Count; i++)
{
// add exact matches
bool noMatch = true;
for (int j = 0; j < dbResult.Count; j++)
{
if (distinctRequestList[i].productId == dbResult[j].ProductId
&& distinctRequestList[i].conditionId == dbResult[j].ConditionId)
{
noMatch = false;
newResult.Add(dbResult[j]);
break;
}
}
// add estimated matches
if (noMatch)
{
for (int j = 0; j < dbResult.Count; j++)
{
if (distinctRequestList[i].productId == dbResult[j].ProductId
&& dbResult[j].ConditionId == newConditionId)
{
if (!newConditionMultipier.ContainsKey(distinctRequestList[i].conditionId))
{
break;
}
var clone = dbResult[j].Clone();
clone.ConditionId = distinctRequestList[i].conditionId;
clone.Price = decimal.Round(clone.Price * newConditionMultipier[distinctRequestList[i].conditionId], 2);
clone.PriceIsEstimated = true;
newResult.Add(clone);
noMatch = false;
break;
}
}
}
if (noMatch)
{
noMatchList.Add(distinctRequestList[i]);
}
}
DistinctResponceCount = newResult.Count;
return newResult;
}
public Model.Product.CompetitivePrice Execute(int productId, int conditionId)
{
Innit();
DistinctRequestCount = 1;
var result = dbRead.Execute(new List<(int, int)> { (productId, conditionId) });
if (!result.Any())
{
DistinctResponceCount = 0;
return null;
}
else
{
DistinctResponceCount = 1;
return result.First();
}
}
private void UpdateConditionMultipers()
{
var conditionInfo = new List<Model.Sku.SkuConditionInfo>();
if (newConditionMultipier.Any())
{
conditionInfo = new Logic.Sku.GetSkuConditionInfo(sqlConnectionString)
.GetByConditionId(newConditionMultipier.Keys.ToList());
}
else
{
conditionInfo = new Logic.Sku.GetSkuConditionInfo(sqlConnectionString).GetAll();
}
for(int i = 0; i < conditionInfo.Count; i++)
{
if (newConditionMultipier.ContainsKey(conditionInfo[i].SkuConditionId))
{
newConditionMultipier[conditionInfo[i].SkuConditionId] = conditionInfo[i].CompetitivePriceMultiplier;
}
else
{
throw new Exception("Condition Id mismatch");
}
}
// check all multipilers have been set
for (int i = 0; i < newConditionMultipier.Count; i++)
{
if (newConditionMultipier.ElementAt(i).Value <= 0)
{
throw new Exception("Invalid price multiplier");
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Sku
{
public class GetSkuConditionInfo
{
private string sqlConnectionString;
private Data.Database.Sku.ReadSkuConditionInfo dbRead;
public GetSkuConditionInfo(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
dbRead = new Data.Database.Sku.ReadSkuConditionInfo(sqlConnectionString);
}
public List<Model.Sku.SkuConditionInfo> GetAll()
{
return dbRead.Read(new List<int>());
}
public List<Model.Sku.SkuConditionInfo> GetByConditionId(List<int> conditionIdList)
{
return dbRead.Read(conditionIdList);
}
}
}

View File

@@ -0,0 +1,386 @@
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Sku.Price
{
public class FbaPricing
{
private string sqlConnectionString;
private bnhtrade.Core.Logic.Log.LogEvent log = new Log.LogEvent();
string err = "FbaPricing Error: ";
private string marginSchemeTaxCode = "T190";
int repriceHoldOnSalePeriod = 14; // days
private int newConditionId = Data.Database.Constants.GetProductConditionIdNew();
private List<Model.Sku.Price.SkuPriceParameter> skuInfo;
private Dictionary<string, Model.Product.CompetitivePrice> competitivePrices;
DateTime crTimeStamp = DateTime.UtcNow;
private int repriceIncrementDivisor = 60;
private Dictionary<string, int> saleCountInPeriod = new Dictionary<string, int>();
private Logic.Account.TaxCalculation taxCalc;
private decimal marginSchemeMargin;
public FbaPricing(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
taxCalc = new Account.TaxCalculation();
crTimeStamp = DateTime.UtcNow;
marginSchemeMargin = taxCalc.GetMarginMultiplier(crTimeStamp);
}
public void Update(bool overrideDayCheck = false)
{
using (var scope = new TransactionScope())
{
string orderChannel = "Amazon.co.uk"; // may in future enable other order channels
// need to add some cheks up here for last stock reconcilliation
// get current sku base pricing details (stock quantity, competative price, VAT info, etc.)
skuInfo = new Data.Database.Sku.Price.ReadParameter(sqlConnectionString).Execute();
if (skuInfo == null || !skuInfo.Any())
{
throw new Exception("Querying the database returned no records.");
}
// create lists that we'll add to during lopp
var loader = new List<Model.Export.AmazonIventoryLoaderFile>();
var crDictionary = new Dictionary<string, Model.Sku.Price.PriceInfo>();
// open needed classes
var readAge = new Data.Database.Import.ReadFbaInventoryAge(sqlConnectionString);
// get current db pricing
var dbDictionary = new Data.Database.Sku.Price.ReadPricingDetail(sqlConnectionString).ReadDictionary(skuInfo.Select(o => o.SkuNumber).ToList(), orderChannel);
// get required competivie prices
var readComp = new Logic.Product.GetCompetitivePrice(sqlConnectionString);
readComp.EstimatePrice = true;
var compPrices = readComp.Execute(skuInfo);
// loop through skus returnd from stock query
for (int i = 0; i < skuInfo.Count(); i++)
{
string skuNumber = skuInfo[i].SkuNumber;
var cr = new Model.Sku.Price.PriceInfo();
if (!overrideDayCheck && dbDictionary.Count > 0 && !OkayToReprice(dbDictionary[skuNumber].PriceInfoTimeStamp))
{ continue; }
// load in values from skuInfo
cr.PriceTypeId = 1;
cr.ReviewRequired = false;
cr.OrderChannel = orderChannel;
cr.OrderChannelQuantity = skuInfo[i].TotalQuantity;
cr.PriceInfoTimeStamp = crTimeStamp;
cr.SkuNumber = skuInfo[i].SkuNumber;
// get inventory age range
var invAge = readAge.BySkuNumber(skuInfo[i].SkuNumber, orderChannel);
if (invAge == null)
{
// this means lost stock, or unreconciled inventory... need to pause these skus else the price could decrease without it being on sale
err += "No records returned from tblImportFbaInventoryAgeReport for skuID=" + skuInfo[i].SkuId;
log.LogError(err);
throw new Exception(err);
}
cr.InventoryAgeMin = invAge.Value.MinAge;
cr.InventoryAgeMax = invAge.Value.MaxAge;
// get minimum prices
cr.UnitMinPriceCost = GetMinPriceCost(i);
cr.UnitMinPriceProfit = GetMinPriceProfit(i);
cr.UnitPurchaseCost = skuInfo[i].UnitCostAverage;
// set competitive price
if (compPrices.ContainsKey(skuInfo[i].SkuNumber))
{
cr.CompetitivePrice = compPrices[skuInfo[i].SkuNumber].Price;
cr.CompetitivePriceIsEstimated = compPrices[skuInfo[i].SkuNumber].PriceIsEstimated;
}
else
{
cr.CompetitivePrice = cr.UnitMinPriceProfit + (cr.UnitMinPriceProfit / 2);
cr.CompetitivePriceIsEstimated = true;
}
// set min max price
cr.RepriceIncrement = cr.CompetitivePrice / repriceIncrementDivisor;
if (dbDictionary.ContainsKey(skuNumber))
{
// sales wihtin period, therefore hold price
if (GetSaleCountInPeriod(i) > 0)
{
cr.MaxPrice = dbDictionary[skuNumber].MaxPrice;
cr.MinPrice = dbDictionary[skuNumber].MinPrice;
cr.RepriceIncrement = dbDictionary[skuNumber].RepriceIncrement;
}
// else reduce
else
{
if (dbDictionary[skuNumber].MaxPrice < dbDictionary[skuNumber].MinPrice)
{
err += "Max price lower than min price";
log.LogError(err);
throw new Exception(err);
}
// redue both prices together
else if (dbDictionary[skuNumber].MaxPrice == dbDictionary[skuNumber].MinPrice)
{
cr.MaxPrice = dbDictionary[skuNumber].MaxPrice - cr.RepriceIncrement;
cr.MinPrice = cr.MaxPrice;
}
// reduce only max until it hits the min
else
{
cr.MaxPrice = dbDictionary[skuNumber].MaxPrice - cr.RepriceIncrement;
if (cr.MaxPrice < dbDictionary[skuNumber].MinPrice)
{
cr.MinPrice = cr.MaxPrice;
}
else
{
cr.MinPrice = dbDictionary[skuNumber].MinPrice;
}
}
}
}
else
{
// new value
cr.MaxPrice = cr.CompetitivePrice * 1.2m;
cr.MinPrice = cr.CompetitivePrice * 1m;
}
// check on min price
cr.UnitMinPriceDestory = GetMinPriceDestroy(i);
if (cr.MaxPrice < cr.UnitMinPriceDestory) { cr.MaxPrice = cr.UnitMinPriceDestory; }
if (cr.MinPrice < cr.UnitMinPriceDestory) { cr.MinPrice = cr.UnitMinPriceDestory; }
// add values to inventory loader list
var item = new Model.Export.AmazonIventoryLoaderFile();
item.Sku = skuInfo[i].SkuNumber;
item.Price = cr.MaxPrice;
item.MinimumAllowedPrice = cr.MinPrice;
item.MaximumAllowedPrice = cr.MaxPrice;
item.SetFulfillmentCenterId(true);
loader.Add(item);
// save current prices to dictionary
if (crDictionary.ContainsKey(skuNumber))
{
err += "Multiple SkuId found in data";
log.LogError(err);
throw new Exception(err);
}
crDictionary.Add(skuNumber, cr);
}
// finish loop
// validate and save values to database
var validate = new Core.Logic.Validate.SkuPriceInfo();
if (!validate.IsValidDatabaseCreate(crDictionary.Values.ToList()))
{
err += "Database object create validation failed";
log.LogError(err, validate.ValidationResultListToString());
throw new Exception(err);
}
new Data.Database.Sku.Price.CreatePricingDetail(sqlConnectionString).Executue(crDictionary.Values.ToList());
// create and upload inventory loader file to amazon
var exportList = new List<Model.Export.AmazonIventoryLoaderFile>();
foreach (var item in crDictionary.Values)
{
var listItem = new Model.Export.AmazonIventoryLoaderFile();
listItem.Sku = item.SkuNumber;
listItem.MinimumAllowedPrice = item.MinPrice;
listItem.MaximumAllowedPrice = item.MaxPrice;
listItem.Price = item.MaxPrice;
listItem.SetFulfillmentCenterId(true);
exportList.Add(listItem);
}
// validate
var vaildateInvLoader = new Validate.AmazonIventoryLoaderFile();
if (!vaildateInvLoader.IsValidFbaPricing(exportList))
{
err += "Inventory loader object validation failed";
log.LogError(err, vaildateInvLoader.ValidationResultListToString());
throw new Exception(err);
}
// create file stream
var config = new CsvHelper.Configuration.CsvConfiguration(CultureInfo.CurrentCulture);
config.Delimiter = "\t";
config.Encoding = Encoding.UTF8;
var stream = new MemoryStream();
using (var writer = new StreamWriter(stream, Encoding.UTF8))
using (var csv = new CsvWriter(writer, config))
{
csv.WriteRecords(exportList);
}
// submit file to database and amazon mws
var submit = new Logic.Export.AmazonSubmitFile(sqlConnectionString);
return;
submit.SubmitInventoryLoader(stream);
scope.Complete();
}
}
private bool OkayToReprice(DateTime lastPriceUpdate)
{
if (lastPriceUpdate == default(DateTime))
{
err += "Invalid, datetime is default.";
log.LogError(err);
throw new Exception(err);
}
bool update = false;
lastPriceUpdate = new DateTime(lastPriceUpdate.Year, lastPriceUpdate.Month, lastPriceUpdate.Day);
DateTime today = new DateTime(crTimeStamp.Year, crTimeStamp.Month, crTimeStamp.Day);
// will only update once on tue, wed or thurs each week.
if (today.DayOfWeek == DayOfWeek.Tuesday || today.DayOfWeek == DayOfWeek.Wednesday || today.DayOfWeek == DayOfWeek.Thursday)
{
if (today > lastPriceUpdate.AddDays(3))
{
update = true;
}
}
return update;
}
/// <summary>
/// Get the minimum sale price to break even.
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private decimal GetMinPriceCost(int i)
{
decimal costPrice = skuInfo[i].UnitCostAverage;
decimal agentFeeFixed = skuInfo[i].AgentFeeFixed;
decimal agentFeeMargin = skuInfo[i].AgentFeeMargin;
decimal vatMargin = skuInfo[i].VatMargin;
decimal price = 0;
if (skuInfo[i].TaxCode == marginSchemeTaxCode)
{
price = (costPrice + agentFeeFixed - (costPrice * marginSchemeMargin))
/ (1 - agentFeeMargin - marginSchemeMargin);
}
else
{
price = (costPrice + agentFeeFixed)
/ (1 - agentFeeMargin - vatMargin);
}
price = decimal.Round(price, 2);
return price;
}
/// <summary>
/// Get the minimum sale price to achieve required profit margin.
/// </summary>
/// <param name="i"></param>
/// <returns>Minimum price in decimal</returns>
private decimal GetMinPriceProfit(int i)
{
decimal costPrice = skuInfo[i].UnitCostAverage;
decimal minProfit = skuInfo[i].PriceMinProfit;
decimal profitMargin = skuInfo[i].ProfitMargin;
decimal agentFeeFixed = skuInfo[i].AgentFeeFixed;
decimal agentFeeMargin = skuInfo[i].AgentFeeMargin;
decimal vatMargin = skuInfo[i].VatMargin;
decimal price = 0;
if (skuInfo[i].TaxCode == marginSchemeTaxCode) // taxcodeinfo now has ismarginscheme boolean
{
price = (costPrice + agentFeeFixed - (costPrice * marginSchemeMargin))
/ (1 - profitMargin - agentFeeMargin - marginSchemeMargin);
}
else
{
price = (costPrice + agentFeeFixed)
/ (1 - profitMargin - agentFeeMargin - vatMargin);
}
price = decimal.Round(price, 2);
// if profit margin is less than min required, redo using min value (not percent)
if (price < minProfit)
{
if (skuInfo[i].TaxCode == marginSchemeTaxCode)
{
price = (minProfit + costPrice + agentFeeFixed - (costPrice * marginSchemeMargin))
/ (1 - agentFeeMargin - marginSchemeMargin);
}
else
{
price = (minProfit + costPrice + agentFeeFixed)
/ (1 - agentFeeMargin - vatMargin);
}
}
price = decimal.Round(price, 2);
return price;
}
private decimal GetMinPriceDestroy(int i)
{
decimal agentFeeFixed = skuInfo[i].AgentFeeFixed;
decimal agentFeeMargin = skuInfo[i].AgentFeeMargin;
decimal vatMargin = skuInfo[i].VatMargin;
decimal price = 0;
if (skuInfo[i].TaxCode == marginSchemeTaxCode)
{
price = (agentFeeFixed) / (1 - agentFeeMargin);
}
else
{
price = (agentFeeFixed) / (1 - agentFeeMargin - vatMargin);
}
price = decimal.Round(price, 2);
return price;
}
private int GetSaleCountInPeriod(int i)
{
if (!saleCountInPeriod.Any())
{
saleCountInPeriod = new Data.Database.Import.ReadFbaSaleShipment(sqlConnectionString)
.GetSaleCount(skuInfo.Select(x => x.SkuNumber).ToList(), DateTime.Now.AddDays(repriceHoldOnSalePeriod * -1), DateTime.Now);
}
if (saleCountInPeriod.ContainsKey(skuInfo[i].SkuNumber))
{
return saleCountInPeriod[skuInfo[i].SkuNumber];
}
else
{
return 0;
}
}
}
}

View File

@@ -0,0 +1,787 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Transactions;
namespace bnhtrade.Core.Logic.Sku.Price
{
public class UpdateRepricingValues
{
//public void Update(string sqlConnectionString, bool skipReportUpdate = false)
//{
// // need to add some cheks up here for last amazon reports import dates
// /*
// * Quite complex this one, order of business
// *
// * Stage 1 - Stock Ledger Query reporting FBA stock (loop through this table)
// * Stage 1 - Read current values from SkuPrice tabel into variables
// * Stage 2 - Get/Set current MIN Price type 'crPriceMin_SkuPriceType'
// * Stage 3 - Set MIN Price, based off type
// * Stage 4 - Get/Set current MAX Price type 'crPriceMin_SkuPriceType'
// * Stage 5 - Set MAX Price, based off type
// * Stage 6 - Final check before db update
// * Stage 7 - Update main/history table
// */
// DateTime crTimeStamp = DateTime.UtcNow;
// int orderChannelId = 2; // may in future enable other order channels
// // refers to max price movements
// decimal repriceDecreaseIncrement = 0.03m; // 3% each week
// int repriceDecreasePeriod = 7; // days
// int repriceHoldOnSalePeriod = 14; // days
// decimal repriceIncreaseIncrement = repriceDecreaseIncrement; // increases price if max sale price is within % of max price
// decimal repriceIncreaseOnStockSold = 0.3m; // percent of total qty sold over sale hold period
// // add a check up here to test for any ASINs that do not have an estimated fee
// // maybe add a constistancy check here (double entries in table (sku and order channel combination)
// using (var conn = new SqlConnection(sqlConnectionString))
// {
// // loop through table
// using (var cmd01 = new SqlCommand("needs deleteing", conn))
// {
// using (var reader01 = cmd01.ExecuteReader())
// {
// var skuPricing = new Data.Database.Sku.Price.ReadParameter(sqlConnectionString).Execute();
// if (skuPricing == null)
// {
// throw new Exception("Querying the database returned no records.");
// }
// else
// {
// new Data.Database.Sku.Price.UpdateSkuPriceIsProcessed(sqlConnectionString).Update();
// }
// using (var scope = new TransactionScope())
// using (var scopeConn = new SqlConnection(sqlConnectionString))
// {
// scopeConn.Open();
// foreach (var sku in skuPricing)
// {
// // switches
// bool insertRequired = false; // skuId does not already exist in table
// bool updateRequired = false; // skuId exists and an edit is required
// bool resetMaxBaseAndMultiplier = false; // Quantity is moving from 0 to >0, lookup/update some values is required
// // current (cr) variables
// var cr = new Model.Sku.Price.DetailResponce();
// cr.OrderChannelQuantity = sku.TotalQuantity;
// cr.UnitAvgCost = sku.UnitCostAverage;
// // Stage 1
// // get db values from inventory table
// var db = new Data.Database.Sku.Price.ReadPricingDetail(sqlConnectionString).Read(sku.SkuId, orderChannelId);
// if (db == null)
// {
// insertRequired = true;
// db = new Model.Sku.Price.DetailResponce();
// // if this is null alot of the following code can be skipped, wip
// }
// // STAGE 2
// // SKU current stock details (i.e. quantity, cost per unit, inventory age, etc.)
// // get inventory age range
// var invAge = new Data.Database.Import.ReadFbaInventoryAge(sqlConnectionString).BySkuId(sku.SkuId, db.OrderChannelId);
// if (invAge == null)
// {
// throw new Exception("No records returned from tblImportFbaInventoryAgeReport for skuID=" + sku.SkuId);
// }
// cr.InventoryAgeMin = invAge.Value.MinAge;
// cr.InventoryAgeMax = invAge.Value.MaxAge;
// // STAGE 3
// // Get/Set Min Price SkukPriceTypeID
// // Do not change the 'crPriceMin_SkuPriceType' value after this stage
// //
// //bool setMinToAuto = true;
// cr.PriceMin_SkuPriceType = 0;
// switch (db.PriceMin_SkuPriceType)
// {
// // Out of Stock
// case 0:
// if (cr.OrderChannelQuantity > 0)
// { cr.PriceMin_SkuPriceType = 1; }
// break;
// //Auto
// case 1:
// cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType;
// break;
// // Fixed SKU price
// case 2:
// cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType;
// break;
// // Manual Price, Permanent
// case 100:
// cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType;
// break;
// // Manual Price, Inv Age Range Includes
// case 110:
// if (db.PriceMinStoredInt >= cr.InventoryAgeMin && db.PriceMinStoredInt <= cr.InventoryAgeMax)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Age Range Less Than
// case 111:
// if (cr.InventoryAgeMax < db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Age Range Greater Than
// case 112:
// if (cr.InventoryAgeMin > db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Less than
// case 120:
// if (cr.OrderChannelQuantity < db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Greater than
// case 121:
// if (cr.OrderChannelQuantity > db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Equal to
// case 122:
// if (cr.OrderChannelQuantity == db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Equal to
// case 123:
// if (cr.OrderChannelQuantity != db.PriceMinStoredInt)
// { cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType; }
// else { cr.PriceMin_SkuPriceType = 1; }
// break;
// default:
// // review of some sort required
// if (db.PriceMin_SkuPriceType >= 200 && db.PriceMin_SkuPriceType < 225)
// {
// cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType;
// cr.ReviewRequired = true;
// }
// // error type
// else if (db.PriceMin_SkuPriceType >= 225 && db.PriceMin_SkuPriceType < 256)
// {
// cr.PriceMin_SkuPriceType = db.PriceMin_SkuPriceType;
// }
// // unhandled id type
// else
// {
// cr.PriceMin_SkuPriceType = 255;
// cr.ReviewRequired = true;
// }
// break;
// }
// // Check manual enteries are present, if they are required
// if ((db.RequireAmountManual && db.PriceMinAmountManual == null) || (db.RequireStordedInt && db.PriceMinStoredInt == null))
// {
// cr.PriceMin_SkuPriceType = 253;
// cr.ReviewRequired = true;
// }
// // Stage 4
// // Set MIN Price
// // calculate the min 'auto' sku price & tax
// decimal crProfitMinAmount;
// decimal crPriceMinCalculatedTax;
// if (sku.TaxRateName == "MS" || sku.TaxRateName == "GA")
// {
// cr.PriceMinAmountAuto = (5m * cr.UnitAvgCost + 6m * sku.AgentFeeFixed) / (-6m * sku.ProfitMargin - 6m * sku.AmazonMargin + 5m);
// crPriceMinCalculatedTax = (cr.PriceMinAmountAuto - cr.UnitAvgCost) * (1 / 6);
// crProfitMinAmount = cr.PriceMinAmountAuto * sku.ProfitMargin;
// }
// else
// {
// cr.PriceMinAmountAuto = (cr.UnitAvgCost + sku.AgentFeeFixed) / (1 - (sku.ProfitMargin + sku.AmazonMargin + sku.VatMargin));
// crPriceMinCalculatedTax = cr.PriceMinAmountAuto * sku.VatMargin;
// crProfitMinAmount = cr.PriceMinAmountAuto * sku.ProfitMargin;
// }
// // if profit margin is less than min required, redo
// if (crProfitMinAmount < sku.PriceMinProfit)
// {
// if (sku.TaxRateName == "MS" || sku.TaxRateName == "GA")
// {
// cr.PriceMinAmountAuto = (6m * sku.PriceMinProfit + 5m * cr.UnitAvgCost + 6m * sku.AgentFeeFixed) / (-6m * sku.AmazonMargin + 5m);
// crPriceMinCalculatedTax = (cr.PriceMinAmountAuto - cr.UnitAvgCost) * (1 / 6);
// crProfitMinAmount = cr.PriceMinAmountAuto * sku.ProfitMargin;
// }
// else
// {
// cr.PriceMinAmountAuto = (cr.UnitAvgCost + sku.AgentFeeFixed + sku.PriceMinProfit) / (1 - (sku.AgentFeeMargin + sku.VatMargin));
// crPriceMinCalculatedTax = cr.PriceMinAmountAuto * sku.VatMargin;
// crProfitMinAmount = cr.PriceMinAmountAuto * sku.ProfitMargin;
// }
// }
// // Calculate current final MIN price values
// if (cr.PriceMin_SkuPriceType == 1)
// {
// cr.PriceMinAmountFinal = cr.PriceMinAmountAuto;
// cr.PriceMinAmountFinalFee = (cr.PriceMinAmountFinal * sku.AgentFeeMargin) + sku.AgentFeeFixed;
// cr.PriceMinAmountFinalTax = crPriceMinCalculatedTax;
// }
// else
// {
// cr.PriceMinAmountFinal = (decimal)db.PriceMinAmountManual;
// cr.PriceMinAmountFinalFee = ((decimal)db.PriceMinAmountManual * sku.AgentFeeMargin) + sku.AgentFeeFixed;
// if (sku.TaxRateName == "MS" || sku.TaxRateName == "GA")
// {
// cr.PriceMinAmountFinalTax = ((decimal)db.PriceMinAmountManual - cr.UnitAvgCost) * (1 / 6);
// }
// else
// {
// cr.PriceMinAmountFinalTax = cr.PriceMinAmountAuto * sku.VatMargin;
// }
// }
// // STAGE 5
// // Get/Set MAX Price SkukPriceTypeID
// // Do not change the 'cr.PriceMax_SkuPriceType' value after this stage
// cr.PriceMax_SkuPriceType = 0;
// switch (db.PriceMax_SkuPriceType)
// {
// // Out of Stock
// case 0:
// if (cr.OrderChannelQuantity > 0)
// { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Auto
// case 1:
// cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType;
// break;
// // Fixed SKU price
// case 2:
// cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType;
// break;
// // Manual Price, Permanent
// case 100:
// cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType;
// break;
// // Manual Price, Inv Age Range Includes
// case 110:
// if (db.PriceMaxStoredInt >= cr.InventoryAgeMin && db.PriceMaxStoredInt <= cr.InventoryAgeMax)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Age Range Less Than
// case 111:
// if (cr.InventoryAgeMax < db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Age Range Greater Than
// case 112:
// if (cr.InventoryAgeMin > db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Less than
// case 120:
// if (cr.OrderChannelQuantity < db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Greater than
// case 121:
// if (cr.OrderChannelQuantity > db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Equal to
// case 122:
// if (cr.OrderChannelQuantity == db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// // Manual Price, Inv Quanity is Not Equal to
// case 123:
// if (cr.OrderChannelQuantity == db.PriceMaxStoredInt)
// { cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType; }
// else { cr.PriceMax_SkuPriceType = 1; }
// break;
// default:
// // review of some sort required
// if (db.PriceMax_SkuPriceType >= 200 && db.PriceMax_SkuPriceType < 225)
// {
// cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType;
// cr.ReviewRequired = true;
// }
// // error type
// else if (db.PriceMax_SkuPriceType >= 225 && db.PriceMax_SkuPriceType < 256)
// {
// cr.PriceMax_SkuPriceType = db.PriceMax_SkuPriceType;
// }
// // unhandled id type
// else
// {
// cr.PriceMax_SkuPriceType = 255;
// cr.ReviewRequired = true;
// }
// break;
// }
// // Check manual enteried are present, if they are required
// if ((db.RequireAmountManualMax && db.PriceMaxAmountManual == null) || (db.RequireStordedIntMax && db.PriceMaxStoredInt == null))
// {
// cr.PriceMax_SkuPriceType = 253;
// cr.ReviewRequired = true;
// }
// // STAGE 6
// // Set MAX Price
// // CASE: Reset MAX-Price base & multiplier values (new record or switching back to auto)
// if (insertRequired || (cr.PriceMax_SkuPriceType == 1 && db.PriceMax_SkuPriceType != 1))
// {
// // get competative price and apply multiplier
// bool usedForceNew = false;
// var request = new bnhtrade.Core.Product.ProductQuery();
// var compPrice = request.ProductCompetitivePriceGet(sqlConnectionString, sku.ProductId, sku.ConditionId);
// if (compPrice.price == null || compPrice.priceDate == null)
// {
// if (sku.ConditionId != 10)
// {
// usedForceNew = true;
// compPrice = request.ProductCompetitivePriceGet(sqlConnectionString, sku.ProductId, 10);
// }
// if (compPrice.price == null || compPrice.priceDate == null)
// {
// cr.PriceMax_SkuPriceType = 254;
// cr.ReviewRequired = true;
// }
// }
// cr.PriceMaxAmountAutoBase = compPrice.price ?? 0;
// if (usedForceNew)
// {
// cr.PriceMaxAmountAutoMultiplier = sku.CompetitivePriceMultiplierNew;
// }
// else
// {
// cr.PriceMaxAmountAutoMultiplier = sku.CompetitivePriceMultiplierMatch;
// }
// // transfer manual values, if required
// if (insertRequired == false)
// {
// cr.PriceMaxAmountManual = db.PriceMaxAmountManual;
// cr.PriceMaxStoredInt = db.PriceMaxStoredInt;
// }
// // set final max value
// cr.PriceMaxAmountFinal = cr.PriceMaxAmountAutoBase * cr.PriceMaxAmountAutoMultiplier;
// }
// // already on auto, adjust existing value, if required
// else if (db.PriceMax_SkuPriceType == 1)
// {
// // get last sale date
// int shipmentsInSaleHoldPeriod = 0;
// // works from last unit ship-date, not the preferred order-date
// // TODO: this could be better when other parts of application get more info
// DateTime shipDateFilter = crTimeStamp.AddDays(repriceHoldOnSalePeriod * -1);
// shipDateFilter = new DateTime(shipDateFilter.Year, shipDateFilter.Month, shipDateFilter.Day);
// using (var cmd04 = new SqlCommand(@"
// SELECT COUNT(tblImportFbaSaleShipment.ImportFbaSaleShipmentID) AS RecordCount
// FROM tblImportFbaSaleShipment
// INNER JOIN tblSku ON tblImportFbaSaleShipment.sku = tblSku.skuSkuNumber
// WHERE (tblSku.skuSkuID = @skuID)
// AND (tblImportFbaSaleShipment.[shipment-date] >= @shipDateFilter)
// ", conn))
// {
// cmd04.Parameters.AddWithValue("@skuID", sku.SkuId);
// cmd04.Parameters.AddWithValue("@shipDateFilter", shipDateFilter);
// shipmentsInSaleHoldPeriod = (int)cmd04.ExecuteScalar();
// }
// // get max item sale price during period
// decimal maxSalePrice = 0;
// if (shipmentsInSaleHoldPeriod > 0)
// {
// using (var cmd05 = new SqlCommand(@"
// SELECT Max(ISNULL([tblImportFbaSaleShipment].[item-price], 0) + ISNULL([tblImportFbaSaleShipment].[item-tax], 0)) AS Expr1
// FROM tblImportFbaSaleShipment
// INNER JOIN tblSku ON tblImportFbaSaleShipment.sku = tblSku.skuSkuNumber
// WHERE (
// ((tblSku.skuSkuID) = @skuID)
// AND ((tblImportFbaSaleShipment.[shipment-date]) >= @shipDateFilter)
// )
// ", conn))
// {
// cmd05.Parameters.AddWithValue("@skuID", sku.SkuId);
// cmd05.Parameters.AddWithValue("@shipDateFilter", shipDateFilter);
// maxSalePrice = (decimal)cmd05.ExecuteScalar();
// }
// }
// DateTime updateDate = new DateTime(db.PriceCreated.Year, db.PriceCreated.Month, db.PriceCreated.Day);
// updateDate = updateDate.AddDays(repriceDecreasePeriod);
// // decrease maxx price
// if (crTimeStamp >= updateDate && shipmentsInSaleHoldPeriod == 0)
// {
// cr.PriceMaxAmountAutoBase = db.PriceMaxAmountAutoBase;
// cr.PriceMaxAmountAutoMultiplier = db.PriceMaxAmountAutoMultiplier - repriceDecreaseIncrement;
// cr.PriceMaxAmountManual = db.PriceMaxAmountManual;
// cr.PriceMaxStoredInt = db.PriceMaxStoredInt;
// cr.PriceMaxAmountFinal = cr.PriceMaxAmountAutoBase * cr.PriceMaxAmountAutoMultiplier;
// }
// else
// {
// // increase price
// if (maxSalePrice >= (db.PriceMaxAmountAutoBase * (db.PriceMaxAmountAutoMultiplier - repriceIncreaseIncrement)))
// {
// cr.PriceMaxAmountAutoBase = db.PriceMaxAmountAutoBase;
// cr.PriceMaxAmountAutoMultiplier = db.PriceMaxAmountAutoMultiplier + repriceIncreaseIncrement;
// cr.PriceMaxAmountManual = db.PriceMaxAmountManual;
// cr.PriceMaxStoredInt = db.PriceMaxStoredInt;
// cr.PriceMaxAmountFinal = cr.PriceMaxAmountAutoBase * cr.PriceMaxAmountAutoMultiplier;
// }
// // remain same
// else
// {
// cr.PriceMaxAmountAutoBase = db.PriceMaxAmountAutoBase;
// cr.PriceMaxAmountAutoMultiplier = db.PriceMaxAmountAutoMultiplier;
// cr.PriceMaxAmountManual = db.PriceMaxAmountManual;
// cr.PriceMaxStoredInt = db.PriceMaxStoredInt;
// cr.PriceMaxAmountFinal = cr.PriceMaxAmountAutoBase * cr.PriceMaxAmountAutoMultiplier;
// }
// }
// }
// // STAGE 7
// // Checks before update
// // max < min
// if (cr.PriceMaxAmountFinal < cr.PriceMinAmountFinal)
// {
// // on auto, set max to min value and update base multipier to reflect new value
// if (cr.PriceMax_SkuPriceType == 1)
// {
// cr.PriceMaxAmountFinal = cr.PriceMinAmountFinal;
// cr.PriceMaxAmountAutoMultiplier = cr.PriceMinAmountFinal / cr.PriceMaxAmountAutoBase;
// }
// else if (cr.PriceMax_SkuPriceType > 0 && cr.PriceMax_SkuPriceType < 200)
// {
// cr.PriceMax_SkuPriceType = 252;
// cr.ReviewRequired = true;
// }
// }
// // this should be last check
// // check for zero values (where there should not be any) -- this could be a life saver i.e. selling item for £0.00
// if (cr.PriceMinAmountFinal * cr.PriceMaxAmountFinal == 0)
// {
// cr.PriceMax_SkuPriceType = 251;
// cr.ReviewRequired = true;
// }
// // STAGE 8
// // Update main/history tables, if required
// // round decimals for db comarison
// cr.UnitAvgCost = decimal.Round(cr.UnitAvgCost, 2);
// cr.PriceMinAmountAuto = decimal.Round(cr.PriceMinAmountAuto, 2);
// cr.PriceMinAmountFinalFee = decimal.Round(cr.PriceMinAmountFinalFee, 2);
// cr.PriceMinAmountFinalTax = decimal.Round(cr.PriceMinAmountFinalTax, 2);
// cr.PriceMinAmountFinal = decimal.Round(cr.PriceMinAmountFinal, 2);
// cr.PriceMaxAmountAutoMultiplier = decimal.Round(cr.PriceMaxAmountAutoMultiplier, 8);
// cr.PriceMaxAmountFinal = decimal.Round(cr.PriceMaxAmountFinal, 2);
// if (insertRequired == false &&
// (
// db.OrderChannelQuantity != cr.OrderChannelQuantity ||
// db.UnitAvgCost != cr.UnitAvgCost ||
// db.InventoryAgeMin != cr.InventoryAgeMin ||
// db.InventoryAgeMax != cr.InventoryAgeMax ||
// db.PriceMin_SkuPriceType != cr.PriceMin_SkuPriceType ||
// db.PriceMinAmountAuto != cr.PriceMinAmountAuto ||
// db.PriceMinAmountFinalFee != cr.PriceMinAmountFinalFee ||
// db.PriceMinAmountFinalTax != cr.PriceMinAmountFinalTax ||
// db.PriceMinAmountFinal != cr.PriceMinAmountFinal ||
// db.PriceMax_SkuPriceType != cr.PriceMax_SkuPriceType ||
// db.PriceMaxAmountAutoBase != cr.PriceMaxAmountAutoBase ||
// db.PriceMaxAmountAutoMultiplier != cr.PriceMaxAmountAutoMultiplier ||
// db.PriceMaxAmountFinal != cr.PriceMaxAmountFinal ||
// db.ReviewRequired != cr.ReviewRequired
// )
// )
// {
// updateRequired = true;
// }
// // insert old data to history table
// if (updateRequired)
// {
// using (var cmd06 = new SqlCommand(@"
// INSERT INTO tblSkuPriceHistory (
// SkuID
// ,OrderChannelID
// ,OrderChannelQuantity
// ,UnitAvgCost
// ,InventoryAgeMin
// ,InventoryAgeMax
// ,PriceMin_SkuPriceType
// ,PriceMinAmountAuto
// ,PriceMinAmountManual
// ,PriceMinStoredInt
// ,PriceMinAmountFinalFee
// ,PriceMinAmountFinalTax
// ,PriceMinAmountFinal
// ,PriceMax_SkuPriceType
// ,PriceMaxAmountAutoBase
// ,PriceMaxAmountAutoMultiplier
// ,PriceMaxAmountManual
// ,PriceMaxStoredInt
// ,PriceMaxAmountFinal
// ,RecordModified
// ,ReviewRequired
// )
// VALUES (
// @skuID
// ,@dbOrderChannelID
// ,@dbOrderChannelQuantity
// ,@dbUnitAvgCost
// ,@dbInventoryAgeMin
// ,@dbInventoryAgeMax
// ,@dbPriceMin_SkuPriceType
// ,@dbPriceMinAmountAuto
// ,@dbPriceMinAmountManual
// ,@dbPriceMinStoredInt
// ,@dbPriceMinAmountFinalFee
// ,@dbPriceMinAmountFinalTax
// ,@dbPriceMinAmountFinal
// ,@dbPriceMax_SkuPriceType
// ,@dbPriceMaxAmountAutoBase
// ,@dbPriceMaxAmountAutoMultiplier
// ,@dbPriceMaxAmountManual
// ,@dbPriceMaxStoredInt
// ,@dbPriceMaxAmountFinal
// ,@dbRecordModified
// ,@dbReviewRequired
// )
// ", scopeConn))
// {
// cmd06.Parameters.AddWithValue("@skuID", sku.SkuId);
// cmd06.Parameters.AddWithValue("@dbOrderChannelID", db.OrderChannelId);
// cmd06.Parameters.AddWithValue("@dbOrderChannelQuantity", db.OrderChannelQuantity);
// cmd06.Parameters.AddWithValue("@dbUnitAvgCost", db.UnitAvgCost);
// cmd06.Parameters.AddWithValue("@dbInventoryAgeMin", db.InventoryAgeMin);
// cmd06.Parameters.AddWithValue("@dbInventoryAgeMax", db.InventoryAgeMax);
// cmd06.Parameters.AddWithValue("@dbPriceMin_SkuPriceType", db.PriceMin_SkuPriceType);
// cmd06.Parameters.AddWithValue("@dbPriceMinAmountAuto", db.PriceMinAmountAuto);
// cmd06.Parameters.AddWithValue("@dbPriceMinAmountManual", db.PriceMinAmountManual);
// cmd06.Parameters.AddWithValue("@dbPriceMinStoredInt", db.PriceMinStoredInt);
// cmd06.Parameters.AddWithValue("@dbPriceMinAmountFinalFee", db.PriceMinAmountFinalFee);
// cmd06.Parameters.AddWithValue("@dbPriceMinAmountFinalTax", db.PriceMinAmountFinalTax);
// cmd06.Parameters.AddWithValue("@dbPriceMinAmountFinal", db.PriceMinAmountFinal);
// cmd06.Parameters.AddWithValue("@dbPriceMax_SkuPriceType", db.PriceMax_SkuPriceType);
// cmd06.Parameters.AddWithValue("@dbPriceMaxAmountAutoBase", db.PriceMaxAmountAutoBase);
// cmd06.Parameters.AddWithValue("@dbPriceMaxAmountAutoMultiplier", db.PriceMaxAmountAutoMultiplier);
// cmd06.Parameters.AddWithValue("@dbPriceMaxAmountManual", db.PriceMaxAmountManual);
// cmd06.Parameters.AddWithValue("@dbPriceMaxStoredInt", db.PriceMaxStoredInt);
// cmd06.Parameters.AddWithValue("@dbPriceMaxAmountFinal", db.PriceMaxAmountFinal);
// cmd06.Parameters.AddWithValue("@dbRecordModified", db.PriceCreated);
// cmd06.Parameters.AddWithValue("@dbReviewRequired", db.ReviewRequired);
// int count = cmd06.ExecuteNonQuery();
// }
// }
// // delete existing data in current table
// if (updateRequired)
// {
// using (var cmd07 = new SqlCommand(@"
// DELETE FROM tblSkuPriceHistory
// WHERE SkuID = @skuID AND OrderChannelID = @dbOrderChannelID
// ", scopeConn))
// {
// cmd07.Parameters.AddWithValue("@skuID", sku.SkuId);
// cmd07.Parameters.AddWithValue("@dbOrderChannelID", db.OrderChannelId);
// int count = cmd07.ExecuteNonQuery();
// }
// }
// // insert new data into current table
// if (updateRequired || insertRequired)
// {
// using (var cmd06 = new SqlCommand(@"
// INSERT INTO tblSkuPrice (
// SkuID
// ,OrderChannelID
// ,OrderChannelQuantity
// ,UnitAvgCost
// ,InventoryAgeMin
// ,InventoryAgeMax
// ,PriceMin_SkuPriceType
// ,PriceMinAmountAuto
// ,PriceMinAmountManual
// ,PriceMinStoredInt
// ,PriceMinAmountFinalFee
// ,PriceMinAmountFinalTax
// ,PriceMinAmountFinal
// ,PriceMax_SkuPriceType
// ,PriceMaxAmountAutoBase
// ,PriceMaxAmountAutoMultiplier
// ,PriceMaxAmountManual
// ,PriceMaxStoredInt
// ,PriceMaxAmountFinal
// ,RecordModified
// ,ReviewRequired
// )
// VALUES (
// @skuID
// ,@crOrderChannelID
// ,@crOrderChannelQuantity
// ,@crUnitAvgCost
// ,@crInventoryAgeMin
// ,@crInventoryAgeMax
// ,@crPriceMin_SkuPriceType
// ,@crPriceMinAmountAuto
// ,@crPriceMinAmountManual
// ,@crPriceMinStoredInt
// ,@crPriceMinAmountFinalFee
// ,@crPriceMinAmountFinalTax
// ,@crPriceMinAmountFinal
// ,@crPriceMax_SkuPriceType
// ,@crPriceMaxAmountAutoBase
// ,@crPriceMaxAmountAutoMultiplier
// ,@crPriceMaxAmountManual
// ,@crPriceMaxStoredInt
// ,@crPriceMaxAmountFinal
// ,@crRecordModified
// ,@crReviewRequired
// )
// ", scopeConn))
// {
// cmd06.Parameters.AddWithValue("@skuID", sku.SkuId);
// cmd06.Parameters.AddWithValue("@crOrderChannelID", db.OrderChannelId);
// cmd06.Parameters.AddWithValue("@crOrderChannelQuantity", cr.OrderChannelQuantity);
// cmd06.Parameters.AddWithValue("@crUnitAvgCost", cr.UnitAvgCost);
// cmd06.Parameters.AddWithValue("@crInventoryAgeMin", cr.InventoryAgeMin);
// cmd06.Parameters.AddWithValue("@crInventoryAgeMax", cr.InventoryAgeMax);
// cmd06.Parameters.AddWithValue("@crPriceMin_SkuPriceType", cr.PriceMin_SkuPriceType);
// cmd06.Parameters.AddWithValue("@crPriceMinAmountAuto", cr.PriceMinAmountAuto);
// cmd06.Parameters.AddWithValue("@crPriceMinAmountManual", cr.PriceMinAmountManual);
// cmd06.Parameters.AddWithValue("@crPriceMinStoredInt", cr.PriceMinStoredInt);
// cmd06.Parameters.AddWithValue("@crPriceMinAmountFinalFee", cr.PriceMinAmountFinalFee);
// cmd06.Parameters.AddWithValue("@crPriceMinAmountFinalTax", cr.PriceMinAmountFinalTax);
// cmd06.Parameters.AddWithValue("@crPriceMinAmountFinal", cr.PriceMinAmountFinal);
// cmd06.Parameters.AddWithValue("@crPriceMax_SkuPriceType", cr.PriceMax_SkuPriceType);
// cmd06.Parameters.AddWithValue("@crPriceMaxAmountAutoBase", cr.PriceMaxAmountAutoBase);
// cmd06.Parameters.AddWithValue("@crPriceMaxAmountAutoMultiplier", cr.PriceMaxAmountAutoMultiplier);
// cmd06.Parameters.AddWithValue("@crPriceMaxAmountManual", cr.PriceMaxAmountManual);
// cmd06.Parameters.AddWithValue("@crPriceMaxStoredInt", cr.PriceMaxStoredInt);
// cmd06.Parameters.AddWithValue("@crPriceMaxAmountFinal", cr.PriceMaxAmountFinal);
// cmd06.Parameters.AddWithValue("@crRecordModified", cr.PriceCreated);
// cmd06.Parameters.AddWithValue("@crReviewRequired", cr.ReviewRequired);
// int count = cmd06.ExecuteNonQuery();
// }
// }
// } // drop out of query while loop here
// // set any records that are not IsProcessed=TRUE to no quantity (i.e. by type) and quantity
// // or... do I copy record to history and delete <------- THIS
// // also, should add check, cross reference inventory age table against stock ledger results to highlight sku where I reckon
// // qty is zero and amazon is >0
// scope.Complete();
// }
// }
// }
// }
//}
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Stock
{
public class Reallocate
{
private string sqlConnectionString;
public Reallocate(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
public int StockReallocateByStockId(int journalTypeId, int stockId, int quantity, int debitStatusId, int creditStatusId,
DateTime entryDate = default(DateTime))
{
if (entryDate == default(DateTime))
{
entryDate = DateTime.Now;
}
// create the list
var posts = new List<(int statusId, int quantity)>();
posts.Add((debitStatusId, quantity));
posts.Add((creditStatusId, (quantity * -1)));
// execute
return Core.Stock.StockJournal.StockJournalInsert(sqlConnectionString, journalTypeId, stockId, posts, entryDate, false);
}
/// <summary>
/// Feed an skuId and quantity into function and the stock will be reallocated
/// </summary>
public List<(int StockJournalId, int Quantity)> StockReallocateBySkuNumber(int journalTypeId, string skuNumber, int quantity, int debitStatusId, int creditStatusId,
bool firstInFirstOut = true, DateTime entryDate = default(DateTime), bool reallocatePartialQuantity = false)
{
var returnList = new List<(int StockJournalId, int Quantity)>();
List<Tuple<int, DateTime, int>> list = Core.Stock.StockJournal.GetStockStatusBalanceBySkuNumber(sqlConnectionString, skuNumber, creditStatusId, entryDate, firstInFirstOut);
if (list == null || !list.Any())
{
return returnList;
}
// quantity check
int avaiableQuantity = 0;
foreach (Tuple<int, DateTime, int> item in list)
{
avaiableQuantity = avaiableQuantity + item.Item3;
}
if (avaiableQuantity < quantity && reallocatePartialQuantity == false)
{
return null;
}
// make the changes
using (TransactionScope scope = new TransactionScope())
{
foreach (Tuple<int, DateTime, int> item in list)
{
if (quantity > item.Item3)
{
int tempInt = StockReallocateByStockId(journalTypeId, item.Item1, item.Item3, debitStatusId, creditStatusId, entryDate);
quantity = quantity - item.Item3;
returnList.Add((tempInt, item.Item3));
}
else
{
int tempInt = StockReallocateByStockId(journalTypeId, item.Item1, quantity, debitStatusId, creditStatusId, entryDate);
returnList.Add((tempInt, quantity));
break;
}
}
scope.Complete();
return returnList;
}
}
}
}

View File

@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Stock
{
public class SkuTransactionPersistance
{
private string err = "Sku Transaction Persistance Error; ";
private string sqlConnectionString;
private Data.Database.Stock.DeleteSkuTransaction dbSkuTransDelete;
private Data.Database.Stock.CreateSkuTransaction dbSkuTransCreate;
private Data.Database.Stock.ReadSkuTransaction dbSkuTransRead;
private Data.Database.Stock.UpdateSkuTransaction dbSkuTransUpdate;
private Logic.Validate.SkuTransaction validateSkuTrans;
private Logic.Log.LogEvent log;
public SkuTransactionPersistance(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
log = new Log.LogEvent();
}
private Data.Database.Stock.DeleteSkuTransaction DatabaseSkuTransDelete(bool forceNew = false)
{
if (dbSkuTransDelete == null || forceNew)
{
dbSkuTransDelete = new Data.Database.Stock.DeleteSkuTransaction(sqlConnectionString);
}
return dbSkuTransDelete;
}
private Data.Database.Stock.CreateSkuTransaction DatabaseSkuTransInsert(bool forceNew = false)
{
if (dbSkuTransCreate == null || forceNew)
{
dbSkuTransCreate = new Data.Database.Stock.CreateSkuTransaction(sqlConnectionString);
}
return dbSkuTransCreate;
}
private Data.Database.Stock.ReadSkuTransaction DatabaseSkuTransRead(bool forceNew = false)
{
if (dbSkuTransRead == null || forceNew)
{
dbSkuTransRead = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString);
}
return dbSkuTransRead;
}
private Data.Database.Stock.UpdateSkuTransaction DatabaseSkuTransUpdate(bool forceNew = false)
{
if (dbSkuTransUpdate == null || forceNew)
{
dbSkuTransUpdate = new Data.Database.Stock.UpdateSkuTransaction(sqlConnectionString);
}
return dbSkuTransUpdate;
}
private Logic.Validate.SkuTransaction Validate()
{
if (validateSkuTrans == null)
{
validateSkuTrans = new Validate.SkuTransaction();
}
return validateSkuTrans;
}
public void Delete(int skuTransactionId)
{
using (var scope = new TransactionScope())
{
try
{
// is there a journal entry attached?
int? journalId = DatabaseSkuTransRead().GetJournalId(skuTransactionId);
if (journalId != null)
{
Core.Stock.StockJournal.StockJournalDelete(sqlConnectionString, (int)journalId);
}
DatabaseSkuTransDelete().ByTransactionId(skuTransactionId);
scope.Complete();
}
catch (Exception ex)
{
scope.Dispose();
throw ex;
}
}
}
public void DeleteJournalEntry(int skuTransactionId)
{
using (var scope = new TransactionScope())
{
try
{
// is there a journal entry attached?
int? journalId = DatabaseSkuTransRead().GetJournalId(skuTransactionId);
if (journalId != null)
{
DatabaseSkuTransUpdate().Update(skuTransactionId, null);
Core.Stock.StockJournal.StockJournalDelete(sqlConnectionString, (int)journalId);
}
scope.Complete();
}
catch (Exception ex)
{
scope.Dispose();
throw ex;
}
}
}
public void Create(Model.Stock.SkuTransaction skuTransaction)
{
if (skuTransaction == null)
{
throw new Exception(err + "Object was null");
}
Validate().Innit();
if (!Validate().DatabaseInsert(skuTransaction))
{
log.LogWarning(err + "Validation failed", Validate().ValidationResultListToString());
throw new Exception(err + "Validation failed");
}
// write to database
DatabaseSkuTransInsert().Create(skuTransaction);
}
public void Update(Model.Stock.SkuTransaction skuTransaction)
{
if (skuTransaction == null)
{
throw new Exception(err + "Object was null");
}
Validate().Innit();
if (!Validate().DatabaseUpdate(skuTransaction))
{
log.LogWarning(err + "Validation failed", Validate().ValidationResultListToString());
throw new Exception(err + "Validation failed");
}
using (var scope = new TransactionScope())
{
// is there an existing journal id that is changing
int? journalId = DatabaseSkuTransRead().GetJournalId(skuTransaction.SkuTransactionId);
if (journalId != null && skuTransaction.IsSetStockJournalId)
{
if (journalId != skuTransaction.StockJournalId)
{
DeleteJournalEntry(skuTransaction.SkuTransactionId);
}
}
dbSkuTransUpdate.Update(skuTransaction);
scope.Complete();
}
}
}
}

View File

@@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Stock
{
public class SkuTransactionReconcile
{
private string sqlConnectionString;
private Data.Database.AmazonShipment.ReadShipmentInfo readShipmentInfo;
private Logic.Stock.SkuTransactionPersistance dbSkuTransaction;
private Logic.Stock.SkuTransactionTypePersistance dbSkuTransactionType;
private Logic.Validate.SkuTransaction validateSkuTrans;
private Logic.Stock.Reallocate stockReallocate;
private Logic.Log.LogEvent logEvent;
private string err = "Reconcile Sku Transaction Exception: ";
public SkuTransactionReconcile(string sqlConnectionString)
{
Innit();
this.sqlConnectionString = sqlConnectionString;
dbSkuTransaction = new SkuTransactionPersistance(sqlConnectionString);
dbSkuTransactionType = new SkuTransactionTypePersistance(sqlConnectionString);
readShipmentInfo = new Data.Database.AmazonShipment.ReadShipmentInfo(sqlConnectionString);
validateSkuTrans = new Validate.SkuTransaction();
stockReallocate = new Logic.Stock.Reallocate(sqlConnectionString);
logEvent = new Log.LogEvent();
}
public int ItemsCompleted { get; private set; }
public int ItemsRemaining { get; private set; }
public DateTime LastItemDateTime { get; private set; }
public string ProgressMessage { get; private set; }
public bool ReconciliationComplete { get; private set; }
public int CurrentTransactionId { get; private set; }
public int CurrentTransactionTypeId { get; private set; }
public Model.Stock.SkuTransaction CurrentSkuTransaction { get; private set; }
private void Innit()
{
ItemsCompleted = 0;
ItemsRemaining = 0;
LastItemDateTime = default(DateTime);
ProgressMessage = null;
ReconciliationComplete = false;
CurrentTransactionId = 0;
CurrentTransactionTypeId = 0;
}
/// <summary>
/// Iterates through the stock transaction table and inserts stock journal entries, where applicable
/// N.B. This function does not make allowances for status' that can create stock (i.e. if a status does not have stock available, this function will halt processing rows)
/// </summary>
/// <param name="updateTransactions">Download and process Amazon reports before starting process</param>
/// <returns></returns>
public void ReconcileStockTransactions(bool updateTransactions)
{
Innit();
string currentMethodName = nameof(ReconcileStockTransactions);
// ensure import table have been processed into transaction table without exception
if (updateTransactions == true)
{
try
{
var preCheck = new bnhtrade.Core.Stock.StockReconciliation();
preCheck.ProcessFbaStockImportData(sqlConnectionString);
}
catch (Exception ex)
{
ProgressMessage = "Precheck failed: " + ex.Message;
return;
}
}
logEvent.LogInformation("Starting ReconcileStockTransactions()");
int recordSkip = 0;
ProcessLostAndFound();
// get list of sku transactions to reconcile
var transList = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString).GetUnreconciled();
ItemsRemaining = transList.Count;
ItemsCompleted = 0;
try
{
// loop through transaction list
for (int i = 0; i < transList.Count; i++)
{
using (var scope = new TransactionScope())
{
Console.Write("\rProcessing record: {0} ({1} skipped)", (i + 1 + recordSkip), recordSkip);
// setup return values
CurrentSkuTransaction = transList[i];
CurrentTransactionId = transList[i].SkuTransactionId;
CurrentTransactionTypeId = transList[i].SkuTransactionTypeId;
LastItemDateTime = transList[i].TransactionDate;
// load type into variable
var transType = dbSkuTransactionType.GetByTypeName(transList[i].SkuTransactionTypeName);
// stop if a new transactiontype is encountered
if (transType.IsNewReviewRequired)
{
ProgressMessage = "New 'Transaction-Type' encountered";
//Console.Write("\r");
//MiscFunction.EventLogInsert(errMessage, 1);
goto Stop;
}
// set debit/credit status' for special cases (i.e. NULL or 0 debit/credit ids in stockTransactionType table)
if (transType.StockJournalEntryEnabled == true && (transType.DebitStockStatusId == 0 || transType.CreditStockStatusId == 0))
{
// FBA Shipment Receipt +ve
if (transType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><+ve>"
|| transType.TypeCode == "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_RECEIPTS_DATA_><-ve>")
{
var shipmentInfo = readShipmentInfo.HeaderByFbaShipmentId(transList[i].Reference);
if (shipmentInfo.IsSetShipmentStockStatusId())
{
// +ve shipment receipt
if (transType.CreditStockStatusId == 0 && transType.DebitStockStatusId > 0)
{ transType.CreditStockStatusId = shipmentInfo.ShipmentStockStatusId; }
// -ve shipment receipt
else if (transType.DebitStockStatusId == 0 && transType.CreditStockStatusId > 0)
{ transType.DebitStockStatusId = shipmentInfo.ShipmentStockStatusId; }
// something went wrong, raise error
else
{
ProgressMessage = "Unable to retrive FBA shipment location/status from tblAmazonShipment for Amazon shipment '" + shipmentInfo.FbaShipmentId + "'.";
recordSkip = recordSkip + 1;
goto Stop;
}
}
}
// something went wrong, raise error
else
{
ProgressMessage = "Coding required. Unhandled special case Transaction-Type encountered (Transaction-Type debit or credit is set to 0).";
recordSkip = recordSkip + 1;
goto Stop;
}
}
// make the changes
if (transType.StockJournalEntryEnabled == false)
{
transList[i].IsProcessed = true;
dbSkuTransaction.Update(transList[i]);
}
else
{
var list = new List<(int StockJournalId, int Quantity)>();
if (transType.FilterStockOnDateTime)
{
list = stockReallocate.StockReallocateBySkuNumber(
transType.StockJournalTypeId,
transList[i].SkuNumber,
transList[i].Quantity,
transType.DebitStockStatusId,
transType.CreditStockStatusId,
transType.FirstInFirstOut,
transList[i].TransactionDate,
false);
}
else
{
list = stockReallocate.StockReallocateBySkuNumber(
transType.StockJournalTypeId,
transList[i].SkuNumber,
transList[i].Quantity,
transType.DebitStockStatusId,
transType.CreditStockStatusId,
transType.FirstInFirstOut,
DateTime.UtcNow,
false);
}
// insufficient balance available
if (list == null || !list.Any())
{
// in special case (found inventory), continue
if (transType.TypeCode.Contains("<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F>"))
{
continue;// <--------------------------------------------------------------------------------------------------- is this the soruce of the bug?
}
else
{
ProgressMessage = "Insurficent status/location balance to relocate stock";
recordSkip = recordSkip + 1;
goto Stop;
}
}
// fail safe
int qtyAllocated = list.Sum(c => c.Quantity);
if (qtyAllocated > transList[i].Quantity)
{
throw new Exception(
currentMethodName + ": StockReallocateBySkuId() returned greater quantity than passed to function"
+ transList[i].SkuTransactionId);
}
// update sku transaction table
int qtyRemain = qtyAllocated;
var newRecordList = new List<Model.Stock.SkuTransaction>();
for (int j = 0; j <= list.Count; j++)
{
// update existing record
if (j == 0)
{
transList[i].Quantity = (short)list[j].Quantity;
transList[i].StockJournalId = list[j].StockJournalId;
transList[i].IsProcessed = true;
dbSkuTransaction.Update(transList[i]);
}
// new record
else if (j < list.Count)
{
var newRecord = transList[i].Clone();
newRecord.Quantity = (short)list[j].Quantity;
newRecord.IsProcessed = true;
newRecord.StockJournalId = list[j].StockJournalId;
newRecordList.Add(newRecord);
}
// new record, unallocated quantity
else if (qtyRemain > 0)
{
var newRecord = transList[i].Clone();
newRecord.Quantity = (short)qtyRemain;
newRecord.IsProcessed = false;
newRecordList.Add(newRecord);
}
if (j < list.Count)
{
qtyRemain = qtyRemain - list[j].Quantity;
}
}
// add new transactions to table
for (int j = 0; j < newRecordList.Count; j++)
{
dbSkuTransaction.Create(newRecordList[j]);
}
}
ItemsCompleted++;
ItemsRemaining++;
scope.Complete();
}
// end of scope
}
// end of loop
}
catch (Exception ex)
{
Console.Write("\r");
ProgressMessage = ex.Message;
return;
}
Stop:
Console.Write("\r");
MiscFunction.EventLogInsert("ProcessStockTransactions() compete. " + ItemsCompleted + " total records processed, " + recordSkip + " rows uncompllete due to insurficent stock.");
MiscFunction.EventLogInsert("ProcessStockTransactions(), " + recordSkip + " rows skipped due to insurficent stock.", 2);
ReconciliationComplete = true;
ProgressMessage = "Operation complete.";
return;
}
/// <summary>
///
/// </summary>
public void ProcessLostAndFound()
{
using (var scope = new TransactionScope())
{
// get list of sku transactions to reconcile
var transList = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString).GetUnreconciled();
ItemsRemaining = transList.Count;
ItemsCompleted = 0;
// need to loop though table and cancel out any found before they are lost (in reality they were never
// lost, therefore should not be entered into journal as lost)
string lost = "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><M><-ve><InventoryMisplaced><SELLABLE>";
string found = "<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F><+ve><InventoryFound><SELLABLE>";
for (int i = 0; i < transList.Count; i++)
{
var transType = dbSkuTransactionType.GetByTypeName(transList[i].SkuTransactionTypeName);
if (transType.TypeCode == found && !transList[i].IsProcessed)
{
string sku = transList[i].SkuNumber;
int foundQty = transList[i].Quantity;
int foundQtyUnAllocated = foundQty;
//loop though list and find matching missing
for (int j = 0; j < transList.Count; j++)
{
// we have a match
if (transList[j].SkuNumber == sku && !transList[j].IsProcessed && transType.TypeCode == lost)
{
// split transaction and break
if (foundQtyUnAllocated - transList[j].Quantity < 0)
{
// create and validate clone
var clone = transList[j].Clone();
clone.Quantity = (short)foundQtyUnAllocated;
clone.IsProcessed = true;
// modifiy and validate existing record
transList[j].IsProcessed = false;
transList[j].Quantity = (short)(transList[j].Quantity - foundQtyUnAllocated);
foundQtyUnAllocated = 0;
// submitt to database
dbSkuTransaction.Create(clone);
dbSkuTransaction.Update(transList[j]);
}
// set as isprocessed and continue
else
{
foundQtyUnAllocated = foundQtyUnAllocated - transList[j].Quantity;
transList[j].IsProcessed = true;
dbSkuTransaction.Update(transList[j]);
}
// break?
if (foundQtyUnAllocated == 0)
{
break;
}
}
}
// update the found record
if (foundQty != foundQtyUnAllocated)
{
// set isprocess = true
if (foundQtyUnAllocated == 0)
{
transList[i].IsProcessed = true;
dbSkuTransaction.Update(transList[i]);
}
// split record
else if (foundQtyUnAllocated > 0)
{
throw new NotImplementedException();
// create clone
var clone = transList[i].Clone();
clone.Quantity -= (short)foundQtyUnAllocated;
clone.IsProcessed = true;
// modifiy and validate existing record
transList[i].IsProcessed = false;
transList[i].Quantity -= (short)foundQtyUnAllocated;
foundQtyUnAllocated = 0;
// submitt to database
dbSkuTransaction.Create(clone);
dbSkuTransaction.Update(transList[i]);
}
// this shouldn't happen
else
{
throw new Exception("Quantity unallocated is negative number");
}
}
}
}
scope.Complete();
}
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Stock
{
public class SkuTransactionTypePersistance
{
private string sqlConnectionString;
private List<Model.Stock.SkuTransactionType> cache;
private Data.Database.Stock.ReadSkuTransactionType dbRead;
public SkuTransactionTypePersistance(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
dbRead = new Data.Database.Stock.ReadSkuTransactionType(sqlConnectionString);
InnitCache();
}
public void InnitCache()
{
cache = new List<Model.Stock.SkuTransactionType>();
}
public Model.Stock.SkuTransactionType GetByTypeCode(string typeCode, bool clearCache = false)
{
if (string.IsNullOrWhiteSpace(typeCode))
{
return null;
}
if (clearCache)
{
InnitCache();
}
else
{
for (int i = 0; i < cache.Count; i++)
{
if (cache[i].TypeCode == typeCode)
{
return cache[i];
}
}
}
var result = dbRead.ByTypeCode(new List<string> { typeCode });
if (result.Any())
{
cache.Add(result[0]);
return result[0];
}
else
{
return null;
}
}
public Model.Stock.SkuTransactionType GetByTypeName(string typeName, bool clearCache = false)
{
if (string.IsNullOrWhiteSpace(typeName))
{
return null;
}
if (clearCache)
{
InnitCache();
}
else
{
for (int i = 0; i < cache.Count; i++)
{
if (cache[i].TypeName == typeName)
{
return cache[i];
}
}
}
var result = dbRead.ByTypeName(new List<string> { typeName });
if (result.Any())
{
cache.Add(result[0]);
return result[0];
}
else
{
return null;
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class CalculateMD5
{
public string Base64(Stream stream)
{
stream.Position = 0;
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] hash = provider.ComputeHash(stream);
string returnvalue = Convert.ToBase64String(hash);
return returnvalue;
}
public string Base64(byte[] byteArray)
{
MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
byte[] hash = provider.ComputeHash(byteArray);
string returnvalue = Convert.ToBase64String(hash);
return returnvalue;
}
}
}

View File

@@ -6,18 +6,18 @@ using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class DateTimeCheck : Validate
public class DateTimeCheck : Validate.Validate
{
public bool IsUtc(DateTime dateTimeToCheck)
{
if (dateTimeToCheck == default(DateTime))
{
ErrorListAdd( "DateTime value set to default.");
ValidationResultAdd( "DateTime value set to default.");
return false;
}
if (dateTimeToCheck.Kind != DateTimeKind.Utc)
{
ErrorListAdd("DateTime not set to UTC kind.");
ValidationResultAdd("DateTime not set to UTC kind.");
return false;
}
return true;
@@ -26,12 +26,12 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (dateTimeToCheck == default(DateTime))
{
ErrorListAdd("DateTime value set to default.");
ValidationResultAdd("DateTime value set to default.");
return false;
}
if (dateTimeToCheck.Kind != DateTimeKind.Local)
{
ErrorListAdd("DateTime not set to Local kind.");
ValidationResultAdd("DateTime not set to Local kind.");
return false;
}
return true;

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class DateTimeParse
{
public DateTime ParseMwsReportDateTime(string reportDateTime)
{
string isoDateTime =
reportDateTime.Substring(6, 4) + "-" +
reportDateTime.Substring(3, 2) + "-" +
reportDateTime.Substring(0, 2) + "T" +
reportDateTime.Substring(11, 2) + ":" +
reportDateTime.Substring(14, 2) + ":" +
reportDateTime.Substring(17, 2) + "Z";
return DateTime.Parse(isoDateTime);
}
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class DecimalCheck : Validate
public class DecimalCheck : Validate.Validate
{
/// <summary>
/// Finds total number of digits in a decimal number, does not include decimal point.
@@ -36,13 +36,13 @@ namespace bnhtrade.Core.Logic.Utilities
int precision = GetPrecision(decimalToCheck);
if (precision > 2)
{
ErrorListAdd("Decimal precision overload");
ValidationResultAdd("Decimal precision overload");
return false;
}
int length = GetLength(decimalToCheck);
if (length > 9)
{
ErrorListAdd("Decimal length overload");
ValidationResultAdd("Decimal length overload");
return false;
}
return true;

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
{
public class StringCheck : Validate
public class StringCheck : Validate.Validate
{
public bool AllowEmpty { get; set; } = false;
public bool AllowWhiteSpace { get; set; } = false;
@@ -22,7 +22,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (!allowNull)
{
ErrorListAdd("String is null");
ValidationResultAdd("String is null");
return false;
}
}
@@ -35,12 +35,12 @@ namespace bnhtrade.Core.Logic.Utilities
{
if ((c >= 'a' && c <= 'z') && upperCaseOnly)
{
ErrorListAdd("String contains lower case numerical charater(s).");
ValidationResultAdd("String contains lower case numerical charater(s).");
return false;
}
else
{
ErrorListAdd("String contains non-alpha charater(s).");
ValidationResultAdd("String contains non-alpha charater(s).");
return false;
}
}
@@ -54,7 +54,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (!allowNull)
{
ErrorListAdd("String is null");
ValidationResultAdd("String is null");
return false;
}
}
@@ -68,12 +68,12 @@ namespace bnhtrade.Core.Logic.Utilities
{
if ((c >= 'a' && c <= 'z') && upperCaseOnly)
{
ErrorListAdd("String contains lower case numerical charater(s).");
ValidationResultAdd("String contains lower case numerical charater(s).");
return false;
}
else
{
ErrorListAdd("String contains non-alphanumeric charater(s).");
ValidationResultAdd("String contains non-alphanumeric charater(s).");
return false;
}
}
@@ -87,7 +87,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (allowNull == false)
{
ErrorListAdd("String is null");
ValidationResultAdd("String is null");
return false;
}
}
@@ -97,7 +97,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (c < '0' || c > '9')
{
ErrorListAdd("String contains non-numeric charater(s).");
ValidationResultAdd("String contains non-numeric charater(s).");
return false;
}
}
@@ -114,7 +114,7 @@ namespace bnhtrade.Core.Logic.Utilities
int length = stringToCheck.Length;
if (length != stringLength)
{
ErrorListAdd("String length (" + length + ") does not equal " + stringLength + " charaters.");
ValidationResultAdd("String length (" + length + ") does not equal " + stringLength + " charaters.");
return false;
}
return true;
@@ -131,7 +131,7 @@ namespace bnhtrade.Core.Logic.Utilities
int length = stringToCheck.Length;
if (length > maxLength)
{
ErrorListAdd("String length (" + length + ") is greater than " + maxLength + " charaters.");
ValidationResultAdd("String length (" + length + ") is greater than " + maxLength + " charaters.");
return false;
}
}
@@ -147,7 +147,7 @@ namespace bnhtrade.Core.Logic.Utilities
int length = stringToCheck.Length;
if (length <= minLength)
{
ErrorListAdd("String length (" + length + ") is less than " + minLength + " charaters.");
ValidationResultAdd("String length (" + length + ") is less than " + minLength + " charaters.");
return false;
}
return true;
@@ -161,7 +161,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (!allowNull)
{
ErrorListAdd("String is null, empty or white space.");
ValidationResultAdd("String is null, empty or white space.");
return false;
}
}
@@ -169,7 +169,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (!AllowEmpty)
{
ErrorListAdd("String is empty.");
ValidationResultAdd("String is empty.");
return false;
}
}
@@ -177,7 +177,7 @@ namespace bnhtrade.Core.Logic.Utilities
{
if (!AllowWhiteSpace)
{
ErrorListAdd("String is white space.");
ValidationResultAdd("String is white space.");
return false;
}
}

View File

@@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic
{
public abstract class Validate
{
private List<string> errorList = new List<string>();
public List<string> ErrorList
{
get { return errorList; }
}
public bool ErrorListIsSet
{
get
{
if (errorList == null || !errorList.Any()) { return false; }
else { return true; }
}
}
protected void ErrorListAdd(string errorString)
{
this.errorList.Add(errorString);
}
protected void ErrorListAdd(List<string> errorList)
{
this.errorList.AddRange(errorList);
}
public string ErrorListToString()
{
if (ErrorListIsSet)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < ErrorList.Count; i++)
{
result.AppendLine(ErrorList[i]);
}
return result.ToString();
}
else { return null; }
}
public void Innit()
{
this.errorList = new List<string>();
}
}
}

View File

@@ -4,9 +4,9 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
namespace bnhtrade.Core.Logic.Validate
{
public class ValidateAccountCode : Validate
public class AccountCode : Validate
{
private Logic.Utilities.StringCheck stringCheck = new Logic.Utilities.StringCheck();
@@ -21,7 +21,7 @@ namespace bnhtrade.Core.Logic.Account
{ return true; }
else
{
ErrorListAdd("Invalid account code.");
ValidationResultAdd("Invalid account code.");
return false;
}
}
@@ -33,7 +33,7 @@ namespace bnhtrade.Core.Logic.Account
}
else
{
ErrorListAdd(stringCheck.ErrorList);
ValidationResultAdd(stringCheck.ValidationResult);
return false;
}
}
@@ -45,7 +45,7 @@ namespace bnhtrade.Core.Logic.Account
}
else
{
ErrorListAdd(stringCheck.ErrorList);
ValidationResultAdd(stringCheck.ValidationResult);
return false;
}
}
@@ -57,7 +57,7 @@ namespace bnhtrade.Core.Logic.Account
}
else
{
ErrorListAdd(stringCheck.ErrorList);
ValidationResultAdd(stringCheck.ValidationResult);
return false;
}
}
@@ -69,7 +69,7 @@ namespace bnhtrade.Core.Logic.Account
}
else
{
ErrorListAdd(stringCheck.ErrorList);
ValidationResultAdd(stringCheck.ValidationResult);
return false;
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Validate
{
public class AmazonIventoryLoaderFile : Validate
{
public bool IsValidFbaPricing(IEnumerable<Model.Export.AmazonIventoryLoaderFile> exportList)
{
if (!IsValid(exportList))
{
return false;
}
return IsValidResult;
}
}
}

View File

@@ -4,16 +4,16 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Import
namespace bnhtrade.Core.Logic.Validate
{
public class ValidateAmazonSettlement : Validate
public class AmazonSettlement : Validate
{
protected Utilities.StringCheck stringCheck = new Utilities.StringCheck();
protected Utilities.DateTimeCheck timeCheck = new Utilities.DateTimeCheck();
protected Account.ValidateCurrencyCode currencyCheck = new Account.ValidateCurrencyCode();
protected Logic.Validate.CurrencyCode currencyCheck = new Logic.Validate.CurrencyCode();
protected Utilities.DecimalCheck decimalCheck = new Utilities.DecimalCheck();
public ValidateAmazonSettlement() : base()
public AmazonSettlement() : base()
{
}
@@ -40,38 +40,38 @@ namespace bnhtrade.Core.Logic.Import
for (int i = 0; i < settlementList.Count; i++)
{
if (!settlementList[i].CurrencyCodeIsSet) { ErrorListAdd("CurrencyCode is a required value."); }
if (!settlementList[i].CurrencyCodeIsSet) { ValidationResultAdd("CurrencyCode is a required value."); }
else { IsValidCurrencyCode(settlementList[i].CurrencyCode); }
if (!settlementList[i].DepositDateIsSet) { ErrorListAdd("DepositDate is a required value."); }
if (!settlementList[i].DepositDateIsSet) { ValidationResultAdd("DepositDate is a required value."); }
else { IsValidDepositDate(settlementList[i].DepositDate); }
if (!settlementList[i].EndDateIsSet) { ErrorListAdd("EndDate is a required value."); }
if (!settlementList[i].EndDateIsSet) { ValidationResultAdd("EndDate is a required value."); }
else { IsValidEndDate(settlementList[i].EndDate); }
if (!settlementList[i].IsProcessedIsSet) { ErrorListAdd("IsProcessed is a required value."); }
if (!settlementList[i].IsProcessedIsSet) { ValidationResultAdd("IsProcessed is a required value."); }
else { IsValidIsProcessed(settlementList[i].IsProcessed); }
if (!settlementList[i].MarketPlaceNameIsSet)
{
if (ValidateMarketPlaceName) { ErrorListAdd("MarketPlaceName is a required value."); }
if (ValidateMarketPlaceName) { ValidationResultAdd("MarketPlaceName is a required value."); }
}
else { IsValidMarketPlaceName(settlementList[i].MarketPlaceName); }
if (!settlementList[i].SettlementIdIsSet) { ErrorListAdd("SettlementId is a required value."); }
if (!settlementList[i].SettlementIdIsSet) { ValidationResultAdd("SettlementId is a required value."); }
else { IsValidSettlementId(settlementList[i].SettlementId); }
if (!settlementList[i].StartDateIsSet) { ErrorListAdd("StartDate is a required value."); }
if (!settlementList[i].StartDateIsSet) { ValidationResultAdd("StartDate is a required value."); }
else { IsValidStartDate(settlementList[i].StartDate); }
if (!settlementList[i].TotalAmountIsSet) { ErrorListAdd("TotalAmount is a required value."); }
if (!settlementList[i].TotalAmountIsSet) { ValidationResultAdd("TotalAmount is a required value."); }
else { IsValidTotalAmount(settlementList[i].TotalAmount); }
// check line list
if (!settlementList[i].SettlementLineListIsSet)
{
ErrorListAdd("Settlement line list is null or empty");
ValidationResultAdd("Settlement line list is null or empty");
continue;
}
else
@@ -88,14 +88,13 @@ namespace bnhtrade.Core.Logic.Import
// check totals
if (lineSum != settlementList[i].TotalAmount)
{
ErrorListAdd("Settlement header total (" + settlementList[i].TotalAmount +
ValidationResultAdd("Settlement header total (" + settlementList[i].TotalAmount +
") does not match line total (" + lineSum + ")");
}
}
}
if (ErrorListIsSet) { return false; }
else { return true; }
return IsValidResult;
}
public bool IsValid(Model.Import.AmazonSettlement.SettlementLine settlementLine)
@@ -111,7 +110,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Amount is a required value.");
ValidationResultAdd("Amount is a required value.");
}
@@ -121,7 +120,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Amount Description is a required value.");
ValidationResultAdd("Amount Description is a required value.");
}
@@ -131,7 +130,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Amount Type is a required value.");
ValidationResultAdd("Amount Type is a required value.");
}
@@ -141,7 +140,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Currency Code is a required value.");
ValidationResultAdd("Currency Code is a required value.");
}
@@ -161,7 +160,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Is Processed is a required value.");
ValidationResultAdd("Is Processed is a required value.");
}
@@ -201,7 +200,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Posted DateTime is a required value.");
ValidationResultAdd("Posted DateTime is a required value.");
}
if (settlementLine.PromotionIdIsSet)
@@ -230,23 +229,22 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Transaction Type is a required value.");
ValidationResultAdd("Transaction Type is a required value.");
}
if (ErrorListIsSet) { return false; }
else { return true; }
return IsValidResult;
}
public bool IsValidSettlementId(string settlementId)
{
if (!stringCheck.IsNumeric(settlementId))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid settlement Id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid settlement Id: " + x).ToList());
return false;
}
if (!stringCheck.MaxLength(settlementId, 50))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid settlement Id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid settlement Id: " + x).ToList());
return false;
}
return true;
@@ -261,7 +259,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(marketPlaceName, 50, true))
{
ErrorListAdd("Invalid market place name.");
ValidationResultAdd("Invalid market place name.");
return false;
}
return true;
@@ -271,7 +269,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!timeCheck.IsUtc(startDate))
{
ErrorListAdd(timeCheck.ErrorList.Select(x => "Invalid StartDate: " + x).ToList());
ValidationResultAdd(timeCheck.ValidationResult.Select(x => "Invalid StartDate: " + x).ToList());
return false;
}
return true;
@@ -281,7 +279,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!timeCheck.IsUtc(endDate))
{
ErrorListAdd(timeCheck.ErrorList.Select(x => "Invalid EndDate: " + x).ToList());
ValidationResultAdd(timeCheck.ValidationResult.Select(x => "Invalid EndDate: " + x).ToList());
return false;
}
return true;
@@ -291,7 +289,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!timeCheck.IsUtc(depositDate))
{
ErrorListAdd(timeCheck.ErrorList.Select(x => "Invalid DepositDate: " + x).ToList());
ValidationResultAdd(timeCheck.ValidationResult.Select(x => "Invalid DepositDate: " + x).ToList());
return false;
}
return true;
@@ -301,7 +299,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!decimalCheck.SqlLength92(totalAmount))
{
ErrorListAdd(decimalCheck.ErrorList.Select(x => "Total Amount Invalid: " + x).ToList());
ValidationResultAdd(decimalCheck.ValidationResult.Select(x => "Total Amount Invalid: " + x).ToList());
return false;
}
return true;
@@ -311,7 +309,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!currencyCheck.IsValidCurrencyCode(currencyCode))
{
ErrorListAdd(currencyCheck.ErrorList.Select(x => "Total Amount CurrencyCode: " + x).ToList());
ValidationResultAdd(currencyCheck.ValidationResult.Select(x => "Total Amount CurrencyCode: " + x).ToList());
return false;
}
return true;
@@ -338,7 +336,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(transactionType, 50))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid transaction type: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid transaction type: " + x).ToList());
return false;
}
return true;
@@ -347,7 +345,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(orderId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid order id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid order id: " + x).ToList());
return false;
}
return true;
@@ -356,7 +354,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(merchantOrderId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid merchant order id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid merchant order id: " + x).ToList());
return false;
}
return true;
@@ -365,7 +363,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(adjustmentId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid adjustment id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid adjustment id: " + x).ToList());
return false;
}
return true;
@@ -374,7 +372,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(shipmentId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid shipment id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid shipment id: " + x).ToList());
return false;
}
return true;
@@ -383,7 +381,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(amountType, 50))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid amount type: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid amount type: " + x).ToList());
return false;
}
return true;
@@ -392,7 +390,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(amountDescription, 100))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid amount description: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid amount description: " + x).ToList());
return false;
}
return true;
@@ -401,7 +399,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!decimalCheck.SqlLength92(amount))
{
ErrorListAdd(decimalCheck.ErrorList.Select(x => "Invalid amount: " + x).ToList());
ValidationResultAdd(decimalCheck.ValidationResult.Select(x => "Invalid amount: " + x).ToList());
return false;
}
return true;
@@ -410,7 +408,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!currencyCheck.IsValidCurrencyCode(currenyCode))
{
ErrorListAdd(currencyCheck.ErrorList.Select(x => "Invalid curreny code: " + x).ToList());
ValidationResultAdd(currencyCheck.ValidationResult.Select(x => "Invalid curreny code: " + x).ToList());
return false;
}
return true;
@@ -419,7 +417,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(fulfillmentId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid fulfillment Id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid fulfillment Id: " + x).ToList());
return false;
}
return true;
@@ -429,7 +427,7 @@ namespace bnhtrade.Core.Logic.Import
var timeCheck = new Logic.Utilities.DateTimeCheck();
if (!timeCheck.IsUtc(postDateTime))
{
ErrorListAdd(@"Invalid post date/time, not set to UTC kind.");
ValidationResultAdd(@"Invalid post date/time, not set to UTC kind.");
return false;
}
return true;
@@ -438,7 +436,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(orderItemCode, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid order item code: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid order item code: " + x).ToList());
return false;
}
return true;
@@ -447,7 +445,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(merchantOrderItemId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid merchant order item id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid merchant order item id: " + x).ToList());
return false;
}
return true;
@@ -456,7 +454,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(merchantAdjustmentItemId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid merchant adjustment item id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid merchant adjustment item id: " + x).ToList());
return false;
}
return true;
@@ -465,7 +463,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(skuNumber, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid sku number: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid sku number: " + x).ToList());
return false;
}
return true;
@@ -478,7 +476,7 @@ namespace bnhtrade.Core.Logic.Import
{
if (!stringCheck.MaxLength(promotionId, 50, true))
{
ErrorListAdd(stringCheck.ErrorList.Select(x => "Invalid promotion id: " + x).ToList());
ValidationResultAdd(stringCheck.ValidationResult.Select(x => "Invalid promotion id: " + x).ToList());
return false;
}
return true;
@@ -491,7 +489,7 @@ namespace bnhtrade.Core.Logic.Import
}
else
{
ErrorListAdd("Export account invoice line id cannot be less than 1");
ValidationResultAdd("Export account invoice line id cannot be less than 1");
return false;
}
}

View File

@@ -5,9 +5,9 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Account
namespace bnhtrade.Core.Logic.Validate
{
public class ValidateCurrencyCode : Validate
public class CurrencyCode : Validate
{
private Logic.Utilities.StringCheck stringCheck = new Logic.Utilities.StringCheck();
public new void Innit()
@@ -23,7 +23,7 @@ namespace bnhtrade.Core.Logic.Account
}
else
{
ErrorListAdd(stringCheck.ErrorList);
ValidationResultAdd(stringCheck.ValidationResult);
return false;
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Validate
{
public class SalesInvoice : Logic.Validate.Validate
{
public SalesInvoice()
{
}
public bool IsValidExportInvoice(IEnumerable<Model.Account.Invoice> invoiceList)
{
if (!IsValid(invoiceList))
{
return false;
}
foreach (var invoice in invoiceList)
{
if (invoice.UnitAmountIsTaxExclusive == false)
{
ValidationResultAdd("Tax inclusive line unit amounts are not supported");
return false;
}
foreach (var invoiceLine in invoice.InvoiceLineList)
{
if (invoiceLine.Quantity != 1)
{
ValidationResultAdd("Quantities greater than 1 are not supported");
return false;
}
}
}
return IsValidResult;
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Validate
{
public class SkuPriceInfo : Validate
{
public SkuPriceInfo()
{
Innit();
}
public bool IsValidDatabaseCreate(List<Model.Sku.Price.PriceInfo> priceInfoList)
{
Innit();
if (!IsValid(priceInfoList))
{
return false;
}
return IsValidResult;
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Validate
{
public class SkuTransaction : Validate
{
public bool DatabaseUpdate(Model.Stock.SkuTransaction skuTransaction)
{
return IsValid(new List<Model.Stock.SkuTransaction> { skuTransaction });
}
public bool DatabaseUpdate(List<Model.Stock.SkuTransaction> skuTransactionList)
{
if (!IsValid(skuTransactionList))
{
return false;
}
for (int i = 0; i < skuTransactionList.Count; i++)
{
if (!skuTransactionList[i].IsSetSkuTransactionId)
{
ValidationResultAdd("StockTransactionId is required");
}
}
return IsValidResult;
}
public bool DatabaseInsert(Model.Stock.SkuTransaction skuTransaction)
{
return IsValid(new List<Model.Stock.SkuTransaction> { skuTransaction });
}
public bool DatabaseInsert(List<Model.Stock.SkuTransaction> skuTransactionList)
{
return IsValid(skuTransactionList);
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Validate
{
public class TaxCodeInfo : Validate
{
public bool IsValidPresistanceGet(Model.Account.TaxCodeInfo taxCodeInfo)
{
return IsValid(taxCodeInfo);
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace bnhtrade.Core.Logic.Validate
{
public abstract class Validate
{
public List<ValidationResult> ValidationResult { get; set; } = new List<ValidationResult>();
public bool IsValidResult
{
get
{
if (ValidationResult.Any()) { return false; }
else { return true; }
}
}
public void Innit()
{
ValidationResult = new List<ValidationResult>();
}
protected bool IsValid(IValidatableObject validatableObject)
{
if (validatableObject == null)
{
ValidationResult.Add(new ValidationResult("Validatable object is null"));
return false;
}
var validationContext = new ValidationContext(validatableObject);
ValidationResult.AddRange(validatableObject.Validate(validationContext));
return IsValidResult;
}
protected bool IsValid(IEnumerable<IValidatableObject> validatableObjectList)
{
if (validatableObjectList == null || !validatableObjectList.Any())
{
ValidationResult.Add(new ValidationResult("Validatable object list is empty or null"));
return false;
}
foreach (var validatableObject in validatableObjectList)
{
if (!IsValid(validatableObject)) { return false; }
}
return IsValidResult;
}
protected void ValidationResultAdd(string result)
{
ValidationResult.Add(new ValidationResult(result));
}
protected void ValidationResultAdd(List<string> result)
{
foreach (var item in result)
{
ValidationResultAdd(item);
}
}
protected void ValidationResultAdd(ValidationResult result)
{
ValidationResult.Add(result);
}
protected void ValidationResultAdd(List<ValidationResult> result)
{
ValidationResult.AddRange(result);
}
public string ValidationResultListToString()
{
if (IsValidResult == false)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < ValidationResult.Count; i++)
{
result.AppendLine(ValidationResult[i].ErrorMessage);
}
return result.ToString();
}
else { return null; }
}
}
}