From 9497812bf5ff77d43edc68ef54df291419232879 Mon Sep 17 00:00:00 2001 From: Catherine Renelle <32781029+catrenelle@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:35:57 -0500 Subject: [PATCH] Fix Reports 400 errors: normalize DateTime to UTC and restore camelCase JSON Query-bound DateTimes have Kind=Unspecified which Npgsql rejects for timestamptz columns, causing InvalidOperationException caught as 400. Also add explicit CamelCase naming policy lost when AddJsonOptions was introduced for the enum converter. Co-Authored-By: Claude Opus 4.6 --- src/Purrse.Api/Controllers/ReportsController.cs | 11 +++++++---- src/Purrse.Api/Program.cs | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Purrse.Api/Controllers/ReportsController.cs b/src/Purrse.Api/Controllers/ReportsController.cs index 60fdc67..bee1877 100644 --- a/src/Purrse.Api/Controllers/ReportsController.cs +++ b/src/Purrse.Api/Controllers/ReportsController.cs @@ -18,17 +18,20 @@ public class ReportsController : ControllerBase [HttpGet("spending-by-category")] public async Task> SpendingByCategory([FromQuery] DateTime startDate, [FromQuery] DateTime endDate, [FromQuery] Guid? accountId) - => Ok(await _reportService.GetSpendingByCategoryAsync(UserId, startDate, endDate, accountId)); + => Ok(await _reportService.GetSpendingByCategoryAsync(UserId, Utc(startDate), Utc(endDate), accountId)); [HttpGet("income-vs-expense")] public async Task> IncomeVsExpense([FromQuery] DateTime startDate, [FromQuery] DateTime endDate) - => Ok(await _reportService.GetIncomeVsExpenseAsync(UserId, startDate, endDate)); + => Ok(await _reportService.GetIncomeVsExpenseAsync(UserId, Utc(startDate), Utc(endDate))); [HttpGet("net-worth")] public async Task> NetWorth([FromQuery] DateTime startDate, [FromQuery] DateTime endDate) - => Ok(await _reportService.GetNetWorthAsync(UserId, startDate, endDate)); + => Ok(await _reportService.GetNetWorthAsync(UserId, Utc(startDate), Utc(endDate))); [HttpGet("cash-flow")] public async Task> CashFlow([FromQuery] DateTime startDate, [FromQuery] DateTime endDate) - => Ok(await _reportService.GetCashFlowAsync(UserId, startDate, endDate)); + => Ok(await _reportService.GetCashFlowAsync(UserId, Utc(startDate), Utc(endDate))); + + private static DateTime Utc(DateTime dt) => + dt.Kind == DateTimeKind.Unspecified ? DateTime.SpecifyKind(dt, DateTimeKind.Utc) : dt; } diff --git a/src/Purrse.Api/Program.cs b/src/Purrse.Api/Program.cs index 14865c1..e64724b 100644 --- a/src/Purrse.Api/Program.cs +++ b/src/Purrse.Api/Program.cs @@ -105,6 +105,7 @@ builder.Services.AddSignalR(); builder.Services.AddControllers() .AddJsonOptions(options => { + options.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase; options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()); }); builder.Services.AddEndpointsApiExplorer();