Keep event revenue at what was paid when the ticket price changes.
The event header revenue and the door summary's pre-sale total were computed as settled tickets × the current event price, so editing the price rewrote revenue for tickets already sold. They now sum the paid payment amounts; the header no longer falls back to count × price. Admin analytics per-event revenue gets the same fix. Bookings that haven't been paid yet owe the current price, so a price or currency change now reprices open `pending` payments in the same transaction as the event update. Paid/refunded history, pending_approval, on_hold and Lightning invoices keep their amount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e203fb6c74
commit
b3584e6c4d
@@ -2,7 +2,7 @@ import { Hono } from 'hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { z } from 'zod';
|
||||
import { db, dbGet, dbAll, events, eventSlugAliases, tickets, payments, eventPaymentOverrides, emailLogs, invoices, siteSettings, isPostgres } from '../db/index.js';
|
||||
import { eq, desc, and, gte, sql } from 'drizzle-orm';
|
||||
import { eq, ne, desc, and, gte, inArray, sql } from 'drizzle-orm';
|
||||
import { requireAuth, getAuthUser } from '../lib/auth.js';
|
||||
import { requireEventPermission, canSeeAttendeePii, redactAttendee } from '../lib/eventPermissions.js';
|
||||
import { generateId, getNow, convertBooleansForDb, toDbDate, toDbDateTz, calculateAvailableSeats } from '../lib/utils.js';
|
||||
@@ -13,6 +13,7 @@ import { resolvePresaleClosure } from '../lib/presale.js';
|
||||
import { canSeeWalkInPrice, parseWalkInPrice } from '../lib/walkInPrice.js';
|
||||
import { publicSalesFields } from '../lib/salesState.js';
|
||||
import { loadDoorMethods } from '../lib/doorPayments.js';
|
||||
import { runOps, updateOp, type TxOp } from '../lib/txOps.js';
|
||||
|
||||
interface UserContext {
|
||||
id: string;
|
||||
@@ -615,11 +616,31 @@ eventsRouter.put('/:id', requireEventPermission('edit_event', { globalRoles: ['a
|
||||
updateData.slug = newSlug;
|
||||
}
|
||||
|
||||
await (db as any)
|
||||
.update(events)
|
||||
.set(updateData)
|
||||
.where(eq((events as any).id, id));
|
||||
|
||||
const ops: TxOp[] = [updateOp(events, updateData, eq((events as any).id, id))];
|
||||
|
||||
// Anyone who booked but hasn't paid yet owes the current price, so open
|
||||
// payments follow a price/currency change. Settled payments keep what was
|
||||
// actually paid. Excluded: pending_approval (the customer already sent the
|
||||
// old amount), on_hold (under review) and Lightning (the invoice is fixed).
|
||||
const newPrice = data.price !== undefined ? data.price : Number(existing.price);
|
||||
const newCurrency = data.currency ?? existing.currency;
|
||||
if (newPrice !== Number(existing.price) || newCurrency !== existing.currency) {
|
||||
ops.push(updateOp(
|
||||
payments,
|
||||
{ amount: newPrice, currency: newCurrency, updatedAt: now },
|
||||
and(
|
||||
eq((payments as any).status, 'pending'),
|
||||
ne((payments as any).provider, 'lightning'),
|
||||
inArray(
|
||||
(payments as any).ticketId,
|
||||
(db as any).select({ id: (tickets as any).id }).from(tickets).where(eq((tickets as any).eventId, id))
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
await runOps(ops);
|
||||
|
||||
const updated = await dbGet(
|
||||
(db as any).select().from(events).where(eq((events as any).id, id))
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user