Fix null payee.

This commit is contained in:
Chris Chen
2026-06-20 18:05:22 -07:00
parent 3558c67fd7
commit 2af169fa60
3 changed files with 42 additions and 9 deletions
@@ -107,11 +107,39 @@ public class ExpenseServiceTests
}
[Fact]
public async Task Create_Reimbursement_AsFinance_IsPendingApproval()
public async Task Create_Reimbursement_AsFinance_OnBehalf_IsPendingApproval_AndLinksPickedMember()
{
// Finance entering on behalf of a member (member explicitly picked) goes straight to the
// approval queue and links the picked member.
var (svc, db, _) = Build();
var id = await svc.CreateAsync(Reimb(), isFinance: true);
Assert.Equal("PendingApproval", (await db.Expenses.FindAsync(id))!.Status);
db.Members.Add(new Member { Id = 9, FirstName_en = "Pat", LastName_en = "Vendor" });
await db.SaveChangesAsync();
var r = Reimb(); r.MemberId = 9;
var id = await svc.CreateAsync(r, isFinance: true);
var e = await db.Expenses.FindAsync(id);
Assert.Equal("PendingApproval", e!.Status);
Assert.Equal(9, e.MemberId);
}
[Fact]
public async Task Create_Reimbursement_AsFinance_SelfService_LinksCallerMember_AndIsDraft()
{
// Regression: a finance/super_admin user filing their OWN reimbursement via "My Reimbursements"
// sends no MemberId. The entry must link to the caller's own member (so the Payee shows their
// legal name) and stay a Draft until they explicitly Submit — not jump to PendingApproval with
// a null member.
var (svc, db, _) = Build("u1");
db.Members.Add(new Member { Id = 7, FirstName_en = "Grace", LastName_en = "Lee" });
db.Users.Add(new AppUser { Id = "u1", MemberId = 7 });
await db.SaveChangesAsync();
var id = await svc.CreateAsync(Reimb(), isFinance: true); // no MemberId on the request
var e = await db.Expenses.FindAsync(id);
Assert.Equal(7, e!.MemberId);
Assert.Equal("Draft", e.Status);
}
[Fact]
+10 -5
View File
@@ -141,12 +141,17 @@ public class ExpenseService : IExpenseService
}
else // StaffReimbursement
{
// Finance entering on behalf of a member goes straight to the approval queue;
// a member's own self-service entry stays a Draft until they explicitly Submit it.
e.Status = isFinance ? "PendingApproval" : "Draft";
// Distinguish the two flows by whether a member was explicitly picked, NOT by role:
// - On-behalf: finance picks a member (Expenses page) -> straight into the approval queue.
// - Self-service: no member picked ("My Reimbursements") -> link to the caller's own member
// so the Payee shows their legal name, and keep it a Draft until they explicitly Submit.
// A finance/super_admin user files their own reimbursements through the self-service flow too,
// so keying off the role alone would leave their entries with a null member (empty Payee).
var isOnBehalf = isFinance && r.MemberId.HasValue;
e.Status = isOnBehalf ? "PendingApproval" : "Draft";
e.SubmittedBy = CurrentUserId;
if (isFinance) e.SubmittedAt = DateTimeOffset.UtcNow;
e.MemberId = isFinance ? r.MemberId : await CallerMemberIdAsync();
if (isOnBehalf) e.SubmittedAt = DateTimeOffset.UtcNow;
e.MemberId = isOnBehalf ? r.MemberId : await CallerMemberIdAsync();
e.VendorName = null;
}