wip, this turned into a maaaaaahooosive job

This commit is contained in:
2025-07-09 13:28:17 +01:00
parent 8c3b00c75c
commit c0f7f1a476
25 changed files with 1888 additions and 1188 deletions

View File

@@ -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)]

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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
{
/// <summary>
/// Old code needs sorting
/// </summary>
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;
}
}
/// <summary>
/// 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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
/// <summary>
/// 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
//
public Dictionary<int, Core.Model.Account.Journal> ReadJournal(List<int> 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
//
/// <summary>
/// Old code needs sorting
/// </summary>

View File

@@ -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);
}
}
}

View File

@@ -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<int>();
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<int>();
using (SqlCommand cmd = new SqlCommand(@"
SELECT tblStockJournalPost.StockStatusID
FROM tblStockJournalPost
WHERE (((tblStockJournalPost.StockJournalID)=@stockJournalId) AND ((tblStockJournalPost.Quantity)>0));
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
debitList.Add(reader.GetInt32(0));
}
}
else
{
throw new Exception("StockJournalID=" + stockJournalId + " has no debits with quantity greater than zero!");
}
}
}
// check no credits for stockId & debit combination have been made since delete entry
string stringSql = @"
SELECT
tblStockJournal.EntryDate
FROM
tblStockJournal
INNER JOIN tblStockJournalPost
ON tblStockJournal.StockJournalID = tblStockJournalPost.StockJournalID
WHERE
tblStockJournal.StockID=@stockId
AND tblStockJournalPost.Quantity<0
AND EntryDate>=@entryDate
AND (";
bool firstDone = false;
foreach (var item in debitList)
{
if (firstDone)
{
stringSql = stringSql + " OR ";
}
stringSql = stringSql + "tblStockJournalPost.StockStatusID=" + item;
firstDone = true;
}
stringSql = stringSql + ");";
using (SqlCommand cmd = new SqlCommand(stringSql, conn))
{
cmd.Parameters.AddWithValue("@stockId", stockId);
cmd.Parameters.AddWithValue("@entryDate", entryDate.ToUniversalTime());
using (var reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
consistancyCheck = true;
}
else
{
consistancyCheck = false;
}
}
}
// delete the posts
StockJournalPostDelete(conn, stockJournalId);
// delete journal entry
using (SqlCommand cmd = new SqlCommand(@"
DELETE FROM tblStockJournal
WHERE StockJournalID=@stockJournalId;
", conn))
{
// add parameters
cmd.Parameters.AddWithValue("@stockJournalId", stockJournalId);
int count = cmd.ExecuteNonQuery();
if (count != 1)
{
throw new Exception("Failed to delete stock journal header.");
}
}
// consistanct check
bool consistencyResult = true;
if (consistancyCheck)
{
// run check
consistencyResult = Stock.StockJournal.WIP_StockJournalConsistencyCheck(sqlConnectionString, stockId, debitList);
}
if (consistencyResult)
{
// commit
scope.Complete();
}
else
{
throw new Exception("Unable to delete stock journal entry, consistancy check failed.");
}
}
private 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<int, int>();
foreach (var post in journalPosts)
{
if (dicStatusQty.ContainsKey(post.statusId) == false)
{
dicStatusQty.Add(post.statusId, post.quantity);
}
else
{
dicStatusQty[post.statusId] = dicStatusQty[post.statusId] + post.quantity;
}
}
// get isCreditOnly for each status
var dicStatusIsCreditOnly = new Dictionary<int, bool>();
foreach (var item in dicStatusQty)
{
using (SqlCommand cmd = new SqlCommand(@"
SELECT IsCreditOnly FROM tblStockStatus WHERE StockStatusID=@statusId;
", conn))
{
cmd.Parameters.AddWithValue("@statusId", item.Key);
dicStatusIsCreditOnly.Add(item.Key, (bool)cmd.ExecuteScalar());
}
}
// check there is only one IsCreditOnly in list and it is allowed in this instance
int isCreditOnlyCount = 0;
foreach (var item in dicStatusIsCreditOnly)
{
if (item.Value)
{
isCreditOnlyCount = isCreditOnlyCount + 1;
}
}
if (isNewStock == false && isCreditOnlyCount > 0)
{
throw new Exception("Attempted credit or debit to 'Is Credit Only' status not allowed, in this instance.");
}
if (isNewStock == true && isCreditOnlyCount != 1)
{
throw new Exception("StockID=" + stockId + ", each stock line must have only have one IsCreditOnly=True status assigned to it.");
}
// ensure debit (or zero credit) isn't made to credit only status
// need to do this check via original post list (i.e. journalPosts)
foreach (var post in journalPosts)
{
// debit check
if (post.quantity >= 0)
{
if (dicStatusIsCreditOnly[post.statusId] == true)
{
throw new Exception("Cannot make debit, or zero quantity credit, to credit only status. StatusID=" + post.statusId);
}
}
}
// balance check for any credits (that are not isCreditOnly=true)
foreach (var item in dicStatusQty)
{
if (item.Value < 0 && dicStatusIsCreditOnly[item.Key] == false)
{
int quantity = new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStockId(stockId, item.Key);
if (quantity + item.Value < 0)
{
throw new Exception("Credit status balance check failed. Available balance " + quantity + ", attempted credit " + item.Value * -1 + ".");
}
}
}
// get this far...
// insert journal posts into database
foreach (var post in journalPosts)
{
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO tblStockJournalPost ( StockJournalID, StockStatusID, Quantity )
VALUES ( @StockJournalId, @stockStatudId, @quantity );
", conn))
{
cmd.Parameters.AddWithValue("@StockJournalId", stockJournalId);
cmd.Parameters.AddWithValue("@stockStatudId", post.statusId);
cmd.Parameters.AddWithValue("@quantity", post.quantity);
// execute
cmd.ExecuteNonQuery();
}
}
}
private 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<int> statusIdEffected = null)
{
if (statusIdEffected == null)
{
statusIdEffected = new List<int>();
}
// 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<int, bool>();
string sqlString = @"
SELECT
tblStockStatus.StockStatusID, tblStockStatus.IsCreditOnly
FROM
tblStockStatus ";
for (var i = 0; i < statusIdEffected.Count; i++)
{
if (i == 0)
{
sqlString = sqlString + " WHERE tblStockStatus.StockStatusID=" + statusIdEffected[i];
}
else
{
sqlString = sqlString + " OR tblStockStatus.StockStatusID=" + statusIdEffected[i];
}
//if (i == (statusIdEffected.Count - 1))
//{
// sqlString = sqlString + ";";
//}
}
sqlString = sqlString + " GROUP BY tblStockStatus.StockStatusID, tblStockStatus.IsCreditOnly;";
// run query & build dictionaries
using (SqlCommand cmd = _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;
}
}
}

