1
0
mirror of https://github.com/bitwarden/server synced 2025-12-26 05:03:18 +00:00

[PM 19727] Update InvoiceUpcoming email content (#6168)

* changes to implement the email

* Refactoring and fix the unit testing

* refactor the code and remove used method

* Fix the failing test

* Update the email templates

* remove the extra space here

* Refactor the descriptions

* Fix the wrong subject header

* Add the in the hyperlink rather than just Help center
This commit is contained in:
cyprain-okeke
2025-09-03 20:33:32 +05:30
committed by GitHub
parent 1dade9d4b8
commit fa8d65cc1f
10 changed files with 914 additions and 1 deletions

View File

@@ -0,0 +1,76 @@
using System.Text.RegularExpressions;
using Stripe;
namespace Bit.Core.Billing.Extensions;
public static class InvoiceExtensions
{
/// <summary>
/// Formats invoice line items specifically for provider invoices, standardizing product descriptions
/// and ensuring consistent tax representation.
/// </summary>
/// <param name="invoice">The Stripe invoice containing line items</param>
/// <param name="subscription">The associated subscription (for future extensibility)</param>
/// <returns>A list of formatted invoice item descriptions</returns>
public static List<string> FormatForProvider(this Invoice invoice, Subscription subscription)
{
var items = new List<string>();
// Return empty list if no line items
if (invoice.Lines == null)
{
return items;
}
foreach (var line in invoice.Lines.Data ?? new List<InvoiceLineItem>())
{
// Skip null lines or lines without description
if (line?.Description == null)
{
continue;
}
var description = line.Description;
// Handle Provider Portal and Business Unit Portal service lines
if (description.Contains("Provider Portal") || description.Contains("Business Unit"))
{
var priceMatch = Regex.Match(description, @"\(at \$[\d,]+\.?\d* / month\)");
var priceInfo = priceMatch.Success ? priceMatch.Value : "";
var standardizedDescription = $"{line.Quantity} × Manage service provider {priceInfo}";
items.Add(standardizedDescription);
}
// Handle tax lines
else if (description.ToLower().Contains("tax"))
{
var priceMatch = Regex.Match(description, @"\(at \$[\d,]+\.?\d* / month\)");
var priceInfo = priceMatch.Success ? priceMatch.Value : "";
// If no price info found in description, calculate from amount
if (string.IsNullOrEmpty(priceInfo) && line.Quantity > 0)
{
var pricePerItem = (line.Amount / 100m) / line.Quantity;
priceInfo = $"(at ${pricePerItem:F2} / month)";
}
var taxDescription = $"{line.Quantity} × Tax {priceInfo}";
items.Add(taxDescription);
}
// Handle other line items as-is
else
{
items.Add(description);
}
}
// Add fallback tax from invoice-level tax if present and not already included
if (invoice.Tax.HasValue && invoice.Tax.Value > 0)
{
var taxAmount = invoice.Tax.Value / 100m;
items.Add($"1 × Tax (at ${taxAmount:F2} / month)");
}
return items;
}
}