Various bug fixs and improvements to stock SKU reconciliation

This commit is contained in:
Bobbie Hodgetts
2020-10-05 22:40:55 +01:00
parent cc2534a3e1
commit ddd2b62743
25 changed files with 1026 additions and 467 deletions

View File

@@ -13,8 +13,6 @@ namespace bnhtrade.ComTypeLib
[InterfaceType(ComInterfaceType.InterfaceIsDual)] [InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IProduct public interface IProduct
{ {
string ReturnStringValue(string stringValue);
double ReturnDateValueAsDouble(string stringValue); double ReturnDateValueAsDouble(string stringValue);
int ProductGetProductIdByCatId(int catId, ConnectionCredential sqlConnCred); int ProductGetProductIdByCatId(int catId, ConnectionCredential sqlConnCred);
@@ -32,20 +30,12 @@ namespace bnhtrade.ComTypeLib
[ProgId("bnhtrade.Product")] [ProgId("bnhtrade.Product")]
public class Product : IProduct public class Product : IProduct
{ {
[ComVisible(false)]
[return: MarshalAs(UnmanagedType.BStr)]
public string ReturnStringValue(string stringValue)
{
return "kj;lk1";
}
[ComVisible(false)]
public double ReturnDateValueAsDouble(string stringValue) public double ReturnDateValueAsDouble(string stringValue)
{ {
DateTime theTimeNow = DateTime.UtcNow; DateTime theTimeNow = DateTime.UtcNow;
return theTimeNow.ToOADate(); return theTimeNow.ToOADate();
// back in vba use the CDate(return) function to convert the double // back in vba use the CDate(return) function to convert the double
// vba Date --> c# DateTime works without marshalling // vba Date --> c# DateTime works without marshalling.
} }
public int ProductGetProductIdByCatId(int catId, ConnectionCredential sqlConnCred) public int ProductGetProductIdByCatId(int catId, ConnectionCredential sqlConnCred)
@@ -104,10 +94,5 @@ namespace bnhtrade.ComTypeLib
Core.Product.ProductQuery.ProductUpdateAmazonEstimateFee(sqlConnCred.ConnectionString, inputTuple); Core.Product.ProductQuery.ProductUpdateAmazonEstimateFee(sqlConnCred.ConnectionString, inputTuple);
} }
} }
} }

View File

@@ -30,6 +30,8 @@ namespace bnhtrade.ComTypeLib
void UnReconcileSkuTransaction(ConnectionCredential sqlConnCred, int skuTransactionId); void UnReconcileSkuTransaction(ConnectionCredential sqlConnCred, int skuTransactionId);
bool StockJournalConsistencyCheck(ConnectionCredential sqlConnCred, int stockId); bool StockJournalConsistencyCheck(ConnectionCredential sqlConnCred, int stockId);
void SkuTransactionAdd(ConnectionCredential sqlConnCred, int quantity, string skuNumber, string transactionTypeCode, DateTime transactionDate);
} }
[ComVisible(true)] [ComVisible(true)]
@@ -63,7 +65,7 @@ namespace bnhtrade.ComTypeLib
{ {
entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc); entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc);
return new Core.Logic.Stock.Reallocate(sqlConnCred.ConnectionString).StockReallocateByStockId(4, stockId, quantity, debitStatusId, creditStatusId, entryDate); return new Core.Logic.Stock.StatusReallocate(sqlConnCred.ConnectionString).ByStockId(entryDate, 4, stockId, quantity, debitStatusId, creditStatusId);
} }
public void StockJournalDelete(ConnectionCredential sqlConnCred, int stockJournalId) public void StockJournalDelete(ConnectionCredential sqlConnCred, int stockJournalId)
@@ -128,5 +130,19 @@ namespace bnhtrade.ComTypeLib
{ {
return Core.Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnCred.ConnectionString, stockId, null); return Core.Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnCred.ConnectionString, stockId, null);
} }
public void SkuTransactionAdd(ConnectionCredential sqlConnCred, int quantity, string skuNumber, string transactionTypeCode, DateTime transactionDate)
{
var trans = new bnhtrade.Core.Model.Stock.SkuTransaction();
trans.IsProcessed = false;
trans.Quantity = (short)quantity;
//trans.Reference
trans.SkuNumber = skuNumber;
trans.SkuTransactionTypeCode = transactionTypeCode;
//trans.StockJournalId;
trans.TransactionDate = transactionDate;
new bnhtrade.Core.Logic.Stock.SkuTransactionPersistance(sqlConnCred.ConnectionString).Create(trans);
}
} }
} }

View File

