This commit is contained in:
2025-07-09 15:05:10 +01:00
parent c0f7f1a476
commit eb959dd6e2
15 changed files with 704 additions and 836 deletions

View File

@@ -70,7 +70,7 @@ namespace bnhtrade.ComTypeLib
public void StockJournalDelete(ConnectionCredential sqlConnCred, int stockJournalId)
{
Core.Stock.StockJournal.StockJournalDelete(sqlConnCred.ConnectionString, stockJournalId);
new Core.Logic.Inventory.StockJournalService().StockJournalDelete(stockJournalId);
}
public object ReconcileStockTransactions(ConnectionCredential sqlConnCred)
@@ -128,7 +128,7 @@ namespace bnhtrade.ComTypeLib
public bool StockJournalConsistencyCheck(ConnectionCredential sqlConnCred, int stockId)
{
return Core.Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnCred.ConnectionString, stockId, null);
return new Core.Logic.Inventory.StockJournalService().WIP_StockJournalConsistencyCheck(stockId, null);
}
public void SkuTransactionAdd(ConnectionCredential sqlConnCred, int quantity, string skuNumber, string transactionTypeCode, DateTime transactionDate)

View File

@@ -383,12 +383,47 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
return returnList;
}
public DateTime ReadJournalEntryDate(int journalId)
{
if (journalId <= 0)
{
throw new ArgumentException("Invalid journal ID provided.", nameof(journalId));
}
string sql = @"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));";
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", journalId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("Journal entry not found for AccountJournalID=" + journalId);
}
return DateTime.SpecifyKind((DateTime)obj, DateTimeKind.Utc);
}
}
/// <summary>
/// Test for locked journal entry
/// </summary>
/// <returns>False on locked journal entry</returns>
public bool ReadJournalIsLocked(int journalId)
{
if (journalId <= 0)
{
throw new ArgumentException("Invalid journal ID provided.", nameof(journalId));
}
string sql = @"
SELECT
tblAccountJournal.IsLocked

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using static bnhtrade.Core.Data.Database.Constants;
namespace bnhtrade.Core.Data.Database.Repository.Implementation
{
@@ -118,7 +119,7 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// insert journal posts into database
//new Data.Database.Stock
Core.Stock.StockJournal.StockJournalPostInsert(conn, stockId, stockJournalId, journalPosts, isNewStock);
StockJournalPostInsert(stockId, stockJournalId, journalPosts, isNewStock);
// consistency check
bool consistencyResult = true;
@@ -158,6 +159,30 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// Read
//
public int ReadJournalTypeIdByStockId(int stockId)
{
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
SELECT tblStockJournal.StockJournalTypeID
FROM tblStock INNER JOIN
tblStockJournal ON tblStock.StockJournalID = tblStockJournal.StockJournalID
WHERE (tblStock.StockID = @stockId);";
cmd.Parameters.AddWithValue("@stockId", stockId);
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
throw new Exception("No stock journal type found for StockID=" + stockId);
}
return (int)obj;
}
}
public int ReadStatusBalanceBySku(string sku, int statusId)
{
int statusBalance = new int();
@@ -255,6 +280,22 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
return statusBalance;
}
public int ReadJournalEntryCountByStockId(int stockId)
{
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
SELECT Count(tblStockJournal.StockJournalID) AS CountOfStockJournalID
FROM tblStockJournal
WHERE (((tblStockJournal.StockID)=@stockId));";
cmd.Parameters.AddWithValue("@stockId", stockId);
return (int)cmd.ExecuteScalar();
}
}
//
// update
//

View File

