This commit is contained in:
2025-07-09 20:19:09 +01:00
parent e2af1e4f99
commit d4170d2b80
13 changed files with 904 additions and 1019 deletions

View File

@@ -35,13 +35,13 @@ namespace bnhtrade.ComTypeLib
public int AccountJournalInsert(ConnectionCredential sqlConnCred, int journalTypeId, DateTime entryDate, 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) string currencyCode, [MarshalAs(UnmanagedType.Currency)] decimal amount, int debitAccountId = 0, int creditAccountId = 0, bool lockEntry = false)
{ {
return new Core.Logic.Account.JournalService().JournalInsert(journalTypeId, entryDate, return new Core.Logic.Account.AccountJournalService().JournalInsert(journalTypeId, entryDate,
currencyCode, amount, debitAccountId, creditAccountId, lockEntry); currencyCode, amount, debitAccountId, creditAccountId, lockEntry);
} }
public bool AccountJournalDelete(ConnectionCredential sqlConnCred, int accountJournalId) public bool AccountJournalDelete(ConnectionCredential sqlConnCred, int accountJournalId)
{ {
return new Core.Logic.Account.JournalService().JournalDelete(accountJournalId); return new Core.Logic.Account.AccountJournalService().DeleteJournal(accountJournalId);
} }
[return: MarshalAs(UnmanagedType.Currency)] [return: MarshalAs(UnmanagedType.Currency)]

View File

