feat: scheduled report digests + per-session capacity (M33)

POST /v1/internal/jobs/reports/dispatch emails each tenant owner a
trailing-window digest (bookings, revenue, per-service and per-staff
breakdown) localized to the tenant locale; zero-activity tenants are
skipped. Metrics cores extracted to tenant-scoped variants so the job
needs no principal.

PATCH /v1/catalog/class-sessions/:id sets a session's seat count with
a confirmed-bookings floor — shrinking below it needs allowOverCapacity,
matching the waitlist-promotion convention. Session rows gain an inline
capacity editor with an overbook confirm state.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-22 21:29:44 +02:00
1 parent e6f1ec38a4
commit e680e0c2b8
14 files changed
+808 -10

No files matched your search

+13
View File
@@ -2,6 +2,8 @@ package db
import (
"context"
"github.com/jackc/pgx/v5"
)
const classTemplateColumns = "id, tenant_id, title, duration_minutes, capacity, created_at"
@@ -132,3 +134,14 @@ func (r *PGRepository) DeleteClassSession(ctx context.Context, sessionID string)
_, err := r.pool.Exec(ctx, `DELETE FROM class_sessions WHERE id = $1`, sessionID)
return err
}
func (r *PGRepository) UpdateClassSessionCapacity(ctx context.Context, sessionID string, capacity int) (ClassSessionRecord, error) {
tag, err := r.pool.Exec(ctx, `UPDATE class_sessions SET capacity = $2 WHERE id = $1`, sessionID, capacity)
if err != nil {
return ClassSessionRecord{}, err
}
if tag.RowsAffected() == 0 {
return ClassSessionRecord{}, pgx.ErrNoRows
}
return r.GetClassSessionByID(ctx, sessionID)
}