fix: normalizeRoom converts NUMERIC baseRate string to number in api.ts
PostgreSQL NUMERIC columns are returned as strings by node-postgres. Added normalizeRoom() that coerces baseRate/hourlyRate/floor to JS numbers before they reach components, eliminating string concatenation bugs like "300005000.005000.00" in price calculations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -337,13 +337,14 @@ export function BookingModal({ open, draft, rooms, bookings = [], onClose, onSav
|
|||||||
if (!room || !form.checkIn || nightCount <= 0) return 0
|
if (!room || !form.checkIn || nightCount <= 0) return 0
|
||||||
const catId = room.categoryId
|
const catId = room.categoryId
|
||||||
const catOverrides = catId ? priceOverrides?.[catId] : undefined
|
const catOverrides = catId ? priceOverrides?.[catId] : undefined
|
||||||
const fallback = room.baseRate ?? 0
|
// room.baseRate is NUMERIC in DB — pg returns it as a string; always coerce
|
||||||
|
const fallback = Number(room.baseRate) || 0
|
||||||
if (!catOverrides) return fallback * nightCount
|
if (!catOverrides) return fallback * nightCount
|
||||||
let sum = 0
|
let sum = 0
|
||||||
for (let i = 0; i < nightCount; i++) {
|
for (let i = 0; i < nightCount; i++) {
|
||||||
const d = format(addDays(parseISO(form.checkIn), i), 'yyyy-MM-dd')
|
const d = format(addDays(parseISO(form.checkIn), i), 'yyyy-MM-dd')
|
||||||
const p = catOverrides[d]
|
const p = Number(catOverrides[d])
|
||||||
sum += (typeof p === 'number' && isFinite(p)) ? p : fallback
|
sum += (isFinite(p) && p > 0) ? p : fallback
|
||||||
}
|
}
|
||||||
return sum
|
return sum
|
||||||
})()
|
})()
|
||||||
|
|||||||
@@ -119,6 +119,15 @@ const CHANNEL_DISPLAY_NAMES: Record<string, string> = {
|
|||||||
onetwotrip: 'OneTwoTrip',
|
onetwotrip: 'OneTwoTrip',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeRoom(r: Room): Room {
|
||||||
|
return {
|
||||||
|
...r,
|
||||||
|
baseRate: Number(r.baseRate) || 0,
|
||||||
|
hourlyRate: r.hourlyRate != null ? Number(r.hourlyRate) : undefined,
|
||||||
|
floor: Number(r.floor) || 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeChannel(raw: Record<string, unknown>): Channel {
|
function normalizeChannel(raw: Record<string, unknown>): Channel {
|
||||||
const name = raw.name as ChannelName
|
const name = raw.name as ChannelName
|
||||||
return {
|
return {
|
||||||
@@ -171,13 +180,13 @@ export const api = {
|
|||||||
// ── Rooms ─────────────────────────────────────────────────────────────────
|
// ── Rooms ─────────────────────────────────────────────────────────────────
|
||||||
rooms: {
|
rooms: {
|
||||||
list: (slug: string) =>
|
list: (slug: string) =>
|
||||||
req<Room[]>('GET', `/api/hotels/${slug}/rooms`),
|
req<Room[]>('GET', `/api/hotels/${slug}/rooms`).then(rooms => rooms.map(normalizeRoom)),
|
||||||
|
|
||||||
create: (slug: string, data: RoomPayload) =>
|
create: (slug: string, data: RoomPayload) =>
|
||||||
req<Room>('POST', `/api/hotels/${slug}/rooms`, toRoomPayload(data)),
|
req<Room>('POST', `/api/hotels/${slug}/rooms`, toRoomPayload(data)).then(normalizeRoom),
|
||||||
|
|
||||||
update: (slug: string, id: string, data: Partial<RoomPayload>) =>
|
update: (slug: string, id: string, data: Partial<RoomPayload>) =>
|
||||||
req<Room>('PATCH', `/api/hotels/${slug}/rooms/${id}`, toRoomPayload(data)),
|
req<Room>('PATCH', `/api/hotels/${slug}/rooms/${id}`, toRoomPayload(data)).then(normalizeRoom),
|
||||||
|
|
||||||
delete: (slug: string, id: string) =>
|
delete: (slug: string, id: string) =>
|
||||||
req<void>('DELETE', `/api/hotels/${slug}/rooms/${id}`),
|
req<void>('DELETE', `/api/hotels/${slug}/rooms/${id}`),
|
||||||
|
|||||||
Reference in New Issue
Block a user