@@ -20,396 +20,78 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// //
// create // 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;
public int AccountJournalInsert(int journalTypeId, DateTime entryDate, bool lockEntry = false)
{
// ensure date is UTC // ensure date is UTC
entryDate = DateTime.SpecifyKind(entryDate, DateTimeKind.Utc); if (entryDate.Kind != DateTimeKind.Utc)
{
throw new Exception("DateTime kind must be utc");
}
// debit and credit locks are checked in journal post method // debit and credit locks are checked in journal post method
using (TransactionScope scope = new TransactionScope()) // insert the journal entry
using (SqlConnection conn = new SqlConnection(SqlConnectionString)) int journalId;
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{ {
conn.Open(); cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
// insert the journal entry
int journalId;
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblAccountJournal INSERT INTO tblAccountJournal
(AccountJournalTypeID, EntryDate, IsLocked) (AccountJournalTypeID, EntryDate, IsLocked)
OUTPUT INSERTED.AccountJournalID OUTPUT INSERTED.AccountJournalID
VALUES VALUES
(@journalTypeId, @entryDate, @lockEntry) (@journalTypeId, @entryDate, @lockEntry);";
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
cmd.Parameters.AddWithValue("@lockEntry", lockEntry);
//execute // add parameters
journalId = (int)cmd.ExecuteScalar(); cmd.Parameters.AddWithValue("@journalTypeId", journalTypeId);
} cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
cmd.Parameters.AddWithValue("@lockEntry", lockEntry);
// insert journal entries //execute
//bool postResult = AccountJournalPostInsert(sqlConnectionString, journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId); journalId = (int)cmd.ExecuteScalar();
bool postResult = AccountJournalPostInsert(journalId, entryDate, currencyCode, amount, debitAccountId, creditAccountId);
scope.Complete();
return journalId;
} }
}
/// <summary> return journalId;
/// Old code needs sorting
/// </summary>
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 // read
// //
public Dictionary<int, Core.Model.Account.Journal> ReadJournal(List<int> journalIdList)
public bool IsJournalDebitAssetType(int accountJournalId)
{ {
var sqlBuilder = new SqlWhereBuilder(); if (accountJournalId <= 0)
//build sql query
string sql = @"
SELECT tblAccountJournal.AccountJournalID
,tblAccountJournal.AccountJournalTypeID
,tblAccountJournal.EntryDate
,tblAccountJournal.PostDate
,tblAccountJournal.LastModified
,tblAccountJournal.IsLocked
,tblAccountJournalPost.AccountJournalPostID
,tblAccountJournalPost.AccountChartOfID
,tblAccountJournalPost.AmountGbp
FROM tblAccountJournal
INNER JOIN tblAccountJournalPost ON tblAccountJournal.AccountJournalID = tblAccountJournalPost.AccountJournalID
WHERE 1 = 1 ";
// build the where statments
if (journalIdList.Any())
{ {
sqlBuilder.In("tblAccountJournal.AccountJournalID", journalIdList, "AND"); throw new ArgumentException("Invalid journal ID provided.", nameof(accountJournalId));
} }
// append where string to the sql
if (sqlBuilder.IsSetSqlWhereString)
{
sql = sql + sqlBuilder.SqlWhereString;
}
// build tuple list
var dbJournalList = new List<(
int AccountJournalId
, int AccountJournalTypeId
, DateTime EntryDate
, DateTime PostDate
, DateTime LastModified
, bool IsLocked
)>();
var dbJournalPostList = new List<(
int AccountJournalId
, int AccountJournalPostId
, int AccountChartOfId
, decimal AmountGbp
)>();
bool hasRows = false;
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand) using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{ {
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction; cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
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;";
if (sqlBuilder.ParameterListIsSet) cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
if ((int)cmd.ExecuteScalar() < 1)
{ {
sqlBuilder.AddParametersToSqlCommand(cmd); return false; // no debit posts of type Asset found
} }
return true;
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
hasRows = true;
int lastJournalId = 0;
while (reader.Read())
{
// read journal header
int journalId = reader.GetInt32(0);
if (journalId != lastJournalId)
{
lastJournalId = journalId;
(int AccountJournalId
, int AccountJournalTypeId
, DateTime EntryDate
, DateTime PostDate
, DateTime LastModified
, bool IsLocked
)
journal =
(journalId
, reader.GetInt32(1)
, DateTime.SpecifyKind(reader.GetDateTime(2), DateTimeKind.Utc)
, DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc)
, DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc)
, reader.GetBoolean(5)
);
dbJournalList.Add(journal);
}
// read journal posts
(int AccountJournalId
, int AccountJournalPostId
, int AccountChartOfId
, decimal AmountGbp
)
journalPost =
(journalId
, reader.GetInt32(6)
, reader.GetInt32(7)
, reader.GetDecimal(8)
);
dbJournalPostList.Add(journalPost);
}
}
}
}
var returnList = new Dictionary<int, Core.Model.Account.Journal>();
if (hasRows)
{
// build lists to filter db results by
var journalTypeIdList = new List<int>();
var accountIdList = new List<int>();
foreach (var item in dbJournalList)
{
journalTypeIdList.Add(item.AccountJournalTypeId);
}
foreach (var item in dbJournalPostList)
{
accountIdList.Add(item.AccountChartOfId);
}
// get journalTypes from db
var journalTypeDict = new AccountJournalRepository(_connection, _transaction).ReadJournalType(journalTypeIdList);
// get accounts from db
var accountDict = new AccountCodeRepository(_connection, _transaction).ReadAccountCode(accountIdList);
// build final return dictionary
foreach (var dbJournal in dbJournalList)
{
// build posts
var newPosts = new List<Core.Model.Account.Journal.Post>();
foreach (var dbJournalPost in dbJournalPostList)
{
if (dbJournalPost.AccountJournalId == dbJournal.AccountJournalId)
{
var newPost = new Core.Model.Account.Journal.Post(
dbJournalPost.AccountJournalPostId
, accountDict[dbJournalPost.AccountChartOfId]
, dbJournalPost.AmountGbp);
newPosts.Add(newPost);
}
}
// create the journal
var newJournal = new Core.Model.Account.Journal(
dbJournal.AccountJournalId
, journalTypeDict[dbJournal.AccountJournalTypeId]
, newPosts
, dbJournal.EntryDate
, dbJournal.PostDate
, dbJournal.LastModified
, dbJournal.IsLocked);
returnList.Add(dbJournal.AccountJournalId, newJournal);
}
}
// all done, return the list herevar
return returnList;
}
public DateTime ReadJournalEntryDate(int journalId)
{
if (journalId <= 0)
{
throw new ArgumentException("Invalid journal ID provided.", nameof(journalId));
}
string sql = @"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));";
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", journalId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("Journal entry not found for AccountJournalID=" + journalId);
}
return DateTime.SpecifyKind((DateTime)obj, DateTimeKind.Utc);
} }
} }
@@ -561,112 +243,10 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// update // 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 // delete
// //
/// <summary>
/// Old code needs sorting
/// </summary>
public bool DeleteJournal(int accountJournalId)
{
bool IsLocked = ReadJournalIsLocked(accountJournalId);
if (IsLocked == true)
{
return false;
}
// make the delete
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = @"
DELETE FROM tblAccountJournalPost
WHERE AccountJournalID=@accountJournalId;";
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
int rows = cmd.ExecuteNonQuery();
if (rows == 0)
{
throw new Exception("Journal entry and/or entry posts do not exist for AccountJournalId=" + accountJournalId);
}
}
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = @"
DELETE FROM tblAccountJournal
WHERE AccountJournalID=@accountJournalId;";
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
cmd.ExecuteNonQuery();
}
return true;
}
} }
} }

