From ddd2b627437d2cc98238058f0ae226f38a11a271 Mon Sep 17 00:00:00 2001 From: Bobbie Hodgetts Date: Mon, 5 Oct 2020 22:40:55 +0100 Subject: [PATCH] Various bug fixs and improvements to stock SKU reconciliation --- src/bnhtrade.ComTypeLib/Product/Product.cs | 17 +- src/bnhtrade.ComTypeLib/Stock/Stock.cs | 18 +- .../Data/Database/Account/ReadAccountCode.cs | 2 +- .../Database/Account/ReadInvoiceLineItem.cs | 2 +- .../Data/Database/Account/ReadTaxCode.cs | 4 +- .../{WhereBuilder.cs => SqlWhereBuilder.cs} | 13 +- .../Data/Database/Stock/ReadSkuTransaction.cs | 69 +++-- .../Data/Database/Stock/ReadStatusBalance.cs | 123 +++++++++ .../Database/Stock/ReadStatusTransaction.cs | 193 ++++++++++++++ .../Data/Database/Stock/ReadStockId.cs | 58 +++++ src/bnhtrade.Core/Logic/Stock/Reallocate.cs | 86 ------ .../Logic/Stock/SkuTransactionPersistance.cs | 20 ++ .../Logic/Stock/SkuTransactionReconcile.cs | 76 +++--- .../Logic/Stock/StatusBalance.cs | 111 ++++++++ .../Logic/Stock/StatusReallocate.cs | 109 ++++++++ src/bnhtrade.Core/Model/Stock/JournalEntry.cs | 25 +- .../Model/Stock/JournalEntryPost.cs | 34 --- .../Model/Stock/SkuTransaction.cs | 8 +- .../Model/Stock/StatusBalance.cs | 106 ++++++++ .../Model/Stock/StatusTransaction.cs | 72 +++++ src/bnhtrade.Core/Program.cs | 246 +----------------- src/bnhtrade.Core/Test/COM/COMClassLib.cs | 13 - src/bnhtrade.Core/Test/Stock/Stock.cs | 64 +++++ src/bnhtrade.Core/bnhtrade.Core.csproj | 13 +- src/bnhtrade.ScheduledTasks/Program.cs | 11 +- 25 files changed, 1026 insertions(+), 467 deletions(-) rename src/bnhtrade.Core/Data/Database/{WhereBuilder.cs => SqlWhereBuilder.cs} (90%) create mode 100644 src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs create mode 100644 src/bnhtrade.Core/Data/Database/Stock/ReadStatusTransaction.cs create mode 100644 src/bnhtrade.Core/Data/Database/Stock/ReadStockId.cs delete mode 100644 src/bnhtrade.Core/Logic/Stock/Reallocate.cs create mode 100644 src/bnhtrade.Core/Logic/Stock/StatusBalance.cs create mode 100644 src/bnhtrade.Core/Logic/Stock/StatusReallocate.cs delete mode 100644 src/bnhtrade.Core/Model/Stock/JournalEntryPost.cs create mode 100644 src/bnhtrade.Core/Model/Stock/StatusBalance.cs create mode 100644 src/bnhtrade.Core/Model/Stock/StatusTransaction.cs delete mode 100644 src/bnhtrade.Core/Test/COM/COMClassLib.cs create mode 100644 src/bnhtrade.Core/Test/Stock/Stock.cs diff --git a/src/bnhtrade.ComTypeLib/Product/Product.cs b/src/bnhtrade.ComTypeLib/Product/Product.cs index 47f52d3..47a1f0c 100644 --- a/src/bnhtrade.ComTypeLib/Product/Product.cs +++ b/src/bnhtrade.ComTypeLib/Product/Product.cs @@ -13,8 +13,6 @@ namespace bnhtrade.ComTypeLib [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IProduct { - string ReturnStringValue(string stringValue); - double ReturnDateValueAsDouble(string stringValue); int ProductGetProductIdByCatId(int catId, ConnectionCredential sqlConnCred); @@ -32,20 +30,12 @@ namespace bnhtrade.ComTypeLib [ProgId("bnhtrade.Product")] 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) { DateTime theTimeNow = DateTime.UtcNow; return theTimeNow.ToOADate(); // 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) @@ -104,10 +94,5 @@ namespace bnhtrade.ComTypeLib Core.Product.ProductQuery.ProductUpdateAmazonEstimateFee(sqlConnCred.ConnectionString, inputTuple); } - } - - - - } diff --git a/src/bnhtrade.ComTypeLib/Stock/Stock.cs b/src/bnhtrade.ComTypeLib/Stock/Stock.cs index 1ca030f..2ac717f 100644 --- a/src/bnhtrade.ComTypeLib/Stock/Stock.cs +++ b/src/bnhtrade.ComTypeLib/Stock/Stock.cs @@ -30,6 +30,8 @@ namespace bnhtrade.ComTypeLib void UnReconcileSkuTransaction(ConnectionCredential sqlConnCred, int skuTransactionId); bool StockJournalConsistencyCheck(ConnectionCredential sqlConnCred, int stockId); + + void SkuTransactionAdd(ConnectionCredential sqlConnCred, int quantity, string skuNumber, string transactionTypeCode, DateTime transactionDate); } [ComVisible(true)] @@ -63,7 +65,7 @@ namespace bnhtrade.ComTypeLib { 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) @@ -128,5 +130,19 @@ namespace bnhtrade.ComTypeLib { 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); + } } } diff --git a/src/bnhtrade.Core/Data/Database/Account/ReadAccountCode.cs b/src/bnhtrade.Core/Data/Database/Account/ReadAccountCode.cs index 0887af7..a19f5d5 100644 --- a/src/bnhtrade.Core/Data/Database/Account/ReadAccountCode.cs +++ b/src/bnhtrade.Core/Data/Database/Account/ReadAccountCode.cs @@ -9,7 +9,7 @@ namespace bnhtrade.Core.Data.Database.Account { public class ReadAccountCode : Connection { - private Data.Database.WhereBuilder sqlWhere = new WhereBuilder(); + private Data.Database.SqlWhereBuilder sqlWhere = new SqlWhereBuilder(); private List resultList; public ReadAccountCode(string sqlConnectionString) : base(sqlConnectionString) diff --git a/src/bnhtrade.Core/Data/Database/Account/ReadInvoiceLineItem.cs b/src/bnhtrade.Core/Data/Database/Account/ReadInvoiceLineItem.cs index ae85e11..6693496 100644 --- a/src/bnhtrade.Core/Data/Database/Account/ReadInvoiceLineItem.cs +++ b/src/bnhtrade.Core/Data/Database/Account/ReadInvoiceLineItem.cs @@ -62,7 +62,7 @@ namespace bnhtrade.Core.Data.Database.Account LEFT OUTER JOIN tblAccountChartOf ON tblAccountInvoiceLineItem.AccountChartOfID_Default = tblAccountChartOf.AccountChartOfID WHERE "; - var whereBuilder = new WhereBuilder(); + var whereBuilder = new SqlWhereBuilder(); whereBuilder.In("tblAccountInvoiceLineItem.ItemCode", itemCodeList); sql += whereBuilder.SqlWhereString; diff --git a/src/bnhtrade.Core/Data/Database/Account/ReadTaxCode.cs b/src/bnhtrade.Core/Data/Database/Account/ReadTaxCode.cs index 61a8275..6765d43 100644 --- a/src/bnhtrade.Core/Data/Database/Account/ReadTaxCode.cs +++ b/src/bnhtrade.Core/Data/Database/Account/ReadTaxCode.cs @@ -9,11 +9,11 @@ namespace bnhtrade.Core.Data.Database.Account { public class ReadTaxCode : Connection { - private Data.Database.WhereBuilder whereBuilder; + private Data.Database.SqlWhereBuilder whereBuilder; public ReadTaxCode(string sqlConnectionString) : base(sqlConnectionString) { - whereBuilder = new WhereBuilder(); + whereBuilder = new SqlWhereBuilder(); } private List Execute(string sqlWhere, Dictionary parameters) diff --git a/src/bnhtrade.Core/Data/Database/WhereBuilder.cs b/src/bnhtrade.Core/Data/Database/SqlWhereBuilder.cs similarity index 90% rename from src/bnhtrade.Core/Data/Database/WhereBuilder.cs rename to src/bnhtrade.Core/Data/Database/SqlWhereBuilder.cs index 9a05760..4c63918 100644 --- a/src/bnhtrade.Core/Data/Database/WhereBuilder.cs +++ b/src/bnhtrade.Core/Data/Database/SqlWhereBuilder.cs @@ -1,16 +1,17 @@ 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 { - public class WhereBuilder + public class SqlWhereBuilder { private int parameterCount; - public WhereBuilder() + public SqlWhereBuilder() { Innit(); } @@ -26,6 +27,14 @@ namespace bnhtrade.Core.Data.Database parameterCount = 0; } + public void AddParametersToSqlCommand(SqlCommand cmd) + { + foreach (var item in ParameterList) + { + cmd.Parameters.AddWithValue(item.Key, item.Value); + } + } + /// /// Used to create a string for an SQL where condition 'In' statement /// diff --git a/src/bnhtrade.Core/Data/Database/Stock/ReadSkuTransaction.cs b/src/bnhtrade.Core/Data/Database/Stock/ReadSkuTransaction.cs index d58a485..96e6b7b 100644 --- a/src/bnhtrade.Core/Data/Database/Stock/ReadSkuTransaction.cs +++ b/src/bnhtrade.Core/Data/Database/Stock/ReadSkuTransaction.cs @@ -10,7 +10,8 @@ namespace bnhtrade.Core.Data.Database.Stock { public class ReadSkuTransaction : Connection { - private Data.Database.WhereBuilder whereBuilder = new WhereBuilder(); + private Data.Database.SqlWhereBuilder whereBuilder = new SqlWhereBuilder(); + private List transactionIdList = new List(); public ReadSkuTransaction(string sqlConnectionString) : base(sqlConnectionString) { @@ -22,7 +23,7 @@ namespace bnhtrade.Core.Data.Database.Stock /// public void Init() { - whereBuilder = new WhereBuilder(); + whereBuilder = new SqlWhereBuilder(); IsReconciled = null; StockTransactionTypeName = null; StockTransactionTypeCode = null; @@ -107,34 +108,44 @@ namespace bnhtrade.Core.Data.Database.Stock INNER JOIN tblSku ON tblStockSkuTransaction.SkuID = tblSku.skuSkuID WHERE 1=1 "; - if (IsReconciled != null) + if (transactionIdList.Any()) { + parameters.Add("@transactionIdList", this.transactionIdList); + sql += @" + AND tblStockSkuTransaction.StockSkuTransactionID IN @transactionIdList "; + } + else + { + if (IsReconciled != null) + { + sql += @" AND tblStockSkuTransaction.IsProcessed ="; - if (IsReconciled.GetValueOrDefault()) - { - sql += " 1 "; + if (IsReconciled.GetValueOrDefault()) + { + sql += " 1 "; + } + else + { + sql += " 0 "; + } } - else - { - sql += " 0 "; - } - } - if (StockTransactionTypeName != null && StockTransactionTypeName.Any()) - { - parameters.Add("@stockTransactionTypeName", StockTransactionTypeName); + if (StockTransactionTypeName != null && StockTransactionTypeName.Any()) + { + parameters.Add("@stockTransactionTypeName", StockTransactionTypeName); - sql += @" + sql += @" AND tblStockSkuTransactionType.TypeName IN @stockTransactionTypeName "; - } + } - if (StockTransactionTypeCode != null && StockTransactionTypeCode.Any()) - { - parameters.Add("@stockTransactionTypeCode", StockTransactionTypeCode); + if (StockTransactionTypeCode != null && StockTransactionTypeCode.Any()) + { + parameters.Add("@stockTransactionTypeCode", StockTransactionTypeCode); - sql += @" + sql += @" AND tblStockSkuTransactionType.TypeCode IN @stockTransactionTypeCode "; + } } sql += @" @@ -158,5 +169,23 @@ namespace bnhtrade.Core.Data.Database.Stock return resultList; } + + /// + /// Get Stock SKU Transaction by ID + /// + /// + /// List of SkuTransaction + public List Read(List transactionIdList) + { + var returnList = new List(); + if (!transactionIdList.Any()) + { + return returnList; + } + this.transactionIdList = transactionIdList; + returnList = Read(); + this.transactionIdList = new List(); + return returnList; + } } } diff --git a/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs new file mode 100644 index 0000000..c5ac004 --- /dev/null +++ b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs @@ -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; + } + } +} diff --git a/src/bnhtrade.Core/Data/Database/Stock/ReadStatusTransaction.cs b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusTransaction.cs new file mode 100644 index 0000000..36b6f5a --- /dev/null +++ b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusTransaction.cs @@ -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) + { + } + + /// + /// Returns a list with balance of stock avaiable by individual StockId + /// + /// SKU Number + /// Stock Status Database PK + /// + 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; + } + + /// + /// Gets list of stock status debits and credits. Includes stock numbers with balance greater than or less than zero. + /// + /// Required, filters results + /// Required, filters results + /// Tuple list is assending order + 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; + } + + /// + /// Gets list of stock status debits and credits. Includes stock numbers with balance greater than or less than zero. + /// + /// Required, filters results by stock status Id + /// Required, filters results by sku number + /// Tuple list is assending order + 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; + } + } +} diff --git a/src/bnhtrade.Core/Data/Database/Stock/ReadStockId.cs b/src/bnhtrade.Core/Data/Database/Stock/ReadStockId.cs new file mode 100644 index 0000000..5c2af2c --- /dev/null +++ b/src/bnhtrade.Core/Data/Database/Stock/ReadStockId.cs @@ -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) + { + } + + /// + /// 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 + /// + /// List of Stock Numbers to retrive from database + /// Dictiony with Stock Number as Key and Stock ID as value + public Dictionary ByStockNumber(List stockNumberList) + { + var result = new Dictionary(); + + 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; + } + } + } + } +} diff --git a/src/bnhtrade.Core/Logic/Stock/Reallocate.cs b/src/bnhtrade.Core/Logic/Stock/Reallocate.cs deleted file mode 100644 index 350735e..0000000 --- a/src/bnhtrade.Core/Logic/Stock/Reallocate.cs +++ /dev/null @@ -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); - } - - - /// - /// Feed an skuId and quantity into function and the stock will be reallocated - /// - 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> list = Core.Stock.StockJournal.GetStockStatusBalanceBySkuNumber(sqlConnectionString, skuNumber, creditStatusId, entryDate, firstInFirstOut); - - if (list == null || !list.Any()) - { - return returnList; - } - - // quantity check - int avaiableQuantity = 0; - foreach (Tuple item in list) - { - avaiableQuantity = avaiableQuantity + item.Item3; - } - if (avaiableQuantity < quantity && reallocatePartialQuantity == false) - { - return null; - } - - // make the changes - using (TransactionScope scope = new TransactionScope()) - { - foreach (Tuple 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; - } - } - } -} diff --git a/src/bnhtrade.Core/Logic/Stock/SkuTransactionPersistance.cs b/src/bnhtrade.Core/Logic/Stock/SkuTransactionPersistance.cs index bd3ad9b..32af94e 100644 --- a/src/bnhtrade.Core/Logic/Stock/SkuTransactionPersistance.cs +++ b/src/bnhtrade.Core/Logic/Stock/SkuTransactionPersistance.cs @@ -189,6 +189,26 @@ namespace bnhtrade.Core.Logic.Stock return resultList; } + /// + /// Retrive SKU Transaction by ID + /// + /// SKU Transaction ID + /// + /// + public List Read(List 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; + } + /// /// Validates and then updates a Stock SKU Tranaction /// diff --git a/src/bnhtrade.Core/Logic/Stock/SkuTransactionReconcile.cs b/src/bnhtrade.Core/Logic/Stock/SkuTransactionReconcile.cs index aa2fc9c..dc9b301 100644 --- a/src/bnhtrade.Core/Logic/Stock/SkuTransactionReconcile.cs +++ b/src/bnhtrade.Core/Logic/Stock/SkuTransactionReconcile.cs @@ -14,7 +14,7 @@ namespace bnhtrade.Core.Logic.Stock private Logic.Stock.SkuTransactionPersistance dbSkuTransaction; private Logic.Stock.SkuTransactionTypePersistance dbSkuTransactionType; private Logic.Validate.SkuTransaction validateSkuTrans; - private Logic.Stock.Reallocate stockReallocate; + private Logic.Stock.StatusReallocate stockReallocate; private Logic.Log.LogEvent logEvent; private string err = "Reconcile Sku Transaction Exception: "; @@ -26,7 +26,7 @@ namespace bnhtrade.Core.Logic.Stock dbSkuTransactionType = new SkuTransactionTypePersistance(sqlConnectionString); readShipmentInfo = new Data.Database.AmazonShipment.ReadShipmentInfo(sqlConnectionString); validateSkuTrans = new Validate.SkuTransaction(); - stockReallocate = new Logic.Stock.Reallocate(sqlConnectionString); + stockReallocate = new Logic.Stock.StatusReallocate(sqlConnectionString); logEvent = new Log.LogEvent(); } @@ -194,50 +194,52 @@ namespace bnhtrade.Core.Logic.Stock } // 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) { - list = stockReallocate.StockReallocateBySkuNumber( + journalList = stockReallocate.BySkuNumber( + transList[i].TransactionDate, transList[i].SkuTransactionType.StockJournalTypeId, transList[i].SkuNumber, transList[i].Quantity, transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.FirstInFirstOut, - transList[i].TransactionDate, - false); + true); } else { - list = stockReallocate.StockReallocateBySkuNumber( + journalList = stockReallocate.BySkuNumber( + DateTime.UtcNow, transList[i].SkuTransactionType.StockJournalTypeId, transList[i].SkuNumber, transList[i].Quantity, transList[i].SkuTransactionType.DebitStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.CreditStockStatusId.GetValueOrDefault(), transList[i].SkuTransactionType.FirstInFirstOut, - DateTime.UtcNow, - false); + true); } // insufficient balance available - if (list == null || !list.Any()) + if (!journalList.Any()) { // in special case (found inventory), continue if (transList[i].SkuTransactionType.TypeCode.Contains("<_GET_FBA_FULFILLMENT_INVENTORY_ADJUSTMENTS_DATA_>")) { + ItemsCompleted++; + ItemsRemaining--; continue; } else { - ProgressMessage = "Insurficent status/location balance to relocate stock"; + ProgressMessage = "Insurficent quantity at status/location to relocate stock"; recordSkip = recordSkip + 1; break; } } // fail safe - int qtyAllocated = list.Sum(c => c.Quantity); + int qtyAllocated = journalList.Sum(c => c.Quantity); if (qtyAllocated > transList[i].Quantity) { throw new Exception( @@ -248,40 +250,41 @@ namespace bnhtrade.Core.Logic.Stock // update sku transaction table int qtyRemain = qtyAllocated; var newRecordList = new List(); - for (int j = 0; j <= list.Count; j++) + for (int j = 0; j < journalList.Count; j++) { // update existing record if (j == 0) { - transList[i].Quantity = (short)list[j].Quantity; - transList[i].StockJournalId = list[j].StockJournalId; + transList[i].Quantity = (short)journalList[j].Quantity; + transList[i].StockJournalId = journalList[j].StockJournalId; transList[i].IsProcessed = true; dbSkuTransaction.Update(transList[i]); } // new record - else if (j < list.Count) + else { var newRecord = transList[i].Clone(); - newRecord.Quantity = (short)list[j].Quantity; + newRecord.Quantity = (short)journalList[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; + newRecord.StockJournalId = journalList[j].StockJournalId; + newRecordList.Add(newRecord); } - if (j < list.Count) - { - qtyRemain = qtyRemain - list[j].Quantity; - } + qtyRemain = qtyRemain - journalList[j].Quantity; } + + // new record for unallocated quantity + if (qtyRemain > 0) + { + var newRecord = transList[i].Clone(); + newRecord.Quantity = (short)qtyRemain; + newRecord.IsProcessed = false; + + newRecordList.Add(newRecord); + } + // add new transactions to table for (int j = 0; j < newRecordList.Count; j++) { @@ -448,7 +451,18 @@ namespace bnhtrade.Core.Logic.Stock public void UnReconcileTransaction(int skuTransactionId) { - dbSkuTransaction.DeleteJournalEntry(skuTransactionId); + var trans = dbSkuTransaction.Read(new List { 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); + } + else if (trans.IsProcessed == true) + { + new Data.Database.Stock.UpdateSkuTransaction(sqlConnectionString).Update(skuTransactionId, false); + } } } } diff --git a/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs b/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs new file mode 100644 index 0000000..2bec851 --- /dev/null +++ b/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs @@ -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; + } + + /// + /// 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). + /// + /// SKU number + /// Status ID + /// Balance as quantity + private int GetAvailableBalanceBySku(string sku, int statusId) + { + return new Data.Database.Stock.ReadStatusBalance(sqlConnectionString).BySku(sku, statusId); + } + + /// + /// Return the avaliable balance of a status at a specified date and time. Useful for checking availability before + /// moving stock/sku retrospectivly + /// + /// SKU number + /// Status ID + /// Date and time you would like to know the balance at + /// + 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 qtyList = new List(); + 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; + } + } +} diff --git a/src/bnhtrade.Core/Logic/Stock/StatusReallocate.cs b/src/bnhtrade.Core/Logic/Stock/StatusReallocate.cs new file mode 100644 index 0000000..251969b --- /dev/null +++ b/src/bnhtrade.Core/Logic/Stock/StatusReallocate.cs @@ -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; + } + + /// + /// Reallocates stock between status' by Stock Id + /// + /// + /// + /// + /// + /// + /// + /// Return newly created stock journal Id + 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); + } + + /// + /// Feed an SKU number and quantity into function and the stock will be reallocated + /// + /// Date and time of the transaction + /// Journal Type ID + /// Sku Number to reallocate + /// Quantity to reallocate + /// Status to move SKU to + /// Status to move SKU from + /// Move stock on first in first out basis + /// Reallocate patial quantity if the full quantity is not available + /// + 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(); + 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; + } + } + } +} diff --git a/src/bnhtrade.Core/Model/Stock/JournalEntry.cs b/src/bnhtrade.Core/Model/Stock/JournalEntry.cs index d4cb893..a8ef8e1 100644 --- a/src/bnhtrade.Core/Model/Stock/JournalEntry.cs +++ b/src/bnhtrade.Core/Model/Stock/JournalEntry.cs @@ -1,17 +1,16 @@ 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 JournalEntry + public class JournalEntry : IValidatableObject { public string TypeTitle { get; set; } - public int StockId { get; set; } - public int StockNumber { get; set; } public DateTime EntryDate { get; set; } @@ -21,5 +20,25 @@ namespace bnhtrade.Core.Model.Stock public DateTime LastModified { get; set; } public bool IsLocked { get; set; } + + public List JournalPosts { get; set; } = new List(); + + 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 Validate(ValidationContext validationContext) + { + throw new NotImplementedException(); + } } } diff --git a/src/bnhtrade.Core/Model/Stock/JournalEntryPost.cs b/src/bnhtrade.Core/Model/Stock/JournalEntryPost.cs deleted file mode 100644 index c2c9a8d..0000000 --- a/src/bnhtrade.Core/Model/Stock/JournalEntryPost.cs +++ /dev/null @@ -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 Validate(ValidationContext validationContext) - { - throw new NotImplementedException(); - - var resultList = new List(); - - if (Quantity == 0) - { - resultList.Add(new ValidationResult("Quantity must be greater than, or less than, zero")); - } - } - } -} diff --git a/src/bnhtrade.Core/Model/Stock/SkuTransaction.cs b/src/bnhtrade.Core/Model/Stock/SkuTransaction.cs index 3be25ee..32fccae 100644 --- a/src/bnhtrade.Core/Model/Stock/SkuTransaction.cs +++ b/src/bnhtrade.Core/Model/Stock/SkuTransaction.cs @@ -200,10 +200,10 @@ namespace bnhtrade.Core.Model.Stock { result.Add(new ValidationResult("Quantity is not set")); } - if (!IsSetSkuTransactionId) - { - result.Add(new ValidationResult("Stock Transaction Id is not set")); - } + //if (!IsSetSkuTransactionId) + //{ + // result.Add(new ValidationResult("Stock Transaction Id is not set")); + //} if (IsSetStockJournalId && (!IsSetIsProcessed || IsProcessed == false)) { result.Add(new ValidationResult("Stock Journal Id is set, IsProcessed must be set to true")); diff --git a/src/bnhtrade.Core/Model/Stock/StatusBalance.cs b/src/bnhtrade.Core/Model/Stock/StatusBalance.cs new file mode 100644 index 0000000..d2679e0 --- /dev/null +++ b/src/bnhtrade.Core/Model/Stock/StatusBalance.cs @@ -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 ByDateList { get; private set; } = new List(); + + 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(); } + + 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; + } + } +} diff --git a/src/bnhtrade.Core/Model/Stock/StatusTransaction.cs b/src/bnhtrade.Core/Model/Stock/StatusTransaction.cs new file mode 100644 index 0000000..d5311d6 --- /dev/null +++ b/src/bnhtrade.Core/Model/Stock/StatusTransaction.cs @@ -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 TransactionList { get; private set; } = new List(); + + 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(); } + + 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); + } + } + } + } + } +} diff --git a/src/bnhtrade.Core/Program.cs b/src/bnhtrade.Core/Program.cs index 2e8c133..0236787 100644 --- a/src/bnhtrade.Core/Program.cs +++ b/src/bnhtrade.Core/Program.cs @@ -1665,7 +1665,7 @@ namespace bnhtrade.Core { 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) { using (TransactionScope scope = new TransactionScope()) @@ -1677,7 +1677,7 @@ namespace bnhtrade.Core int accountJournalId = Account.AccountQuery.AccountJournalInsert(sqlConnectionString, accountJournalType, entryDate, currencyCode, amount); // 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); 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) { stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc); @@ -2073,8 +2073,7 @@ namespace bnhtrade.Core stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc); } } - - return WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId); + return new Stock.StockCreate().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) @@ -2083,7 +2082,7 @@ namespace bnhtrade.Core int stockJournalType = 2; 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) @@ -2463,7 +2462,7 @@ namespace bnhtrade.Core { 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) { @@ -2689,132 +2688,6 @@ namespace bnhtrade.Core 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> 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> list = new List>(); - - 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(stockId, journalDate, balance)); - } - if (sortAgeAscending == false) - { - list.Reverse(); - } - - return list; - } - else - { - return null; - } - } - } - } - } - catch (Exception) - { - throw; - } - } - public static int GetStockIdByStockJournalId(string sqlConnectionString, int stockJournalId) { try @@ -7766,111 +7639,4 @@ namespace bnhtrade.Core 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()); - } - } - } } \ No newline at end of file diff --git a/src/bnhtrade.Core/Test/COM/COMClassLib.cs b/src/bnhtrade.Core/Test/COM/COMClassLib.cs deleted file mode 100644 index 665e686..0000000 --- a/src/bnhtrade.Core/Test/COM/COMClassLib.cs +++ /dev/null @@ -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 - { - - } -} diff --git a/src/bnhtrade.Core/Test/Stock/Stock.cs b/src/bnhtrade.Core/Test/Stock/Stock.cs new file mode 100644 index 0000000..dd28865 --- /dev/null +++ b/src/bnhtrade.Core/Test/Stock/Stock.cs @@ -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 idList = new List(); + 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); + } + } +} diff --git a/src/bnhtrade.Core/bnhtrade.Core.csproj b/src/bnhtrade.Core/bnhtrade.Core.csproj index 540417d..7b31198 100644 --- a/src/bnhtrade.Core/bnhtrade.Core.csproj +++ b/src/bnhtrade.Core/bnhtrade.Core.csproj @@ -105,13 +105,17 @@ + + + - + + @@ -120,7 +124,7 @@ - + @@ -161,12 +165,12 @@ - + + - @@ -178,6 +182,7 @@ + diff --git a/src/bnhtrade.ScheduledTasks/Program.cs b/src/bnhtrade.ScheduledTasks/Program.cs index 16607a1..934cc6a 100644 --- a/src/bnhtrade.ScheduledTasks/Program.cs +++ b/src/bnhtrade.ScheduledTasks/Program.cs @@ -289,7 +289,7 @@ namespace bnhtradeScheduledTasks Console.WriteLine("<4> Test Import"); Console.WriteLine("<5> Test Logic"); Console.WriteLine("<6> Test SKU"); - Console.WriteLine("<7> Test COM Class"); + Console.WriteLine("<7> Test Stock"); Console.WriteLine("<8> Test xxxxxxx"); Console.WriteLine("<9> Detele Sku Transaction 'n'"); Console.WriteLine(); @@ -366,14 +366,7 @@ namespace bnhtradeScheduledTasks { Console.Clear(); - //string conString = ConfigurationManager.ConnectionStrings["bnhtradeDbConnString"].ConnectionString; - //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); + new bnhtrade.Core.Test.Stock.Stock(sqlConnectionString); Console.WriteLine("Done"); Console.WriteLine("Complete, press any key to continue...");