using System.Security; namespace BankOperationsUpdate.WorkerService.OperationsUpdate; public class UpdateOperations { private IRepositoryWrapper _repositoryWrapper; private readonly ILogger _logger; private readonly IServiceProvider _provider; public UpdateOperations(ILogger logger, IServiceProvider provider) { _logger = logger; _provider = provider; } public void WhenStart() { using (var scope = _provider.CreateScope()) { _repositoryWrapper = scope.ServiceProvider.GetRequiredService(); SetTransactionsInitial(); } } public void Execute() { using (var scope = _provider.CreateScope()) { _repositoryWrapper = scope.ServiceProvider.GetRequiredService(); AddConfirmedOperations(); } using (var scope = _provider.CreateScope()) { _repositoryWrapper = scope.ServiceProvider.GetRequiredService(); UpdateConfirmedTransactions(); } } #region Different Get methods public IQueryable GetAllOperations() => _repositoryWrapper.BankOperation.GetAll(); public IQueryable GetAllConfirmedOperations() => _repositoryWrapper.BankOperation.GetAllConfirmed(Guid.Empty, Guid.Empty); public IQueryable GetAllConfirmedOperations(Guid accountId, Guid businessId) => _repositoryWrapper.BankOperation.GetAllConfirmed(accountId, businessId); public IQueryable GetAllNewOperations() => _repositoryWrapper.BankOperation.GetAllNew(Guid.Empty, Guid.Empty); public IQueryable GetAllNewOperations(Guid accountId, Guid businessId) => _repositoryWrapper.BankOperation.GetAllNew(accountId, businessId); public IQueryable GetAllBankBalances() => _repositoryWrapper.BankAccountBalance.FindAll(); public BankAccountBalance GetAccountBalanceByBusinessId(Guid accountId, Guid businessId) => _repositoryWrapper.BankAccountBalance.GetByBankAccountId(accountId, businessId).FirstOrDefault(); public IQueryable GetTransactionsByAccountId(Guid accountId, Guid businessId) => _repositoryWrapper.Transaction.GetByAccountId(accountId, businessId); public IQueryable GetAllTransactions() => _repositoryWrapper.Transaction.FindAll(); public Transaction GetTransactionByOperationId(Guid operationId) => _repositoryWrapper.Transaction.GetByOperationId(operationId); #endregion void SetTransactionsInitial() { var bankBalances = GetAllBankBalances().ToList(); for(int i = 0; i < bankBalances.Count; i++) { _logger.LogDebug("Process for account {0}", bankBalances[i].Id); SetTransactionsForAccount(bankBalances[i]!.BankAccountId.Value, bankBalances[i]!.BusinessId.Value); } } void AddConfirmedOperations() { //get all operations with Confirmed status var operations = GetAllConfirmedOperations().OrderBy(x => x.DocDate).ThenBy(x => x.OrderId).ToList(); //get all operations with New status var newOperations = GetAllNewOperations().OrderBy(x => x.DocDate).ThenBy(x => x.OrderId).ToList(); //get all bankOperationIds that added to transactions var transactiondHashSet = GetAllTransactions().Select(x => x.BankOperationId).ToHashSet(); BankAccountBalance accountBalance; Transaction transaction; foreach (var item in operations) { //if transaction with opertion id doesn't exist then add new transatcion and change balance if (!transactiondHashSet.Contains(item.Id)) { try { accountBalance = GetAccountBalanceByBusinessId(item.BankAccountId!.Value, item.BusinessId!.Value); //change balance on certain amount accountBalance.Amount = accountBalance.Amount!.Value + item.IncomeAmount!.Value - item.OutcomeAmount!.Value; //create new transaction transaction = new Transaction { Id = Guid.NewGuid(), BankOperationId = item.Id, IncomeAmount = item.IncomeAmount, OutcomeAmount = item.OutcomeAmount, BalanceAmount = item.IncomeAmount!.Value - item.OutcomeAmount!.Value, ModifiedDate = item.DocDate, BusinessId = item.BusinessId.Value, BankAccountId = item.BankAccountId.Value, CreateDate = DateTime.Now, OperationCreateDate = item.CreatedDate ?? item.DocDate!.Value, IsChanged = true, OperationOrderId = item.OrderId, }; _logger.LogInformation(Environment.NewLine + "Created new Transaction {0}", transaction.Id); _repositoryWrapper.Transaction.Create(transaction); _repositoryWrapper.BankAccountBalance.Update(accountBalance); _repositoryWrapper.Save(); _repositoryWrapper.Transaction.Detach(transaction); _repositoryWrapper.BankAccountBalance.Detach(accountBalance); } catch(Exception ex) { _logger.LogError(ex.Message); } } else if(transactiondHashSet.Contains(item.Id) && item.IsChanged == true) { UpdateChangedOperations(item, true); } } foreach (var item in newOperations) { if (transactiondHashSet.Contains(item.Id)) { UpdateChangedOperations(item, false); } } } void UpdateChangedOperations(BankOperation operation, bool IsChanged) { Transaction transaction = GetTransactionByOperationId(operation.Id); var accountBalance = GetAccountBalanceByBusinessId(operation.BankAccountId.Value, operation.BusinessId.Value); decimal incomeDifference, outcomeDifference; //if it is bankOperation with new status that already was in transatctions if (!IsChanged) { //get the income or outcome sum of rollBackcked transaction incomeDifference = -1 * transaction.IncomeAmount.Value; outcomeDifference = -1 * transaction.OutcomeAmount.Value; _repositoryWrapper.Transaction.Delete(transaction); } //else if it is confirmed transaction that has already in transactions else { //get the income or outcome difference that opertion was changed incomeDifference = operation.IncomeAmount.Value - transaction.IncomeAmount.Value; outcomeDifference = operation.OutcomeAmount.Value - transaction.OutcomeAmount.Value; transaction.IncomeAmount += incomeDifference; transaction.OutcomeAmount += outcomeDifference; operation.IsChanged = false; _logger.LogInformation("Updated transaction with id: {0}", transaction.Id); _repositoryWrapper.Transaction.Update(transaction); _repositoryWrapper.BankOperation.Update(operation); } accountBalance.Amount = accountBalance.Amount + incomeDifference - outcomeDifference; _repositoryWrapper.BankAccountBalance.Update(accountBalance); _repositoryWrapper.Save(); _repositoryWrapper.Transaction.Detach(transaction); _repositoryWrapper.BankAccountBalance.Detach(accountBalance); //get the transactions that come after modified transaction and change their balanceAmount on cahnged certain amount var transactionsList = GetTransactionsByAccountId(operation.BankAccountId.Value, operation.BusinessId.Value) .Where(x => x.BusinessId == operation.BusinessId && x.BankAccountId == operation.BankAccountId && DateTime.Compare(x.ModifiedDate.Value, transaction.ModifiedDate.Value) >= 0) .OrderBy(x => x.ModifiedDate) .ThenBy(x => x.OperationOrderId).ToList(); foreach(var item in transactionsList) { item.BalanceAmount = item.BalanceAmount + incomeDifference - outcomeDifference; _logger.LogInformation("Updated transaction with id: {0}", transaction.Id); _repositoryWrapper.Transaction.Update(item); _repositoryWrapper.Save(); _repositoryWrapper.Transaction.Detach(item); } } //update all new added transactions for updating balance amount of items that comes after updated item void UpdateConfirmedTransactions() { var bankAccounts = GetAllBankBalances().ToList(); List transactions; decimal newValue = 0M; foreach (var account in bankAccounts) { transactions = GetTransactionsByAccountId(account.BankAccountId!.Value, account.BusinessId!.Value).OrderBy(x => x.ModifiedDate).ThenBy(x => x.OperationOrderId).ToList(); for (int i = 1; i < transactions.Count; i++) { if (transactions[i].IsChanged == true) { transactions[i].BalanceAmount += transactions[i - 1].BalanceAmount; newValue += transactions[i].IncomeAmount!.Value - transactions[i].OutcomeAmount!.Value; transactions[i].IsChanged = false; } else { transactions[i].BalanceAmount += newValue; } _repositoryWrapper.Transaction.Update(transactions[i]); _repositoryWrapper.Save(); } } } /// /// Create transatction for each bank operation if they don't exist /// /// /// void SetTransactionsForAccount(Guid accountId, Guid businessId) { //get all confirmed operations by accountId and businessId and order them by DocDate var operations = GetAllConfirmedOperations(accountId, businessId).OrderBy(x => x.DocDate).ThenBy(x => x.OrderId).ToList(); // get Unitque bankOpertionsIds from transatctions var transactionsHashSet = GetTransactionsByAccountId(accountId, businessId).Select(x => x.BankOperationId).ToHashSet(); Transaction transaction = new Transaction(); //get current balance by summarizing all Incomes and outcomes from transacitons var currentBalance = GetTransactionsByAccountId(accountId, businessId).Sum(x => (x.IncomeAmount - x.OutcomeAmount)); for (int i = 0; i < operations.Count && transactionsHashSet.Count == 0; i++) { //if BankOperationsIds doesn't contain confirmed operations then.. if (!transactionsHashSet.Contains(operations[i].Id) ) { //change balance on certain income or outcome amount currentBalance = currentBalance + operations[i].IncomeAmount!.Value - operations[i].OutcomeAmount!.Value; transaction.Id = Guid.NewGuid(); transaction.IncomeAmount = operations[i].IncomeAmount; transaction.OutcomeAmount = operations[i].OutcomeAmount; transaction.BankOperationId = operations[i].Id; transaction.IsChanged = false; transaction.ModifiedDate = operations[i].DocDate; transaction.BusinessId = businessId; transaction.BankAccountId = accountId; transaction.CreateDate = DateTime.Now; transaction.OperationCreateDate = operations[i].CreatedDate ?? transaction.ModifiedDate!.Value; transaction.BalanceAmount = currentBalance ?? 0; transaction.OperationOrderId = operations[i].OrderId; //create transaction _repositoryWrapper.Transaction.Create(transaction); _repositoryWrapper.Save(); } } _logger.LogInformation($"{operations.Count} count records was written"); } }