View File

@@ -23,40 +23,6 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
{ {
} }
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) public void WIP_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId)
{ {
@@ -87,10 +53,6 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
throw new Exception("Houston we have a problem! An account journal entry is assigned to " + count + " stock lines."); 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.... // delete after testing....
@@ -135,55 +97,5 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// } // }
//} //}
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);
}
} }
} }

View File

@@ -296,6 +296,36 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
} }
} }
public int? ReadTypeIdStatusCreditId(int stockJournalTypeId)
{
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
SELECT
tblStockJournalType.StockStatusID_Credit
FROM
tblStockJournalType
WHERE
tblStockJournalType.StockJournalTypeID=@stockJournalTypeId
AND tblStockJournalType.StockStatusID_Credit Is Not Null;";
cmd.Parameters.AddWithValue("@stockJournalTypeId", stockJournalTypeId);
object obj = cmd.ExecuteScalar();
if (obj == null || obj == DBNull.Value)
{
return null;
}
else
{
return (int)obj;
}
}
}
// //
// update // update
// //

View File

@@ -22,168 +22,64 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
// create // create
// //
public int WIP_StockInsertSub(int productId, int conditionId, int accountTaxCodeId, public int InsertNewStock(int skuId, int accountJournalId)
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId)
{ {
stockJournalEntryDate = DateTime.SpecifyKind(stockJournalEntryDate, DateTimeKind.Utc); using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
// 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); cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = @"
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 INSERT INTO tblStock
(SkuID, AccountJournalID) (SkuID, AccountJournalID)
OUTPUT INSERTED.StockID OUTPUT INSERTED.StockID
VALUES VALUES
(@skuId, @accountJournalId); (@skuId, @accountJournalId);";
", conn))
{
cmd.Parameters.AddWithValue("@skuId", skuId); cmd.Parameters.AddWithValue("@skuId", skuId);
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId); cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockId = (int)cmd.ExecuteScalar(); return (int)cmd.ExecuteScalar();
} }
// insert stock journal entry
var journalPosts = new List<(int statusId, int quantity)>();
journalPosts.Add((statusDebitId, quantity));
journalPosts.Add((statusCreditId, (quantity * -1)));
int stockJournalId = Stock.StockJournal.StockJournalInsert(sqlConnectionString, stockJournalTypeId, stockId, journalPosts, stockJournalEntryDate, true);
// update the stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET StockJournalID=@stockJournalId
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
cmd.Parameters.AddWithValue("@stockId", stockId);
count = cmd.ExecuteNonQuery();
if (count < 1)
{
throw new Exception("New stock insert cancelled, failed to update StockJournalID");
}
}
scope.Complete();
return stockId;
}
public int WIP_StockInsertPurchase(int productId, int conditionId, int accountTaxCodeId, int accountJournalId, int quantity, int statusDebitId)
{
DateTime stockJournalEntryDate;
int stockJournalTypeId = 1;
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
// retrive info from purchase invoice line/transaction
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));
", conn))
{
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
stockJournalEntryDate = DateTime.SpecifyKind((DateTime)cmd.ExecuteScalar(), DateTimeKind.Utc);
}
}
return WIP_StockInsertSub(productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
}
public int WIP_StockInsertOwnerIntroduced(decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
{
int accountJournalType = 3;
int stockJournalType = 2;
string currencyCode = "GBP";
return WIP_StockInsert(accountJournalType, stockJournalType, currencyCode, amount, quantity, productId, conditionId, accountTaxCodeId, entryDate, debitStatusId);
} }
// //
// read // read
// //
public int CountStockTableRecords(List<int> accountJournalId = null)
{
throw new NotImplementedException("This needs testing before use");
var sqlWhere = new SqlWhereBuilder();
string sql = @"
SELECT Count(tblStock.StockID) AS CountOfID
FROM tblStock
WHERE 1=1 ";
if (accountJournalId != null)
{
sqlWhere.In("AccountJournalID", accountJournalId, "AND");
}
if (sqlWhere.IsSetSqlWhereString)
{
sql += sqlWhere.SqlWhereString;
}
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.Transaction = _transaction as SqlTransaction;
cmd.CommandText = sql;
if (sqlWhere.ParameterListIsSet)
{
sqlWhere.AddParametersToSqlCommand(cmd);
}
return (int)cmd.ExecuteScalar();
}
}
public int? ReadStockJournalId(int stockId) public int? ReadStockJournalId(int stockId)
{ {
if (stockId <= 0) if (stockId <= 0)
@@ -337,251 +233,5 @@ namespace bnhtrade.Core.Data.Database.Repository.Implementation
return cmd.ExecuteNonQuery(); return cmd.ExecuteNonQuery();
} }
} }
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
new Core.Logic.Inventory.StockJournalService().StockJournalDelete(stockJournalId);
// delete stock table entry
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = cmd.ExecuteNonQuery();
if (count != 1)
{
throw new Exception("StockID = " + stockId + " delete failed");
}
else
{
scope.Complete();
}
}
}
}
// to be used by other methods within a transaction scope
private void WIP_StockDeleteSubAccountJournalEntry(int stockId)
{
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
var trans = conn.BeginTransaction();
// get the account journal id
int accountJournalId = 0;
using (SqlCommand cmd = new SqlCommand(@"
SELECT AccountJournalID
FROM tblStock
WHERE StockID=@stockId;
", conn))
{
cmd.Transaction = trans;
cmd.Parameters.AddWithValue("@stockId", stockId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("StockID=" + stockId + " does not exist.");
}
else if (obj == DBNull.Value)
{
throw new Exception("AccountJournalID not found for StockID=" + stockId);
}
else
{
accountJournalId = (int)obj;
}
}
// remove entry from stock table
using (SqlCommand cmd = new SqlCommand(@"
UPDATE tblStock
SET AccountJournalID=NULL
WHERE StockID=@stockId;
", conn))
{
cmd.Transaction = trans;
cmd.Parameters.AddWithValue("@stockId", stockId);
int count = cmd.ExecuteNonQuery();
if (count != 2) // must include last modified trigger
{
throw new Exception("Failed to set AccountJournalID to NULL on stock table for StockID=" + stockId);
}
}
// delete account journal entry
new Data.Database.Repository.Implementation.AccountJournalRepository(conn, trans).DeleteJournal(accountJournalId);
trans.Commit();
}
}
public void WIP_StockDeletePurchase(int stockId)
{
WIP_StockDeleteSub(sqlConnectionString, stockId);
}
public void WIP_StockDeleteOwnerIntroduced(int stockId)
{
WIP_StockDelete(sqlConnectionString, stockId);
}
} }
} }

