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 ReadJournal(List 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(); if (hasRows) { // build lists to filter db results by var journalTypeIdList = new List(); var accountIdList = new List(); 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(); 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); } } /// /// Old code needs sorting /// 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; } /// /// Old code needs sorting /// 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; } } }