diff --git a/backend/src/index.ts b/backend/src/index.ts index c336ba5..e0efb21 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2962,13 +2962,13 @@ app.post("/api/periods/:periodType/:period/close", requireUser, async (req, res) false AS ticket_closed FROM tickets t JOIN sessions s ON s.ticket_id = t.id - WHERE s.started_at >= $2::timestamptz AND s.started_at < $3::timestamptz - AND s.user_id = $4 + WHERE s.started_at >= $1::timestamptz AND s.started_at < $2::timestamptz + AND s.user_id = $3 GROUP BY t.id, t.ticket_number HAVING COUNT(*) FILTER (WHERE s.billing_status IS NULL) > 0 ORDER BY t.ticket_number ASC; `, - [period.start, period.startIso, period.endIso, userId] + [period.startIso, period.endIso, userId] ); if (blockersResult.rows.length > 0) { diff --git a/backend/src/migrations.ts b/backend/src/migrations.ts index 8c49f9d..0bb27c4 100644 --- a/backend/src/migrations.ts +++ b/backend/src/migrations.ts @@ -7,9 +7,25 @@ async function findMigrationAdmin() { async function ensureClosureOwnership(table: string, column: string, primaryKeyColumns: string[], adminId: string | null) { await query(`ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS user_id BIGINT REFERENCES users(id);`); + if (adminId) { await query(`UPDATE ${table} SET user_id = $1 WHERE user_id IS NULL;`, [adminId]); } + + const duplicateMatchers = [...primaryKeyColumns, "user_id"] + .map((key) => `older.${key} IS NOT DISTINCT FROM newer.${key}`) + .join(" AND "); + + await query(` + DELETE FROM ${table} older + USING ${table} newer + WHERE ${duplicateMatchers} + AND ( + older.closed_at < newer.closed_at + OR (older.closed_at = newer.closed_at AND older.ctid < newer.ctid) + ); + `); + await query(` DO $$ BEGIN