View File

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

View File

@@ -8,9 +8,6 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
{ {
internal interface IPurchaseRepository 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_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId);
void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId);
} }
} }

View File

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

View File

@@ -8,10 +8,8 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
{ {
internal interface IStockRepository internal interface IStockRepository
{ {
int WIP_StockInsertSub(int productId, int conditionId, int accountTaxCodeId, int InsertNewStock(int skuId, int accountJournalId);
int accountJournalId, int stockJournalTypeId, DateTime stockJournalEntryDate, int quantity, int statusDebitId); int CountStockTableRecords(List<int> accountJournalId = null);
int WIP_StockInsertPurchase(int productId, int conditionId, int accountTaxCodeId, int accountJournalId, int quantity, int statusDebitId);
int WIP_StockInsertOwnerIntroduced(decimal amount, int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId);
int? ReadAccountJournalId(int stockId); int? ReadAccountJournalId(int stockId);
int? ReadStockJournalId(int stockId); int? ReadStockJournalId(int stockId);
int UpdateAccountJournalId(int stockId, int? accountJournalID); int UpdateAccountJournalId(int stockId, int? accountJournalID);
@@ -20,7 +18,5 @@ namespace bnhtrade.Core.Data.Database.Repository.Interface
void WIP_StockDelete(int stockId); void WIP_StockDelete(int stockId);
void WIP_StockDeleteSub(int stockId); void WIP_StockDeleteSub(int stockId);
void WIP_StockDeleteSubAccountJournalEntry(int stockId); void WIP_StockDeleteSubAccountJournalEntry(int stockId);
void WIP_StockDeletePurchase(int stockId);
void WIP_StockDeleteOwnerIntroduced(int stockId);
} }
} }

