Fix null payee.
This commit is contained in:
@@ -107,11 +107,39 @@ public class ExpenseServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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 (svc, db, _) = Build();
|
||||||
var id = await svc.CreateAsync(Reimb(), isFinance: true);
|
db.Members.Add(new Member { Id = 9, FirstName_en = "Pat", LastName_en = "Vendor" });
|
||||||
Assert.Equal("PendingApproval", (await db.Expenses.FindAsync(id))!.Status);
|
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]
|
[Fact]
|
||||||
|
|||||||
@@ -141,12 +141,17 @@ public class ExpenseService : IExpenseService
|
|||||||
}
|
}
|
||||||
else // StaffReimbursement
|
else // StaffReimbursement
|
||||||
{
|
{
|
||||||
// Finance entering on behalf of a member goes straight to the approval queue;
|
// Distinguish the two flows by whether a member was explicitly picked, NOT by role:
|
||||||
// a member's own self-service entry stays a Draft until they explicitly Submit it.
|
// - On-behalf: finance picks a member (Expenses page) -> straight into the approval queue.
|
||||||
e.Status = isFinance ? "PendingApproval" : "Draft";
|
// - 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;
|
e.SubmittedBy = CurrentUserId;
|
||||||
if (isFinance) e.SubmittedAt = DateTimeOffset.UtcNow;
|
if (isOnBehalf) e.SubmittedAt = DateTimeOffset.UtcNow;
|
||||||
e.MemberId = isFinance ? r.MemberId : await CallerMemberIdAsync();
|
e.MemberId = isOnBehalf ? r.MemberId : await CallerMemberIdAsync();
|
||||||
e.VendorName = null;
|
e.VendorName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
<button kendoButton fillMode="flat" themeColor="primary" (click)="print(dataItem)">Print</button>
|
<button kendoButton fillMode="flat" themeColor="primary" (click)="print(dataItem)">Print</button>
|
||||||
<button *ngIf="canSign(dataItem)" kendoButton fillMode="flat" themeColor="success"
|
<button *ngIf="canSign(dataItem)" kendoButton fillMode="flat" themeColor="success"
|
||||||
(click)="openSign(dataItem)">簽收</button>
|
(click)="openSign(dataItem)">簽收</button>
|
||||||
<button *ngIf="dataItem.signed" kendoButton fillMode="flat" (click)="viewSignature(dataItem)">Signature</button>
|
<!-- <button *ngIf="dataItem.signed" kendoButton fillMode="flat" (click)="viewSignature(dataItem)">Signature</button> -->
|
||||||
<button *ngIf="dataItem.signed" kendoButton fillMode="flat" themeColor="primary"
|
<button *ngIf="dataItem.signed" kendoButton fillMode="flat" themeColor="primary"
|
||||||
(click)="printReceipt(dataItem)">收據</button>
|
(click)="printReceipt(dataItem)">收據</button>
|
||||||
<button *ngIf="canVoid(dataItem)" kendoButton fillMode="flat" themeColor="error"
|
<button *ngIf="canVoid(dataItem)" kendoButton fillMode="flat" themeColor="error"
|
||||||
|
|||||||
Reference in New Issue
Block a user