diff --git a/src/bnhtrade.ComTypeLib/Account/Account.cs b/src/bnhtrade.ComTypeLib/Account/Account.cs
index bf784bc..bba9393 100644
--- a/src/bnhtrade.ComTypeLib/Account/Account.cs
+++ b/src/bnhtrade.ComTypeLib/Account/Account.cs
@@ -35,13 +35,13 @@ namespace bnhtrade.ComTypeLib
public int AccountJournalInsert(ConnectionCredential sqlConnCred, int journalTypeId, DateTime entryDate,
string currencyCode, [MarshalAs(UnmanagedType.Currency)] decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
{
- return new Core.Logic.Account.Journal().AccountJournalInsert(journalTypeId, entryDate,
+ return new Core.Logic.Account.JournalService().JournalInsert(journalTypeId, entryDate,
currencyCode, amount, debitAccountId, creditAccountId, lockEntry);
}
public bool AccountJournalDelete(ConnectionCredential sqlConnCred, int accountJournalId)
{
- return new Core.Logic.Account.Journal().AccountJournalDelete(accountJournalId);
+ return new Core.Logic.Account.JournalService().JournalDelete(accountJournalId);
}
[return: MarshalAs(UnmanagedType.Currency)]
diff --git a/src/bnhtrade.ComTypeLib/Purchase/PurchaseLine.cs b/src/bnhtrade.ComTypeLib/Purchase/PurchaseLine.cs
index b4d7979..600a18f 100644
--- a/src/bnhtrade.ComTypeLib/Purchase/PurchaseLine.cs
+++ b/src/bnhtrade.ComTypeLib/Purchase/PurchaseLine.cs
@@ -35,32 +35,30 @@ namespace bnhtrade.ComTypeLib.Purchase
string currencyCode, [MarshalAs(UnmanagedType.Currency)] decimal amountNet,
DateTime entryDate)
{
- Core.Purchase.PurchaseQuery.WIP_PurchaseLineTransactionNetInsert(sqlConnCred.ConnectionString,
- purchaseLineId, currencyCode, amountNet, entryDate);
+ new Core.Logic.Purchase.PurchaseService().WIP_PurchaseLineTransactionNetInsert(purchaseLineId, currencyCode, amountNet, entryDate);
}
public void PurchaseLineTransactionNetUpdate(ConnectionCredential sqlConnCred, int accountJouranlId,
string currencyCode, [MarshalAs(UnmanagedType.Currency)] decimal amountNet, int debitAccountId)
{
- Core.Purchase.PurchaseQuery.WIP_PurchaseLineTransactionNetUpdate(sqlConnCred.ConnectionString,
- accountJouranlId, currencyCode, amountNet, debitAccountId);
+ new Core.Logic.Purchase.PurchaseService().WIP_PurchaseLineTransactionNetUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId);
}
public void PurchaseLineTransactionDelete(ConnectionCredential sqlConnCred, int purchaseLineId, int accountJournalId)
{
- Core.Purchase.PurchaseQuery.WIP_PurchaseLineTransactionDelete(sqlConnCred.ConnectionString, purchaseLineId, accountJournalId);
+ new Core.Logic.Purchase.PurchaseService().WIP_PurchaseLineTransactionDelete(purchaseLineId, accountJournalId);
}
public int PurchaseLineTransactionStockInsert(ConnectionCredential sqlConnCred, int accountJournalId,
string currencyCode, [MarshalAs(UnmanagedType.Currency)] decimal amount, int quantity, int productId, int conditionId,
int accountTaxCodeId, int stockDebitStatusId)
{
- return Core.Stock.StockCreate.WIP_StockInsertPurchase(sqlConnCred.ConnectionString, productId, conditionId, accountTaxCodeId, accountJournalId, quantity, stockDebitStatusId);
+ return new Core.Logic.Inventory.StockService().WIP_StockInsertPurchase(productId, conditionId, accountTaxCodeId, accountJournalId, quantity, stockDebitStatusId);
}
public void PurchaseLineTransactionStockDelete(ConnectionCredential sqlConnCred, int stockId)
{
- Core.Stock.StockCreate.WIP_StockDeletePurchase(sqlConnCred.ConnectionString, stockId);
+ new Core.Logic.Inventory.StockService().WIP_StockDeletePurchase(stockId);
}
}
}
diff --git a/src/bnhtrade.ComTypeLib/Stock/Stock.cs b/src/bnhtrade.ComTypeLib/Stock/Stock.cs
index dde191c..e9d6fe9 100644
--- a/src/bnhtrade.ComTypeLib/Stock/Stock.cs
+++ b/src/bnhtrade.ComTypeLib/Stock/Stock.cs
@@ -43,22 +43,22 @@ namespace bnhtrade.ComTypeLib
{
public int StockInsertPurchase(ConnectionCredential sqlConnCred, int productId, int conditionId, int accountTaxCodeId, int accountJournalId, int quantity, int statusDebitId)
{
- return Core.Stock.StockCreate.WIP_StockInsertPurchase(sqlConnCred.ConnectionString, productId, conditionId, accountTaxCodeId, accountJournalId, quantity, statusDebitId);
+ return new Core.Logic.Inventory.StockService().WIP_StockInsertPurchase(productId, conditionId, accountTaxCodeId, accountJournalId, quantity, statusDebitId);
}
public int StockInsertOwnerIntroduced(ConnectionCredential sqlConnCred, [MarshalAs(UnmanagedType.Currency)] decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
{
- return Core.Stock.StockCreate.WIP_StockInsertOwnerIntroduced(sqlConnCred.ConnectionString, amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId);
+ return new Core.Logic.Inventory.StockService().WIP_StockInsertOwnerIntroduced(amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId);
}
public void StockDeletePurchase(ConnectionCredential sqlConnCred, int stockId)
{
- Core.Stock.StockCreate.WIP_StockDeletePurchase(sqlConnCred.ConnectionString, stockId);
+ new Core.Logic.Inventory.StockService().WIP_StockDeletePurchase(stockId);
}
public void StockDeleteOwnerIntroduced(ConnectionCredential sqlConnCred, int stockId)
{
- Core.Stock.StockCreate.WIP_StockDeleteOwnerIntroduced(sqlConnCred.ConnectionString, stockId);
+ new Core.Logic.Inventory.StockService().WIP_StockDeleteOwnerIntroduced(stockId);
}
public int StockReallocate(ConnectionCredential sqlConnCred, int stockId, int quantity, int debitStatusId, int creditStatusId, DateTime entryDate)
diff --git a/src/bnhtrade.Core/Data/Database/Account/CreateJournal.cs b/src/bnhtrade.Core/Data/Database/Account/CreateJournal.cs
deleted file mode 100644
index bfbb086..0000000
--- a/src/bnhtrade.Core/Data/Database/Account/CreateJournal.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data.SqlClient;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Transactions;
-
-namespace bnhtrade.Core.Data.Database.Account
-{
- internal class CreateJournal : Connection
- {
- ///
- /// Old code needs sorting
- ///
- public int AccountJournalInsert(int journalTypeId, DateTime entryDate, string currencyCode,
- decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
- {
- int defaultDebit = 0;
- int defaultCredit = 0;
-
- // ensure date is UTC
- entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc);
-
- // debit and credit locks are checked in journal post method
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(SqlConnectionString))
- {
- conn.Open();
-
- // insert the journal entry
- int journalId;
-
- using (SqlCommand cmd = new SqlCommand(@"
- INSERT INTO tblAccountJournal
- (AccountJournalTypeID, EntryDate, IsLocked)
- OUTPUT INSERTED.AccountJournalID
- VALUES
- (@journalTypeId, @entryDate, @lockEntry)
- ", conn))
- {
- // add parameters
- cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
- cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
- cmd.Parameters.AddWithValue("@lockEntry", lockEntry);
-
- //execute
- journalId = (int)cmd.ExecuteScalar();
- }
-
- // insert journal entries
- //bool postResult = AccountJournalPostInsert(sqlConnectionString, journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
- bool postResult = AccountJournalPostInsert(journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
-
- scope.Complete();
- return journalId;
- }
- }
-
- ///
- /// Old code needs sorting
- ///
- internal bool AccountJournalPostInsert(int journalId, DateTime entryDate,
- string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0)
- {
- int defaultDebit;
- int defaultCredit;
- entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc);
-
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(SqlConnectionString))
- {
- conn.Open();
-
- // ensure their are no other entries
- using (SqlCommand cmd = new SqlCommand(@"
- SELECT
- Count(tblAccountJournalPost.AccountJournalPostID) AS CountOfAccountJournalPostID
- FROM
- tblAccountJournalPost
- WHERE
- (((tblAccountJournalPost.AccountJournalID)=@AccountJournalID));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@AccountJournalID", journalId);
-
- int count = (int)cmd.ExecuteScalar();
-
- if (count > 0)
- {
- throw new Exception("Unable the insert journal posts, post already present AccountJournalID=" + journalId);
- }
- }
-
- //checks
- using (SqlCommand cmd = new SqlCommand(@"
- SELECT
- tblAccountJournalType.ChartOfAccountID_Debit, tblAccountJournalType.ChartOfAccountID_Credit
- FROM
- tblAccountJournal
- INNER JOIN tblAccountJournalType
- ON tblAccountJournal.AccountJournalTypeID = tblAccountJournalType.AccountJournalTypeID
- WHERE
- (((tblAccountJournal.AccountJournalID)=@journalId));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@journalId", journalId);
-
- using (SqlDataReader reader = cmd.ExecuteReader())
- {
- if (reader.Read())
- {
- // debit check
- if (reader.IsDBNull(0))
- {
- if (debitAccountId == 0)
- {
- throw new Exception("Debit Account ID required, default not set for journal type");
- }
- }
- else
- {
- defaultDebit = reader.GetInt32(0);
- if (debitAccountId == 0)
- {
- debitAccountId = defaultDebit;
- }
- else if (debitAccountId != defaultDebit)
- {
- throw new Exception("Debit Account ID supplied does not match default set for journal type");
- }
-
- }
- // credit check
- if (reader.IsDBNull(1))
- {
- if (creditAccountId == 0)
- {
- throw new Exception("Credit Account ID required, default not set for journal type");
- }
- }
- else
- {
- defaultCredit = reader.GetInt32(1);
- if (creditAccountId == 0)
- {
- creditAccountId = defaultCredit;
- }
- else if (creditAccountId != defaultCredit)
- {
- throw new Exception("Credit Account ID supplied does not match default set for journal type");
- }
- }
- }
- else
- {
- throw new Exception("AccountJournalID '" + journalId + "' does not exist.");
- }
- }
- }
-
- // currency conversion
- if (currencyCode != "GBP")
- {
- amount = new Logic.Account.CurrencyService().CurrencyConvertToGbp(currencyCode, amount, entryDate);
- }
-
- // ensure decimal is rounded
- amount = Math.Round(amount, 2);
-
- // insert debit post
- using (SqlCommand cmd = new SqlCommand(@"
- INSERT INTO tblAccountJournalPost
- (AccountJournalID, AccountChartOfID, AmountGbp)
- VALUES
- (@AccountJournalId, @AccountChartOfId, @AmountGbp)
- ", conn))
- {
- // add parameters
- cmd.Parameters.AddWithValue("@AccountJournalId", journalId);
- cmd.Parameters.AddWithValue("@AccountChartOfId", debitAccountId);
- cmd.Parameters.AddWithValue("@AmountGbp", amount);
-
- cmd.ExecuteNonQuery();
- }
-
- // insert credit post
- using (SqlCommand cmd = new SqlCommand(@"
- INSERT INTO tblAccountJournalPost
- (AccountJournalID, AccountChartOfID, AmountGbp)
- VALUES
- (@AccountJournalId, @AccountChartOfId, @AmountGbp)
- ", conn))
- {
- // add parameters
- cmd.Parameters.AddWithValue("@AccountJournalId", journalId);
- cmd.Parameters.AddWithValue("@AccountChartOfId", creditAccountId);
- cmd.Parameters.AddWithValue("@AmountGbp", (amount * -1));
-
- cmd.ExecuteNonQuery();
- }
-
- scope.Complete();
- return true;
- }
- }
- }
-}
diff --git a/src/bnhtrade.Core/Data/Database/Account/UpdateJournal.cs b/src/bnhtrade.Core/Data/Database/Account/UpdateJournal.cs
deleted file mode 100644
index c7de34a..0000000
--- a/src/bnhtrade.Core/Data/Database/Account/UpdateJournal.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data.SqlClient;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Transactions;
-
-namespace bnhtrade.Core.Data.Database.Account
-{
- internal class UpdateJournal : Connection
- {
- public bool AccountJournalPostUpdate(int journalId, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0)
- {
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(SqlConnectionString))
- {
- conn.Open();
-
- // retrive journal entry date
- DateTime entryDate;
- using (SqlCommand cmd = new SqlCommand(@"
- SELECT
- tblAccountJournal.EntryDate
- FROM
- tblAccountJournal
- WHERE
- (((tblAccountJournal.AccountJournalID)=@accountJournalId));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@accountJournalId", journalId);
-
- entryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
- }
-
- // delete the original posts
- using (SqlCommand cmd = new SqlCommand(@"
- DELETE FROM
- tblAccountJournalPost
- WHERE
- (((tblAccountJournalPost.AccountJournalID)=@accountJournalId));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@accountJournalId", journalId);
-
- cmd.ExecuteNonQuery();
- }
-
- //insert new posts
- //bool postResult = AccountJournalPostInsert(sqlConnectionString, journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
- bool postResult = new Data.Database.Account.CreateJournal().AccountJournalPostInsert(journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
-
- // update modified date on journal
- using (SqlCommand cmd = new SqlCommand(@"
- UPDATE
- tblAccountJournal
- SET
- tblAccountJournal.LastModified=@utcNow
- WHERE
- (((tblAccountJournal.AccountJournalID)=@accountJournalId));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@accountJournalId", journalId);
- cmd.Parameters.AddWithValue("@utcNow", DateTime.UtcNow);
-
- cmd.ExecuteNonQuery();
- }
-
- scope.Complete();
- }
- return true;
- }
-
- }
-}
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Implementation/JournalRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Implementation/AccountJournalRepository.cs
similarity index 55%
rename from src/bnhtrade.Core/Data/Database/Repository/Implementation/JournalRepository.cs
rename to src/bnhtrade.Core/Data/Database/Repository/Implementation/AccountJournalRepository.cs
index 7646118..ab4c3ca 100644
--- a/src/bnhtrade.Core/Data/Database/Repository/Implementation/JournalRepository.cs
+++ b/src/bnhtrade.Core/Data/Database/Repository/Implementation/AccountJournalRepository.cs
@@ -11,12 +11,209 @@ using System.Transactions;
namespace bnhtrade.Core.Data.Database.Repository.Implementation
{
- internal class JournalRepository : _Base, IJournalRepository
+ internal class AccountJournalRepository : _Base, IAccountJournalRepository
{
- public JournalRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
+ public AccountJournalRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
{
}
+ //
+ // create
+ //
+ public int AccountJournalInsert(int journalTypeId, DateTime entryDate, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
+ {
+ int defaultDebit = 0;
+ int defaultCredit = 0;
+
+ // ensure date is UTC
+ entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc);
+
+ // debit and credit locks are checked in journal post method
+ using (TransactionScope scope = new TransactionScope())
+ using (SqlConnection conn = new SqlConnection(SqlConnectionString))
+ {
+ conn.Open();
+
+ // insert the journal entry
+ int journalId;
+
+ using (SqlCommand cmd = new SqlCommand(@"
+ INSERT INTO tblAccountJournal
+ (AccountJournalTypeID, EntryDate, IsLocked)
+ OUTPUT INSERTED.AccountJournalID
+ VALUES
+ (@journalTypeId, @entryDate, @lockEntry)
+ ", conn))
+ {
+ // add parameters
+ cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
+ cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
+ cmd.Parameters.AddWithValue("@lockEntry", lockEntry);
+
+ //execute
+ journalId = (int)cmd.ExecuteScalar();
+ }
+
+ // insert journal entries
+ //bool postResult = AccountJournalPostInsert(sqlConnectionString, journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
+ bool postResult = AccountJournalPostInsert(journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
+
+ scope.Complete();
+ return journalId;
+ }
+ }
+
+ ///
+ /// Old code needs sorting
+ ///
+ internal bool AccountJournalPostInsert(int journalId, DateTime entryDate, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0)
+ {
+ int defaultDebit;
+ int defaultCredit;
+ entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc);
+
+ using (TransactionScope scope = new TransactionScope())
+ using (SqlConnection conn = new SqlConnection(SqlConnectionString))
+ {
+ conn.Open();
+
+ // ensure their are no other entries
+ using (SqlCommand cmd = new SqlCommand(@"
+ SELECT
+ Count(tblAccountJournalPost.AccountJournalPostID) AS CountOfAccountJournalPostID
+ FROM
+ tblAccountJournalPost
+ WHERE
+ (((tblAccountJournalPost.AccountJournalID)=@AccountJournalID));
+ ", conn))
+ {
+ cmd.Parameters.AddWithValue("@AccountJournalID", journalId);
+
+ int count = (int)cmd.ExecuteScalar();
+
+ if (count > 0)
+ {
+ throw new Exception("Unable the insert journal posts, post already present AccountJournalID=" + journalId);
+ }
+ }
+
+ //checks
+ using (SqlCommand cmd = new SqlCommand(@"
+ SELECT
+ tblAccountJournalType.ChartOfAccountID_Debit, tblAccountJournalType.ChartOfAccountID_Credit
+ FROM
+ tblAccountJournal
+ INNER JOIN tblAccountJournalType
+ ON tblAccountJournal.AccountJournalTypeID = tblAccountJournalType.AccountJournalTypeID
+ WHERE
+ (((tblAccountJournal.AccountJournalID)=@journalId));
+ ", conn))
+ {
+ cmd.Parameters.AddWithValue("@journalId", journalId);
+
+ using (SqlDataReader reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ // debit check
+ if (reader.IsDBNull(0))
+ {
+ if (debitAccountId == 0)
+ {
+ throw new Exception("Debit Account ID required, default not set for journal type");
+ }
+ }
+ else
+ {
+ defaultDebit = reader.GetInt32(0);
+ if (debitAccountId == 0)
+ {
+ debitAccountId = defaultDebit;
+ }
+ else if (debitAccountId != defaultDebit)
+ {
+ throw new Exception("Debit Account ID supplied does not match default set for journal type");
+ }
+
+ }
+ // credit check
+ if (reader.IsDBNull(1))
+ {
+ if (creditAccountId == 0)
+ {
+ throw new Exception("Credit Account ID required, default not set for journal type");
+ }
+ }
+ else
+ {
+ defaultCredit = reader.GetInt32(1);
+ if (creditAccountId == 0)
+ {
+ creditAccountId = defaultCredit;
+ }
+ else if (creditAccountId != defaultCredit)
+ {
+ throw new Exception("Credit Account ID supplied does not match default set for journal type");
+ }
+ }
+ }
+ else
+ {
+ throw new Exception("AccountJournalID '" + journalId + "' does not exist.");
+ }
+ }
+ }
+
+ // currency conversion
+ if (currencyCode != "GBP")
+ {
+ amount = new Logic.Account.CurrencyService().CurrencyConvertToGbp(currencyCode, amount, entryDate);
+ }
+
+ // ensure decimal is rounded
+ amount = Math.Round(amount, 2);
+
+ // insert debit post
+ using (SqlCommand cmd = new SqlCommand(@"
+ INSERT INTO tblAccountJournalPost
+ (AccountJournalID, AccountChartOfID, AmountGbp)
+ VALUES
+ (@AccountJournalId, @AccountChartOfId, @AmountGbp)
+ ", conn))
+ {
+ // add parameters
+ cmd.Parameters.AddWithValue("@AccountJournalId", journalId);
+ cmd.Parameters.AddWithValue("@AccountChartOfId", debitAccountId);
+ cmd.Parameters.AddWithValue("@AmountGbp", amount);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ // insert credit post
+ using (SqlCommand cmd = new SqlCommand(@"
+ INSERT INTO tblAccountJournalPost
+ (AccountJournalID, AccountChartOfID, AmountGbp)
+ VALUES
+ (@AccountJournalId, @AccountChartOfId, @AmountGbp)
+ ", conn))
+ {
+ // add parameters
+ cmd.Parameters.AddWithValue("@AccountJournalId", journalId);
+ cmd.Parameters.AddWithValue("@AccountChartOfId", creditAccountId);
+ cmd.Parameters.AddWithValue("@AmountGbp", (amount * -1));
+
+ cmd.ExecuteNonQuery();
+ }
+
+ scope.Complete();
+ return true;
+ }
+ }
+
+ //
+ // read
+ //
+
public Dictionary ReadJournal(List journalIdList)
{
var sqlBuilder = new SqlWhereBuilder();
@@ -146,7 +343,7 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
}
// get journalTypes from db
- var journalTypeDict = new JournalRepository(_connection, _transaction).ReadJournalType(journalTypeIdList);
+ var journalTypeDict = new AccountJournalRepository(_connection, _transaction).ReadJournalType(journalTypeIdList);
// get accounts from db
var accountDict = new AccountCodeRepository(_connection, _transaction).ReadAccountCode(accountIdList);
@@ -325,6 +522,74 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
return returnList;
}
+ //
+ // update
+ //
+
+ public bool AccountJournalPostUpdate(int journalId, string currencyCode, decimal amount, int debitAccountId = 0, int creditAccountId = 0)
+ {
+ // retrive journal entry date
+ DateTime entryDate;
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ SELECT
+ tblAccountJournal.EntryDate
+ FROM
+ tblAccountJournal
+ WHERE
+ (((tblAccountJournal.AccountJournalID)=@accountJournalId));";
+
+ cmd.Parameters.AddWithValue("@accountJournalId", journalId);
+
+ entryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
+ }
+
+ // delete the original posts
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ DELETE FROM
+ tblAccountJournalPost
+ WHERE
+ (((tblAccountJournalPost.AccountJournalID)=@accountJournalId));";
+
+ cmd.Parameters.AddWithValue("@accountJournalId", journalId);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ //insert new posts
+ bool postResult = AccountJournalPostInsert(journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
+
+ // update modified date on journal
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ UPDATE
+ tblAccountJournal
+ SET
+ tblAccountJournal.LastModified=@utcNow
+ WHERE
+ (((tblAccountJournal.AccountJournalID)=@accountJournalId));";
+
+ cmd.Parameters.AddWithValue("@accountJournalId", journalId);
+ cmd.Parameters.AddWithValue("@utcNow", DateTime.UtcNow);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ return true;
+ }
+
+
+ //
+ // delete
+ //
+
///
/// Old code needs sorting
///
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Implementation/PurchaseRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Implementation/PurchaseRepository.cs
new file mode 100644
index 0000000..60c370e
--- /dev/null
+++ b/src/bnhtrade.Core/Data/Database/Repository/Implementation/PurchaseRepository.cs
@@ -0,0 +1,189 @@
+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;
+using System.Transactions;
+
+namespace bnhtrade.Core.Data.Database.Repository.Implementation
+{
+ internal class PurchaseRepository : _Base, IPurchaseRepository
+ {
+ private static int accountJournalTypeIdNet = 1;
+ private static int accountJournalTypeIdTax = 2;
+ private static int stockJournalTypeId = 1;
+ private static int creditAccountId = 59;
+ private static int creditStatusId = 13;
+ private static int defaultAccountId = 138;
+
+ public PurchaseRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
+ {
+ }
+
+ public void WIP_PurchaseLineTransactionNetInsert(int purchaseLineId, string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0)
+ {
+ // default to 'Inventory, Receivable/Processing'
+ if (debitAccountId < 1)
+ {
+ debitAccountId = defaultAccountId;
+ }
+
+ // create account journal entry
+ int journalId = new Data.Database.Repository.Implementation.AccountJournalRepository(_connection, _transaction).
+ AccountJournalInsert(accountJournalTypeIdNet, entryDate, currencyCode, amountNet, debitAccountId);
+
+ // add transaction to purchase line transaction table
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ INSERT INTO
+ tblPurchaseLineTransaction
+ ( PurchaseLineID, AccountJournalID )
+ VALUES
+ ( @purchaseLineID, @accountJournalID );";
+
+ cmd.Parameters.AddWithValue("@purchaseLineID", purchaseLineId);
+ cmd.Parameters.AddWithValue("@accountJournalID", journalId);
+
+ int count = cmd.ExecuteNonQuery();
+
+ if (count != 1)
+ {
+ throw new Exception("Failed to insert record to tblPurchaseLineTransaction table");
+ }
+ }
+ }
+
+ public void WIP_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId)
+ {
+ // stock accountId check
+ if (debitAccountId == 86)
+ {
+ int count = 0;
+
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ SELECT Count(tblStock.StockID) AS CountOfStockID
+ FROM tblStock
+ WHERE (((tblStock.AccountJournalID)=@accountJouranlId));
+";
+ cmd.Parameters.AddWithValue("@accountJouranlId", accountJouranlId);
+
+ count = (int)cmd.ExecuteScalar();
+ }
+
+ if (count == 0)
+ {
+ throw new Exception("Add account journal entry to stock before attempting this operation.");
+ }
+ else if (count > 1)
+ {
+ throw new Exception("Houston we have a problem! An account journal entry is assigned to " + count + " stock lines.");
+ }
+ }
+
+ // make the update
+ bool result = new Data.Database.Repository.Implementation.AccountJournalRepository(_connection, _transaction).
+ AccountJournalPostUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId, creditAccountId);
+ }
+
+ // delete after testing....
+
+ //public void WIP_PurchaseLineTransactionStockDelete(string sqlConnectionString, int accountJournalId, int stockId)
+ //{
+ // using (TransactionScope scope = new TransactionScope())
+ // using (SqlConnection conn = new SqlConnection(sqlConnectionString))
+ // {
+ // conn.Open();
+
+ // // get stock cost
+ // (int quantity, decimal totalCost) result = Stock.StockJournal.StockGetTotalQuantityAndCost(sqlConnectionString, stockId);
+ // decimal amount = result.totalCost;
+
+ // // delete accountJournalId from stock table
+ // using (SqlCommand cmd = new SqlCommand(@"
+ // UPDATE tblStock
+ // SET AccountJournalID=Null
+ // WHERE StockID=@stockId AND AccountJournalID=@accountJournalId;
+ // ", conn))
+ // {
+ // cmd.Parameters.AddWithValue("@stockId", stockId);
+ // cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
+
+ // int count = cmd.ExecuteNonQuery();
+
+ // if (count != 1)
+ // {
+ // throw new Exception("Integrity check failure! StockID=" + stockId + ", AccountJournalID=" + accountJournalId +
+ // " combination not found on stock table.");
+ // }
+ // }
+
+ // // reset the account journal to default
+ // Account.AccountQuery.AccountJournalPostUpdate(sqlConnectionString, accountJournalId, "GBP", amount, defaultAccountId, creditAccountId);
+
+ // // delete the stock entry
+ // Stock.StockCreate.WIP_StockDeleteSub(sqlConnectionString, stockId);
+
+ // scope.Complete();
+ // }
+ //}
+
+ public void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId)
+ {
+ // check accountJournalId does not exist in stock table
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ SELECT StockID
+ FROM tblStock
+ WHERE AccountJournalID=@accountJournalId;";
+
+ cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
+
+ using (var reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ if (reader.Read())
+ {
+ throw new Exception("Integrity check failure! AccountJournalID=" + accountJournalId + " exists multiple time in stock table!");
+ }
+ else
+ {
+ throw new Exception("Delete stock first before proceeding, AccountJournalID=" + accountJournalId);
+ }
+ }
+ }
+ }
+
+ // delete line in purchase line transaction table
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ DELETE FROM tblPurchaseLineTransaction
+ WHERE AccountJournalID=@accountJournalId;";
+
+ cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
+
+ int count = cmd.ExecuteNonQuery();
+
+ if (count != 1)
+ {
+ throw new Exception("Operation cancelled, failed to delete entry in tblPurchaseLineTransaction WHERE AccountJournalID=" + accountJournalId);
+ }
+ }
+
+ // delete account journal entry
+ new AccountJournalRepository(_connection, _transaction).DeleteJournal(accountJournalId);
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Implementation/StockJournalRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Implementation/StockJournalRepository.cs
new file mode 100644
index 0000000..58aa142
--- /dev/null
+++ b/src/bnhtrade.Core/Data/Database/Repository/Implementation/StockJournalRepository.cs
@@ -0,0 +1,717 @@
+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;
+using System.Transactions;
+
+namespace bnhtrade.Core.Data.Database.Repository.Implementation
+{
+ internal class StockJournalRepository : _Base, IStockJournalRepository
+ {
+ public StockJournalRepository(IDbConnection connection, IDbTransaction transaction) : base(connection, transaction)
+ {
+ }
+
+ //
+ // Create
+ //
+
+ // only to be assessable via code, use stock relocate to move stock bewtween status'
+ public int StockJournalInsert(int journalTypeId, int stockId, List<(int statusId, int quantity)> journalPosts,
+ DateTime entryDate, bool isNewStock = false)
+ {
+ /*
+ * TODO: currently the consistancy check checks the journal after the entry has been inserted to the db, if the check fails
+ * the transaction scope is disposed, and the ne journal entries roll back. However, if this is done within a higher
+ * level transaction scope, this nested dispose() also rolls back the all transacopes scopes it is a child of.
+ *
+ * Therefore, a consistancy check needs to be simulated in code to negate the need to rollback/dispose of a db transaction.
+ * This would also have some slight performance benefits.
+ *
+ * Once you've done this, fix the SkuTransactionReconcile class: Currently it's transactionscope only covers updates.
+ * Need to set the scope to cover the intial table read (to lock the records). The issue above restricts this.
+ */
+
+
+ // balance and status IsCredit checks made by post insert function
+
+ // create the journal entry
+ int stockJournalId;
+ //consitancy check is required?
+ bool consistencyRequired = true;
+ // get date of most recent debit for status' that I will be crediting
+ if (isNewStock == false)
+ {
+ // build sql string
+ string stringSql = @"
+ SELECT
+ tblStockJournal.EntryDate
+ FROM
+ tblStockJournal
+ INNER JOIN tblStockJournalPost
+ ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
+ WHERE
+ tblStockJournal.StockID=@stockId
+ AND EntryDate>=@entryDate
+ AND tblStockJournalPost.Quantity>0
+ AND (";
+
+ bool firstDone = false;
+ foreach (var item in journalPosts)
+ {
+ if (item.quantity < 0)
+ {
+ if (firstDone)
+ {
+ stringSql = stringSql + " OR ";
+ }
+ stringSql = stringSql + "tblStockJournalPost.StockStatusID=" + item.statusId;
+ firstDone = true;
+ }
+ }
+ stringSql = stringSql + ");";
+
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.CommandText = stringSql;
+ cmd.Transaction = _transaction as SqlTransaction;
+
+ cmd.Parameters.AddWithValue("@stockId", stockId);
+ cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
+
+ using (var reader = cmd.ExecuteReader())
+ {
+ if (reader.HasRows)
+ {
+ consistencyRequired = true;
+ }
+ else
+ {
+ consistencyRequired = false;
+ }
+ }
+ }
+ }
+
+ // create journal entry
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ INSERT INTO tblStockJournal ( EntryDate, StockJournalTypeID, StockID, IsLocked )
+ OUTPUT INSERTED.StockJournalID
+ VALUES ( @EntryDate, @journalTypeId, @stockID, @isLocked );";
+
+ cmd.Parameters.AddWithValue("@stockID", stockId);
+ cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
+ cmd.Parameters.AddWithValue("@EntryDate", entryDate.ToUniversalTime());
+ cmd.Parameters.AddWithValue("@isLocked", isNewStock);
+
+ //execute
+ stockJournalId = (int)cmd.ExecuteScalar();
+ }
+
+ // insert journal posts into database
+ //new Data.Database.Stock
+ Core.Stock.StockJournal.StockJournalPostInsert(conn, stockId, stockJournalId, journalPosts, isNewStock);
+
+ // consistency check
+ bool consistencyResult = true;
+ if (consistencyRequired)
+ {
+ consistencyResult = false;
+ // build list of effected status'
+ var statusIdEffected = new List();
+ foreach (var item in journalPosts)
+ {
+ if (item.quantity < 0)
+ {
+ statusIdEffected.Add(item.statusId);
+ }
+ }
+ // run check
+ consistencyResult = Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnectionString, stockId, statusIdEffected);
+ }
+
+ if (consistencyResult)
+ {
+ // commit
+ scope.Complete();
+ return stockJournalId;
+ }
+ else
+ {
+ throw new Exception("Unable to insert stock journal entry, consistancy check failed.");
+ }
+ }
+
+
+
+
+
+ //
+ // Read
+ //
+
+ public int ReadStatusBalanceBySku(string sku, int statusId)
+ {
+ int statusBalance = new int();
+
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ 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);";
+
+ 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 ReadStatusBalanceByStockNumber(int stockNumber, int statusId)
+ {
+ int statusBalance = new int();
+
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ 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);";
+
+ 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 ReadStatusBalanceByStockId(int stockId, int statusId)
+ {
+ int statusBalance = new int();
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ SELECT
+ SUM(tblStockJournalPost.Quantity) AS Balance
+ FROM
+ tblStockJournal
+ INNER JOIN tblStockJournalPost
+ ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
+ WHERE
+ (tblStockJournal.StockID = @stockId )
+ AND (tblStockJournalPost.StockStatusID = @statusId );";
+
+ 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;
+ }
+
+ //
+ // update
+ //
+
+
+ //
+ // Delete
+ //
+
+ public void StockJournalDelete(int stockJournalId)
+ {
+ // 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();
+ 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 void StockJournalPostInsert(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();
+ 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();
+ 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 void StockJournalPostDelete(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
+ }
+ }
+
+ public bool WIP_StockJournalConsistencyCheck( int stockId, List statusIdEffected = null)
+ {
+ if (statusIdEffected == null)
+ {
+ statusIdEffected = new List();
+ }
+
+ // if no list supplied, build list of all used status' for stockId
+ if (statusIdEffected.Count == 0)
+ {
+ using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ SELECT
+ tblStockJournalPost.StockStatusID
+ FROM
+ tblStockJournal
+ INNER JOIN tblStockJournalPost
+ ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
+ WHERE
+ tblStockJournal.StockID=@stockId
+ GROUP BY
+ tblStockJournalPost.StockStatusID;";
+
+ 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();
+ 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 = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = sqlString;
+ 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 = _connection.CreateCommand() as SqlCommand)
+ {
+ cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = @"
+ 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;";
+
+ 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;
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Implementation/_BoilerPlate.cs b/src/bnhtrade.Core/Data/Database/Repository/Implementation/_BoilerPlate.cs
index c7f4974..03ae6da 100644
--- a/src/bnhtrade.Core/Data/Database/Repository/Implementation/_BoilerPlate.cs
+++ b/src/bnhtrade.Core/Data/Database/Repository/Implementation/_BoilerPlate.cs
@@ -44,8 +44,8 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
- cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
+ cmd.CommandText = sql;
sqlwhere.AddParametersToSqlCommand(cmd);
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Interface/IJournalRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Interface/IAccountJournalRepository.cs
similarity index 56%
rename from src/bnhtrade.Core/Data/Database/Repository/Interface/IJournalRepository.cs
rename to src/bnhtrade.Core/Data/Database/Repository/Interface/IAccountJournalRepository.cs
index 8ad3d04..6de9735 100644
--- a/src/bnhtrade.Core/Data/Database/Repository/Interface/IJournalRepository.cs
+++ b/src/bnhtrade.Core/Data/Database/Repository/Interface/IAccountJournalRepository.cs
@@ -6,8 +6,10 @@ using System.Threading.Tasks;
namespace bnhtrade.Core.Data.Database.Repository.Interface
{
- internal interface IJournalRepository
+ internal interface IAccountJournalRepository
{
+ 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 ReadJournal(List journalIdList);
bool ReadJournalIsLocked(int journalId);
Dictionary ReadJournalType(List journalTypeIds = null, List typeTitles = null);
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Interface/IPurchaseRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Interface/IPurchaseRepository.cs
new file mode 100644
index 0000000..4e6bbbb
--- /dev/null
+++ b/src/bnhtrade.Core/Data/Database/Repository/Interface/IPurchaseRepository.cs
@@ -0,0 +1,16 @@
+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 IPurchaseRepository
+ {
+ void WIP_PurchaseLineTransactionNetInsert(int purchaseLineId, string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0);
+ void WIP_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId);
+ void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId);
+
+ }
+}
diff --git a/src/bnhtrade.Core/Data/Database/Repository/Interface/IStockJournalRepository.cs b/src/bnhtrade.Core/Data/Database/Repository/Interface/IStockJournalRepository.cs
new file mode 100644
index 0000000..54e4296
--- /dev/null
+++ b/src/bnhtrade.Core/Data/Database/Repository/Interface/IStockJournalRepository.cs
@@ -0,0 +1,20 @@
+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 IStockJournalRepository
+ {
+ int ReadStatusBalanceBySku(string sku, int statusId);
+ int ReadStatusBalanceByStockNumber(int stockNumber, int statusId);
+ int ReadStatusBalanceByStockId(int stockId, int statusId);
+ Dictionary ReadStatusBalanceByStatusId(int statusId);
+ void StockJournalDelete(int stockJournalId);
+ void StockJournalPostInsert(int stockId, int stockJournalId, List<(int statusId, int quantity)> journalPosts, bool isNewStock = false);
+ void StockJournalPostDelete(int stockJournalId);
+ bool WIP_StockJournalConsistencyCheck(int stockId, List statusIdEffected = null);
+ }
+}
diff --git a/src/bnhtrade.Core/Data/Database/Stock/JournalCrud.cs b/src/bnhtrade.Core/Data/Database/Stock/JournalCrud.cs
index 65b0dfe..4364612 100644
--- a/src/bnhtrade.Core/Data/Database/Stock/JournalCrud.cs
+++ b/src/bnhtrade.Core/Data/Database/Stock/JournalCrud.cs
@@ -373,7 +373,7 @@ namespace bnhtrade.Core.Data.Database.Stock
{
if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false)
{
- int quantity = new Data.Database.Stock.ReadStatusBalance().ByStockId(stockId, item.Key);
+ int quantity = new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStockId(stockId, item.Key);
if (quantity + item.Value < 0)
{
diff --git a/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs
index 93ae836..0d30750 100644
--- a/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs
+++ b/src/bnhtrade.Core/Data/Database/Stock/ReadStatusBalance.cs
@@ -13,76 +13,7 @@ namespace bnhtrade.Core.Data.Database.Stock
{
}
- 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)
+ public int ReadStatusBalanceByStockId(int stockId, int statusId)
{
int statusBalance = new int();
using (SqlConnection conn = new SqlConnection(SqlConnectionString))
@@ -125,7 +56,7 @@ namespace bnhtrade.Core.Data.Database.Stock
///
/// The stock status id
/// Dictionary of SKUs and the balance of each
- public Dictionary ByStatusId(int statusId)
+ public Dictionary ReadStatusBalanceByStatusId(int statusId)
{
var returnList = new Dictionary();
diff --git a/src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs b/src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs
index cfe61d8..4f5dd05 100644
--- a/src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs
+++ b/src/bnhtrade.Core/Data/Database/UnitOfWork/IUnitOfWork.cs
@@ -16,9 +16,11 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
IExportInvoiceRepository ExportInvoiceRepository { get; }
IAmazonSettlementRepository AmazonSettlementRepository { get; }
IInvoiceRepository InvoiceRepository { get; }
- IJournalRepository JournalRepository { get; }
+ IPurchaseRepository PurchaseRepository { get; }
+ IAccountJournalRepository JournalRepository { get; }
ISequenceGenerator SequenceGenerator { get; }
ISkuRepository SkuRepository { get; }
+ IStockJournalRepository StockJournalRepository { get; }
IStockRepository StockRepository { get; }
// Methods to manage the transaction
diff --git a/src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs b/src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs
index 4ab6005..9cbf4cf 100644
--- a/src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs
+++ b/src/bnhtrade.Core/Data/Database/UnitOfWork/UnitOfWork.cs
@@ -24,9 +24,11 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
private ICurrencyRepository _currencyRepository;
private IExportInvoiceRepository _exportInvoiceRepository;
private IInvoiceRepository _invoiceRepository;
- private IJournalRepository _journalRepository;
+ private IPurchaseRepository _purchaseRepository;
+ private IAccountJournalRepository _journalRepository;
private ISequenceGenerator _sequenceGenerator;
private ISkuRepository _skuRepository;
+ private IStockJournalRepository _stockJournalRespository;
private IStockRepository _stockRepository;
internal UnitOfWork()
@@ -108,13 +110,25 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
}
}
- public IJournalRepository JournalRepository
+ public IPurchaseRepository PurchaseRepository
+ {
+ get
+ {
+ if (_purchaseRepository == null)
+ {
+ _purchaseRepository = new PurchaseRepository(_connection, _transaction);
+ }
+ return _purchaseRepository;
+ }
+ }
+
+ public IAccountJournalRepository JournalRepository
{
get
{
if (_journalRepository == null)
{
- _journalRepository = new JournalRepository(_connection, _transaction);
+ _journalRepository = new AccountJournalRepository(_connection, _transaction);
}
return _journalRepository;
}
@@ -144,6 +158,18 @@ namespace bnhtrade.Core.Data.Database.UnitOfWork
}
}
+ public IStockJournalRepository StockJournalRepository
+ {
+ get
+ {
+ if (_stockJournalRespository == null)
+ {
+ _stockJournalRespository = new StockJournalRepository(_connection, _transaction);
+ }
+ return _stockJournalRespository;
+ }
+ }
+
public IStockRepository StockRepository
{
get
diff --git a/src/bnhtrade.Core/Logic/Account/Journal.cs b/src/bnhtrade.Core/Logic/Account/Journal.cs
deleted file mode 100644
index 8cd434b..0000000
--- a/src/bnhtrade.Core/Logic/Account/Journal.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data.SqlClient;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Transactions;
-
-namespace bnhtrade.Core.Logic.Account
-{
- public class Journal : UnitOfWorkBase
- {
- public int AccountJournalInsert(int journalTypeId, DateTime entryDate, string currencyCode,
- decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
- {
- return new Data.Database.Account.CreateJournal().AccountJournalInsert(journalTypeId, entryDate, currencyCode,
- amount, debitAccountId, creditAccountId, lockEntry);
- }
-
- public bool AccountJournalDelete(int accountJournalId)
- {
- return WithUnitOfWork(uow =>
- {
- bool result = uow.JournalRepository.DeleteJournal(accountJournalId);
- CommitIfOwned(uow);
- return result;
- });
- }
- }
-}
diff --git a/src/bnhtrade.Core/Logic/Account/JournalService.cs b/src/bnhtrade.Core/Logic/Account/JournalService.cs
new file mode 100644
index 0000000..a9fd445
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Account/JournalService.cs
@@ -0,0 +1,44 @@
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
+using System.Collections.Generic;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Transactions;
+
+namespace bnhtrade.Core.Logic.Account
+{
+ public class JournalService : UnitOfWorkBase
+ {
+ public JournalService()
+ {
+ }
+
+ internal JournalService(IUnitOfWork unitOfWork) : base(unitOfWork)
+ {
+ }
+
+ public int JournalInsert(int journalTypeId, DateTime entryDate, string currencyCode,
+ decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
+ {
+ return WithUnitOfWork(uow =>
+ {
+ int journalId = uow.JournalRepository.AccountJournalInsert(journalTypeId, entryDate, currencyCode,
+ amount, debitAccountId, creditAccountId, lockEntry);
+ CommitIfOwned(uow);
+ return journalId;
+ });
+ }
+
+ public bool JournalDelete(int accountJournalId)
+ {
+ return WithUnitOfWork(uow =>
+ {
+ bool result = uow.JournalRepository.DeleteJournal(accountJournalId);
+ CommitIfOwned(uow);
+ return result;
+ });
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Logic/Sku/SkuService.cs b/src/bnhtrade.Core/Logic/Inventory/SkuService.cs
similarity index 88%
rename from src/bnhtrade.Core/Logic/Sku/SkuService.cs
rename to src/bnhtrade.Core/Logic/Inventory/SkuService.cs
index 2ad1696..9151c22 100644
--- a/src/bnhtrade.Core/Logic/Sku/SkuService.cs
+++ b/src/bnhtrade.Core/Logic/Inventory/SkuService.cs
@@ -1,13 +1,18 @@
-using System;
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Transactions;
using System.Windows.Forms;
-namespace bnhtrade.Core.Logic.Sku
+namespace bnhtrade.Core.Logic.Inventory
{
public class SkuService : UnitOfWorkBase
{
+ public SkuService() : base() { }
+
+ internal SkuService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
+
///
/// Used for retriving an SKU ID by parameters. If no match, can create a new SKU if required.
///
diff --git a/src/bnhtrade.Core/Logic/Inventory/StockJournalService.cs b/src/bnhtrade.Core/Logic/Inventory/StockJournalService.cs
new file mode 100644
index 0000000..cb974cf
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Inventory/StockJournalService.cs
@@ -0,0 +1,45 @@
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace bnhtrade.Core.Logic.Inventory
+{
+ public class StockJournalService : UnitOfWorkBase
+ {
+ public StockJournalService() { }
+
+ internal StockJournalService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
+
+ public void StockJournalDelete(int stockJournalId)
+ {
+ WithUnitOfWork(uow =>
+ {
+ if (stockJournalId <= 0)
+ {
+ throw new ArgumentException("Stock journal ID must be greater than zero", nameof(stockJournalId));
+ }
+ bool result = uow.StockJournalRepository.DeleteStockJournal(stockJournalId);
+ CommitIfOwned(uow);
+ if (!result)
+ {
+ throw new InvalidOperationException($"Failed to delete stock journal with ID {stockJournalId}");
+ }
+ });
+ }
+
+ public bool WIP_StockJournalConsistencyCheck(int stockId, List statusIdEffected = null)
+ {
+ return WithUnitOfWork(uow =>
+ {
+ if (stockId <= 0)
+ {
+ throw new ArgumentException("Stock ID must be greater than zero", nameof(stockId));
+ }
+ return uow.StockJournalRepository.WIP_StockJournalConsistencyCheck(stockId, statusIdEffected);
+ });
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Logic/Inventory/StockService.cs b/src/bnhtrade.Core/Logic/Inventory/StockService.cs
new file mode 100644
index 0000000..c20588f
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Inventory/StockService.cs
@@ -0,0 +1,443 @@
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Transactions;
+using static System.Formats.Asn1.AsnWriter;
+
+namespace bnhtrade.Core.Logic.Inventory
+{
+ public class StockService : UnitOfWorkBase
+ {
+ public StockService() : base() { }
+
+ internal StockService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
+
+ 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))
+ {
+ conn.Open();
+
+ // add account journal entry
+ int accountJournalId = new Logic.Account.JournalService().JournalInsert(accountJournalType, entryDate, currencyCode, amount);
+
+ // make the stock insert
+ int stockId = WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId,
+ accountJournalId, stockJournalType, entryDate, quantity, debitStatusId);
+
+ scope.Complete();
+ 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))
+ {
+ 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");
+ }
+ }
+ }
+
+ // 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!");
+ }
+
+ // 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();
+
+ 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);
+
+ // remove stock
+ WIP_StockDeleteSub(sqlConnectionString, stockId);
+
+ scope.Complete();
+ }
+ }
+
+ private void WIP_StockDeleteSub(int stockId)
+ {
+ using (TransactionScope scope = new TransactionScope())
+ using (SqlConnection conn = new SqlConnection(sqlConnectionString))
+ {
+ conn.Open();
+
+ // 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.");
+ }
+ }
+ }
+
+ // 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
+ Core.Stock.StockJournal.StockJournalDelete(sqlConnectionString, 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 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);
+ }
+
+ public void WIP_StockDeletePurchase(int stockId)
+ {
+ WIP_StockDeleteSub(sqlConnectionString, stockId);
+ }
+
+ public void WIP_StockDeleteOwnerIntroduced(int stockId)
+ {
+ WIP_StockDelete(sqlConnectionString, stockId);
+ }
+
+ }
+}
diff --git a/src/bnhtrade.Core/Logic/Inventory/StockStatusService.cs b/src/bnhtrade.Core/Logic/Inventory/StockStatusService.cs
new file mode 100644
index 0000000..55f8bb4
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Inventory/StockStatusService.cs
@@ -0,0 +1,39 @@
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace bnhtrade.Core.Logic.Inventory
+{
+ public class StockStatusService : UnitOfWorkBase
+ {
+ public StockStatusService() : base() { }
+
+ internal StockStatusService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
+
+ ///
+ /// 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 WithUnitOfWork(uow =>
+ {
+ if (string.IsNullOrWhiteSpace(sku))
+ {
+ throw new ArgumentException("SKU number is null, empty, or whitespace", nameof(sku));
+ }
+
+ return uow.StockJournalRepository.ReadStatusBalanceBySku(sku, statusId);
+ });
+ }
+
+
+ }
+}
diff --git a/src/bnhtrade.Core/Logic/Purchase/PurchaseService.cs b/src/bnhtrade.Core/Logic/Purchase/PurchaseService.cs
new file mode 100644
index 0000000..ea1cc8e
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Purchase/PurchaseService.cs
@@ -0,0 +1,44 @@
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Transactions;
+
+namespace bnhtrade.Core.Logic.Purchase
+{
+ public class PurchaseService : UnitOfWorkBase
+ {
+ public PurchaseService() : base() { }
+
+ internal PurchaseService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
+
+ public void WIP_PurchaseLineTransactionNetInsert(int purchaseLineId, string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0)
+ {
+ WithUnitOfWork(uow =>
+ {
+ uow.PurchaseRepository.WIP_PurchaseLineTransactionNetInsert(purchaseLineId, currencyCode, amountNet, entryDate, debitAccountId);
+ CommitIfOwned(uow);
+ });
+ }
+
+ public void WIP_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId)
+ {
+ WithUnitOfWork(uow =>
+ {
+ uow.PurchaseRepository.WIP_PurchaseLineTransactionNetUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId);
+ CommitIfOwned(uow);
+ });
+ }
+
+ public void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId)
+ {
+ WithUnitOfWork(uow =>
+ {
+ uow.PurchaseRepository.WIP_PurchaseLineTransactionDelete(purchaseLineId, accountJournalId);
+ CommitIfOwned(uow);
+ });
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs b/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs
index d5fa3d6..34c7330 100644
--- a/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs
+++ b/src/bnhtrade.Core/Logic/Stock/StatusBalance.cs
@@ -12,22 +12,9 @@ namespace bnhtrade.Core.Logic.Stock
{
}
- ///
- /// 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().BySku(sku, statusId);
- }
-
///
/// Return the avaliable balance of a status, includes datetime that each stockNumber became avaiable.
- /// Useful for checking availability before moving stock/sku retrospectivly
+ /// Useful for checking availability before moving stock/sku retrospectivly
///
/// SKU number
/// Status ID
@@ -131,7 +118,7 @@ namespace bnhtrade.Core.Logic.Stock
/// Dictionary of SKUs and the repective balance of each
public Dictionary GetSkuQuantity(int statusId)
{
- return new Data.Database.Stock.ReadStatusBalance().ByStatusId(statusId);
+ return new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStatusId(statusId);
}
}
}
diff --git a/src/bnhtrade.Core/Program.cs b/src/bnhtrade.Core/Program.cs
index c3fa03d..60f95b5 100644
--- a/src/bnhtrade.Core/Program.cs
+++ b/src/bnhtrade.Core/Program.cs
@@ -1,4 +1,5 @@
-using bnhtrade.Core.Logic;
+using bnhtrade.Core.Data.Database.UnitOfWork;
+using bnhtrade.Core.Logic;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
@@ -234,573 +235,8 @@ namespace bnhtrade.Core
namespace Stock
{
- public class StockCreate : UnitOfWorkBase
+ public class StockJournal : UnitOfWorkBase
{
- 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())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- // add account journal entry
- int accountJournalId = new Logic.Account.Journal().AccountJournalInsert(accountJournalType, entryDate, currencyCode, amount);
-
- // make the stock insert
- int stockId = WIP_StockInsertSub(sqlConnectionString, productId, conditionId, accountTaxCodeId,
- accountJournalId, stockJournalType, entryDate, quantity, debitStatusId);
-
- scope.Complete();
- return stockId;
- }
- }
-
- 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);
-
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- // 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.Sku.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 static void WIP_StockDelete(string sqlConnectionString, 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");
- }
- }
- }
-
- // 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!");
- }
-
- // 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();
-
- if (count > 1)
- {
- throw new Exception("Delete " + count + " stock journal entries (other than source entry), before peforming this operation.");
- }
- }
-
- // remove account journal entry
- Stock.StockCreate.WIP_StockDeleteSubAccountJournalEntry(sqlConnectionString, stockId);
-
- // remove stock
- Stock.StockCreate.WIP_StockDeleteSub(sqlConnectionString, stockId);
-
- scope.Complete();
- }
- }
-
- private static void WIP_StockDeleteSub(string sqlConnectionString, int stockId)
- {
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- // 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.");
- }
- }
- }
-
- // 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
- Stock.StockJournal.StockJournalDelete(sqlConnectionString, 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 static void WIP_StockDeleteSubAccountJournalEntry(string sqlConnectionString, 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.JournalRepository(conn, trans).DeleteJournal(accountJournalId);
-
- trans.Commit();
- }
- }
-
- public static int WIP_StockInsertPurchase(string sqlConnectionString, 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 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)
- {
- int accountJournalType = 3;
- int stockJournalType = 2;
- string currencyCode = "GBP";
-
- 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)
- {
- WIP_StockDeleteSub(sqlConnectionString, stockId);
- }
-
- public static void WIP_StockDeleteOwnerIntroduced(string sqlConnectionString, int stockId)
- {
- WIP_StockDelete(sqlConnectionString, stockId);
- }
-
- }
-
- public class StockJournal
- {
- // only to be assessable via code, use stock relocate to move stock bewtween status'
- public static int StockJournalInsert(string sqlConnectionString, int journalTypeId, int stockId, List<(int statusId, int quantity)> journalPosts,
- DateTime entryDate, bool isNewStock = false)
- {
- /*
- * TODO: currently the consistancy check checks the journal after the entry has been inserted to the db, if the check fails
- * the transaction scope is disposed, and the ne journal entries roll back. However, if this is done within a higher
- * level transaction scope, this nested dispose() also rolls back the all transacopes scopes it is a child of.
- *
- * Therefore, a consistancy check needs to be simulated in code to negate the need to rollback/dispose of a db transaction.
- * This would also have some slight performance benefits.
- *
- * Once you've done this, fix the SkuTransactionReconcile class: Currently it's transactionscope only covers updates.
- * Need to set the scope to cover the intial table read (to lock the records). The issue above restricts this.
- */
-
-
- // balance and status IsCredit checks made by post insert function
-
- // create the journal entry
- int stockJournalId;
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- //consitancy check is required?
- bool consistencyRequired = true;
- // get date of most recent debit for status' that I will be crediting
- if (isNewStock == false)
- {
- // build sql string
- string stringSql = @"
- SELECT
- tblStockJournal.EntryDate
- FROM
- tblStockJournal
- INNER JOIN tblStockJournalPost
- ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
- WHERE
- tblStockJournal.StockID=@stockId
- AND EntryDate>=@entryDate
- AND tblStockJournalPost.Quantity>0
- AND (";
-
- bool firstDone = false;
- foreach (var item in journalPosts)
- {
- if (item.quantity < 0)
- {
- if (firstDone)
- {
- stringSql = stringSql + " OR ";
- }
- stringSql = stringSql + "tblStockJournalPost.StockStatusID=" + item.statusId;
- 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)
- {
- consistencyRequired = true;
- }
- else
- {
- consistencyRequired = false;
- }
- }
- }
- }
-
- // create journal entry
- using (SqlCommand cmd = new SqlCommand(@"
- INSERT INTO tblStockJournal ( EntryDate, StockJournalTypeID, StockID, IsLocked )
- OUTPUT INSERTED.StockJournalID
- VALUES ( @EntryDate, @journalTypeId, @stockID, @isLocked );
- ", conn))
- {
- // add parameters
- cmd.Parameters.AddWithValue("@stockID", stockId);
- cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
- cmd.Parameters.AddWithValue("@EntryDate", entryDate.ToUniversalTime());
- cmd.Parameters.AddWithValue("@isLocked", isNewStock);
-
- //execute
- stockJournalId = (int)cmd.ExecuteScalar();
- }
-
- // insert journal posts into database
- //new Data.Database.Stock
- Core.Stock.StockJournal.StockJournalPostInsert(conn, stockId, stockJournalId, journalPosts, isNewStock);
-
- // consistency check
- bool consistencyResult = true;
- if (consistencyRequired)
- {
- consistencyResult = false;
- // build list of effected status'
- var statusIdEffected = new List();
- foreach (var item in journalPosts)
- {
- if (item.quantity < 0)
- {
- statusIdEffected.Add(item.statusId);
- }
- }
- // run check
- consistencyResult = Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnectionString, stockId, statusIdEffected);
- }
-
- if (consistencyResult)
- {
- // commit
- scope.Complete();
- return stockJournalId;
- }
- else
- {
- throw new Exception("Unable to insert stock journal entry, consistancy check failed.");
- }
- }
- }
-
public static void StockJournalDelete(string sqlConnectionString, int stockJournalId)
{
using (TransactionScope scope = new TransactionScope())
@@ -1034,7 +470,7 @@ namespace bnhtrade.Core
{
if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false)
{
- int quantity = new Data.Database.Stock.ReadStatusBalance().ByStockId(stockId, item.Key);
+ int quantity = new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStockId(stockId, item.Key);
if (quantity + item.Value < 0)
{
@@ -1323,202 +759,6 @@ namespace bnhtrade.Core
}
}
- namespace Purchase
- {
- public class PurchaseQuery
- {
- private static int accountJournalTypeIdNet = 1;
- private static int accountJournalTypeIdTax = 2;
- private static int stockJournalTypeId = 1;
- private static int creditAccountId = 59;
- private static int creditStatusId = 13;
- private static int defaultAccountId = 138;
-
- public static void WIP_PurchaseLineTransactionNetInsert(string sqlConnectionString, int purchaseLineId,
- string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0)
- {
- // default to 'Inventory, Receivable/Processing'
- if (debitAccountId < 1)
- {
- debitAccountId = defaultAccountId;
- }
-
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- // create account journal entry
- int journalId = new Data.Database.Account.CreateJournal().AccountJournalInsert(
- accountJournalTypeIdNet, entryDate, currencyCode, amountNet, debitAccountId);
-
- // add transaction to purchase line transaction table
- using (SqlCommand cmd = new SqlCommand(@"
- INSERT INTO
- tblPurchaseLineTransaction
- ( PurchaseLineID, AccountJournalID )
- VALUES
- ( @purchaseLineID, @accountJournalID );
- ", conn))
- {
- cmd.Parameters.AddWithValue("@purchaseLineID", purchaseLineId);
- cmd.Parameters.AddWithValue("@accountJournalID", journalId);
-
- int count = cmd.ExecuteNonQuery();
-
- if (count != 1)
- {
- throw new Exception("Operation cancelled, failed to update tblPurchaseLineTransaction!");
- }
- }
-
- //commit the transaction and return value
- scope.Complete();
- }
- }
-
- public static void WIP_PurchaseLineTransactionNetUpdate(string sqlConnectionString, int accountJouranlId,
- string currencyCode, decimal amountNet, int debitAccountId)
- {
- using (TransactionScope scope = new TransactionScope())
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
-
- // stock accountId check
- if (debitAccountId == 86)
- {
- int count = 0;
-
- using (SqlCommand cmd = new SqlCommand(@"
- SELECT Count(tblStock.StockID) AS CountOfStockID
- FROM tblStock
- WHERE (((tblStock.AccountJournalID)=@accountJouranlId));
- ", conn))
- {
- cmd.Parameters.AddWithValue("@accountJouranlId", accountJouranlId);
-
- count = (int)cmd.ExecuteScalar();
- }
-
- if (count == 0)
- {
- throw new Exception("Add account journal entry to stock before attempting this operation.");
- }
- else if (count > 1)
- {
- throw new Exception("Houston we have a problem! An account journal entry is assigned to " + count + " stock lines.");
- }
- }
-
- // make the update
- bool result = new Data.Database.Account.UpdateJournal().AccountJournalPostUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId, creditAccountId);
-
- scope.Complete();
- }
- }
-
- // delete after testing....
-
- //public static void WIP_PurchaseLineTransactionStockDelete(string sqlConnectionString, int accountJournalId, int stockId)
- //{
- // using (TransactionScope scope = new TransactionScope())
- // using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- // {
- // conn.Open();
-
- // // get stock cost
- // (int quantity, decimal totalCost) result = Stock.StockJournal.StockGetTotalQuantityAndCost(sqlConnectionString, stockId);
- // decimal amount = result.totalCost;
-
- // // delete accountJournalId from stock table
- // using (SqlCommand cmd = new SqlCommand(@"
- // UPDATE tblStock
- // SET AccountJournalID=Null
- // WHERE StockID=@stockId AND AccountJournalID=@accountJournalId;
- // ", conn))
- // {
- // cmd.Parameters.AddWithValue("@stockId", stockId);
- // cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
-
- // int count = cmd.ExecuteNonQuery();
-
- // if (count != 1)
- // {
- // throw new Exception("Integrity check failure! StockID=" + stockId + ", AccountJournalID=" + accountJournalId +
- // " combination not found on stock table.");
- // }
- // }
-
- // // reset the account journal to default
- // Account.AccountQuery.AccountJournalPostUpdate(sqlConnectionString, accountJournalId, "GBP", amount, defaultAccountId, creditAccountId);
-
- // // delete the stock entry
- // Stock.StockCreate.WIP_StockDeleteSub(sqlConnectionString, stockId);
-
- // scope.Complete();
- // }
- //}
-
- public static void WIP_PurchaseLineTransactionDelete(string sqlConnectionString, int purchaseLineId, int accountJournalId)
- {
- using (SqlConnection conn = new SqlConnection(sqlConnectionString))
- {
- conn.Open();
- var trans = conn.BeginTransaction();
-
- // check accountJournalId does not exist in stock table
- using (SqlCommand cmd = new SqlCommand(@"
- SELECT StockID
- FROM tblStock
- WHERE AccountJournalID=@accountJournalId;
- ", conn))
- {
- cmd.Transaction = trans;
- cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
-
- using (var reader = cmd.ExecuteReader())
- {
- if (reader.Read())
- {
- if (reader.Read())
- {
- throw new Exception("Integrity check failure! AccountJournalID=" + accountJournalId + " exists multiple time in stock table!");
- }
- else
- {
- throw new Exception("Delete stock first before proceeding, AccountJournalID=" + accountJournalId);
- }
- }
- }
- }
-
- // delete line in purchase line transaction table
- using (SqlCommand cmd = new SqlCommand(@"
- DELETE FROM tblPurchaseLineTransaction
- WHERE AccountJournalID=@accountJournalId;
- ", conn))
- {
- cmd.Transaction = trans;
- cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
-
- int count = cmd.ExecuteNonQuery();
-
- if (count != 1)
- {
- throw new Exception("Operation cancelled, failed to delete entry in tblPurchaseLineTransaction WHERE AccountJournalID=" + accountJournalId);
- }
- }
-
- // delete account journal entry
- new Data.Database.Repository.Implementation.JournalRepository(conn, trans).DeleteJournal(accountJournalId);
-
- trans.Commit();
- }
- }
- }
- }
-
public class MiscFunction
{
public static string TraceMessage(