View File

@@ -0,0 +1,633 @@
using bnhtrade.Core.Data.Database;
using bnhtrade.Core.Data.Database.Repository.Implementation;
using bnhtrade.Core.Data.Database.UnitOfWork;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace bnhtrade.Core.Logic.Account
{
public class AccountJournalService : UnitOfWorkBase
{
public AccountJournalService()
{
}
internal AccountJournalService(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 =>
{
// insert header record
int journalId = uow.AccountJournalRepository.AccountJournalInsert(journalTypeId, entryDate, lockEntry);
// insert post record
int defaultDebit;
int defaultCredit;
// 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();
}
return true;
CommitIfOwned(uow);
return journalId;
});
}
public Dictionary<int, Core.Model.Account.Journal> ReadJournal(List<int> journalIdList)
{
var sqlBuilder = new SqlWhereBuilder();
//build sql query
string sql = @"
SELECT tblAccountJournal.AccountJournalID
,tblAccountJournal.AccountJournalTypeID
,tblAccountJournal.EntryDate
,tblAccountJournal.PostDate
,tblAccountJournal.LastModified
,tblAccountJournal.IsLocked
,tblAccountJournalPost.AccountJournalPostID
,tblAccountJournalPost.AccountChartOfID
,tblAccountJournalPost.AmountGbp
FROM tblAccountJournal
INNER JOIN tblAccountJournalPost ON tblAccountJournal.AccountJournalID = tblAccountJournalPost.AccountJournalID
WHERE 1 = 1 ";
// build the where statments
if (journalIdList.Any())
{
sqlBuilder.In("tblAccountJournal.AccountJournalID", journalIdList, "AND");
}
// append where string to the sql
if (sqlBuilder.IsSetSqlWhereString)
{
sql = sql + sqlBuilder.SqlWhereString;
}
// build tuple list
var dbJournalList = new List<(
int AccountJournalId
, int AccountJournalTypeId
, DateTime EntryDate
, DateTime PostDate
, DateTime LastModified
, bool IsLocked
)>();
var dbJournalPostList = new List<(
int AccountJournalId
, int AccountJournalPostId
, int AccountChartOfId
, decimal AmountGbp
)>();
bool hasRows = false;
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
if (sqlBuilder.ParameterListIsSet)
{
sqlBuilder.AddParametersToSqlCommand(cmd);
}
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
hasRows = true;
int lastJournalId = 0;
while (reader.Read())
{
// read journal header
int journalId = reader.GetInt32(0);
if (journalId != lastJournalId)
{
lastJournalId = journalId;
(int AccountJournalId
, int AccountJournalTypeId
, DateTime EntryDate
, DateTime PostDate
, DateTime LastModified
, bool IsLocked
)
journal =
(journalId
, reader.GetInt32(1)
, DateTime.SpecifyKind(reader.GetDateTime(2), DateTimeKind.Utc)
, DateTime.SpecifyKind(reader.GetDateTime(3), DateTimeKind.Utc)
, DateTime.SpecifyKind(reader.GetDateTime(4), DateTimeKind.Utc)
, reader.GetBoolean(5)
);
dbJournalList.Add(journal);
}
// read journal posts
(int AccountJournalId
, int AccountJournalPostId
, int AccountChartOfId
, decimal AmountGbp
)
journalPost =
(journalId
, reader.GetInt32(6)
, reader.GetInt32(7)
, reader.GetDecimal(8)
);
dbJournalPostList.Add(journalPost);
}
}
}
}
var returnList = new Dictionary<int, Core.Model.Account.Journal>();
if (hasRows)
{
// build lists to filter db results by
var journalTypeIdList = new List<int>();
var accountIdList = new List<int>();
foreach (var item in dbJournalList)
{
journalTypeIdList.Add(item.AccountJournalTypeId);
}
foreach (var item in dbJournalPostList)
{
accountIdList.Add(item.AccountChartOfId);
}
// get journalTypes from db
var journalTypeDict = new AccountJournalRepository(_connection, _transaction).ReadJournalType(journalTypeIdList);
// get accounts from db
var accountDict = new AccountCodeRepository(_connection, _transaction).ReadAccountCode(accountIdList);
// build final return dictionary
foreach (var dbJournal in dbJournalList)
{
// build posts
var newPosts = new List<Core.Model.Account.Journal.Post>();
foreach (var dbJournalPost in dbJournalPostList)
{
if (dbJournalPost.AccountJournalId == dbJournal.AccountJournalId)
{
var newPost = new Core.Model.Account.Journal.Post(
dbJournalPost.AccountJournalPostId
, accountDict[dbJournalPost.AccountChartOfId]
, dbJournalPost.AmountGbp);
newPosts.Add(newPost);
}
}
// create the journal
var newJournal = new Core.Model.Account.Journal(
dbJournal.AccountJournalId
, journalTypeDict[dbJournal.AccountJournalTypeId]
, newPosts
, dbJournal.EntryDate
, dbJournal.PostDate
, dbJournal.LastModified
, dbJournal.IsLocked);
returnList.Add(dbJournal.AccountJournalId, newJournal);
}
}
// all done, return the list herevar
return returnList;
}
public DateTime ReadJournalEntryDate(int journalId)
{
if (journalId <= 0)
{
throw new ArgumentException("Invalid journal ID provided.", nameof(journalId));
}
string sql = @"
SELECT tblAccountJournal.EntryDate
FROM tblAccountJournal
WHERE (((tblAccountJournal.AccountJournalID)=@accountJournalId));";
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = sql;
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", journalId);
object obj = cmd.ExecuteScalar();
if (obj == null)
{
throw new Exception("Journal entry not found for AccountJournalID=" + journalId);
}
return DateTime.SpecifyKind((DateTime)obj, DateTimeKind.Utc);
}
}
/// <summary>
/// Old code needs sorting
/// </summary>
public bool AccountJournalPostInsert(IUnitOfWork uow, 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;
}
}
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;
}
/// <summary>
/// Old code needs sorting
/// </summary>
public bool DeleteJournal(int accountJournalId)
{
bool IsLocked = ReadJournalIsLocked(accountJournalId);
if (IsLocked == true)
{
return false;
}
// make the delete
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = @"
DELETE FROM tblAccountJournalPost
WHERE AccountJournalID=@accountJournalId;";
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
int rows = cmd.ExecuteNonQuery();
if (rows == 0)
{
throw new Exception("Journal entry and/or entry posts do not exist for AccountJournalId=" + accountJournalId);
}
}
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{
cmd.CommandText = @"
DELETE FROM tblAccountJournal
WHERE AccountJournalID=@accountJournalId;";
cmd.Transaction = _transaction as SqlTransaction;
cmd.Parameters.AddWithValue("@accountJournalId", accountJournalId);
cmd.ExecuteNonQuery();
}
return true;
}
}
}