@@ -9,7 +9,7 @@ namespace bnhtrade.Core.Data.Database.Account
{ {
public class ReadAccountCode : Connection public class ReadAccountCode : Connection
{ {
private Data.Database.WhereBuilder sqlWhere = new WhereBuilder(); private Data.Database.SqlWhereBuilder sqlWhere = new SqlWhereBuilder();
private List<Model.Account.AccountCode> resultList; private List<Model.Account.AccountCode> resultList;
public ReadAccountCode(string sqlConnectionString) : base(sqlConnectionString) public ReadAccountCode(string sqlConnectionString) : base(sqlConnectionString)

View File

@@ -62,7 +62,7 @@ namespace bnhtrade.Core.Data.Database.Account
LEFT OUTER JOIN tblAccountChartOf ON tblAccountInvoiceLineItem.AccountChartOfID_Default = tblAccountChartOf.AccountChartOfID LEFT OUTER JOIN tblAccountChartOf ON tblAccountInvoiceLineItem.AccountChartOfID_Default = tblAccountChartOf.AccountChartOfID
WHERE "; WHERE ";
var whereBuilder = new WhereBuilder(); var whereBuilder = new SqlWhereBuilder();
whereBuilder.In("tblAccountInvoiceLineItem.ItemCode", itemCodeList); whereBuilder.In("tblAccountInvoiceLineItem.ItemCode", itemCodeList);
sql += whereBuilder.SqlWhereString; sql += whereBuilder.SqlWhereString;

View File

@@ -9,11 +9,11 @@ namespace bnhtrade.Core.Data.Database.Account
{ {
public class ReadTaxCode : Connection public class ReadTaxCode : Connection
{ {
private Data.Database.WhereBuilder whereBuilder; private Data.Database.SqlWhereBuilder whereBuilder;
public ReadTaxCode(string sqlConnectionString) : base(sqlConnectionString) public ReadTaxCode(string sqlConnectionString) : base(sqlConnectionString)
{ {
whereBuilder = new WhereBuilder(); whereBuilder = new SqlWhereBuilder();
} }
private List<Model.Account.TaxCodeInfo> Execute(string sqlWhere, Dictionary<string, object> parameters) private List<Model.Account.TaxCodeInfo> Execute(string sqlWhere, Dictionary<string, object> parameters)

View File

@@ -1,16 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database namespace bnhtrade.Core.Data.Database
{ {
public class WhereBuilder public class SqlWhereBuilder
{ {
private int parameterCount; private int parameterCount;
public WhereBuilder() public SqlWhereBuilder()
{ {
Innit(); Innit();
} }
@@ -26,6 +27,14 @@ namespace bnhtrade.Core.Data.Database
parameterCount = 0; parameterCount = 0;
} }
public void AddParametersToSqlCommand(SqlCommand cmd)
{
foreach (var item in ParameterList)
{
cmd.Parameters.AddWithValue(item.Key, item.Value);
}
}
/// <summary> /// <summary>
/// Used to create a string for an SQL where condition 'In' statement /// Used to create a string for an SQL where condition 'In' statement
/// </summary> /// </summary>

View File

@@ -10,7 +10,8 @@ namespace bnhtrade.Core.Data.Database.Stock
{ {
public class ReadSkuTransaction : Connection public class ReadSkuTransaction : Connection
{ {
private Data.Database.WhereBuilder whereBuilder = new WhereBuilder(); private Data.Database.SqlWhereBuilder whereBuilder = new SqlWhereBuilder();
private List<int> transactionIdList = new List<int>();
public ReadSkuTransaction(string sqlConnectionString) : base(sqlConnectionString) public ReadSkuTransaction(string sqlConnectionString) : base(sqlConnectionString)
{ {
@@ -22,7 +23,7 @@ namespace bnhtrade.Core.Data.Database.Stock
/// </summary> /// </summary>
public void Init() public void Init()
{ {
whereBuilder = new WhereBuilder(); whereBuilder = new SqlWhereBuilder();
IsReconciled = null; IsReconciled = null;
StockTransactionTypeName = null; StockTransactionTypeName = null;
StockTransactionTypeCode = null; StockTransactionTypeCode = null;
@@ -107,6 +108,15 @@ namespace bnhtrade.Core.Data.Database.Stock
INNER JOIN tblSku ON tblStockSkuTransaction.SkuID = tblSku.skuSkuID INNER JOIN tblSku ON tblStockSkuTransaction.SkuID = tblSku.skuSkuID
WHERE 1=1 "; WHERE 1=1 ";
if (transactionIdList.Any())
{
parameters.Add("@transactionIdList", this.transactionIdList);
sql += @"
AND tblStockSkuTransaction.StockSkuTransactionID IN @transactionIdList ";
}
else
{
if (IsReconciled != null) if (IsReconciled != null)
{ {
sql += @" sql += @"
@@ -136,6 +146,7 @@ namespace bnhtrade.Core.Data.Database.Stock
sql += @" sql += @"
AND tblStockSkuTransactionType.TypeCode IN @stockTransactionTypeCode "; AND tblStockSkuTransactionType.TypeCode IN @stockTransactionTypeCode ";
} }
}
sql += @" sql += @"
ORDER BY tblStockSkuTransaction.TransactionDate ASC, tblStockSkuTransaction.StockSkuTransactionID DESC;"; ORDER BY tblStockSkuTransaction.TransactionDate ASC, tblStockSkuTransaction.StockSkuTransactionID DESC;";
@@ -158,5 +169,23 @@ namespace bnhtrade.Core.Data.Database.Stock
return resultList; return resultList;
} }
/// <summary>
/// Get Stock SKU Transaction by ID
/// </summary>
/// <param name="transactionIdList"></param>
/// <returns>List of SkuTransaction</returns>
public List<Model.Stock.SkuTransaction> Read(List<int> transactionIdList)
{
var returnList = new List<Model.Stock.SkuTransaction>();
if (!transactionIdList.Any())
{
return returnList;
}
this.transactionIdList = transactionIdList;
returnList = Read();
this.transactionIdList = new List<int>();
return returnList;
}
} }
} }

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.Stock
{
public class ReadStatusBalance : Connection
{
public ReadStatusBalance(string sqlConnectionString) : base(sqlConnectionString)
{
}
public int BySku(string sku, int statusId)
{
int statusBalance = new int();
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT SUM(tblStockJournalPost.Quantity) AS Balance
FROM tblStockJournal INNER JOIN
tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID INNER JOIN
tblStock ON tblStockJournal.StockID = tblStock.StockID INNER JOIN
tblSku ON tblStock.SkuID = tblSku.skuSkuID
WHERE (tblStockJournalPost.StockStatusID = @statusId) AND (tblSku.skuSkuNumber = @sku)
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@sku", sku);
cmd.Parameters.AddWithValue("@statusId", statusId);
// execute
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
statusBalance = 0;
}
else
{
statusBalance = (int)obj;
}
}
}
return statusBalance;
}
public int ByStockNumber(int stockNumber, int statusId)
{
int statusBalance = new int();
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT SUM(tblStockJournalPost.Quantity) AS Balance
FROM tblStockJournal INNER JOIN
tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID INNER JOIN
tblStock ON tblStockJournal.StockID = tblStock.StockID
WHERE (tblStockJournalPost.StockStatusID = @statusId) AND (tblStock.StockNumber = @stockNumber)
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockNumber", stockNumber);
cmd.Parameters.AddWithValue("@statusId", statusId);
// execute
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
statusBalance = 0;
}
else
{
statusBalance = (int)obj;
}
}
}
return statusBalance;
}
public int ByStockId(int stockId, int statusId)
{
int statusBalance = new int();
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT
SUM(tblStockJournalPost.Quantity) AS Balance
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
(tblStockJournal.StockID = @stockId )
AND (tblStockJournalPost.StockStatusID = @statusId )
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockId", stockId);
cmd.Parameters.AddWithValue("@statusId", statusId);
// execute
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
statusBalance = 0;
}
else
{
statusBalance = (int)obj;
}
}
}
return statusBalance;
}
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
namespace bnhtrade.Core.Data.Database.Stock
{
public class ReadStatusTransaction : Connection
{
public ReadStatusTransaction(string sqlConnectionString) : base(sqlConnectionString)
{
}
/// <summary>
/// Returns a list with balance of stock avaiable by individual StockId
/// </summary>
/// <param name="skuNumber">SKU Number</param>
/// <param name="stockStatusId">Stock Status Database PK</param>
/// <returns></returns>
public List<(string SkuNumber, int StockNumber, int Balance)> BySkuNumber(string skuNumber, int stockStatusId)
{
var returnList = new List<(string SkuNumber, int StockNumber, int Balance)>();
if (string.IsNullOrWhiteSpace(skuNumber))
{
return returnList;
}
string strSQL = @"
SELECT tblSku.skuSkuNumber
,tblStock.StockNumber
,SUM(tblStockJournalPost.Quantity) AS Balance
FROM tblStockJournal
INNER JOIN tblStock ON tblStockJournal.StockID = tblStock.StockID
INNER JOIN tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
INNER JOIN tblSku ON tblStock.SkuID = tblSku.skuSkuID
WHERE (tblStockJournalPost.StockStatusID = @stockStatusId)
AND (tblSku.skuSkuNumber = @skuNumber)
GROUP BY tblSku.skuSkuNumber
,tblStock.StockNumber
";
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strSQL, conn))
{
// add parameters
cmd.Parameters.AddWithValue("@skuNumber", skuNumber);
cmd.Parameters.AddWithValue("@stockStatusId", stockStatusId);
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
returnList.Add((
SkuNumber: reader.GetString(0),
StockNumber: reader.GetInt32(1),
Balance: reader.GetInt32(2)));
}
}
}
}
}
return returnList;
}
/// <summary>
/// Gets list of stock status debits and credits. Includes stock numbers with balance greater than or less than zero.
/// </summary>
/// <param name="stockStatusId">Required, filters results</param>
/// <param name="stockNumber">Required, filters results</param>
/// <returns>Tuple list is assending order</returns>
public Model.Stock.StatusTransaction ByStockNumber(int stockStatusId, int stockNumber)
{
var result = new Model.Stock.StatusTransaction();
result.StockStatusId = stockStatusId;
string strSQL = @"
SELECT a.StockNumber a.skuSkuNumber AS SkuNumber
FROM
(
SELECT tblSku.skuSkuNumber, tblStock.StockNumber, SUM(tblStockJournalPost.Quantity) AS Balance
FROM tblStockJournal INNER JOIN
tblStock ON tblStockJournal.StockID = tblStock.StockID INNER JOIN
tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID INNER JOIN
tblSku ON tblStock.SkuID = tblSku.skuSkuID
WHERE (tblStockJournalPost.StockStatusID = @stockStatusId) AND (tblSku.skuSkuNumber IN @stockNumberList)
GROUP BY tblSku.skuSkuNumber, tblStock.StockNumber
) a
WHERE a.Balance <> 0
";
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strSQL, conn))
{
var param = new Dapper.DynamicParameters();
param.Add("@stockStatusId", stockStatusId);
param.Add("@stockNumberList", stockNumber);
var dbresults = conn.Query(strSQL, param);
foreach (var item in dbresults)
{
result.Sku = item.SkuNumber;
result.AddBalanceTransaction(item.EntryDate, item.StockNumber, item.Quantity);
}
}
}
return result;
}
/// <summary>
/// Gets list of stock status debits and credits. Includes stock numbers with balance greater than or less than zero.
/// </summary>
/// <param name="stockStatusId">Required, filters results by stock status Id</param>
/// <param name="sku">Required, filters results by sku number</param>
/// <returns>Tuple list is assending order</returns>
public Model.Stock.StatusTransaction BySku(int stockStatusId, string sku)
{
var result = new Model.Stock.StatusTransaction();
result.Sku = sku;
result.StockStatusId = stockStatusId;
if (string.IsNullOrWhiteSpace(sku))
{
return result;
}
string strSQL = @"
SELECT tblStockJournal.EntryDate
,tblStockJournalPost.StockStatusID
,tblStock.StockNumber
,tblStockJournalPost.Quantity
FROM tblStockJournalPost
INNER JOIN tblStockJournal ON tblStockJournalPost.StockJournalID = tblStockJournal.StockJournalID
INNER JOIN tblStock ON tblStockJournal.StockID = tblStock.StockID
WHERE (tblStockJournalPost.StockStatusID = @stockStatusId)
AND (
tblStock.StockNumber IN (
SELECT a.StockNumber
FROM (
SELECT tblStock.StockNumber
,SUM(tblStockJournalPost.Quantity) AS Balance
FROM tblStockJournal
INNER JOIN tblStock ON tblStockJournal.StockID = tblStock.StockID
INNER JOIN tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
INNER JOIN tblSku ON tblStock.SkuID = tblSku.skuSkuID
WHERE (tblStockJournalPost.StockStatusID = @stockStatusId)
AND (tblSku.skuSkuNumber = @sku)
GROUP BY tblSku.skuSkuNumber
,tblStock.StockNumber
) a
WHERE a.Balance <> 0
)
)
ORDER BY tblStockJournal.EntryDate
";
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strSQL, conn))
{
var param = new Dapper.DynamicParameters();
param.Add("@stockStatusId", stockStatusId);
param.Add("@sku", sku);
var dbList = conn.Query(strSQL, param);
foreach (var item in dbList)
{
result.AddBalanceTransaction(item.EntryDate, item.StockNumber, item.Quantity);
}
}
}
return result;
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using bnhtrade.Core.Data.Database;
namespace bnhtrade.Core.Data.Database.Stock
{
public class ReadStockId : Connection
{
public ReadStockId(string sqlConnectionString) : base(sqlConnectionString)
{
}
/// <summary>
/// Tempory function to retrive the database stock ID/PK from a stock number. Reliance on a stock ID will
/// eventually be desgined out of the application
/// </summary>
/// <param name="stockNumberList">List of Stock Numbers to retrive from database</param>
/// <returns>Dictiony with Stock Number as Key and Stock ID as value</returns>
public Dictionary<int, int> ByStockNumber(List<int> stockNumberList)
{
var result = new Dictionary<int, int>();
string strSQL = @"
SELECT StockNumber, StockID
FROM tblStock
WHERE ";
var whereBuilder = new Database.SqlWhereBuilder();
whereBuilder.In("StockNumber", stockNumberList, strSQL);
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(whereBuilder.SqlWhereString, conn))
{
// add parameters
whereBuilder.AddParametersToSqlCommand(cmd);
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result.Add(reader.GetInt32(0), reader.GetInt32(1));
}
}
return result;
}
}
}
}
}

