diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/StatementBuilderTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/StatementBuilderTests.cs index 4294bdbc..aa17a70a 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/StatementBuilderTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/StatementBuilderTests.cs @@ -58,6 +58,8 @@ public async Task GetStatementHtml_ReturnsHtmlWithReplacedTokens() { html.ShouldContain(this._merchant.Addresses.First().AddressLine1); html.ShouldContain("Transactions"); html.ShouldContain("Fees"); + html.ShouldContain("01/05/2025"); + html.ShouldContain("02/05/2025"); html.ShouldContain("100"); html.ShouldContain("10"); html.ShouldContain("body{color:red;}"); @@ -131,4 +133,4 @@ public async Task GetStatementHtml_ThrowsIfMerchantContactMissing() { await Should.ThrowAsync(async () => { await _builder.GetStatementHtml(merchantStatementAggregate, merchant, _cancellationToken); }); } } -} \ No newline at end of file +} diff --git a/TransactionProcessor.BusinessLogic/Services/StatementBuilder.cs b/TransactionProcessor.BusinessLogic/Services/StatementBuilder.cs index f92151cb..429e08cf 100644 --- a/TransactionProcessor.BusinessLogic/Services/StatementBuilder.cs +++ b/TransactionProcessor.BusinessLogic/Services/StatementBuilder.cs @@ -67,8 +67,8 @@ public async Task> GetStatementHtml(MerchantStatementAggregate st return Result.Invalid("Statement has not yet been generated"); String mainHtml = await this.FileSystem.File.ReadAllTextAsync($"{path}/Templates/Email/statement.html", cancellationToken); - - var anonymousHeader = new { + List<(Int32 lineNumber, MerchantStatementLine statementLine)> statementLines = statementHeader.GetStatementLines(); + mainHtml = this.ReplaceTokens(mainHtml, new { StatementId = statementAggregate.AggregateId, EstateName = "Demo Estate", MerchantName = merchant.MerchantName, @@ -79,46 +79,52 @@ public async Task> GetStatementHtml(MerchantStatementAggregate st MerchantPostcode = merchant.Addresses.First().PostalCode, MerchantContactNumber = merchant.Contacts.First().ContactPhoneNumber, StatementDate = statementHeader.StatementDate, - }; - - List<(Int32 lineNumber, MerchantStatementLine statementLine)> statementLines = statementHeader.GetStatementLines(); + }); + mainHtml = this.ReplaceTokens(mainHtml, new { + StatementTotal = statementLines.Sum(sl => sl.statementLine.Amount), + TransactionsValue = statementLines.Where(sl => sl.statementLine.LineType == 1).Sum(sl => sl.statementLine.Amount), + TransactionFeesValue = statementLines.Where(sl => sl.statementLine.LineType == 2).Sum(sl => sl.statementLine.Amount) + }); + mainHtml = mainHtml.Replace("[StatementLinesData]", await this.BuildStatementLinesHtml(path, statementLines, cancellationToken)); - var anonymousFooter = new { StatementTotal = statementLines.Sum(sl => sl.statementLine.Amount), TransactionsValue = statementLines.Where(sl => sl.statementLine.LineType == 1).Sum(sl => sl.statementLine.Amount), TransactionFeesValue = statementLines.Where(sl => sl.statementLine.LineType == 2).Sum(sl => sl.statementLine.Amount) }; + mainHtml = await this.AddCSSToHtml(mainHtml, "{bootstrapcss}", "bootstrap/css/bootstrap.min.css", cancellationToken); + mainHtml = await this.AddCSSToHtml(mainHtml, "{fontawesomemincss}", "fontawesome/css/fontawesome.min.css", cancellationToken); + mainHtml = await this.AddCSSToHtml(mainHtml, "{fontawesomesolidcss}", "fontawesome/css/solid.css", cancellationToken); - // Statement header class first - PropertyInfo[] statementHeaderProperties = anonymousHeader.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + return Result.Success(mainHtml); + } - // Do the replaces for the transaction - foreach (PropertyInfo propertyInfo in statementHeaderProperties) { - mainHtml = mainHtml.Replace($"[{propertyInfo.Name}]", propertyInfo.GetValue(anonymousHeader)?.ToString()); + private async Task BuildStatementLinesHtml(IDirectoryInfo path, + List<(Int32 lineNumber, MerchantStatementLine statementLine)> statementLines, + CancellationToken cancellationToken) + { + StringBuilder lines = new StringBuilder(); + String lineTemplate = await this.FileSystem.File.ReadAllTextAsync($"{path}/Templates/Email/statementline.html", cancellationToken); + + foreach ((Int32 lineNumber, MerchantStatementLine statementLine) statementLineContainer in statementLines) { + String populatedLine = this.ReplaceTokens(lineTemplate, new { + StatementLineNumber = statementLineContainer.lineNumber + 1, + StatementLineDate = statementLineContainer.statementLine.DateTime.ToString("dd/MM/yyyy"), + StatementLineDescription = statementLineContainer.statementLine.Description, + StatementLineAmount = statementLineContainer.statementLine.Amount + }); + lines.Append(populatedLine); } - PropertyInfo[] statementFooterProperties = anonymousFooter.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + return lines.ToString(); + } - // Do the replaces for the transaction - foreach (PropertyInfo propertyInfo in statementFooterProperties) { - mainHtml = mainHtml.Replace($"[{propertyInfo.Name}]", propertyInfo.GetValue(anonymousFooter)?.ToString()); - } + private String ReplaceTokens(String html, + Object tokenSource) + { + String result = html; + PropertyInfo[] properties = tokenSource.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - StringBuilder lines = new StringBuilder(); - String lineHtml = await this.FileSystem.File.ReadAllTextAsync($"{path}/Templates/Email/statementline.html", cancellationToken); - foreach ((Int32 lineNumber, MerchantStatementLine statementLine) statementLineContainer in statementHeader.GetStatementLines()) { - var anonymousLine = new { StatementLineNumber = statementLineContainer.lineNumber + 1, StatementLineDate = statementLineContainer.statementLine.DateTime.ToString("dd/MM/yyyy"), StatementLineDescription = statementLineContainer.statementLine.Description, StatementLineAmount = statementLineContainer.statementLine.Amount }; - PropertyInfo[] statementLineProperties = anonymousLine.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo propertyInfo in statementLineProperties) { - lineHtml = lineHtml.Replace($"[{propertyInfo.Name}]", propertyInfo.GetValue(anonymousLine)?.ToString()); - } - - lines.Append(lineHtml); + foreach (PropertyInfo propertyInfo in properties) { + result = result.Replace($"[{propertyInfo.Name}]", propertyInfo.GetValue(tokenSource)?.ToString()); } - mainHtml = mainHtml.Replace("[StatementLinesData]", lines.ToString()); - - mainHtml = await this.AddCSSToHtml(mainHtml, "{bootstrapcss}", "bootstrap/css/bootstrap.min.css", cancellationToken); - mainHtml = await this.AddCSSToHtml(mainHtml, "{fontawesomemincss}", "fontawesome/css/fontawesome.min.css", cancellationToken); - mainHtml = await this.AddCSSToHtml(mainHtml, "{fontawesomesolidcss}", "fontawesome/css/solid.css", cancellationToken); - - return Result.Success(mainHtml); + return result; } private async Task AddCSSToHtml(String html, String tag, String fileName, CancellationToken cancellationToken) {