11import { Injectable , Inject , NotFoundException , BadRequestException } from '@nestjs/common' ;
22import { eq , and } from 'drizzle-orm' ;
3+ import Decimal from 'decimal.js' ;
34import { bookings , reservations , guests , ratePlans , roomTypes , folios , rooms } from '@haip/database' ;
45import { DRIZZLE } from '../../database/database.module' ;
56import { AvailabilityService } from '../reservation/availability.service' ;
@@ -60,8 +61,11 @@ export class ConnectBookingService {
6061 const arrival = new Date ( dto . checkIn ) ;
6162 const departure = new Date ( dto . checkOut ) ;
6263 const nights = Math . ceil ( ( departure . getTime ( ) - arrival . getTime ( ) ) / ( 1000 * 60 * 60 * 24 ) ) ;
63- const baseAmount = parseFloat ( ratePlan . baseAmount ) ;
64- const totalAmount = baseAmount * nights ;
64+ // Monetary math via Decimal (baseAmount is a numeric string from PG)
65+ const baseAmountDec = new Decimal ( ratePlan . baseAmount ) ;
66+ const totalAmountDec = baseAmountDec . times ( nights ) ;
67+ const baseAmount = baseAmountDec . toNumber ( ) ;
68+ const totalAmount = totalAmountDec . toNumber ( ) ;
6569
6670 // 5. Generate confirmation number
6771 const confirmationNumber = `HAIP-${ Date . now ( ) . toString ( 36 ) . toUpperCase ( ) } -${ randomUUID ( ) . slice ( 0 , 4 ) . toUpperCase ( ) } ` ;
@@ -91,7 +95,7 @@ export class ConnectBookingService {
9195 nights,
9296 roomTypeId : dto . roomTypeId ,
9397 ratePlanId : dto . ratePlanId ,
94- totalAmount : totalAmount . toString ( ) ,
98+ totalAmount : totalAmountDec . toFixed ( 2 ) ,
9599 currencyCode : ratePlan . currencyCode ,
96100 adults : dto . adults ,
97101 children : dto . children ?? 0 ,
@@ -209,12 +213,12 @@ export class ConnectBookingService {
209213 checkIn : reservation . arrivalDate ,
210214 checkOut : reservation . departureDate ,
211215 roomType : roomType ?. name ?? 'Unknown' ,
212- rateAmount : parseFloat ( reservation . totalAmount ) ,
216+ rateAmount : new Decimal ( reservation . totalAmount ) . toNumber ( ) ,
213217 currencyCode : reservation . currencyCode ,
214218 roomAssigned : ! ! reservation . roomId ,
215219 roomNumber,
216220 folioExists : ! ! folio ,
217- folioBalance : folio ? parseFloat ( folio . balance ) : undefined ,
221+ folioBalance : folio ? new Decimal ( folio . balance ) . toNumber ( ) : undefined ,
218222 lastModified : reservation . updatedAt ?. toISOString ( ) ?? reservation . createdAt . toISOString ( ) ,
219223 verifiedAt : new Date ( ) . toISOString ( ) ,
220224 } ;
@@ -247,8 +251,9 @@ export class ConnectBookingService {
247251 }
248252
249253 const updateFields : Record < string , any > = { updatedAt : new Date ( ) } ;
250- let costDifference = 0 ;
251- const previousAmount = parseFloat ( reservation . totalAmount ) ;
254+ let costDifferenceDec = new Decimal ( 0 ) ;
255+ const previousAmountDec = new Decimal ( reservation . totalAmount ) ;
256+ const previousAmount = previousAmountDec . toNumber ( ) ;
252257
253258 // Handle guest detail updates
254259 if ( dto . guestFirstName || dto . guestLastName ) {
@@ -302,17 +307,17 @@ export class ConnectBookingService {
302307 const arrival = new Date ( newCheckIn ) ;
303308 const departure = new Date ( newCheckOut ) ;
304309 const nights = Math . ceil ( ( departure . getTime ( ) - arrival . getTime ( ) ) / ( 1000 * 60 * 60 * 24 ) ) ;
305- const newTotal = parseFloat ( ratePlan . baseAmount ) * nights ;
310+ const newTotalDec = new Decimal ( ratePlan . baseAmount ) . times ( nights ) ;
306311
307312 updateFields [ 'arrivalDate' ] = newCheckIn ;
308313 updateFields [ 'departureDate' ] = newCheckOut ;
309314 updateFields [ 'nights' ] = nights ;
310315 updateFields [ 'roomTypeId' ] = newRoomTypeId ;
311316 updateFields [ 'ratePlanId' ] = newRatePlanId ;
312- updateFields [ 'totalAmount' ] = newTotal . toString ( ) ;
317+ updateFields [ 'totalAmount' ] = newTotalDec . toFixed ( 2 ) ;
313318 updateFields [ 'currencyCode' ] = ratePlan . currencyCode ;
314319
315- costDifference = newTotal - previousAmount ;
320+ costDifferenceDec = newTotalDec . minus ( previousAmountDec ) ;
316321 }
317322
318323 // Apply update
@@ -336,9 +341,9 @@ export class ConnectBookingService {
336341 confirmationNumber,
337342 reservationId : reservation . id ,
338343 status : updated . status ,
339- previousAmount : Math . round ( previousAmount * 100 ) / 100 ,
340- newAmount : Math . round ( parseFloat ( updated . totalAmount ) * 100 ) / 100 ,
341- costDifference : Math . round ( costDifference * 100 ) / 100 ,
344+ previousAmount : Number ( previousAmountDec . toFixed ( 2 ) ) ,
345+ newAmount : Number ( new Decimal ( updated . totalAmount ) . toFixed ( 2 ) ) ,
346+ costDifference : Number ( costDifferenceDec . toFixed ( 2 ) ) ,
342347 modifiedAt : new Date ( ) . toISOString ( ) ,
343348 } ;
344349 }
@@ -454,15 +459,16 @@ export class ConnectBookingService {
454459 taxRate : number ,
455460 ) {
456461 const breakdown = [ ] ;
462+ const baseAmountDec = new Decimal ( baseAmount ) ;
463+ const taxPerNightDec = baseAmountDec . times ( taxRate ) . div ( 100 ) ;
457464 for ( let i = 0 ; i < nights ; i ++ ) {
458465 const date = new Date ( arrival ) ;
459466 date . setDate ( date . getDate ( ) + i ) ;
460467 const dateStr = date . toISOString ( ) . split ( 'T' ) [ 0 ] ! ;
461- const tax = baseAmount * ( taxRate / 100 ) ;
462468 breakdown . push ( {
463469 date : dateStr ,
464- rate : Math . round ( baseAmount * 100 ) / 100 ,
465- tax : Math . round ( tax * 100 ) / 100 ,
470+ rate : Number ( baseAmountDec . toFixed ( 2 ) ) ,
471+ tax : Number ( taxPerNightDec . toFixed ( 2 ) ) ,
466472 } ) ;
467473 }
468474 return breakdown ;
@@ -476,7 +482,9 @@ export class ConnectBookingService {
476482 }
477483
478484 private calculateCancellationPenalty ( ratePlan : any , reservation : any ) {
479- const totalAmount = parseFloat ( reservation . totalAmount ) ;
485+ // Money math via Decimal; refundAmount / penaltyAmount are displayed as currency
486+ const totalAmountDec = new Decimal ( reservation . totalAmount ) ;
487+ const totalAmount = totalAmountDec . toNumber ( ) ;
480488
481489 // Non-refundable rate
482490 if ( ratePlan ?. type === 'promotional' ) {
@@ -504,11 +512,11 @@ export class ConnectBookingService {
504512
505513 // First night penalty
506514 const nights = reservation . nights || 1 ;
507- const firstNightAmount = totalAmount / nights ;
515+ const firstNightAmountDec = totalAmountDec . div ( nights ) ;
508516 return {
509517 penaltyApplied : true ,
510- penaltyAmount : Math . round ( firstNightAmount * 100 ) / 100 ,
511- refundAmount : Math . round ( ( totalAmount - firstNightAmount ) * 100 ) / 100 ,
518+ penaltyAmount : Number ( firstNightAmountDec . toFixed ( 2 ) ) ,
519+ refundAmount : Number ( totalAmountDec . minus ( firstNightAmountDec ) . toFixed ( 2 ) ) ,
512520 policyDescription : 'First night charge applies — cancelled within 24 hours of check-in.' ,
513521 } ;
514522 }
0 commit comments