/// <summary>
        ///     Transfers money from the source account to the destination account.
        /// </summary>
        /// <param name="sourceAccount">The source account.</param>
        /// <param name="destAccount">The dest account.</param>
        /// <param name="amount">The amount to transfer.</param>
        /// <param name="mode">The conversion mode.</param>
        /// <param name="description">The description of the transaction.</param>
        /// <returns>
        ///     The operation.
        /// </returns>
        public async Task TransferMoney(IAccount sourceAccount, IAccount destAccount, decimal amount, AmountConversionMode mode, string description)
        {
            var oldSourceBalance = sourceAccount.Balance;
            var oldDestBalance   = destAccount.Balance;
            await _original.TransferMoney(sourceAccount, destAccount, amount, mode, description);

            _hubContext.All.onBalanceChanged(CreateMessage(sourceAccount, oldSourceBalance, description));
            _hubContext.All.onBalanceChanged(CreateMessage(destAccount, oldDestBalance, description));
        }
Esempio n. 2
0
        /// <summary>
        ///     Transfers money from the source account to the destination account.
        /// </summary>
        /// <param name="sourceAccount">The source account.</param>
        /// <param name="destAccount">The dest account.</param>
        /// <param name="amount">The amount to transfer.</param>
        /// <param name="mode">The conversion mode.</param>
        /// <param name="description">The description of the transaction.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException">
        /// </exception>
        /// <exception cref="System.ArgumentException">Amount should be greater than 0</exception>
        /// <exception cref="BankingServiceException">The account does not have enough amount of money</exception>
        public async Task TransferMoney(IAccount sourceAccount, IAccount destAccount, decimal amount, AmountConversionMode mode, string description)
        {
            if (sourceAccount == null)
            {
                throw new ArgumentNullException(nameof(sourceAccount));
            }

            if (destAccount == null)
            {
                throw new ArgumentNullException(nameof(destAccount));
            }

            if (amount <= 0)
            {
                throw new ArgumentException("Amount should be greater than 0", nameof(amount));
            }

            if (sourceAccount.Balance < amount)
            {
                throw new BankingServiceException("The account does not have enough amount of money");
            }

            using (var transaction = _databaseContext.DemandTransaction())
            {
                try
                {
                    // get exchange rates and commissions
                    decimal exhangeRate;
                    decimal commissionPercent;
                    if (string.Equals(sourceAccount.Currency, destAccount.Currency, StringComparison.InvariantCultureIgnoreCase))
                    {
                        exhangeRate       = 1.0m;
                        commissionPercent = 0.0m;
                    }
                    else
                    {
                        exhangeRate = await _exchangeRateService.GetExhangeRateAsync(sourceAccount.Currency, destAccount.Currency);

                        commissionPercent = DefaultCommission;
                    }

                    // calc bank commission and new balances
                    var commission = Math.Round(amount * commissionPercent, 2);
                    // in case of commission, source description should include it
                    string sourceDescription;
                    string destDescription;
                    switch (mode)
                    {
                    case AmountConversionMode.SourceToTarget:
                        // source account is charged to pay the bank commission
                        var sourceAmount = amount;
                        var destAmount   = Math.Round(exhangeRate * (amount - commission), 2);
                        sourceAccount.Balance = sourceAccount.Balance - amount;
                        destAccount.Balance   = destAccount.Balance + destAmount;

                        sourceDescription = $"{description} Amount {sourceAmount.ToString("N2", CultureInfo.InvariantCulture)} {sourceAccount.Currency}.";
                        destDescription   = $"{description} Amount {destAmount.ToString("N2", CultureInfo.InvariantCulture)} {destAccount.Currency}.";

                        if (commission > 0)
                        {
                            sourceDescription += $" Bank commission {commission.ToString("N2", CultureInfo.InvariantCulture)} {sourceAccount.Currency}";
                        }

                        break;

                    case AmountConversionMode.TargetToSource:
                        // destination account is charged to pay the bank commission
                        sourceAmount          = Math.Round(amount / exhangeRate, 2);
                        destAmount            = amount - commission;
                        sourceAccount.Balance = sourceAccount.Balance - sourceAmount;
                        destAccount.Balance   = destAccount.Balance + destAmount;

                        sourceDescription = $"{description} Amount {sourceAmount.ToString("N2", CultureInfo.InvariantCulture)} {sourceAccount.Currency}.";
                        destDescription   = $"{description} Amount {destAmount.ToString("N2", CultureInfo.InvariantCulture)} {destAccount.Currency}.";

                        if (commission > 0)
                        {
                            destDescription += $" Bank commission {commission.ToString("N2", CultureInfo.InvariantCulture)} {destAccount.Currency}";
                        }

                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
                    }

                    // write journals
                    _journalService.WriteTransferJournal(sourceAccount, destAccount, sourceDescription, destDescription);

                    // update database
                    _databaseContext.Accounts.Update(sourceAccount);
                    _databaseContext.Accounts.Update(destAccount);

                    // update bank revenues, if any
                    if (commission > 0)
                    {
                        _bankBalanceService.AddRevenue(commission, $"A commission for money transfer from the account {sourceAccount.AccountNumber} to the account {destAccount.AccountNumber}");
                    }

                    // commit transaction
                    transaction.Commit();
                }
                catch
                {
                    transaction.Rollback();
                    throw;
                }
            }
        }