@@ -1,12 +1,13 @@
using bnhtrade.Core.Data.Database._BoilerPlate;
using bnhtrade.Core.Data.Database.Repository.Interface;
using Microsoft.Data.SqlClient;
using bnhtrade.Core.Data.Database.Repository.Interface;
using bnhtrade.Core.Data.Database.UnitOfWork;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using static System.Formats.Asn1.AsnWriter;
namespace bnhtrade.Core.Data.Database.Repository.Implementation
{
@@ -16,126 +17,424 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
{
}
public List<Model.Stock.Status> ReadStatus(List<int> statusIds = null, List<int> statusTypeIds = null)
{
var sqlBuilder = new SqlWhereBuilder();
var returnList = new List<Model.Stock.Status>();
//
// create
//
//build sql query
string sql = @"
SELECT [StockStatusID]
,[StatusCode]
,[StockStatus]
,[StockStatusTypeID]
,[Reference]
,[ForeignKeyID]
,[IsCreditOnly]
,[IsClosed]
,[RecordCreated]
FROM [e2A].[dbo].[tblStockStatus]
WHERE 1=1 ";
public int WIP_StockInsertSub(int productId, int conditionId, int accountTaxCodeId,
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId)
{
stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc);
// build the where statments
if (statusIds.Any())
// ensure account journal id hasn't already been added to stock table
int count = 0;
using (SqlCommand cmd = new SqlCommand(@"
SELECT Count(tblStock.StockID) AS CountOfID
FROM tblStock
WHERE (((tblStock.AccountJournalID)=@accountJouranlId));
", conn))
{
sqlBuilder.In("StockStatusID", statusIds, "AND");
}
if (statusTypeIds.Any())
{
sqlBuilder.In("StockStatusTypeID", statusTypeIds, "AND");
cmd.Parameters.AddWithValue("@accountJouranlId", accountJournalId);
count = (int)cmd.ExecuteScalar();
}
// append where string to the sql
if (sqlBuilder.IsSetSqlWhereString)
if (count == 1)
{
sql = sql + sqlBuilder.SqlWhereString;
throw new Exception("Add account journal entry already assigned to stock line.");
}
else if (count > 1)
{
throw new Exception("Houston we have a problem! An account journal entry is assigned to " + count + " stock lines.");
}
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
// ensure the debit for the account journal transaction is to an 'Asset' account type
using (SqlCommand cmd = new SqlCommand(@"
SELECT
Count(tblAccountJournalPost.AccountJournalPostID) AS CountOfAccountJournalPostID
FROM
(tblAccountJournalPost
INNER JOIN tblAccountChartOf
ON tblAccountJournalPost.AccountChartOfID = tblAccountChartOf.AccountChartOfID)
INNER JOIN tblAccountChartOfType
ON tblAccountChartOf.AccountChartOfTypeID = tblAccountChartOfType.AccountChartOfTypeID
WHERE
tblAccountJournalPost.AmountGbp>=0
AND tblAccountChartOfType.BasicType='Asset'
AND tblAccountJournalPost.AccountJournalID=@accountJournalId;
", conn))
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
if (sqlBuilder.ParameterListIsSet)
if ((int)cmd.ExecuteScalar() < 1)
{
sqlBuilder.AddParametersToSqlCommand(cmd);
throw new Exception("Supplied AccountJournal entry must debit an 'Asset' account type.");
}
}
using (SqlDataReader reader = cmd.ExecuteReader())
// get statusCreditId for stock journal type
int statusCreditId;
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournalType.StockStatusID_Credit
FROM
tblStockJournalType
WHERE
tblStockJournalType.StockJournalTypeID=@stockJournalTypeId
AND tblStockJournalType.StockStatusID_Credit Is Not Null;
", conn))
{
var typeDict = new StockRepository(_connection, _transaction).ReadStatusType();
cmd.Parameters.AddWithValue("@stockJournalTypeId", stockJournalTypeId);
while (reader.Read())
object obj = cmd.ExecuteScalar();
if (obj == null)
{
int statusId = reader.GetInt32(0);
int? statusCode = null;
if (!reader.IsDBNull(1)) { statusCode = reader.GetInt32(1); }
string stockStatus = reader.GetString(2);
int typeId = reader.GetInt32(3);
string reference = null;
if (!reader.IsDBNull(4)) { reference = reader.GetString(4); }
int? foreignKeyId = null;
if (!reader.IsDBNull(5)) { foreignKeyId = reader.GetInt32(5); }
bool isCreditOnly = reader.GetBoolean(6);
bool isClosed = reader.GetBoolean(7);
DateTime recordCreated = DateTime.SpecifyKind(reader.GetDateTime(8), DateTimeKind.Utc);
throw new Exception("Default credit status not set for StockJournalTypeID=" + stockJournalTypeId);
}
else
{
statusCreditId = (int)obj;
}
}
var newItem = new Model.Stock.Status(statusId
, statusCode
, stockStatus
, typeDict[typeId]
, reference
, foreignKeyId
, isCreditOnly
, isClosed
, recordCreated
);
// get/set an skuId
int skuId = new Logic.Inventory.SkuService().GetSkuId(productId, conditionId, accountTaxCodeId, true);
returnList.Add(newItem);
// add the entry to the stock table (minus stockJournalId)
int stockId = 0;
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblStock
(SkuID, AccountJournalID)
OUTPUT INSERTED.StockID
VALUES
(@skuId, @accountJournalId);
", conn))
{
cmd.Parameters.AddWithValue("@skuId", skuId);
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockId = (int)cmd.ExecuteScalar();
}
// insert stock journal entry
var journalPosts = new List<(int statusId, int quantity)>();
journalPosts.Add((statusDebitId, quantity));
journalPosts.Add((statusCreditId, (quantity * -1)));
int stockJournalId = Stock.StockJournal.StockJournalInsert(sqlConnectionString, stockJournalTypeId, stockId, journalPosts, stockJournalEntryDate, true);
// update the stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET StockJournalID=@stockJournalId
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
cmd.Parameters.AddWithValue("@stockId", stockId);
count = cmd.ExecuteNonQuery();
if (count < 1)
{
throw new Exception("New stock insert cancelled, failed to update StockJournalID");
}
}
scope.Complete();
return stockId;
}
public int WIP_StockInsertPurchase(int productId, int conditionId, int accountTaxCodeId, int accountJournalId, int quantity, int statusDebitId)
{
DateTime stockJournalEntryDate;
int stockJournalTypeId = 1;
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// retrive info from purchase invoice line/transaction
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));
", conn))
{
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
}
}
return WIP_StockInsertSub(productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
}
public int WIP_StockInsertOwnerIntroduced(decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
{
int accountJournalType = 3;
int stockJournalType = 2;
string currencyCode = "GBP";
return WIP_StockInsert(accountJournalType, stockJournalType, currencyCode, amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId);
}
//
// read
//
//
// update
//
//
// delete
//
private void WIP_StockDelete(int stockId)
{
int accountJournalType = 0;
int stockJournalType = 0;
// get stock and account types
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// ensure stockId is owner-introduced
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournal.StockJournalTypeID, tblAccountJournal.AccountJournalTypeID
FROM
(tblStock INNER JOIN tblAccountJournal
ON tblStock.AccountJournalID = tblAccountJournal.AccountJournalID)
INNER JOIN tblStockJournal
ON tblStock.StockJournalID = tblStockJournal.StockJournalID
WHERE
(((tblStock.StockID)=@stockId));
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
accountJournalType = reader.GetInt32(1);
stockJournalType = reader.GetInt32(0);
}
else
{
throw new Exception("Integrity check failed, cancelling StockDeleteOwnerIntroduced");
}
}
}
return returnList;
// check stock journal type is not restricted
// owner inventory introduced
if (stockJournalType == 2)
{
// no dramas
}
else
{
throw new Exception("Manual delete of this stock type is not supported, use the method that created it!");
}
public Dictionary<int, Model.Stock.StatusType> ReadStatusType()
// check there is only one stock journal entry for stock item
using (SqlCommand cmd = new SqlCommand(@"
SELECT Count(tblStockJournal.StockJournalID) AS CountOfStockJournalID
FROM tblStockJournal
WHERE (((tblStockJournal.StockID)=@stockId));
", conn))
{
var returnDict = new Dictionary<int, Model.Stock.StatusType>();
cmd.Parameters.AddWithValue("@stockId", stockId);
// get all account info before we start
var accountDict = new AccountCodeRepository(_connection, _transaction).ReadAccountCode();
int count = (int)cmd.ExecuteScalar();
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
if (count > 1)
{
cmd.CommandText = @"
SELECT [StockStatusTypeID]
,[StatusTypeName]
,[ForeignKeyType]
,[ReferenceType]
,[AccountChartOfID]
FROM [e2A].[dbo].[tblStockStatusType]";
cmd.Transaction = _transaction as SqlTransaction;
throw new Exception("Delete " + count + " stock journal entries (other than source entry), before peforming this operation.");
}
}
using (SqlDataReader reader = cmd.ExecuteReader())
// remove account journal entry
WIP_StockDeleteSubAccountJournalEntry(sqlConnectionString, stockId);
// remove stock
WIP_StockDeleteSub(sqlConnectionString, stockId);
scope.Complete();
}
}
private void WIP_StockDeleteSub(int stockId)
{
var accountIdDict = new Dictionary<int, int>();
while (reader.Read())
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
int statusTypeId = reader.GetInt32(0);
string name = reader.GetString(1);
string foreignKey = null;
if (!reader.IsDBNull(2)) { foreignKey = reader.GetString(2); }
string reference = null;
if (!reader.IsDBNull(3)) { reference = reader.GetString(3); }
uint accountId = (uint)reader.GetInt32(4);
conn.Open();
var statusType = new Model.Stock.StatusType(statusTypeId, name, foreignKey, reference, accountDict[(int)accountId]);
returnDict.Add(statusTypeId, statusType);
// check for accountJournalId on stock table
using (SqlCommand cmd = new SqlCommand(@"
SELECT AccountJournalID
FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("StockID=" + stockId + " does not exist.");
}
else if (obj == DBNull.Value)
{
// nothing to do all is good
}
else
{
int id = (int)obj;
if (id > 0)
{
throw new Exception("StockID=" + stockId + " remove account journal entry using method that created it first.");
}
}
}
return returnDict;
// get stockJournalId
int stockJournalId;
using (SqlCommand cmd = new SqlCommand(@"
SELECT StockJournalID
FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
try
{
stockJournalId = (int)cmd.ExecuteScalar();
}
catch
{
throw new Exception("Could not retrive StockJournalID for StockID=" + stockId);
}
}
// remove stockJournalId from stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET StockJournalID=NULL
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = cmd.ExecuteNonQuery();
if (count != 2) // we need to count the LastUpdated trigger!
{
throw new Exception("Failed to remove StockJournalID from stock table StockID=" + stockId);
}
}
// delete stock journal entry
new Core.Logic.Inventory.StockJournalService().StockJournalDelete(stockJournalId);
// delete stock table entry
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = cmd.ExecuteNonQuery();
if (count != 1)
{
throw new Exception("StockID = " + stockId + " delete failed");
}
else
{
scope.Complete();
}
}
}
}
// to be used by other methods within a transaction scope
private void WIP_StockDeleteSubAccountJournalEntry(int stockId)
{
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
var trans = conn.BeginTransaction();
// get the account journal id
int accountJournalId = 0;
using (SqlCommand cmd = new SqlCommand(@"
SELECT AccountJournalID
FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Transaction = trans;
cmd.Parameters.AddWithValue("@stockId", stockId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("StockID=" + stockId + " does not exist.");
}
else if (obj == DBNull.Value)
{
throw new Exception("AccountJournalID not found for StockID=" + stockId);
}
else
{
accountJournalId = (int)obj;
}
}
// remove entry from stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET AccountJournalID=NULL
WHERE StockID=@stockId;
", conn))
{
cmd.Transaction = trans;
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = cmd.ExecuteNonQuery();
if (count != 2) // must include last modified trigger
{
throw new Exception("Failed to set AccountJournalID to NULL on stock table for StockID=" + stockId);
}
}
// delete account journal entry
new Data.Database.Repository.Implementation.AccountJournalRepository(conn, trans).DeleteJournal(accountJournalId);
trans.Commit();
}
}
public void WIP_StockDeletePurchase(int stockId)
{
WIP_StockDeleteSub(sqlConnectionString, stockId);
}
public void WIP_StockDeleteOwnerIntroduced(int stockId)
{
WIP_StockDelete(sqlConnectionString, stockId);
}
}
}

