using System.Globalization; using System.Text; namespace General_Ledger_Service.Services.CsvDocuments { // Shared row-building/escaping helper for every report's CSV writer (05_LKAS_Report_PDF_Templates.md // Section 10) - parallel to Services/PdfDocuments/ReportPageHeader.cs, one small shared piece // reused by seven otherwise-independent writer classes, not a base class (CLAUDE.md Rule 2). public class CsvBuilder { private readonly StringBuilder _sb = new(); public CsvBuilder AddRow(params object?[] values) { _sb.AppendLine(string.Join(",", values.Select(Escape))); return this; } public byte[] Build() { // UTF-8 BOM so Excel (the realistic consumer of a downloaded .csv) recognizes the // encoding instead of guessing at a locale-default codepage. var preamble = Encoding.UTF8.GetPreamble(); var body = Encoding.UTF8.GetBytes(_sb.ToString()); var result = new byte[preamble.Length + body.Length]; preamble.CopyTo(result, 0); body.CopyTo(result, preamble.Length); return result; } private static string Escape(object? value) { var text = value switch { null => "", decimal d => d.ToString(CultureInfo.InvariantCulture), DateOnly d => d.ToString("yyyy-MM-dd"), _ => value.ToString() ?? "" }; return text.Contains(',') || text.Contains('"') || text.Contains('\n') ? $"\"{text.Replace("\"", "\"\"")}\"" : text; } } }