From 4fa35dadc318d981973ec9f62c4a3c7a6ca66505 Mon Sep 17 00:00:00 2001 From: Spencer Twaddle <7374698+stwaddle@users.noreply.github.com> Date: Wed, 6 May 2026 22:24:30 -0500 Subject: [PATCH] Fix reorder: normalize SortOrder after move, remove AsNoTracking from write path The shift-in-place approach broke when SortOrder values had gaps from soft deletes. Switch to remove/insert then assign SortOrder = index, which is correct regardless of initial values. Also fix OutgosController Reorder which had AsNoTracking applied from the M-3 change, silently dropping saves. Co-Authored-By: Claude Sonnet 4.6 --- src/Budget.Api/Controllers/IncomesController.cs | 9 ++++----- src/Budget.Api/Controllers/OutgosController.cs | 11 +++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Budget.Api/Controllers/IncomesController.cs b/src/Budget.Api/Controllers/IncomesController.cs index 2a2879c..87e6c62 100644 --- a/src/Budget.Api/Controllers/IncomesController.cs +++ b/src/Budget.Api/Controllers/IncomesController.cs @@ -105,11 +105,10 @@ public class IncomesController(AppDbContext db, BudgetAuthorizationService authz var oldIdx = incomes.IndexOf(item); var newIdx = Math.Clamp(req.NewIndex, 0, incomes.Count - 1); if (oldIdx == newIdx) return NoContent(); - if (newIdx < oldIdx) - for (int i = newIdx; i < oldIdx; i++) incomes[i].SortOrder++; - else - for (int i = oldIdx + 1; i <= newIdx; i++) incomes[i].SortOrder--; - item.SortOrder = newIdx; + incomes.RemoveAt(oldIdx); + incomes.Insert(newIdx, item); + for (int i = 0; i < incomes.Count; i++) + incomes[i].SortOrder = i; await db.SaveChangesAsync(); return NoContent(); } diff --git a/src/Budget.Api/Controllers/OutgosController.cs b/src/Budget.Api/Controllers/OutgosController.cs index 53512a3..05018c5 100644 --- a/src/Budget.Api/Controllers/OutgosController.cs +++ b/src/Budget.Api/Controllers/OutgosController.cs @@ -116,17 +116,16 @@ public class OutgosController(AppDbContext db, BudgetAuthorizationService authz) { if (TryGetUserId(out var userId) is { } err) return err; if (!await authz.CanWriteAsync(budgetId, userId)) return Forbid(); - var outgos = await db.Outgos.AsNoTracking().Where(o => o.BudgetId == budgetId).OrderBy(o => o.SortOrder).ToListAsync(); + var outgos = await db.Outgos.Where(o => o.BudgetId == budgetId).OrderBy(o => o.SortOrder).ToListAsync(); var item = outgos.FirstOrDefault(o => o.Id == req.Id); if (item is null) return NotFound(); var oldIdx = outgos.IndexOf(item); var newIdx = Math.Clamp(req.NewIndex, 0, outgos.Count - 1); if (oldIdx == newIdx) return NoContent(); - if (newIdx < oldIdx) - for (int i = newIdx; i < oldIdx; i++) outgos[i].SortOrder++; - else - for (int i = oldIdx + 1; i <= newIdx; i++) outgos[i].SortOrder--; - item.SortOrder = newIdx; + outgos.RemoveAt(oldIdx); + outgos.Insert(newIdx, item); + for (int i = 0; i < outgos.Count; i++) + outgos[i].SortOrder = i; await db.SaveChangesAsync(); return NoContent(); }