namespace ROLAC.API.Services.Disbursement; /// /// Converts a monetary amount to the English words used on a check, e.g. /// 1234.56 → "One Thousand Two Hundred Thirty-Four and 56/100 Dollars". /// Pure and dependency-free so it is easily unit-tested. /// public static class AmountToWords { private static readonly string[] Ones = [ "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", ]; private static readonly string[] Tens = [ "", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety", ]; // index 1.. → 10^(3*index) private static readonly string[] Scales = ["", "Thousand", "Million", "Billion", "Trillion"]; public static string Convert(decimal amount) { if (amount < 0) amount = 0m; // Round half-up to cents. amount = Math.Round(amount, 2, MidpointRounding.AwayFromZero); var dollars = (long)Math.Floor(amount); var cents = (int)Math.Round((amount - dollars) * 100m, MidpointRounding.AwayFromZero); var words = dollars == 0 ? "Zero" : ThreeDigitGroupsToWords(dollars); return $"{words} and {cents:00}/100 Dollars"; } private static string ThreeDigitGroupsToWords(long n) { // Split into groups of three digits, low to high. var groups = new List(); while (n > 0) { groups.Add((int)(n % 1000)); n /= 1000; } var parts = new List(); for (var i = groups.Count - 1; i >= 0; i--) { if (groups[i] == 0) continue; var group = HundredsToWords(groups[i]); var scale = Scales[i]; parts.Add(string.IsNullOrEmpty(scale) ? group : $"{group} {scale}"); } return string.Join(" ", parts); } private static string HundredsToWords(int n) { var parts = new List(); if (n >= 100) { parts.Add($"{Ones[n / 100]} Hundred"); n %= 100; } if (n >= 20) { var t = Tens[n / 10]; var o = n % 10; parts.Add(o == 0 ? t : $"{t}-{Ones[o]}"); } else if (n > 0) { parts.Add(Ones[n]); } return string.Join(" ", parts); } }