Files
Spanglish/backend/src/lib/finance/audit.ts
T
MichilisandClaude Opus 5.5 e203fb6c74 Add per-event finance, event team permissions and public door sales state.
Event finance
- Finance tab on the event page: P&L summary with a revenue-to-result
  waterfall, costs and other income in one ledger, and the partner split
  with payouts. Lifecycle stepper (Selling, Adding costs, Ready to close,
  Finalized) with what is left to do; closing the books goes through a
  checklist dialog that freezes the numbers.
- Expense modal shows only the fields each calculation type needs, a live
  preview with the event's real counts, and a category suggested from the
  description. Date inputs follow the UI language.
- Global Finance page: profit per event with outliers clipped and labelled,
  and a "Ready to close" list of past events whose books are still open.
- Calculation service (integer PYG, basis points) with pinned regression
  scenarios; PYG formatting centralised in lib/money with locale-aware
  separators.

Event team permissions
- Per-event members with role presets and requireEventPermission; the
  header stat "Confirmed" is relabelled "Not checked in yet", which is
  what it counts.

Public sales state
- One sales state (online, door, sold out, ended, external, cancelled) for
  the event page, listings and JSON-LD, with the door price and tenders
  shown only while people can still pay at the door.

Frontend unit tests run with vitest (npm test in frontend/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:11:46 +00:00

33 lines
1.1 KiB
TypeScript

// finance_audit_log rows. Returned as TxOps so every change and its audit row
// commit (or roll back) together.
import { financeAuditLog } from '../../db/index.js';
import { generateId, getNow } from '../utils.js';
import { insertOp, type TxOp } from '../txOps.js';
export type FinanceEntity =
| 'expense' | 'other_income' | 'partner' | 'finance_state' | 'member'
| 'expense_category' | 'expense_template' | 'expense_template_pack' | 'payment_fee';
export function financeAuditOp(entry: {
eventId: string | null;
actorUserId: string;
entityType: FinanceEntity;
entityId: string | null;
action: string;
before?: unknown;
after?: unknown;
}): TxOp {
return insertOp(financeAuditLog, {
id: generateId(),
eventId: entry.eventId,
actorUserId: entry.actorUserId,
entityType: entry.entityType,
entityId: entry.entityId,
action: entry.action,
beforeJson: entry.before === undefined || entry.before === null ? null : JSON.stringify(entry.before),
afterJson: entry.after === undefined || entry.after === null ? null : JSON.stringify(entry.after),
createdAt: getNow(),
});
}