| Field |
Value |
| Date |
09 Jun 2026 |
| Status |
Complete |
| GitHub Commits |
6637a72, 190dc57 |
Admin tooling improvements: granular invoice deletion, force-delete for paid invoices/billing runs,
portal user management UI, and a full customer password reset flow via email.
Previously, DELETE /api/billing-runs/{id}/delete-run/ was blocked if any invoice had confirmed payments.
Added ?force=true query parameter that also deletes payment records.
- Normal delete: still blocked for paid invoices (safety)
- ?force=true: deletes payment records, line items, CDRs unmarked, billing run deleted
- Error response includes paid_invoices list and payment_count for frontend
- Frontend: two-step confirm dialog showing invoice list and payment count warning
- Commit 6637a72
New DELETE /api/invoices/{id}/delete-invoice/ endpoint:
- Unmarks all associated CDRs (invoiced=False, clears invoice FK)
- Deletes line items and invoice
- Deletes PDF file from disk if present (media/invoices/YC-YYYY-NNNN.pdf)
- Blocked for paid invoices unless ?force=true
- Delete button added to every row on /invoices page
- Same two-step confirm pattern as billing run force-delete
All 11 PDFs on dev and 16 PDFs on prod were orphaned (no matching DB invoice). All removed.
Going forward, delete-invoice endpoint cleans up PDFs automatically.
New page at /portal-users (Configuration -> Portal Users in sidebar):
- Table: email, customer name, active status (toggle), last login
- Add user: email, customer dropdown, password, active/inactive
- Edit user: email, active status
- Set Password: separate modal, min 8 chars
- Delete: confirm dialog
- Backend: PortalUserViewSet at GET/POST/PATCH/DELETE /api/portal-users/
- set-password action: POST /api/portal-users/{id}/set-password/
- Replaces manual shell commands to create portal users
Full forgot/reset password flow for the customer portal:
- POST /api/portal/forgot-password/ — generates 32-char URL-safe token, saves with 1hr expiry, sends branded HTML email. Always returns 200 (no user enumeration).
- POST /api/portal/reset-password/ — validates token + expiry, sets new password, clears token.
Built from request.build_absolute_uri() — automatically uses the correct host (dev vs prod). No SITE_URL config needed.
- /portal/forgot-password — email entry, shows check-your-email confirmation
- /portal/reset-password?token=... — password + confirm fields, auto-redirects to login on success
- Portal login: Forgot password? link added below sign in button
Generated ed25519 SSH key on dev server (crip@yealin-dev).
Public key added to prod ~/.ssh/authorized_keys.
Claude Code can now SSH to prod directly for wiki updates and admin tasks.
- Invoice numbers restart at YC-YYYY-0001 after all invoices are deleted (pending fix: invoice sequence table)
- Portal user management is now a UI at /portal-users — no shell required
- Password reset token expires after 1 hour
- PDF files are deleted from disk when invoice is deleted via the UI
Back to Sessions Index