using System.Text; using System.Text.Json; using Distribution.Features.GeneralLedger; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Distribution.Features.FinanceReport; /// /// Proxies the General Ledger service's reports (TrialBalance, GeneralLedger, BalanceSheet, ProfitAndLoss, CashFlow, /// BudgetVsActual, TaxSummary). Query parameters are forwarded as-is; Json is returned as the GL data payload and /// Pdf/Csv are returned as downloadable files. /// [ApiController] [Route("api/reports/finance")] public class FinanceReportController(IGeneralLedgerClient gl) : ControllerBase { /// Active GL accounts (code + name) for the report filters' account dropdown. [HttpGet("accounts")] [Authorize("finance-reports.view")] public async Task Accounts(CancellationToken ct) { GeneralLedgerResult result; try { result = await gl.SendAsync(HttpMethod.Get, "v1/accounts?isActive=true", null, ct); } catch (HttpRequestException) { return StatusCode(502, new { error = "The General Ledger service is unreachable." }); } if (!result.Success || GlJson.Data(result) is not { } data) return StatusCode(502, new { error = GlJson.Message(result) ?? "Could not load GL accounts." }); var items = GlJson.Get(data, "items"); var accounts = items is { ValueKind: JsonValueKind.Array } ? items.Value.EnumerateArray() .Select(a => new { accountCode = GlJson.GetString(a, "accountCode") ?? "", accountName = GlJson.GetString(a, "accountName") ?? "" }) .OrderBy(a => a.accountCode, StringComparer.Ordinal) .ToList() : []; return Ok(accounts); } [HttpGet("{reportType}")] [Authorize("finance-reports.view")] public async Task Get(string reportType, [FromQuery] string outputFormat = "Json", CancellationToken ct = default) { var query = new StringBuilder($"v1/reports?reportType={Uri.EscapeDataString(reportType)}&outputFormat={Uri.EscapeDataString(outputFormat)}"); foreach (var (key, values) in Request.Query) { if (key is "reportType" or "outputFormat") continue; foreach (var v in values) query.Append('&').Append(Uri.EscapeDataString(key)).Append('=').Append(Uri.EscapeDataString(v ?? "")); } GeneralLedgerResult result; try { result = await gl.SendAsync(HttpMethod.Get, query.ToString(), null, ct); } catch (HttpRequestException) { return StatusCode(502, new { error = "The General Ledger service is unreachable." }); } if (!result.Success) return StatusCode(result.StatusCode is >= 400 and < 500 ? result.StatusCode : 502, new { error = GlJson.Message(result) ?? "The General Ledger service rejected the request." }); var data = GlJson.Data(result); if (data is null) return StatusCode(502, new { error = "The General Ledger service returned no data." }); if (!outputFormat.Equals("Json", StringComparison.OrdinalIgnoreCase)) { var base64 = GlJson.GetString(data.Value, "contentBase64"); if (base64 is null) return StatusCode(502, new { error = "The General Ledger service returned no file." }); return File(Convert.FromBase64String(base64), GlJson.GetString(data.Value, "contentType") ?? "application/octet-stream", GlJson.GetString(data.Value, "fileName")); } return Content(data.Value.GetRawText(), "application/json"); } }