import * as moment from 'moment'; import * as _ from 'lodash'; import { Models } from './Models'; export namespace Helpers { export function getFullName(user: Models.IUser) { return `${user.firstName} ${user.lastName}`; } export function getPricesForTripDays(property: Models.IProperty, arrivalDay: string, departureDay: string): number[] { if (property.pricing === null) { throw new Error( `Property ${property._id} does not have pricing set.` ); } const prices: number[] = []; let temp = moment(arrivalDay); while (temp.isBefore(departureDay, "day")) { const isWeekend = temp.weekday() === 5 || temp.weekday() === 6; const range = Helpers.findApplicableRange( property.pricing.customRanges, temp ); if (range !== null) { prices.push(range.price); } else if (isWeekend) { prices.push(property.pricing.weekendDefault); } else { prices.push(property.pricing.weekdayDefault); } temp.add(1, "day"); } return prices; } export function getBookingCost(property: Models.IProperty, arrivalDay: string, departureDay: string, serviceFee: number, hostFee: number) { if (property.pricing === null) { throw new Error( `Property ${property._id} does not have pricing set.` ); } const prices = getPricesForTripDays(property, arrivalDay, departureDay); let bookingFee = 0; for(const price of prices) { bookingFee += price; } const cleaningFee = property.pricing.cleaningFee; const bookingCost: Models.IBookingCost = { propertyId: property._id, arrivalDay, departureDay, bookingFee: bookingFee, cleaningFee, serviceFee: (bookingFee + cleaningFee) * serviceFee, hostFee: (bookingFee + cleaningFee) * hostFee }; return bookingCost; } export function getHostTotal(bookingCost: Models.IBookingCost) { return bookingCost.cleaningFee + bookingCost.bookingFee - bookingCost.hostFee; } export function getPricingValue(weekdayDefault: number, weekendDefault: number, cleaningFee: number) { return weekdayDefault + weekendDefault + cleaningFee; } export function getPricingValueByDates(property: Models.IFilteredProperty, arrival: string, departure: string, serviceFee: number, hostFee: number) { const bookingCost = Helpers.getBookingCost(property, arrival, departure, serviceFee, hostFee); const pricingValue = Helpers.getTotal(bookingCost); return pricingValue; } export function getTotal(bookingCost: Models.IBookingCost) { return bookingCost.cleaningFee + bookingCost.serviceFee + bookingCost.bookingFee; } export function getDateRangeStr(start: moment.Moment | null, end: moment.Moment | null) { if (start === null || end === null) { return undefined; } if(start.isSame(end, 'day')) { return `${start.format('MMMM D')}`; } if (start.isSame(end, 'month')) { return `${start.format('MMMM D')} - ${end.format('D')}`; } else { return `${start.format('MMMM D')} - ${end.format('MMMM D')}`; } } /** * Used to validate a new custom price range on a property. * @param a Custom Price Range * @param b Custom Price Range */ function isConflicting(a: Models.ICustomPriceRange, b: Models.ICustomPriceRange) { return moment(a.startDay).isBetween(b.startDay, b.endDay) || moment(a.endDay).isBetween(b.startDay, b.endDay); } /** * Used to validate a new custom price range on a property. * @param ranges * @param addition */ export function isValidAddition(ranges: Models.ICustomPriceRange[], addition: Models.ICustomPriceRange) { for (let i = 0; i < ranges.length; i++) { if (isConflicting(ranges[i], addition)) { return `There is conflict with price range on ${getDateRangeStr(moment(ranges[i].startDay), moment(ranges[i].endDay))}`; } } return null; } /** * Used when computing trip cost to see if certain trip days fall into custom price range. * @param ranges * @param m */ export function findApplicableRange(ranges: Models.ICustomPriceRange[], m: moment.Moment) { for (let i = 0; i < ranges.length; i++) { if (m.isSameOrAfter(ranges[i].startDay, 'day') && m.isSameOrBefore(ranges[i].endDay, 'day')) { return ranges[i]; } } return null; } /** * Used to get info for a specific property booking mode * @param mode */ export function getPropertyBookingModeInfo(mode: Models.PropertyBookingMode): Models.IPropertyBookingModeInfo { switch(mode) { case Models.PropertyBookingMode.Instant: return { name: "Instant" }; case Models.PropertyBookingMode.ReservationByRequest: return { name: "Reservation By Request" }; } return { name: mode }; } /** * Used to get info for a specific amenity type * @param amenity */ export function getAmenityInfo(amenity: Models.AmenityType): Models.IAmenityInfo { switch(amenity) { case Models.AmenityType.WiFi: return { name: "Wi-Fi" }; case Models.AmenityType.AirConditioning: return { name: "Air Conditioning" }; case Models.AmenityType.Heater: return { name: "Heater" }; case Models.AmenityType.Washer: return { name: "Washer" }; case Models.AmenityType.Dryer: return { name: "Dryer" }; case Models.AmenityType.IroningSet: return { name: "Iron / Ironing Board" }; case Models.AmenityType.Towels: return { name: "Towels" }; case Models.AmenityType.BeddingAndLinens: return { name: "Bedding & Linens" }; case Models.AmenityType.Toiletries: return { name: "Toiletries" }; case Models.AmenityType.HairDryer: return { name: "Hair Dryer" }; case Models.AmenityType.TV: return { name: "TV" }; case Models.AmenityType.Cable: return { name: "Cable" }; case Models.AmenityType.Streaming: return { name: "Streaming" }; case Models.AmenityType.SmokeAlarm: return { name: "Smoke Alarm" }; case Models.AmenityType.CarbonMonoxideAlarm: return { name: "Carbon Monoxide Alarm" }; case Models.AmenityType.FireExtinguisher: return { name: "Fire Extinguisher" }; case Models.AmenityType.AirMattress: return { name: "Air Mattress" }; case Models.AmenityType.ExteriorSecurityCamera: return { name: "Exterior Security Camera" }; case Models.AmenityType.InteriorSecurityCamera: return { name: "Interior Security Camera" }; case Models.AmenityType.FullKitchen: return { name: "Full Kitchen" }; case Models.AmenityType.Oven: return { name: "Oven" }; case Models.AmenityType.Stove: return { name: "Stove" }; case Models.AmenityType.Refrigerator: return { name: "Refrigerator" }; case Models.AmenityType.CoffeeMaker: return { name: "Coffee Maker" }; case Models.AmenityType.DishWasher: return { name: "Dish Washer" }; case Models.AmenityType.Microwave: return { name: "Microwave" }; case Models.AmenityType.DishesAndUtensils: return { name: "Dishes & Utensils" }; case Models.AmenityType.PotsAndPans: return { name: "Pots & Pans" }; case Models.AmenityType.StreetParking: return { name: "Street Parking" }; case Models.AmenityType.Driveway: return { name: "Driveway" }; case Models.AmenityType.Garage: return { name: "Garage" }; } return { name: amenity }; } /** * Used when computing trip cost to see if certain trip days fall into custom price range. * @param property * @param date */ export function getPropertyNightMinimum(property: Models.IProperty, date: moment.Moment) { let nightMinimum: number = property.nightMinimum; if(property.datedRules != null) { for(const rule of property.datedRules) { const startMoment = moment(rule.startDay); const endMoment = moment(rule.endDay); if(rule.nightMinimum != null && date.isSameOrAfter(startMoment, 'day') && date.isBefore(endMoment, 'day')) { nightMinimum = rule.nightMinimum; break; } } } return nightMinimum; } }