View File

@@ -1,44 +0,0 @@
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.AccountJournalRepository.AccountJournalInsert(journalTypeId, entryDate, currencyCode,
amount, debitAccountId, creditAccountId, lockEntry);
CommitIfOwned(uow);
return journalId;
});
}
public bool JournalDelete(int accountJournalId)
{
return WithUnitOfWork(uow =>
{
bool result = uow.AccountJournalRepository.DeleteJournal(accountJournalId);
CommitIfOwned(uow);
return result;
});
}
}
}

View File

@@ -1,4 +1,5 @@
using bnhtrade.Core.Data.Database.UnitOfWork; using bnhtrade.Core.Data.Database.UnitOfWork;
using bnhtrade.Core.Logic.Account;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -22,9 +23,9 @@ namespace bnhtrade.Core.Logic.Inventory
return WithUnitOfWork(uow => return WithUnitOfWork(uow =>
{ {
stockJournalEntryDate = uow.AccountJournalRepository.ReadJournalEntryDate(accountJournalId); stockJournalEntryDate = new AccountJournalService(uow).ReadJournalEntryDate(accountJournalId);
int result = uow.StockRepository.WIP_StockInsertSub( int result = WIP_StockInsertSub(
productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId); uow, productId, conditionId, accountTaxCodeId, accountJournalId, stockJournalTypeId, stockJournalEntryDate, quantity, statusDebitId);
CommitIfOwned(uow); CommitIfOwned(uow);
return result; return result;
}); });
@@ -48,15 +49,68 @@ namespace bnhtrade.Core.Logic.Inventory
int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId) int quantity, int productId, int conditionId, int accountTaxCodeId, DateTime entryDate, int debitStatusId)
{ {
// add account journal entry // add account journal entry
int accountJournalId = new Logic.Account.JournalService(uow).JournalInsert(accountJournalType, entryDate, currencyCode, amount); int accountJournalId = new Logic.Account.AccountJournalService(uow).JournalInsert(accountJournalType, entryDate, currencyCode, amount);
// make the stock insert // make the stock insert
int stockId = uow.StockRepository.WIP_StockInsertSub(productId, conditionId, accountTaxCodeId, int stockId = WIP_StockInsertSub(uow, productId, conditionId, accountTaxCodeId,
accountJournalId, stockJournalType, entryDate, quantity, debitStatusId); accountJournalId, stockJournalType, entryDate, quantity, debitStatusId);
return stockId; return stockId;
} }
private int WIP_StockInsertSub(IUnitOfWork uow, 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 = uow.StockRepository.CountStockTableRecords(new List<int> { accountJournalId });
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
bool isIt = uow.AccountJournalRepository.IsJournalDebitAssetType(accountJournalId);
if (!isIt)
{
throw new Exception("Supplied AccountJournal entry must debit an 'Asset' account type.");
}
// get statusCreditId for stock journal type
int? statusCreditId = uow.StockJournalRepository.ReadTypeIdStatusCreditId(stockJournalTypeId);
if (statusCreditId == null)
{
throw new Exception("Default credit status not set for StockJournalTypeID=" + stockJournalTypeId);
}
// get/set an skuId
int skuId = new Logic.Inventory.SkuService(uow).GetSkuId(productId, conditionId, accountTaxCodeId, true);
// add the entry to the stock table (minus stockJournalId)
int stockId = uow.StockRepository.InsertNewStock(skuId, accountJournalId);
// insert stock journal entry
var journalPosts = new List<(int statusId, int quantity)>();
journalPosts.Add((statusDebitId, quantity));
journalPosts.Add((statusCreditId.Value, (quantity * -1)));
int stockJournalId = uow.StockJournalRepository.StockJournalInsert(stockJournalTypeId, stockId, journalPosts, stockJournalEntryDate, true);
// update the stock table
count = uow.StockRepository.UpdateStockJournalId(stockId, stockJournalId);
if (count < 1)
{
throw new Exception("New stock insert cancelled, failed to update StockJournalID");
}
return stockId;
}
public void WIP_StockDeletePurchase(int stockId) public void WIP_StockDeletePurchase(int stockId)
{ {
@@ -162,8 +216,7 @@ namespace bnhtrade.Core.Logic.Inventory
} }
// delete account journal entry // delete account journal entry
uow.AccountJournalRepository.DeleteJournal(accountJournalId); new AccountJournalService(uow).DeleteJournal(accountJournalId);
} }
} }
} }