View File

@@ -1,86 +0,0 @@
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

@@ -189,6 +189,26 @@ namespace bnhtrade.Core.Logic.Stock
return resultList; return resultList;
} }
/// <summary>
/// Retrive SKU Transaction by ID
/// </summary>
/// <param name="SkuTransactionId">SKU Transaction ID</param>
/// <param name="retriveTransactionTypeInfo"></param>
/// <returns></returns>
public List<Model.Stock.SkuTransaction> Read(List<int> SkuTransactionId, bool retriveTransactionTypeInfo = true)
{
var dbRead = new Data.Database.Stock.ReadSkuTransaction(sqlConnectionString);
var resultList = dbRead.Read(SkuTransactionId);
if (retriveTransactionTypeInfo)
{
var dbReadType = new Logic.Stock.SkuTransactionTypePersistance(sqlConnectionString);
dbReadType.GetBySkuTransaction(resultList);
}
return resultList;
}
/// <summary> /// <summary>
/// Validates and then updates a Stock SKU Tranaction /// Validates and then updates a Stock SKU Tranaction
/// </summary> /// </summary>

View File

@@ -14,7 +14,7 @@ namespace bnhtrade.Core.Logic.Stock
private Logic.Stock.SkuTransactionPersistance dbSkuTransaction; private Logic.Stock.SkuTransactionPersistance dbSkuTransaction;
private Logic.Stock.SkuTransactionTypePersistance dbSkuTransactionType; private Logic.Stock.SkuTransactionTypePersistance dbSkuTransactionType;
private Logic.Validate.SkuTransaction validateSkuTrans; private Logic.Validate.SkuTransaction validateSkuTrans;
private Logic.Stock.Reallocate stockReallocate; private Logic.Stock.StatusReallocate stockReallocate;
private Logic.Log.LogEvent logEvent; private Logic.Log.LogEvent logEvent;
private string err = "Reconcile Sku Transaction Exception: "; private string err = "Reconcile Sku Transaction Exception: ";
@@ -26,7 +26,7 @@ namespace bnhtrade.Core.Logic.Stock
dbSkuTransactionType = new SkuTransactionTypePersistance(sqlConnectionString); dbSkuTransactionType = new SkuTransactionTypePersistance(sqlConnectionString);
readShipmentInfo = new Data.Database.AmazonShipment.ReadShipmentInfo(sqlConnectionString); readShipmentInfo = new Data.Database.AmazonShipment.ReadShipmentInfo(sqlConnectionString);
validateSkuTrans = new Validate.SkuTransaction(); validateSkuTrans = new Validate.SkuTransaction();
stockReallocate = new Logic.Stock.Reallocate(sqlConnectionString); stockReallocate = new Logic.Stock.StatusReallocate(sqlConnectionString);
logEvent = new Log.LogEvent(); logEvent = new Log.LogEvent();
} }
@@ -194,50 +194,52 @@ namespace bnhtrade.Core.Logic.Stock
} }
// make the journal entries // make the journal entries
var list = new List<(int StockJournalId, int Quantity)>(); var journalList = new List<(int StockJournalId, int Quantity)>();
if (transList[i].SkuTransactionType.FilterStockOnDateTime) if (transList[i].SkuTransactionType.FilterStockOnDateTime)
{ {
list = stockReallocate.StockReallocateBySkuNumber( journalList = stockReallocate.BySkuNumber(
transList[i].TransactionDate,
transList[i].SkuTransactionType.StockJournalTypeId, transList[i].SkuTransactionType.StockJournalTypeId,
transList[i].SkuNumber, transList[i].SkuNumber,
transList[i].Quantity, transList[i].Quantity,
transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.FirstInFirstOut, transList[i].SkuTransactionType.FirstInFirstOut,
transList[i].TransactionDate, true);
false);
} }
else else
{ {
list = stockReallocate.StockReallocateBySkuNumber( journalList = stockReallocate.BySkuNumber(
DateTime.UtcNow,
transList[i].SkuTransactionType.StockJournalTypeId, transList[i].SkuTransactionType.StockJournalTypeId,
transList[i].SkuNumber, transList[i].SkuNumber,
transList[i].Quantity, transList[i].Quantity,
transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(),
transList[i].SkuTransactionType.FirstInFirstOut, transList[i].SkuTransactionType.FirstInFirstOut,
DateTime.UtcNow, true);
false);
} }
// insufficient balance available // insufficient balance available
if (list == null || !list.Any()) if (!journalList.Any())
{ {
// in special case (found inventory), continue // in special case (found inventory), continue
if (transList[i].SkuTransactionType.TypeCode.Contains("<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F>")) if (transList[i].SkuTransactionType.TypeCode.Contains("<AmazonReport><_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_><F>"))
{ {
ItemsCompleted++;
ItemsRemaining--;
continue; continue;
} }
else else
{ {
ProgressMessage = "Insurficent status/location balance to relocate stock"; ProgressMessage = "Insurficent quantity at status/location to relocate stock";
recordSkip = recordSkip + 1; recordSkip = recordSkip + 1;
break; break;
} }
} }
// fail safe // fail safe
int qtyAllocated = list.Sum(c => c.Quantity); int qtyAllocated = journalList.Sum(c => c.Quantity);
if (qtyAllocated > transList[i].Quantity) if (qtyAllocated > transList[i].Quantity)
{ {
throw new Exception( throw new Exception(
@@ -248,40 +250,41 @@ namespace bnhtrade.Core.Logic.Stock
// update sku transaction table // update sku transaction table
int qtyRemain = qtyAllocated; int qtyRemain = qtyAllocated;
var newRecordList = new List<Model.Stock.SkuTransaction>(); var newRecordList = new List<Model.Stock.SkuTransaction>();
for (int j = 0; j <= list.Count; j++) for (int j = 0; j < journalList.Count; j++)
{ {
// update existing record // update existing record
if (j == 0) if (j == 0)
{ {
transList[i].Quantity = (short)list[j].Quantity; transList[i].Quantity = (short)journalList[j].Quantity;
transList[i].StockJournalId = list[j].StockJournalId; transList[i].StockJournalId = journalList[j].StockJournalId;
transList[i].IsProcessed = true; transList[i].IsProcessed = true;
dbSkuTransaction.Update(transList[i]); dbSkuTransaction.Update(transList[i]);
} }
// new record // new record
else if (j < list.Count) else
{ {
var newRecord = transList[i].Clone(); var newRecord = transList[i].Clone();
newRecord.Quantity = (short)list[j].Quantity; newRecord.Quantity = (short)journalList[j].Quantity;
newRecord.IsProcessed = true; newRecord.IsProcessed = true;
newRecord.StockJournalId = list[j].StockJournalId; newRecord.StockJournalId = journalList[j].StockJournalId;
newRecordList.Add(newRecord); newRecordList.Add(newRecord);
} }
// new record, unallocated quantity
else if (qtyRemain > 0) qtyRemain = qtyRemain - journalList[j].Quantity;
}
// new record for unallocated quantity
if (qtyRemain > 0)
{ {
var newRecord = transList[i].Clone(); var newRecord = transList[i].Clone();
newRecord.Quantity = (short)qtyRemain; newRecord.Quantity = (short)qtyRemain;
newRecord.IsProcessed = false; newRecord.IsProcessed = false;
newRecordList.Add(newRecord); newRecordList.Add(newRecord);
} }
if (j < list.Count)
{
qtyRemain = qtyRemain - list[j].Quantity;
}
}
// add new transactions to table // add new transactions to table
for (int j = 0; j < newRecordList.Count; j++) for (int j = 0; j < newRecordList.Count; j++)
{ {
@@ -447,8 +450,19 @@ namespace bnhtrade.Core.Logic.Stock
} }
public void UnReconcileTransaction(int skuTransactionId) public void UnReconcileTransaction(int skuTransactionId)
{
var trans = dbSkuTransaction.Read(new List<int> { skuTransactionId }, false).FirstOrDefault();
if (trans == null) { return; }
// test if journal entry needs deleting, or just set to isprocessed = false
if (trans.IsProcessed == true && trans.IsSetStockJournalId)
{ {
dbSkuTransaction.DeleteJournalEntry(skuTransactionId); dbSkuTransaction.DeleteJournalEntry(skuTransactionId);
} }
else if (trans.IsProcessed == true)
{
new Data.Database.Stock.UpdateSkuTransaction(sqlConnectionString).Update(skuTransactionId, false);
}
}
} }
} }

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Stock
{
public class StatusBalance
{
private string sqlConnectionString;
public StatusBalance(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
/// <summary>
/// Return the avaliable balance of a status. Uses a more efficent sql/code. However, balance requests should
/// generally also involve a date/time (i.e. the system does allow a stock transaction in the future, therefore
/// this method may give an available quantity, but transfers before that date wouod not be possible).
/// </summary>
/// <param name="sku">SKU number</param>
/// <param name="statusId">Status ID</param>
/// <returns>Balance as quantity</returns>
private int GetAvailableBalanceBySku(string sku, int statusId)
{
return new Data.Database.Stock.ReadStatusBalance(sqlConnectionString).BySku(sku, statusId);
}
/// <summary>
/// Return the avaliable balance of a status at a specified date and time. Useful for checking availability before
/// moving stock/sku retrospectivly
/// </summary>
/// <param name="sku">SKU number</param>
/// <param name="statusId">Status ID</param>
/// <param name="atDate">Date and time you would like to know the balance at</param>
/// <returns></returns>
public Model.Stock.StatusBalance GetBySku(string sku, int statusId)
{
if (string.IsNullOrWhiteSpace(sku))
{
throw new Exception("SKU number is null, empty, or whitespace");
}
// get list of transactions for availale stock
var stockTransaction = new Data.Database.Stock.ReadStatusTransaction(sqlConnectionString);
var transList = stockTransaction.BySku(statusId, sku);
// create quantity list
List<int> qtyList = new List<int>();
for (int i = 0; i < transList.TransactionList.Count; i++)
{
qtyList.Add(transList.TransactionList[i].Quantity);
}
// tally list
// loop, in reverse, to find credits to tally with debits
for (int iCr = qtyList.Count - 1; iCr > -1; iCr--)
{
if (qtyList[iCr] < 0)
{
int crStockNumber = transList.TransactionList[iCr].StockNumber;
DateTime crDate = transList.TransactionList[iCr].EntryDate;
// loop, in reverse, to find debits
for (int iDr = qtyList.Count - 1; iDr > -1; iDr--)
{
// find debits, last in first out (filter by date)
if (transList.TransactionList[iDr].StockNumber == crStockNumber
&& transList.TransactionList[iDr].EntryDate <= crDate
&& qtyList[iDr] > 0)
{
// credit fully assigned
if ((qtyList[iCr] + qtyList[iDr]) >= 0)
{
qtyList[iDr] = qtyList[iDr] + qtyList[iCr];
qtyList[iCr] = 0;
break;
}
// credit partially assigned
else
{
qtyList[iCr] = qtyList[iDr] + qtyList[iCr];
qtyList[iDr] = 0;
}
}
}
}
}
// build result list from tally results
var result = new Model.Stock.StatusBalance();
result.Sku = transList.Sku;
result.StockStatusId = transList.StockStatusId;
for (int i = 0; i < qtyList.Count; i++)
{
if (qtyList[i] != 0)
{
result.AddBalanceTransaction(
transList.TransactionList[i].EntryDate,
transList.TransactionList[i].StockNumber,
qtyList[i]);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,109 @@
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 StatusReallocate
{
private string sqlConnectionString;
public StatusReallocate(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
}
/// <summary>
/// Reallocates stock between status' by Stock Id
/// </summary>
/// <param name="journalTypeId"></param>
/// <param name="stockId"></param>
/// <param name="quantity"></param>
/// <param name="debitStatusId"></param>
/// <param name="creditStatusId"></param>
/// <param name="entryDate"></param>
/// <returns>Return newly created stock journal Id</returns>
public int ByStockId(DateTime entryDate, int journalTypeId, int stockId, int quantity, int debitStatusId, int creditStatusId)
{
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 SKU number and quantity into function and the stock will be reallocated
/// </summary>
/// <param name="entryDate">Date and time of the transaction</param>
/// <param name="journalTypeId">Journal Type ID</param>
/// <param name="skuNumber">Sku Number to reallocate</param>
/// <param name="quantity">Quantity to reallocate</param>
/// <param name="debitStatusId">Status to move SKU to</param>
/// <param name="creditStatusId">Status to move SKU from</param>
/// <param name="firstInFirstOut">Move stock on first in first out basis</param>
/// <param name="reallocatePartialQuantity">Reallocate patial quantity if the full quantity is not available</param>
/// <returns></returns>
public List<(int StockJournalId, int Quantity)> BySkuNumber(DateTime entryDate, int journalTypeId, string skuNumber, int quantity, int debitStatusId, int creditStatusId,
bool firstInFirstOut = true, bool reallocatePartialQuantity = false)
{
var returnList = new List<(int StockJournalId, int Quantity)>();
// get balance of status and check for avaliable quantity
var statusBalance = new Logic.Stock.StatusBalance(sqlConnectionString).GetBySku(skuNumber, creditStatusId);
if (statusBalance.GetAvaliableQuantity(entryDate) <= 0
|| (statusBalance.CheckAvaliableQuantity(quantity, entryDate) == false && reallocatePartialQuantity == false
))
{
return returnList;
}
// temp code start
// until use of stockId is designed out of application
var getStockId = new Data.Database.Stock.ReadStockId(sqlConnectionString);
var stockIdDictionary = new Dictionary<int, int>();
foreach (var item in statusBalance.ByDateList)
{
if (!stockIdDictionary.ContainsKey(item.StockNumber))
{
stockIdDictionary.Add(item.StockNumber, 0);
}
}
stockIdDictionary = getStockId.ByStockNumber(stockIdDictionary.Keys.ToList());
// temp code finish
//make the changes
using (TransactionScope scope = new TransactionScope())
{
foreach (var item in statusBalance.ByDateList)
{
if (quantity > item.Quantity)
{
int journalId = ByStockId(entryDate, journalTypeId, stockIdDictionary[item.StockNumber], item.Quantity, debitStatusId, creditStatusId);
quantity = quantity - item.Quantity;
returnList.Add((journalId, item.Quantity));
}
else
{
int journalId = ByStockId(entryDate, journalTypeId, stockIdDictionary[item.StockNumber], quantity, debitStatusId, creditStatusId);
returnList.Add((journalId, quantity));
break;
}
}
scope.Complete();
return returnList;
}
}
}
}

View File

@@ -1,17 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace bnhtrade.Core.Model.Stock namespace bnhtrade.Core.Model.Stock
{ {
public class JournalEntry public class JournalEntry : IValidatableObject
{ {
public string TypeTitle { get; set; } public string TypeTitle { get; set; }
public int StockId { get; set; }
public int StockNumber { get; set; } public int StockNumber { get; set; }
public DateTime EntryDate { get; set; } public DateTime EntryDate { get; set; }
@@ -21,5 +20,25 @@ namespace bnhtrade.Core.Model.Stock
public DateTime LastModified { get; set; } public DateTime LastModified { get; set; }
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
public List<JournalEntryPost> JournalPosts { get; set; } = new List<JournalEntryPost>();
public class JournalEntryPost
{
public int JournalPostId { get; set; }
public int StockStatusId { get; set; }
[Required()]
public string StockStatus { get; set; }
[Required()]
public int Quantity { get; set; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
throw new NotImplementedException();
}
} }
} }

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Model.Stock
{
public class JournalEntryPost : IValidatableObject
{
public int JournalPostId { get; set; }
public int StockStatusId { get; set; }
[Required()]
public string StockStatus { get; set; }
[Required()]
public int Quantity { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
throw new NotImplementedException();
var resultList = new List<ValidationResult>();
if (Quantity == 0)
{
resultList.Add(new ValidationResult("Quantity must be greater than, or less than, zero"));
}
}
}
}

View File

@@ -200,10 +200,10 @@ namespace bnhtrade.Core.Model.Stock
{ {
result.Add(new ValidationResult("Quantity is not set")); result.Add(new ValidationResult("Quantity is not set"));
} }
if (!IsSetSkuTransactionId) //if (!IsSetSkuTransactionId)
{ //{
result.Add(new ValidationResult("Stock Transaction Id is not set")); // result.Add(new ValidationResult("Stock Transaction Id is not set"));
} //}
if (IsSetStockJournalId && (!IsSetIsProcessed || IsProcessed == false)) if (IsSetStockJournalId && (!IsSetIsProcessed || IsProcessed == false))
{ {
result.Add(new ValidationResult("Stock Journal Id is set, IsProcessed must be set to true")); result.Add(new ValidationResult("Stock Journal Id is set, IsProcessed must be set to true"));

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Model.Stock
{
public class StatusBalance
{
public int StockStatusId { get; set; }
public string Sku { get; set; }
public List<ByDate> ByDateList { get; private set; } = new List<ByDate>();
public class ByDate
{
public DateTime EntryDate { get; set; }
public int StockNumber { get; set; }
public int Quantity { get; set; }
}
public void AddBalanceTransaction(DateTime entryDate, int stockNumber, int quantity)
{
if (entryDate == new DateTime())
{
throw new Exception("Entry date set to default value");
}
var item = new ByDate();
item.EntryDate = entryDate;
item.StockNumber = stockNumber;
item.Quantity = quantity;
if (ByDateList == null) { ByDateList = new List<ByDate>(); }
if (ByDateList.Count == 0 || ByDateList.Last().EntryDate <= item.EntryDate)
{
ByDateList.Add(item);
}
else
{
for (int i = 0; i < ByDateList.Count; i++)
{
if (entryDate <= ByDateList[i].EntryDate)
{
i++;
ByDateList.Insert(i, item);
}
}
}
}
public bool CheckAvaliableQuantity(int quantity, DateTime onDateTime)
{
if (GetAvaliableQuantity(onDateTime) < quantity)
{
return false;
}
else
{
return true;
}
}
public int GetAvaliableQuantity()
{
if (!ByDateList.Any())
{
return 0;
}
int qty = 0;
for (int i = 0; i < ByDateList.Count; i++)
{
qty += ByDateList[i].Quantity;
}
return qty;
}
public int GetAvaliableQuantity(DateTime onDateTime)
{
if (!ByDateList.Any())
{
return 0;
}
int qty = 0;
for (int i = 0; i < ByDateList.Count; i++)
{
if (ByDateList[i].EntryDate <= onDateTime)
{
qty += ByDateList[i].Quantity;
}
}
return qty;
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Model.Stock
{
public class StatusTransaction
{
public int StockStatusId { get; set; }
public string Sku { get; set; }
public int Balance
{
get
{
if (TransactionList.Any())
{
return TransactionList.Sum(x => x.Quantity);
}
else
{
return 0;
}
}
}
public List<Transaction> TransactionList { get; private set; } = new List<Transaction>();
public class Transaction
{
public DateTime EntryDate { get; set; }
public int StockNumber { get; set; }
public int Quantity { get; set; }
}
public void AddBalanceTransaction(DateTime entryDate, int stockNumber, int quantity)
{
if (entryDate == new DateTime())
{
throw new Exception("Entry date set to default value");
}
var item = new Transaction();
item.EntryDate = entryDate;
item.StockNumber = stockNumber;
item.Quantity = quantity;
if (TransactionList == null) { TransactionList = new List<Transaction>(); }
if (TransactionList.Count == 0 || TransactionList.Last().EntryDate <= item.EntryDate )
{
TransactionList.Add(item);
}
else
{
for (int i = 0; i < TransactionList.Count; i++)
{
if (entryDate <= TransactionList[i].EntryDate)
{
i++;
TransactionList.Insert(i, item);
}
}
}
}
}
}

View File

@@ -1665,7 +1665,7 @@ namespace bnhtrade.Core
{ {
public class StockCreate public class StockCreate
{ {
private static int WIP_StockInsert(string sqlConnectionString, int accountJournalType, int stockJournalType, string currencyCode, decimal amount, private int WIP_StockInsert(string sqlConnectionString, int accountJournalType, int stockJournalType, string currencyCode, decimal amount,
int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId) int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
{ {
using (TransactionScope scope = new TransactionScope()) using (TransactionScope scope = new TransactionScope())
@@ -1677,7 +1677,7 @@ namespace bnhtrade.Core
int accountJournalId = Account.AccountQuery.AccountJournalInsert(sqlConnectionString, accountJournalType, entryDate, currencyCode, amount); int accountJournalId = Account.AccountQuery.AccountJournalInsert(sqlConnectionString, accountJournalType, entryDate, currencyCode, amount);
// make the stock insert // make the stock insert
int stockId = StockCreate.WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId, int stockId = WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId,
accountJournalId, stockJournalType, entryDate, quantity, debitStatusId); accountJournalId, stockJournalType, entryDate, quantity, debitStatusId);
scope.Complete(); scope.Complete();
@@ -1685,7 +1685,7 @@ namespace bnhtrade.Core
} }
} }
private static int WIP_StockInsertSub(string sqlConnectionString, int productId, int conditionId, int accountTaxCodeId, private int WIP_StockInsertSub(string sqlConnectionString, int productId, int conditionId, int accountTaxCodeId,
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId) int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId)
{ {
stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc); stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc);
@@ -2073,8 +2073,7 @@ namespace bnhtrade.Core
stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc); stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
} }
} }
return new Stock.StockCreate().WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
return WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
} }
public static int WIP_StockInsertOwnerIntroduced(string sqlConnectionString, decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId) public static int WIP_StockInsertOwnerIntroduced(string sqlConnectionString, decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
@@ -2083,7 +2082,7 @@ namespace bnhtrade.Core
int stockJournalType = 2; int stockJournalType = 2;
string currencyCode = "GBP"; string currencyCode = "GBP";
return WIP_StockInsert(sqlConnectionString, accountJournalType, stockJournalType, currencyCode, amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId); return new Stock.StockCreate().WIP_StockInsert(sqlConnectionString, accountJournalType, stockJournalType, currencyCode, amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId);
} }
public static void WIP_StockDeletePurchase(string sqlConnectionString, int stockId) public static void WIP_StockDeletePurchase(string sqlConnectionString, int stockId)
@@ -2463,7 +2462,7 @@ namespace bnhtrade.Core
{ {
if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false) if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false)
{ {
int quantity = Stock.StockJournal.StockStatusBalanceByStockId(conn.ConnectionString, stockId, item.Key); int quantity = new Data.Database.Stock.ReadStatusBalance(conn.ConnectionString).ByStockId(stockId, item.Key);
if (quantity + item.Value < 0) if (quantity + item.Value < 0)
{ {
@@ -2689,132 +2688,6 @@ namespace bnhtrade.Core
return true; return true;
} }
public static int StockStatusBalanceByStockId(string sqlConnectionString, int stockId, int statusId)
{
int statusBalance = new int();
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
SELECT
SUM(tblStockJournalPost.Quantity) AS Balance
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
(tblStockJournal.StockID = @stockId )
AND (tblStockJournalPost.StockStatusID = @statusId )
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockId", stockId);
cmd.Parameters.AddWithValue("@statusId", statusId);
// execute
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
statusBalance = 0;
}
else
{
statusBalance = (int)obj;
}
}
}
return statusBalance;
}
// Function returns a list with balance of stock avaiable by individual stockId ordered by earliest added to status (stock journal entrydate)
// beforeDate - used to filter results, useful when reallocating stock retrospectivly (i.e. avoid case were stock is
// moved out of status, ahead of it being moved into that status) -- really this should be the calling function that does this!!
public static List<Tuple<int, DateTime, int>> GetStockStatusBalanceBySkuNumber
(string sqlConnectionString, string skuNumber, int stockStatusId, DateTime? maxDateUtc = null, bool sortAgeAscending = true)
{
//build sql statement
string strSQL = @"
SELECT a.JournalDate
,a.balance
,a.stockID
FROM (
SELECT MIN(tblStockJournal.EntryDate) AS JournalDate
,SUM(tblStockJournalPost.Quantity) AS Balance
,tblStock.StockID
FROM tblStockJournal
INNER JOIN tblStock ON tblStockJournal.StockID = tblStock.StockID
INNER JOIN tblStockJournalPost ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
INNER JOIN tblSku ON tblStock.SkuID = tblSku.skuSkuID
WHERE (tblStockJournalPost.StockStatusID = @stockStatusId)
AND (tblSku.skuSkuNumber = @skuNumber)
GROUP BY tblStock.StockID
HAVING (SUM(tblStockJournalPost.Quantity) > 0)
) a ";
if (maxDateUtc != null)
{
strSQL = strSQL + @"
WHERE a.JournalDate <= @beforeDateUtc
";
}
strSQL = strSQL + @"
ORDER BY a.JournalDate;
";
try
{
using (SqlConnection sqlConn = new SqlConnection(sqlConnectionString))
{
sqlConn.Open();
using (SqlCommand cmd = new SqlCommand(strSQL, sqlConn))
{
// add parameters
cmd.Parameters.AddWithValue("@skuNumber", skuNumber);
cmd.Parameters.AddWithValue("@stockStatusId", stockStatusId);
if (maxDateUtc != null)
{ cmd.Parameters.AddWithValue("@beforeDateUtc", maxDateUtc.Value.ToUniversalTime()); }
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
List<Tuple<int, DateTime, int>> list = new List<Tuple<int, DateTime, int>>();
int index01 = reader.GetOrdinal("StockID");
int index02 = reader.GetOrdinal("JournalDate");
int index03 = reader.GetOrdinal("Balance");
while (reader.Read())
{
int stockId = reader.GetInt32(index01);
DateTime journalDate = reader.GetDateTime(index02);
int balance = reader.GetInt32(index03);
journalDate = DateTime.SpecifyKind(journalDate, DateTimeKind.Utc);
list.Add(new Tuple<int, DateTime, int>(stockId, journalDate, balance));
}
if (sortAgeAscending == false)
{
list.Reverse();
}
return list;
}
else
{
return null;
}
}
}
}
}
catch (Exception)
{
throw;
}
}
public static int GetStockIdByStockJournalId(string sqlConnectionString, int stockJournalId) public static int GetStockIdByStockJournalId(string sqlConnectionString, int stockJournalId)
{ {
try try
@@ -7766,111 +7639,4 @@ namespace bnhtrade.Core
return fullPath; return fullPath;
} }
} }
public class TempFunction
{
public void UpdateSkuCost(string sqlConnectionString)
{
MiscFunction.EventLogInsert("Starting temp function UpdateSkuCost()");
int count = 0;
try
{
using (SqlConnection sqlConn = new SqlConnection(sqlConnectionString))
{
sqlConn.Open();
using (SqlCommand cmd01 = sqlConn.CreateCommand())
{
//query for list of all sku's with stock
cmd01.CommandText = @"
SELECT sku, [mfn-fulfillable-quantity], [afn-total-quantity]
FROM tblImportFbaManageInventory
WHERE ( [mfn-fulfillable-quantity] > 0 ) OR ( [afn-total-quantity] > 0 )
";
using (SqlDataReader reader01 = cmd01.ExecuteReader())
{
// retrive index of columns for faster operation
int indexOfColumn1 = reader01.GetOrdinal("sku");
int indexOfColumn2 = reader01.GetOrdinal("mfn-fulfillable-quantity");
int indexOfColumn3 = reader01.GetOrdinal("afn-total-quantity");
while (reader01.Read())
{
count = count + 1;
Console.Write("\rProcessing record #" + count);
//assign values
string skuNumber = reader01.GetString(indexOfColumn1);
int mfnTotal;
if (reader01.IsDBNull(indexOfColumn2)) { mfnTotal = 0; }
else { mfnTotal = reader01.GetInt32(indexOfColumn2); }
int afnTotal;
if (reader01.IsDBNull(indexOfColumn3)) { afnTotal = 0; }
else { afnTotal = reader01.GetInt32(indexOfColumn3); }
int total = mfnTotal + afnTotal;
//query for average unit cost
using (SqlCommand cmd02 = new SqlCommand(@"
SELECT AVG(Q.UnitCost) AS AvgCost
FROM(
SELECT TOP (@total) UnitQuantity, n, UnitCost
FROM(tblNumbers INNER JOIN tblStock ON n <= UnitQuantity) INNER JOIN tblSku ON SkuID = skuSkuID
WHERE(skuSkuNumber = @skuNumber)
ORDER BY StockID DESC
) Q
", sqlConn))
{
cmd02.Parameters.AddWithValue("@total", total);
cmd02.Parameters.AddWithValue("@skuNumber", skuNumber);
decimal AvgCost = 0;
object obj = cmd02.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
AvgCost = 0;
}
else
{
AvgCost = Convert.ToDecimal(obj);
}
AvgCost = Math.Round(AvgCost, 2);
//Console.WriteLine(skuNumber + " " + AvgCost);
//update sku table
using (SqlCommand cmd03 = sqlConn.CreateCommand())
{
cmd03.Parameters.AddWithValue("@skuNumber", skuNumber);
cmd03.Parameters.AddWithValue("@AvgCost", AvgCost);
cmd03.Parameters.AddWithValue("@timeStamp", DateTime.UtcNow);
Console.Write(" £" + AvgCost );
cmd03.CommandText =
"UPDATE tblSku " +
"SET skuSkuAvgCost = @AvgCost, skuSkuAvgCostUpdate = @timeStamp " +
"WHERE skuSkuNumber = @skuNumber;";
cmd03.ExecuteNonQuery();
}
}
}
}
}
Console.Write("\r");
MiscFunction.EventLogInsert("UpdateSkuCost() operation complete. " + count + " total SKU average cost(s) updated");
}
}
catch (Exception ex)
{
MiscFunction.EventLogInsert("UpdateSkuCost() operation exceltion. See 'details' for further information.", 1, ex.ToString());
Console.WriteLine(ex.ToString());
}
}
}
} }

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Test.COM
{
class COMClassLib
{
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Test.Stock
{
public class Stock
{
private string sqlConnectionString;
public Stock(string sqlConnectionString)
{
this.sqlConnectionString = sqlConnectionString;
// method you want to start here
UnreconcileSkuTrnasction(79808);
}
public void ReadStatusBalance()
{
var result = new bnhtrade.Core.Data.Database.Stock.ReadStatusTransaction(sqlConnectionString)
.ByStockNumber(12, 5839);
}
public void StatusBalance()
{
string sku = "003190-10";
int statusId = 12; // fba sold
var atDate = new DateTime(2017, 02, 01, 21, 54, 30);
DateTime.TryParse("22/12/2017 16:35:58", out atDate);
var result = new Core.Logic.Stock.StatusBalance(sqlConnectionString).GetBySku(sku, statusId);
}
public void ReadStockId()
{
List<int> idList = new List<int>();
idList.Add(123);
idList.Add(333);
idList.Add(1788);
var result = new Data.Database.Stock.ReadStockId(sqlConnectionString).ByStockNumber(idList);
}
public void SkuTransactionAdd()
{
var trans = new bnhtrade.Core.Model.Stock.SkuTransaction();
trans.IsProcessed = false;
trans.Quantity = 1;
//trans.Reference
trans.SkuNumber = "005642-41";
trans.SkuTransactionTypeCode = "ManualAdjustment005";
//trans.StockJournalId;
trans.TransactionDate = new DateTime(2020, 01, 29, 17, 00, 00);
new bnhtrade.Core.Logic.Stock.SkuTransactionPersistance(sqlConnectionString).Create(trans);
}
public void UnreconcileSkuTrnasction(int transactoinId)
{
new bnhtrade.Core.Logic.Stock.SkuTransactionReconcile(sqlConnectionString).UnReconcileTransaction(transactoinId);
}
}
}

View File

@@ -105,13 +105,17 @@
<Compile Include="Data\Database\Stock\DeleteSkuTransaction.cs" /> <Compile Include="Data\Database\Stock\DeleteSkuTransaction.cs" />
<Compile Include="Data\Database\Stock\ReadSkuTransaction.cs" /> <Compile Include="Data\Database\Stock\ReadSkuTransaction.cs" />
<Compile Include="Data\Database\Stock\ReadSkuTransactionType.cs" /> <Compile Include="Data\Database\Stock\ReadSkuTransactionType.cs" />
<Compile Include="Data\Database\Stock\ReadStatusBalance.cs" />
<Compile Include="Data\Database\Stock\ReadStatusTransaction.cs" />
<Compile Include="Data\Database\Stock\ReadStockId.cs" />
<Compile Include="Data\Database\Stock\UpdateSkuTransaction.cs" /> <Compile Include="Data\Database\Stock\UpdateSkuTransaction.cs" />
<Compile Include="Data\Database\WhereBuilder.cs" /> <Compile Include="Data\Database\SqlWhereBuilder.cs" />
<Compile Include="Extensions.cs" /> <Compile Include="Extensions.cs" />
<Compile Include="Logic\Account\GetAccountCodeInfo.cs" /> <Compile Include="Logic\Account\GetAccountCodeInfo.cs" />
<Compile Include="Logic\Account\GetInvoiceLineItem.cs" /> <Compile Include="Logic\Account\GetInvoiceLineItem.cs" />
<Compile Include="Logic\Account\TaxCalculation.cs" /> <Compile Include="Logic\Account\TaxCalculation.cs" />
<Compile Include="Logic\Account\GetTaxCodeInfo.cs" /> <Compile Include="Logic\Account\GetTaxCodeInfo.cs" />
<Compile Include="Logic\Stock\StatusBalance.cs" />
<Compile Include="Logic\Validate\AccountCode.cs" /> <Compile Include="Logic\Validate\AccountCode.cs" />
<Compile Include="Logic\Validate\CurrencyCode.cs" /> <Compile Include="Logic\Validate\CurrencyCode.cs" />
<Compile Include="Logic\Validate\SalesInvoice.cs" /> <Compile Include="Logic\Validate\SalesInvoice.cs" />
@@ -120,7 +124,7 @@
<Compile Include="Logic\Export\AmazonSubmitFile.cs" /> <Compile Include="Logic\Export\AmazonSubmitFile.cs" />
<Compile Include="Logic\Export\AmazonSubmitFileStatus.cs" /> <Compile Include="Logic\Export\AmazonSubmitFileStatus.cs" />
<Compile Include="Logic\Sku\GetSkuConditionInfo.cs" /> <Compile Include="Logic\Sku\GetSkuConditionInfo.cs" />
<Compile Include="Logic\Stock\Reallocate.cs" /> <Compile Include="Logic\Stock\StatusReallocate.cs" />
<Compile Include="Logic\Stock\SkuTransactionReconcile.cs" /> <Compile Include="Logic\Stock\SkuTransactionReconcile.cs" />
<Compile Include="Logic\Stock\SkuTransactionPersistance.cs" /> <Compile Include="Logic\Stock\SkuTransactionPersistance.cs" />
<Compile Include="Logic\Stock\SkuTransactionTypePersistance.cs" /> <Compile Include="Logic\Stock\SkuTransactionTypePersistance.cs" />
@@ -161,12 +165,12 @@
<Compile Include="Model\Sku\Price\SkuPriceParameter.cs" /> <Compile Include="Model\Sku\Price\SkuPriceParameter.cs" />
<Compile Include="Model\Sku\SkuConditionInfo.cs" /> <Compile Include="Model\Sku\SkuConditionInfo.cs" />
<Compile Include="Model\Stock\JournalEntry.cs" /> <Compile Include="Model\Stock\JournalEntry.cs" />
<Compile Include="Model\Stock\JournalEntryPost.cs" />
<Compile Include="Model\Stock\SkuTransactionType.cs" /> <Compile Include="Model\Stock\SkuTransactionType.cs" />
<Compile Include="Model\Stock\SkuTransaction.cs" /> <Compile Include="Model\Stock\SkuTransaction.cs" />
<Compile Include="Model\Stock\StatusBalance.cs" />
<Compile Include="Model\Stock\StatusTransaction.cs" />
<Compile Include="Test\Account\Account.cs" /> <Compile Include="Test\Account\Account.cs" />
<Compile Include="Test\AutoExec.cs" /> <Compile Include="Test\AutoExec.cs" />
<Compile Include="Test\COM\COMClassLib.cs" />
<Compile Include="Test\Export\Export.cs" /> <Compile Include="Test\Export\Export.cs" />
<Compile Include="Test\Import\AmazonSettlement.cs" /> <Compile Include="Test\Import\AmazonSettlement.cs" />
<Compile Include="Test\InboundShipmentInfo.cs" /> <Compile Include="Test\InboundShipmentInfo.cs" />
@@ -178,6 +182,7 @@
<Compile Include="Test\SQLLoop.cs" /> <Compile Include="Test\SQLLoop.cs" />
<Compile Include="Model\AmazonFba\ShipmentInfo.cs" /> <Compile Include="Model\AmazonFba\ShipmentInfo.cs" />
<Compile Include="Model\Sku\Sku.cs" /> <Compile Include="Model\Sku\Sku.cs" />
<Compile Include="Test\Stock\Stock.cs" />
<Compile Include="Test\_BoilerPlate\ClassFromSql.cs" /> <Compile Include="Test\_BoilerPlate\ClassFromSql.cs" />
<Compile Include="Test\_BoilerPlate\Sql.cs" /> <Compile Include="Test\_BoilerPlate\Sql.cs" />
<Compile Include="UI\Console\ProgressBar.cs" /> <Compile Include="UI\Console\ProgressBar.cs" />

View File

@@ -289,7 +289,7 @@ namespace bnhtradeScheduledTasks
Console.WriteLine("<4> Test Import"); Console.WriteLine("<4> Test Import");
Console.WriteLine("<5> Test Logic"); Console.WriteLine("<5> Test Logic");
Console.WriteLine("<6> Test SKU"); Console.WriteLine("<6> Test SKU");
Console.WriteLine("<7> Test COM Class"); Console.WriteLine("<7> Test Stock");
Console.WriteLine("<8> Test xxxxxxx"); Console.WriteLine("<8> Test xxxxxxx");
Console.WriteLine("<9> Detele Sku Transaction 'n'"); Console.WriteLine("<9> Detele Sku Transaction 'n'");
Console.WriteLine(); Console.WriteLine();
@@ -366,14 +366,7 @@ namespace bnhtradeScheduledTasks
{ {
Console.Clear(); Console.Clear();
//string conString = ConfigurationManager.ConnectionStrings["bnhtradeDbConnString"].ConnectionString; new bnhtrade.Core.Test.Stock.Stock(sqlConnectionString);
//var builder = new SqlConnectionStringBuilder(conString);
//var cred = new bnhtrade.ComTypeLib.Credential.ConnectionCredential();
//cred.Password = builder.Password;
//cred.UserId = builder.UserID;
//var obj = new bnhtrade.ComTypeLib.Stock().ReconcileStockTransactions(cred);
Console.WriteLine("Done"); Console.WriteLine("Done");
Console.WriteLine("Complete, press any key to continue..."); Console.WriteLine("Complete, press any key to continue...");