View File

@@ -0,0 +1,141 @@
using bnhtrade.Core.Data.Database._BoilerPlate;
using bnhtrade.Core.Data.Database.Repository.Interface;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.Repository.Implementation
{
internal class StockStatusRepository : _Base, IStockStatusRepository
{
public StockStatusRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
{
}
public List<Model.Stock.Status> ReadStatus(List<int> statusIds = null, List<int> statusTypeIds = null)
{
var sqlBuilder = new SqlWhereBuilder();
var returnList = new List<Model.Stock.Status>();
//build sql query
string sql = @"
SELECT [StockStatusID]
,[StatusCode]
,[StockStatus]
,[StockStatusTypeID]
,[Reference]
,[ForeignKeyID]
,[IsCreditOnly]
,[IsClosed]
,[RecordCreated]
FROM [e2A].[dbo].[tblStockStatus]
WHERE 1=1 ";
// build the where statments
if (statusIds.Any())
{
sqlBuilder.In("StockStatusID", statusIds, "AND");
}
if (statusTypeIds.Any())
{
sqlBuilder.In("StockStatusTypeID", statusTypeIds, "AND");
}
// append where string to the sql
if (sqlBuilder.IsSetSqlWhereString)
{
sql = sql + sqlBuilder.SqlWhereString;
}
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
if (sqlBuilder.ParameterListIsSet)
{
sqlBuilder.AddParametersToSqlCommand(cmd);
}
using (SqlDataReader reader = cmd.ExecuteReader())
{
var typeDict = new StockStatusRepository(_connection, _transaction).ReadStatusType();
while (reader.Read())
{
int statusId = reader.GetInt32(0);
int? statusCode = null;
if (!reader.IsDBNull(1)) { statusCode = reader.GetInt32(1); }
string stockStatus = reader.GetString(2);
int typeId = reader.GetInt32(3);
string reference = null;
if (!reader.IsDBNull(4)) { reference = reader.GetString(4); }
int? foreignKeyId = null;
if (!reader.IsDBNull(5)) { foreignKeyId = reader.GetInt32(5); }
bool isCreditOnly = reader.GetBoolean(6);
bool isClosed = reader.GetBoolean(7);
DateTime recordCreated = DateTime.SpecifyKind(reader.GetDateTime(8), DateTimeKind.Utc);
var newItem = new Model.Stock.Status(statusId
, statusCode
, stockStatus
, typeDict[typeId]
, reference
, foreignKeyId
, isCreditOnly
, isClosed
, recordCreated
);
returnList.Add(newItem);
}
}
}
return returnList;
}
public Dictionary<int, Model.Stock.StatusType> ReadStatusType()
{
var returnDict = new Dictionary<int, Model.Stock.StatusType>();
// get all account info before we start
var accountDict = new AccountCodeRepository(_connection, _transaction).ReadAccountCode();
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = @"
SELECT [StockStatusTypeID]
,[StatusTypeName]
,[ForeignKeyType]
,[ReferenceType]
,[AccountChartOfID]
FROM [e2A].[dbo].[tblStockStatusType]";
cmd.Transaction = _transaction as SqlTransaction;
using (SqlDataReader reader = cmd.ExecuteReader())
{
var accountIdDict = new Dictionary<int, int>();
while (reader.Read())
{
int statusTypeId = reader.GetInt32(0);
string name = reader.GetString(1);
string foreignKey = null;
if (!reader.IsDBNull(2)) { foreignKey = reader.GetString(2); }
string reference = null;
if (!reader.IsDBNull(3)) { reference = reader.GetString(3); }
uint accountId = (uint)reader.GetInt32(4);
var statusType = new Model.Stock.StatusType(statusTypeId, name, foreignKey, reference, accountDict[(int)accountId]);
returnDict.Add(statusTypeId, statusType);
}
}
}
return returnDict;
}
}
}

