Пример #1
0
        public void RequestToDebitAccount()
        {
            // Arrange
            var reference = Guid.NewGuid().ToString("N");
            var message   = new RequestAccountDebit("9237452718263673", reference, "withdrawal", decimal.Parse("10"),
                                                    "234532", new Dictionary <string, string>().ToImmutableDictionary());

            // Act
            _debitActorRef.Tell(message);
            var res = ExpectMsg <AccountDebitResponse>(TimeSpan.FromMinutes(5));

            // Assert
            Assert.Equal("200", res.StatusResponse.Code);
        }
Пример #2
0
        private void DoRequestAccountDebit(RequestAccountDebit request)
        {
            Log(CommonLogLevel.Debug, $"received request to debit {request.AccountNumber} with {request.Amount}", null,
                null);


            //we generate a unique ticket for every transaction
            var ticket = Guid.NewGuid().ToString("N");

            /*
             * 1. Find the account
             * 2. Execute conditions on account - we shall use Expression Trees
             * 3. Apply outcome
             * 4. Persist results
             */
            var db = new BillingwareDataContext();

            var account = db.Accounts.AsNoTracking().FirstOrDefault(a => a.AccountNumber == request.AccountNumber);

            if (account == null)
            {
                Sender.Tell(
                    new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, decimal.Zero,
                                             decimal.Zero, new CommonStatusResponse(message: $"account {request.AccountNumber} not found.", code: "404", subCode: "404.1")), Self);
                return;
            }

            var generalConditions =
                db.Conditions.AsNoTracking().Where(c => c.Active && c.ConditionApplicatorType == ConditionApplicatorType.All).ToList()
            ;

            var specificConditions = db.Conditions.AsNoTracking().Where(c =>
                                                                        c.Active && c.ConditionApplicatorType == ConditionApplicatorType.One &&
                                                                        c.AppliedToAccountNumbers.Contains(request.AccountNumber)).ToList();



            var allTransactionsCount   = long.Parse(_tranactionsStatsCache.Find($"count.{request.AccountNumber}").Result.ToString());
            var debitTransactionSum    = decimal.Parse(_tranactionsStatsCache.Find($"sum.debit.{request.AccountNumber}").Result.ToString());
            var creditTransactionSum   = decimal.Parse(_tranactionsStatsCache.Find($"sum.credit.{request.AccountNumber}").Result.ToString());
            var debitTransactionCount  = long.Parse(_tranactionsStatsCache.Find($"count.debit.{request.AccountNumber}").Result.ToString());
            var creditTransactionCount = long.Parse(_tranactionsStatsCache.Find($"count.credit.{request.AccountNumber}").Result.ToString());

            // var allConditions = new List<Condition>();

            bool passedSpecificConditions = false;

            if (specificConditions.Any())
            {
                //allConditions.AddRange(specificConditions);

                var perAccountOutcomeList = ConditionEvaluatorHelper.EvaluateConditionAndGetOutcomeIds(account, specificConditions, new ClientPayloadData("debit", request.Reference, request.Amount),
                                                                                                       creditTransactionSum, creditTransactionCount, debitTransactionSum, debitTransactionCount,
                                                                                                       allTransactionsCount);


                if (perAccountOutcomeList.Any())
                {
                    //check if there's any "halt" type in the outcome list...then,
                    //raise an event to actually inspect the outcome of the condition evaluation
                    //it would also increment the stats cache values
                    var outcomes = db.Outcomes.AsNoTracking().Where(o => perAccountOutcomeList.Contains(o.Id) && o.Active)
                                   .OrderBy(i => i.Id);

                    if (!outcomes.Any())
                    {
                        Sender.Tell(
                            new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance, account.Balance
                                                     , new CommonStatusResponse(message: $"condition was satisfied but no active outcome could be applied.", code: "500", subCode: "500.1")), Self);
                        return;
                    }


                    if (outcomes.Any(o => o.OutcomeType == OutcomeType.Halt))
                    {
                        Sender.Tell(
                            new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                     account.Balance, new CommonStatusResponse(message: $"condition was satisfied. but at least one outcome HALTs the process", code: "403", subCode: "403.1")), Self);
                        return;
                    }

                    passedSpecificConditions = true;
                    //Publish(new DebitAccount(request));

                    //Publish(new PersistTransaction(request.AccountNumber, DateTime.Now, request.Narration, request.Amount,
                    //    account.Balance, account.Balance - request.Amount, TransactionType.Debit, true, string.Empty, true,
                    //    request.ClientId, request.Reference, ticket));

                    //Sender.Tell(
                    //    new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                    //        account.Balance - request.Amount, new CommonStatusResponse(message: "Successful")), Self);
                    //return;
                }
            }

            if (generalConditions.Any())
            {
                // allConditions.AddRange(generalConditions);

                var generalOutcomeList = ConditionEvaluatorHelper.EvaluateConditionAndGetOutcomeIds(account, generalConditions, new ClientPayloadData("debit", request.Reference, request.Amount),
                                                                                                    creditTransactionSum, creditTransactionCount, debitTransactionSum, debitTransactionCount,
                                                                                                    allTransactionsCount);


                if (generalOutcomeList.Any())
                {
                    //check if there's any "halt" type in the outcome list...then,
                    //raise an event to actually inspect the outcome of the condition evaluation
                    //it would also increment the stats cache values
                    var outcomes = db.Outcomes.AsNoTracking().Where(o => generalOutcomeList.Contains(o.Id) && o.Active)
                                   .OrderBy(i => i.Id);

                    if (!outcomes.Any())
                    {
                        //but.....
                        if (passedSpecificConditions)
                        {
                            Publish(new DebitAccount(request));

                            Publish(new PersistTransaction(request.AccountNumber, DateTime.Now, request.Narration, request.Amount,
                                                           account.Balance, account.Balance - request.Amount, TransactionType.Debit, true, string.Empty, true,
                                                           request.ClientId, request.Reference, ticket));

                            Sender.Tell(
                                new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                         account.Balance - request.Amount, new CommonStatusResponse(message: "Successful")), Self);
                            return;
                        }
                        Sender.Tell(
                            new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                     account.Balance, new CommonStatusResponse(message: $"condition was satisfied but no active outcome could be applied.", code: "500", subCode: "500.1")), Self);
                        return;
                    }


                    if (outcomes.Any(o => o.OutcomeType == OutcomeType.Halt))
                    {
                        //but.....
                        if (passedSpecificConditions)
                        {
                            Publish(new DebitAccount(request));

                            Publish(new PersistTransaction(request.AccountNumber, DateTime.Now, request.Narration, request.Amount,
                                                           account.Balance, account.Balance - request.Amount, TransactionType.Debit, true, string.Empty, true,
                                                           request.ClientId, request.Reference, ticket));

                            Sender.Tell(
                                new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                         account.Balance - request.Amount, new CommonStatusResponse(message: "Successful")), Self);
                            return;
                        }
                        Sender.Tell(
                            new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                     account.Balance, new CommonStatusResponse(message: $"condition was satisfied. but at least one outcome HALTs the process", code: "403", subCode: "403.1")), Self);
                        return;
                    }

                    Publish(new DebitAccount(request));

                    Publish(new PersistTransaction(request.AccountNumber, DateTime.Now, request.Narration, request.Amount,
                                                   account.Balance, account.Balance - request.Amount, TransactionType.Debit, true, string.Empty, true,
                                                   request.ClientId, request.Reference, ticket));

                    Sender.Tell(
                        new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                                 account.Balance - request.Amount, new CommonStatusResponse(message: "Successful")), Self);
                    return;
                }
            }


            //if there are no conditions so we still proceed with the intended action
            Publish(new DebitAccount(request));

            Publish(new PersistTransaction(request.AccountNumber, DateTime.Now, request.Narration, request.Amount,
                                           account.Balance, account.Balance - request.Amount, TransactionType.Debit, false, "no condition", true,
                                           request.ClientId, request.Reference, ticket));

            Sender.Tell(
                new AccountDebitResponse(request.Reference, request.AccountNumber, ticket, request.Amount, account.Balance,
                                         account.Balance - request.Amount, new CommonStatusResponse(message: "Successful")), Self);
            return;
        }
Пример #3
0
 public DebitAccount(RequestAccountDebit request)
 {
     Request = request;
 }