View File

@@ -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);

View File

@@ -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<int, Core.Model.Account.Journal> ReadJournal(List<int> journalIdList);
bool ReadJournalIsLocked(int journalId);
Dictionary<int, Model.Account.JournalType> ReadJournalType(List<int> journalTypeIds = null, List<string> typeTitles = null);

View File

@@ -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);
}
}

View File

@@ -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<string, int> 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<int> statusIdEffected = null);
}
}

View File

@@ -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)
{

View File

@@ -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
/// </summary>
/// <param name="statusId">The stock status id</param>
/// <returns>Dictionary of SKUs and the balance of each</returns>
public Dictionary<string, int> ByStatusId(int statusId)
public Dictionary<string, int> ReadStatusBalanceByStatusId(int statusId)
{
var returnList = new Dictionary<string, int>();

View File

@@ -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

View File

@@ -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

View File

@@ -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;
});
}
}
}

View File

@@ -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;
});
}
}
}

View File

@@ -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) { }
/// <summary>
/// Used for retriving an SKU ID by parameters. If no match, can create a new SKU if required.
/// </summary>

View File

@@ -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<int> 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);
});
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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) { }
/// <summary>
/// Return the avaliable balance of a status. Uses a more efficent sql/code. However, balance requests should
/// generally also involve a date/time (i.e. the system does allow a stock transaction in the future, therefore
/// this method may give an available quantity, but transfers before that date wouod not be possible).
/// </summary>
/// <param name="sku">SKU number</param>
/// <param name="statusId">Status ID</param>
/// <returns>Balance as quantity</returns>
private int GetAvailableBalanceBySku(string sku, int statusId)
{
return WithUnitOfWork(uow =>
{
if (string.IsNullOrWhiteSpace(sku))
{
throw new ArgumentException("SKU number is null, empty, or whitespace", nameof(sku));
}
return uow.StockJournalRepository.ReadStatusBalanceBySku(sku, statusId);
});
}
}
}

View File

@@ -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);
});
}
}
}

View File

@@ -12,19 +12,6 @@ namespace bnhtrade.Core.Logic.Stock
{
}
/// <summary>
/// Return the avaliable balance of a status. Uses a more efficent sql/code. However, balance requests should
/// generally also involve a date/time (i.e. the system does allow a stock transaction in the future, therefore
/// this method may give an available quantity, but transfers before that date wouod not be possible).
/// </summary>
/// <param name="sku">SKU number</param>
/// <param name="statusId">Status ID</param>
/// <returns>Balance as quantity</returns>
private int GetAvailableBalanceBySku(string sku, int statusId)
{
return new Data.Database.Stock.ReadStatusBalance().BySku(sku, statusId);
}
/// <summary>
/// Return the avaliable balance of a status, includes datetime that each stockNumber became avaiable.
/// Useful for checking availability before moving stock/sku retrospectivly
@@ -131,7 +118,7 @@ namespace bnhtrade.Core.Logic.Stock
/// <returns>Dictionary of SKUs and the repective balance of each</returns>
public Dictionary<string, int> GetSkuQuantity(int statusId)
{
return new Data.Database.Stock.ReadStatusBalance().ByStatusId(statusId);
return new Data.Database.Stock.ReadStatusBalance().ReadStatusBalanceByStatusId(statusId);
}
}
}

View File

@@ -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<int>();
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(