public static async Task <Tuple <string, bool> > AccrueInterestForSpecificAccount ([ActivityTrigger] IDurableActivityContext accrueInterestContext) { const decimal DEBIT_INTEREST_RATE = 0.001M; const decimal CREDIT_INTEREST_RATE = 0.0005M; string accountNumber = accrueInterestContext.GetInput <string>(); #region Tracing telemetry Activity.Current.AddTag("Account Number", accountNumber); #endregion if (!string.IsNullOrEmpty(accountNumber)) { EventStream bankAccountEvents = new EventStream(new EventStreamAttribute("Bank", "Account", accountNumber)); if (await bankAccountEvents.Exists()) { // Has the accrual been done today for this account? Classification clsAccruedToday = new Classification(new ClassificationAttribute("Bank", "Account", accountNumber, nameof(InterestAccruedToday))); ClassificationResponse isAccrued = await clsAccruedToday.Classify <InterestAccruedToday>(); if (isAccrued.Result != ClassificationResponse.ClassificationResults.Include) { // Get the account balance Projection prjBankAccountBalance = new Projection(new ProjectionAttribute("Bank", "Account", accountNumber, nameof(Balance))); // Get the current account balance, as at midnight Balance projectedBalance = await prjBankAccountBalance.Process <Balance>(DateTime.Today); if (null != projectedBalance) { Account.Events.InterestAccrued evAccrued = new Account.Events.InterestAccrued() { Commentary = $"Daily scheduled interest accrual", AccrualEffectiveDate = DateTime.Today // set the accrual to midnight today }; // calculate the accrual amount if (projectedBalance.CurrentBalance >= 0) { // Using the credit rate evAccrued.AmountAccrued = CREDIT_INTEREST_RATE * projectedBalance.CurrentBalance; evAccrued.InterestRateInEffect = CREDIT_INTEREST_RATE; } else { // Use the debit rate evAccrued.AmountAccrued = DEBIT_INTEREST_RATE * projectedBalance.CurrentBalance; evAccrued.InterestRateInEffect = DEBIT_INTEREST_RATE; } try { await bankAccountEvents.AppendEvent(evAccrued, isAccrued.AsOfSequence); } catch (EventSourcingOnAzureFunctions.Common.EventSourcing.Exceptions.EventStreamWriteException) { // We can't be sure this hasn't already run... return(new Tuple <string, bool>(accountNumber, false)); } } } } } return(new Tuple <string, bool>(accountNumber, true)); }
public static async Task <HttpResponseMessage> AccrueInterestRun( [HttpTrigger(AuthorizationLevel.Function, "POST", Route = @"AccrueInterest/{accountnumber}")] HttpRequestMessage req, string accountnumber, [EventStream("Bank", "Account", "{accountnumber}")] EventStream bankAccountEvents, [Projection("Bank", "Account", "{accountnumber}", nameof(Balance))] Projection prjBankAccountBalance, [Classification("Bank", "Account", "{accountnumber}", nameof(InterestAccruedToday))] Classification clsAccruedToday) { // Set the start time for how long it took to process the message DateTime startTime = DateTime.UtcNow; if (!await bankAccountEvents.Exists()) { // You cannot accrue interest if the account does not exist return(req.CreateResponse <ProjectionFunctionResponse>(System.Net.HttpStatusCode.Forbidden, ProjectionFunctionResponse.CreateResponse(startTime, true, $"Account {accountnumber} does not exist", 0))); } ClassificationResponse isAccrued = await clsAccruedToday.Classify <InterestAccruedToday>(); if (isAccrued.Result == ClassificationResponse.ClassificationResults.Include) { // The accrual for today has been performed for this account return(req.CreateResponse <ProjectionFunctionResponse>(System.Net.HttpStatusCode.Forbidden, ProjectionFunctionResponse.CreateResponse(startTime, true, $"Interest accrual already done on account {accountnumber} today", isAccrued.AsOfSequence), FunctionResponse.MEDIA_TYPE )); } // get the request body... InterestAccrualData data = await req.Content.ReadAsAsync <InterestAccrualData>(); // Get the current account balance, as at midnight Balance projectedBalance = await prjBankAccountBalance.Process <Balance>(DateTime.Today); if (null != projectedBalance) { Account.Events.InterestAccrued evAccrued = new Account.Events.InterestAccrued() { Commentary = data.Commentary, AccrualEffectiveDate = DateTime.Today // set the accrual to midnight today }; if (projectedBalance.CurrentBalance >= 0) { // Using the credit rate evAccrued.AmountAccrued = data.CreditInterestRate * projectedBalance.CurrentBalance; } else { // Use the debit rate evAccrued.AmountAccrued = data.DebitInterestRate * projectedBalance.CurrentBalance; } try { await bankAccountEvents.AppendEvent(evAccrued, isAccrued.AsOfSequence); } catch (EventSourcingOnAzureFunctions.Common.EventSourcing.Exceptions.EventStreamWriteException exWrite) { return(req.CreateResponse <ProjectionFunctionResponse>(System.Net.HttpStatusCode.Forbidden, ProjectionFunctionResponse.CreateResponse(startTime, true, $"Failed to write interest accrual event {exWrite.Message}", projectedBalance.CurrentSequenceNumber), FunctionResponse.MEDIA_TYPE)); } return(req.CreateResponse <ProjectionFunctionResponse>(System.Net.HttpStatusCode.OK, ProjectionFunctionResponse.CreateResponse(startTime, false, $"Interest accrued for account {accountnumber} is {evAccrued.AmountAccrued}", projectedBalance.CurrentSequenceNumber), FunctionResponse.MEDIA_TYPE)); } else { return(req.CreateResponse <ProjectionFunctionResponse>(System.Net.HttpStatusCode.Forbidden, ProjectionFunctionResponse.CreateResponse(startTime, true, $"Unable to get current balance for account {accountnumber} for interest accrual", 0), FunctionResponse.MEDIA_TYPE )); } }