View File

@@ -1,4 +1,6 @@
using bnhtrade.Core.Data.Database.UnitOfWork; using bnhtrade.Core.Data.Database.Repository.Implementation;
using bnhtrade.Core.Data.Database.UnitOfWork;
using bnhtrade.Core.Logic.Account;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -10,17 +12,50 @@ namespace bnhtrade.Core.Logic.Purchase
{ {
public class PurchaseService : UnitOfWorkBase public class PurchaseService : UnitOfWorkBase
{ {
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 PurchaseService() : base() { } public PurchaseService() : base() { }
internal PurchaseService(IUnitOfWork unitOfWork) : base(unitOfWork) { } internal PurchaseService(IUnitOfWork unitOfWork) : base(unitOfWork) { }
public void WIP_PurchaseLineTransactionNetInsert(int purchaseLineId, string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0) public void WIP_PurchaseLineTransactionNetInsert(int purchaseLineId, string currencyCode, decimal amountNet, DateTime entryDate, int debitAccountId = 0)
{ {
WithUnitOfWork(uow => // default to 'Inventory, Receivable/Processing'
if (debitAccountId < 1)
{ {
uow.PurchaseRepository.WIP_PurchaseLineTransactionNetInsert(purchaseLineId, currencyCode, amountNet, entryDate, debitAccountId); debitAccountId = defaultAccountId;
CommitIfOwned(uow); }
});
// 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) public void WIP_PurchaseLineTransactionNetUpdate(int accountJouranlId, string currencyCode, decimal amountNet, int debitAccountId)
@@ -28,17 +63,60 @@ namespace bnhtrade.Core.Logic.Purchase
WithUnitOfWork(uow => WithUnitOfWork(uow =>
{ {
uow.PurchaseRepository.WIP_PurchaseLineTransactionNetUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId); uow.PurchaseRepository.WIP_PurchaseLineTransactionNetUpdate(accountJouranlId, currencyCode, amountNet, debitAccountId);
new AccountJournalService(uow).AccountJournalPostUpdate(accountJouranlId, currencyCode, amountNet, creditAccountId);
CommitIfOwned(uow); CommitIfOwned(uow);
}); });
} }
public void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId) public void WIP_PurchaseLineTransactionDelete(int purchaseLineId, int accountJournalId)
{ {
WithUnitOfWork(uow => // check accountJournalId does not exist in stock table
using (SqlCommand cmd = _connection.CreateCommand() as SqlCommand)
{ {
uow.PurchaseRepository.WIP_PurchaseLineTransactionDelete(purchaseLineId, accountJournalId); cmd.Transaction = _transaction as SqlTransaction;
CommitIfOwned(uow); 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 AccountJournalService(uow).DeleteJournal(accountJournalId);
} }
} }
} }