View File

@@ -11,6 +11,7 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
int AccountJournalInsert(int journalTypeId, DateTime entryDate, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false);
bool AccountJournalPostInsert(int journalId, DateTime entryDate, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0);
Dictionary<int, Core.Model.Account.Journal> ReadJournal(List<int> journalIdList);
DateTime ReadJournalEntryDate(int journalId);
bool ReadJournalIsLocked(int journalId);
Dictionary<int, Model.Account.JournalType> ReadJournalType(List<int> journalTypeIds = null, List<string> typeTitles = null);
bool DeleteJournal(int accountJournalId);

View File

@@ -8,9 +8,11 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
{
internal interface IStockJournalRepository
{
int ReadJournalTypeIdByStockId(int stockId);
int ReadStatusBalanceBySku(string sku, int statusId);
int ReadStatusBalanceByStockNumber(int stockNumber, int statusId);
int ReadStatusBalanceByStockId(int stockId, int statusId);
int ReadJournalEntryCountByStockId(int stockId);
Dictionary<string, int> ReadStatusBalanceByStatusId(int statusId);
void StockJournalDelete(int stockJournalId);
void StockJournalPostInsert(int stockId, int stockJournalId, List<(int statusId, int quantity)> journalPosts, bool isNewStock = false);

View File

@@ -8,7 +8,14 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
{
internal interface IStockRepository
{
List<Model.Stock.Status> ReadStatus(List<int> statusIds = null, List<int> statusTypeIds = null);
Dictionary<int, Model.Stock.StatusType> ReadStatusType();
int WIP_StockInsertSub(int productId, int conditionId, int accountTaxCodeId,
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId);
int WIP_StockInsertPurchase(int productId, int conditionId, int accountTaxCodeId, int accountJournalId, int quantity, int statusDebitId);
int WIP_StockInsertOwnerIntroduced(decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId);
void WIP_StockDelete(int stockId);
void WIP_StockDeleteSub(int stockId);
void WIP_StockDeleteSubAccountJournalEntry(int stockId);
void WIP_StockDeletePurchase(int stockId);
void WIP_StockDeleteOwnerIntroduced(int stockId);
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.Repository.Interface
{
internal interface IStockStatusRepository
{
List<Model.Stock.Status> ReadStatus(List<int> statusIds = null, List<int> statusTypeIds = null);
Dictionary<int, Model.Stock.StatusType> ReadStatusType();
}
}

View File

@@ -20,8 +20,9 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
IAccountJournalRepository JournalRepository { get; }
ISequenceGenerator SequenceGenerator { get; }
ISkuRepository SkuRepository { get; }
IStockJournalRepository StockJournalRepository { get; }
IStockRepository StockRepository { get; }
IStockJournalRepository StockJournalRepository { get; }
IStockStatusRepository StockStatusRepository { get; }
// Methods to manage the transaction
void Commit();

View File

@@ -28,8 +28,9 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
private IAccountJournalRepository _journalRepository;
private ISequenceGenerator _sequenceGenerator;
private ISkuRepository _skuRepository;
private IStockJournalRepository _stockJournalRespository;
private IStockRepository _stockRepository;
private IStockJournalRepository _stockJournalRespository;
private IStockStatusRepository _stockStatusRepository;
internal UnitOfWork()
{
@@ -158,6 +159,18 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
}
}
public IStockRepository StockRepository
{
get
{
if (_stockRepository == null)
{
_stockRepository = new StockRepository(_connection, _transaction);
}
return _stockRepository;
}
}
public IStockJournalRepository StockJournalRepository
{
get
@@ -170,15 +183,15 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
}
}
public IStockRepository StockRepository
public IStockStatusRepository StockStatusRepository
{
get
{
if (_stockRepository == null)
if (_stockStatusRepository == null)
{
_stockRepository = new StockRepository(_connection, _transaction);
_stockStatusRepository = new StockStatusRepository(_connection, _transaction);
}
return _stockRepository;
return _stockStatusRepository;
}
}

View File

@@ -21,15 +21,14 @@ namespace bnhtrade.Core.Logic.Inventory
{
throw new ArgumentException("Stock journal ID must be greater than zero", nameof(stockJournalId));
}
bool result = uow.StockJournalRepository.DeleteStockJournal(stockJournalId);
uow.StockJournalRepository.StockJournalDelete(stockJournalId);
CommitIfOwned(uow);
if (!result)
{
throw new InvalidOperationException($"Failed to delete stock journal with ID {stockJournalId}");
}
});
}
// can be used before commiting an sql insert, update or delete to the stock journal to ensure a status does not fall below 0
// (unless the status is enabled to do so)
// set empty list or statusIdEffected to null to check entier stock entries for consistency
public bool WIP_StockJournalConsistencyCheck(int stockId, List<int> statusIdEffected = null)
{
return WithUnitOfWork(uow =>

View File

@@ -18,187 +18,30 @@ namespace bnhtrade.Core.Logic.Inventory
private int WIP_StockInsert(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())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
return WithUnitOfWork(uow =>
{
conn.Open();
// add account journal entry
int accountJournalId = new Logic.Account.JournalService().JournalInsert(accountJournalType, entryDate, currencyCode, amount);
int accountJournalId = new Logic.Account.JournalService(uow).JournalInsert(accountJournalType, entryDate, currencyCode, amount);
// make the stock insert
int stockId = WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId,
int stockId = uow.StockRepository.WIP_StockInsertSub(productId, conditionId, accountTaxCodeId,
accountJournalId, stockJournalType, entryDate, quantity, debitStatusId);
scope.Complete();
CommitIfOwned(uow);
return stockId;
}
}
private int WIP_StockInsertSub(int productId, int conditionId, int accountTaxCodeId,
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId)
{
stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc);
// ensure account journal id hasn't already been added to stock table
int count = 0;
using (SqlCommand cmd = new SqlCommand(@"
SELECT Count(tblStock.StockID) AS CountOfID
FROM tblStock
WHERE (((tblStock.AccountJournalID)=@accountJouranlId));
", conn))
{
cmd.Parameters.AddWithValue("@accountJouranlId", accountJournalId);
count = (int)cmd.ExecuteScalar();
}
if (count == 1)
{
throw new Exception("Add account journal entry already assigned to stock line.");
}
else if (count > 1)
{
throw new Exception("Houston we have a problem! An account journal entry is assigned to " + count + " stock lines.");
}
// ensure the debit for the account journal transaction is to an 'Asset' account type
using (SqlCommand cmd = new SqlCommand(@"
SELECT
Count(tblAccountJournalPost.AccountJournalPostID) AS CountOfAccountJournalPostID
FROM
(tblAccountJournalPost
INNER JOIN tblAccountChartOf
ON tblAccountJournalPost.AccountChartOfID = tblAccountChartOf.AccountChartOfID)
INNER JOIN tblAccountChartOfType
ON tblAccountChartOf.AccountChartOfTypeID = tblAccountChartOfType.AccountChartOfTypeID
WHERE
tblAccountJournalPost.AmountGbp>=0
AND tblAccountChartOfType.BasicType='Asset'
AND tblAccountJournalPost.AccountJournalID=@accountJournalId;
", conn))
{
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
if ((int)cmd.ExecuteScalar() < 1)
{
throw new Exception("Supplied AccountJournal entry must debit an 'Asset' account type.");
}
}
// get statusCreditId for stock journal type
int statusCreditId;
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournalType.StockStatusID_Credit
FROM
tblStockJournalType
WHERE
tblStockJournalType.StockJournalTypeID=@stockJournalTypeId
AND tblStockJournalType.StockStatusID_Credit Is Not Null;
", conn))
{
cmd.Parameters.AddWithValue("@stockJournalTypeId", stockJournalTypeId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("Default credit status not set for StockJournalTypeID=" + stockJournalTypeId);
}
else
{
statusCreditId = (int)obj;
}
}
// get/set an skuId
int skuId = new Logic.Inventory.SkuService().GetSkuId(productId, conditionId, accountTaxCodeId, true);
// add the entry to the stock table (minus stockJournalId)
int stockId = 0;
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblStock
(SkuID, AccountJournalID)
OUTPUT INSERTED.StockID
VALUES
(@skuId, @accountJournalId);
", conn))
{
cmd.Parameters.AddWithValue("@skuId", skuId);
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockId = (int)cmd.ExecuteScalar();
}
// insert stock journal entry
var journalPosts = new List<(int statusId, int quantity)>();
journalPosts.Add((statusDebitId, quantity));
journalPosts.Add((statusCreditId, (quantity * -1)));
int stockJournalId = Stock.StockJournal.StockJournalInsert(sqlConnectionString, stockJournalTypeId, stockId, journalPosts, stockJournalEntryDate, true);
// update the stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET StockJournalID=@stockJournalId
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
cmd.Parameters.AddWithValue("@stockId", stockId);
count = cmd.ExecuteNonQuery();
if (count < 1)
{
throw new Exception("New stock insert cancelled, failed to update StockJournalID");
}
}
scope.Complete();
return stockId;
});
}
private void WIP_StockDelete(int stockId)
{
int accountJournalType = 0;
int stockJournalType = 0;
// get stock and account types
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
WithUnitOfWork(uow =>
{
conn.Open();
// ensure stockId is owner-introduced
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournal.StockJournalTypeID, tblAccountJournal.AccountJournalTypeID
FROM
(tblStock INNER JOIN tblAccountJournal
ON tblStock.AccountJournalID = tblAccountJournal.AccountJournalID)
INNER JOIN tblStockJournal
ON tblStock.StockJournalID = tblStockJournal.StockJournalID
WHERE
(((tblStock.StockID)=@stockId));
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
accountJournalType = reader.GetInt32(1);
stockJournalType = reader.GetInt32(0);
}
else
{
throw new Exception("Integrity check failed, cancelling StockDeleteOwnerIntroduced");
}
}
}
stockJournalType = uow.StockJournalRepository.ReadJournalTypeIdByStockId(stockId);
// check stock journal type is not restricted
// owner inventory introduced
@@ -211,22 +54,13 @@ namespace bnhtrade.Core.Logic.Inventory
throw new Exception("Manual delete of this stock type is not supported, use the method that created it!");
}
// check there is only one stock journal entry for stock item
using (SqlCommand cmd = new SqlCommand(@"
SELECT Count(tblStockJournal.StockJournalID) AS CountOfStockJournalID
FROM tblStockJournal
WHERE (((tblStockJournal.StockID)=@stockId));
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = (int)cmd.ExecuteScalar();
// check there is only one stock journal entry for stock item (i.e. the source entry)
int count = uow.StockJournalRepository.ReadJournalEntryCountByStockId(stockId);
if (count > 1)
{
throw new Exception("Delete " + count + " stock journal entries (other than source entry), before peforming this operation.");
}
}
// remove account journal entry
WIP_StockDeleteSubAccountJournalEntry(sqlConnectionString, stockId);
@@ -235,7 +69,23 @@ namespace bnhtrade.Core.Logic.Inventory
WIP_StockDeleteSub(sqlConnectionString, stockId);
scope.Complete();
}
});
}
private void WIP_StockDeleteSub(int stockId)
@@ -312,7 +162,7 @@ namespace bnhtrade.Core.Logic.Inventory
}
// delete stock journal entry
Core.Stock.StockJournal.StockJournalDelete(sqlConnectionString, stockJournalId);
new Core.Logic.Inventory.StockJournalService().StockJournalDelete(stockJournalId);
// delete stock table entry
using (SqlCommand cmd = new SqlCommand(@"
@@ -401,23 +251,14 @@ namespace bnhtrade.Core.Logic.Inventory
DateTime stockJournalEntryDate;
int stockJournalTypeId = 1;
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
return WithUnitOfWork(uow =>
{
conn.Open();
// retrive info from purchase invoice line/transaction
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));
", conn))
{
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
}
}
return WIP_StockInsertSub(productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
stockJournalEntryDate = uow.JournalRepository.ReadJournalEntryDate(accountJournalId);
int result = uow.StockRepository.WIP_StockInsertSub(
productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
CommitIfOwned(uow);
return result;
});
}
public int WIP_StockInsertOwnerIntroduced(decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)

