using BusinessLogic.Classes.Result; using BusinessLogic.Entities; using BusinessLogic.Extensions; using BusinessLogic.Interfaces.Persistence; using BusinessLogic.Interfaces.Result; using BusinessLogic.Interfaces.Security; using BusinessLogic.Interfaces.Services; using BusinessLogic.Models; using BusinessLogic.Models.Suspense; using log4net; using System; using System.Collections.Generic; using System.Linq; namespace BusinessLogic.Suspense.JournalAllocation { public class CombinedTransactionJournalAllocationStrategy : IJournalAllocationStrategy { private readonly ILog _logger; private readonly IUnitOfWork _unitOfWork; private readonly ISecurityContext _securityContext; private readonly ISuspenseJournalService _suspenseJournalService; private readonly IJournalAllocationStrategyValidator _journalAllocationStrategyValidator; private readonly Guid _processId; private string _debitMopCode; private string _suspenseTransactionVatCode; private string _suspenseTransactionFundCode; public CombinedTransactionJournalAllocationStrategy( ILog logger , IUnitOfWork unitOfWork , ISecurityContext securityContext , ISuspenseJournalService suspenseJournalService , IJournalAllocationStrategyValidator journalAllocationStrategyValidator) { _logger = logger; _unitOfWork = unitOfWork; _securityContext = securityContext; _suspenseJournalService = suspenseJournalService; _journalAllocationStrategyValidator = journalAllocationStrategyValidator; _processId = Guid.NewGuid(); } private string GetCreditNoteMopCode() { return _unitOfWork.Mops.GetAll(true).FirstOrDefault(x => x.IsAJournalReallocation()).MopCode; } private string GetDebitMopCode() { return _unitOfWork.Mops.GetAll(true).FirstOrDefault(x => x.IsAJournal()).MopCode; } private string GetSuspenseTransactionVatCode() { return _unitOfWork.Vats.GetAll(true).FirstOrDefault(x => x.IsASuspenseTransactionVatCode()).VatCode; } private string GetSuspenseTransactionFundCode() { return _unitOfWork.Funds.GetAll(true).FirstOrDefault(x => x.IsASuspenseTransactionFund()).FundCode; } public IResult Execute( List suspenseItems, List journalItems, List creditNotes) { try { Lock(suspenseItems); var suspenses = GetSuspenseItems(); Validate(suspenses, journalItems, creditNotes); Allocate(suspenses.First(), journalItems, creditNotes); } catch (SuspenseJournalAllocationException ex) { _logger.Error(ex); return new Result(ex.Message); } catch (Exception ex) { _logger.Error(ex); return new Result("Unable to journal the items"); } finally { try { Unlock(); } catch (Exception e) { _logger.Error("Unable to unlock suspense records!", e); } } return new Result(); } private void Lock(List suspenseItems) { _unitOfWork.Suspenses.Lock(suspenseItems, _processId); _unitOfWork.Complete(_securityContext.UserId); } private void Unlock() { _unitOfWork.Suspenses.Unlock(_processId); _unitOfWork.Complete(_securityContext.UserId); } private List GetSuspenseItems() { return _unitOfWork.Suspenses .GetSuspensesBeingProcessed(_processId) .Select(x => new SuspenseWrapper(x)) .OrderByDescending(x => x.Item.Amount) .ToList(); } private void Validate(List suspenses, List journalItems, List creditNotes) { _journalAllocationStrategyValidator.Validate(new JournalAllocationStrategyValidatorArgs() { Suspenses = suspenses, JournalItems = journalItems, CreditNotes = creditNotes }); } private Guid _transferReference = Guid.NewGuid(); private Guid _transferGuid = Guid.NewGuid(); private string _pspReference = string.Empty; private DateTime _suspenseTransactionDate; private void Allocate( SuspenseWrapper suspense, List journalItems, List creditNotes) { _pspReference = _suspenseJournalService.GetPspReference(); _suspenseTransactionDate = suspense.Item.CreatedAt; _debitMopCode = GetDebitMopCode(); _suspenseTransactionVatCode = GetSuspenseTransactionVatCode(); _suspenseTransactionFundCode = GetSuspenseTransactionFundCode(); var suspenseTransaction = CreateSuspenseTransaction(suspense); foreach (var journal in journalItems) { var journalTransaction = CreateJournalTransaction(journal, suspense); CreateSuspenseProcessedTransaction(journal, journalTransaction, suspense, suspenseTransaction); } CreateCreditNoteJournalTransactions(creditNotes, suspense); } private ProcessedTransaction CreateSuspenseTransaction(SuspenseWrapper suspense) { var transferItem = new TransferItem() { AccountReference = suspense.Item.AccountNumber, Amount = -suspense.Item.Amount, FundCode = _suspenseTransactionFundCode, MopCode = _debitMopCode, Narrative = suspense.Item.Narrative, ImportId = suspense.Item.ImportId.Value, VatCode = _suspenseTransactionVatCode }; return (ProcessedTransaction)_suspenseJournalService.CreateJournal( transferItem, _transferReference, _transferGuid, _pspReference, _suspenseTransactionDate).Data; } private ProcessedTransaction CreateJournalTransaction(Journal journal, SuspenseWrapper suspense) { var transferItem = new TransferItem() { AccountReference = journal.AccountReference, Amount = journal.Amount, FundCode = journal.FundCode, MopCode = journal.MopCode, Narrative = string.IsNullOrWhiteSpace(journal.Narrative) ? suspense.Item.Narrative : journal.Narrative, ImportId = suspense.Item.ImportId.Value, VatCode = journal.VatCode }; return (ProcessedTransaction)_suspenseJournalService.CreateJournal( transferItem, _transferReference, _transferGuid, _pspReference, _suspenseTransactionDate).Data; } private void CreateSuspenseProcessedTransaction( Journal journal, ProcessedTransaction journalTransaction, SuspenseWrapper suspense, ProcessedTransaction suspenseTransaction) { if (journal.Amount >= suspense.AmountRemaining) { journal.Amount -= suspense.AmountRemaining; var a = new SuspenseProcessedTransaction() { SuspenseId = suspense.Item.Id, Amount = suspense.AmountRemaining, CreatedAt = DateTime.Now, CreatedByUserId = _securityContext.UserId, TransactionIn = journalTransaction, TransactionOut = suspenseTransaction }; suspense.Item.SuspenseProcessedTransactions.Add(a); } else if (journal.Amount < suspense.AmountRemaining) { var a = new SuspenseProcessedTransaction() { SuspenseId = suspense.Item.Id, Amount = journal.Amount, CreatedAt = DateTime.Now, CreatedByUserId = _securityContext.UserId, TransactionIn = journalTransaction, TransactionOut = suspenseTransaction }; suspense.Item.SuspenseProcessedTransactions.Add(a); journal.Amount = 0; } } private void CreateCreditNoteJournalTransactions(List creditNotes, SuspenseWrapper suspense) { if (creditNotes.IsNullOrEmpty()) return; var creditMopCode = GetCreditNoteMopCode(); foreach (var creditNote in creditNotes) { var transferItem = new TransferItem() { AccountReference = creditNote.AccountReference, Amount = -creditNote.Amount, FundCode = creditNote.FundCode, MopCode = creditMopCode, Narrative = suspense.Item.Narrative, ImportId = suspense.Item.ImportId.Value, VatCode = creditNote.VatCode }; _ = _suspenseJournalService.CreateJournal( transferItem, _transferReference, _transferGuid, _pspReference, _suspenseTransactionDate).Data; } } } }