Final-review findings:
- ExpenseCategoriesController was finance-only at the class level, but the member
self-service reimbursement form reads the category list to populate its dropdown,
so members got 403 and could not submit. Open GET to any authenticated user;
keep group/subcategory writes finance-only (mirrors MinistriesController).
Verified live with a member-role account: reads 200, writes 403, self-submit 200.
- MonthlyStatementService Update/Finalize now use FirstOrDefaultAsync for
convention consistency with the rest of the service layer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live verification revealed the JWT carries the user id in the 'sub' claim
(NameClaimType=sub, MapInboundClaims=false), so ClaimTypes.NameIdentifier is
null at runtime. This caused ExpensesController.GetMine/GetById to throw
NullReferenceException (500) on the '!.Value', and made the services fall back
to 'system' — silently defeating the self-ownership guard. Resolve via
NameIdentifier (unit tests) then 'sub' (real tokens). Adds a regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>