View File

@@ -100,7 +100,7 @@ namespace bnhtrade.Core.Logic.Stock
var sku = readSku.BySkuNumber(statusTransaction.SkuNumber);
// get the status obj
var status = uow.StockRepository.ReadStatus(new List<int> { statusTransaction.StockStatusId })[0];
var status = uow.StockStatusRepository.ReadStatus(new List<int> { statusTransaction.StockStatusId })[0];
return new Model.Stock.StatusBalance(status, sku, entryList);
}

View File

@@ -233,532 +233,6 @@ namespace bnhtrade.Core
}
}
namespace Stock
{
public class StockJournal : UnitOfWorkBase
{
public static void StockJournalDelete(string sqlConnectionString, int stockJournalId)
{
using (TransactionScope scope = new TransactionScope())
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// get date for journal entry
DateTime entryDate;
int stockId;
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblStockJournal.EntryDate, StockID
FROM tblStockJournal
WHERE (((tblStockJournal.StockJournalID)=@stockJournalId));
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
entryDate = DateTime.SpecifyKind(reader.GetDateTime(0), DateTimeKind.Utc);
stockId = reader.GetInt32(1);
}
else
{
throw new Exception("StockJournalID=" + stockJournalId + " does not exist!");
}
}
}
// is consistancy check required
bool consistancyCheck;
// build list of debits that are to be deleted
var debitList = new List<int>();
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblStockJournalPost.StockStatusID
FROM tblStockJournalPost
WHERE (((tblStockJournalPost.StockJournalID)=@stockJournalId) AND ((tblStockJournalPost.Quantity)>0));
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
debitList.Add(reader.GetInt32(0));
}
}
else
{
throw new Exception("StockJournalID=" + stockJournalId + " has no debits with quantity greater than zero!");
}
}
}
// check no credits for stockId & debit combination have been made since delete entry
string stringSql = @"
SELECT
tblStockJournal.EntryDate
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
tblStockJournal.StockID=@stockId
AND tblStockJournalPost.Quantity<0
AND EntryDate>=@entryDate
AND (";
bool firstDone = false;
foreach (var item in debitList)
{
if (firstDone)
{
stringSql = stringSql + " OR ";
}
stringSql = stringSql + "tblStockJournalPost.StockStatusID=" + item;
firstDone = true;
}
stringSql = stringSql + ");";
using (SqlCommand cmd = new SqlCommand(stringSql, conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
consistancyCheck = true;
}
else
{
consistancyCheck = false;
}
}
}
// delete the posts
StockJournalPostDelete(conn, stockJournalId);
// delete journal entry
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM tblStockJournal
WHERE StockJournalID=@stockJournalId;
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
int count = cmd.ExecuteNonQuery();
if (count != 1)
{
throw new Exception("Failed to delete stock journal header.");
}
}
// consistanct check
bool consistencyResult = true;
if (consistancyCheck)
{
// run check
consistencyResult = Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnectionString, stockId, debitList);
}
if (consistencyResult)
{
// commit
scope.Complete();
}
else
{
throw new Exception("Unable to delete stock journal entry, consistancy check failed.");
}
}
}
private static void StockJournalPostInsert(SqlConnection conn, int stockId, int stockJournalId,
List<(int statusId, int quantity)> journalPosts, bool isNewStock = false)
{
//checks
if (journalPosts.Count > 2)
{
// I have purposely made the code to accept split transaction incase of future requirements, however, db design is simpler this way.
throw new Exception("Stock journal does not currently support split transactions (greater than two posts)." + journalPosts.Count + " number posts attempted.");
}
else if (journalPosts.Count < 2)
{
// list not long enough
throw new Exception("Stock journal entry requires minium of two posts, entry of " + journalPosts.Count + " number posts attempted.");
}
if (journalPosts.Sum(item => item.quantity) != 0)
{
// credits and debits do not match
throw new Exception("Sum of credits and debits do not resolve to zero.");
}
// group credits and debits by status
var dicStatusQty = new Dictionary<int, int>();
foreach (var post in journalPosts)
{
if (dicStatusQty.ContainsKey(post.statusId) == false)
{
dicStatusQty.Add(post.statusId, post.quantity);
}
else
{
dicStatusQty[post.statusId] = dicStatusQty[post.statusId] + post.quantity;
}
}
// get isCreditOnly for each status
var dicStatusIsCreditOnly = new Dictionary<int, bool>();
foreach (var item in dicStatusQty)
{
using (SqlCommand cmd = new SqlCommand(@"
SELECT IsCreditOnly FROM tblStockStatus WHERE StockStatusID=@statusId;
", conn))
{
cmd.Parameters.AddWithValue("@statusId", item.Key);
dicStatusIsCreditOnly.Add(item.Key, (bool)cmd.ExecuteScalar());
}
}
// check there is only one IsCreditOnly in list and it is allowed in this instance
int isCreditOnlyCount = 0;
foreach (var item in dicStatusIsCreditOnly)
{
if (item.Value)
{
isCreditOnlyCount = isCreditOnlyCount + 1;
}
}
if (isNewStock == false && isCreditOnlyCount > 0)
{
throw new Exception("Attempted credit or debit to 'Is Credit Only' status not allowed, in this instance.");
}
if (isNewStock == true && isCreditOnlyCount != 1)
{
throw new Exception("StockID=" + stockId + ", each stock line must have only have one IsCreditOnly=True status assigned to it.");
}
// ensure debit (or zero credit) isn't made to credit only status
// need to do this check via original post list (i.e. journalPosts)
foreach (var post in journalPosts)
{
// debit check
if (post.quantity >= 0)
{
if (dicStatusIsCreditOnly[post.statusId] == true)
{
throw new Exception("Cannot make debit, or zero quantity credit, to credit only status. StatusID=" + post.statusId);
}
}
}
// balance check for any credits (that are not isCreditOnly=true)
foreach (var item in dicStatusQty)
{
if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false)
{
int quantity = new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStockId(stockId, item.Key);
if (quantity + item.Value < 0)
{
throw new Exception("Credit status balance check failed. Available balance " + quantity + ", attempted credit " + item.Value * -1 + ".");
}
}
}
// get this far...
// insert journal posts into database
foreach (var post in journalPosts)
{
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblStockJournalPost ( StockJournalID, StockStatusID, Quantity )
VALUES ( @StockJournalId, @stockStatudId, @quantity );
", conn))
{
cmd.Parameters.AddWithValue("@StockJournalId", stockJournalId);
cmd.Parameters.AddWithValue("@stockStatudId", post.statusId);
cmd.Parameters.AddWithValue("@quantity", post.quantity);
// execute
cmd.ExecuteNonQuery();
}
}
}
private static void StockJournalPostDelete(SqlConnection conn, int stockJournalId)
{
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM tblStockJournalPost
WHERE StockJournalID=@stockJournalId
", conn))
{
cmd.Parameters.AddWithValue("@StockJournalId", stockJournalId);
// execute
cmd.ExecuteNonQuery();
// the calling method must compete any transaction-scope on the connection
}
}
// can be used before commiting an sql insert, update or delete to the stock journal to ensure a status does not fall below 0
// (unless the status is enabled to do so)
// set empty list or statusIdEffected to null to check entier stock entries for consistency
public static bool WIP_StockJournalConsistencyCheck(string sqlConnectionString, int stockId, List<int> statusIdEffected = null)
{
if (statusIdEffected == null)
{
statusIdEffected = new List<int>();
}
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// if no list supplied, build list of all used status' for stockId
if (statusIdEffected.Count == 0)
{
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournalPost.StockStatusID
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
tblStockJournal.StockID=@stockId
GROUP BY
tblStockJournalPost.StockStatusID;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
statusIdEffected.Add(reader.GetInt32(0));
}
}
else
{
throw new Exception("No stock journal entries exist for StockID=" + stockId);
}
}
}
}
// build the sql string to build creditCreate bool
var dicStatusCreditOnly = new Dictionary<int, bool>();
string sqlString = @"
SELECT
tblStockStatus.StockStatusID, tblStockStatus.IsCreditOnly
FROM
tblStockStatus ";
for (var i = 0; i < statusIdEffected.Count; i++)
{
if (i == 0)
{
sqlString = sqlString + " WHERE tblStockStatus.StockStatusID=" + statusIdEffected[i];
}
else
{
sqlString = sqlString + " OR tblStockStatus.StockStatusID=" + statusIdEffected[i];
}
//if (i == (statusIdEffected.Count - 1))
//{
// sqlString = sqlString + ";";
//}
}
sqlString = sqlString + " GROUP BY tblStockStatus.StockStatusID, tblStockStatus.IsCreditOnly;";
// run query & build dictionaries
using (SqlCommand cmd = new SqlCommand(sqlString, conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
dicStatusCreditOnly.Add(reader.GetInt32(0), reader.GetBoolean(1));
while (reader.Read())
{
dicStatusCreditOnly.Add(reader.GetInt32(0), reader.GetBoolean(1));
}
}
else
{
throw new Exception("Error, no journal entries found for StockID=" + stockId);
}
}
}
// check integrity of supplied statusIds
foreach (int statusId in statusIdEffected)
{
if (!dicStatusCreditOnly.ContainsKey(statusId))
{
throw new Exception("Supplied StatusId (" + statusId + ") doesn't exist for StockId=" + stockId);
}
}
// loop through each statudId and check integrity, if createdEnabled=false
foreach (int statudId in statusIdEffected)
{
if (dicStatusCreditOnly[statudId] == false)
{
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockJournal.EntryDate, tblStockJournalPost.Quantity
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
tblStockJournal.StockID=@stockId
AND tblStockJournalPost.StockStatusID=@statudId
ORDER BY
tblStockJournal.EntryDate;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
cmd.Parameters.AddWithValue("@statudId", statudId);
using (SqlDataReader reader = cmd.ExecuteReader())
{
// read first line into variables
reader.Read();
int quantity = reader.GetInt32(1);
DateTime entryDate = DateTime.SpecifyKind(reader.GetDateTime(0), DateTimeKind.Utc);
while (true)
{
// compare to next values
if (reader.Read())
{
DateTime nextEntryDate = DateTime.SpecifyKind(reader.GetDateTime(0), DateTimeKind.Utc);
// don't check quantites for transactions on same date
if (entryDate == nextEntryDate)
{
entryDate = nextEntryDate;
quantity = quantity + reader.GetInt32(1);
}
// check if dates are different
else
{
if (quantity < 0)
{
return false;
}
else
{
entryDate = nextEntryDate;
quantity = quantity + reader.GetInt32(1);
}
}
}
// end if no records, check quantity
else
{
if (quantity < 0)
{
return false;
}
break;
}
}
}
}
}
}
}
// get this far, all good
return true;
}
public static int StockTransactionTypeIdInsert(string sqlConnectionString, int stockJournalTypeId, string typeCode, bool transScopeSuppress = true)
{
int transactionTypeId;
var scopeOption = new TransactionScopeOption();
if (transScopeSuppress)
{
scopeOption = TransactionScopeOption.Suppress;
}
else
{
scopeOption = TransactionScopeOption.Required;
}
using (TransactionScope scope = new TransactionScope(scopeOption))
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
//check to see if type already exists, return id of that if it does
using (SqlCommand cmd = new SqlCommand(@"
SELECT
tblStockSkuTransactionType.StockSkuTransactionTypeID
FROM
tblStockSkuTransactionType
WHERE
tblStockSkuTransactionType.StockJournalTypeID=@stockJournalTypeId
AND tblStockSkuTransactionType.TypeCode=@typeCode;
", conn))
{
cmd.Parameters.AddWithValue("@typeCode", typeCode);
cmd.Parameters.AddWithValue("@stockJournalTypeId", stockJournalTypeId);
object obj = cmd.ExecuteScalar();
if (!(obj == null || obj == DBNull.Value))
{
return (int)obj;
}
}
// insert new and retrive new value
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblStockSkuTransactionType
( TypeName, StockJournalTypeID, TypeCode )
OUTPUT
INSERTED.StockSkuTransactionTypeID
VALUES
( @typeName, @stockJournalTypeId, @typeCode );
", conn))
{
cmd.Parameters.AddWithValue("@typeName", typeCode);
cmd.Parameters.AddWithValue("@typeCode", typeCode);
cmd.Parameters.AddWithValue("@stockJournalTypeId", stockJournalTypeId);
transactionTypeId = (int)cmd.ExecuteScalar();
}
scope.Complete();
}
return transactionTypeId;
}
}
}
public class MiscFunction
{
public static string TraceMessage(