Files
pfadi-bussle/db/booking.ts
Thomas Ruoff 9f3b6bb2e1 remover booker, that's overdosed
It also brings the problem of consolidating bookers over multiple
bookings. The amount of data is not justifying having it in an own
entity
2021-06-21 23:21:23 +02:00

148 lines
4.0 KiB
TypeScript

import * as mongoose from 'mongoose'
import { v4 as uuidv4 } from 'uuid'
import { dateFormatBackend, getDays, nowInTz } from '../helpers/date'
import { Bill } from './bill'
import { BOOKING_STATUS, VALIDATION_ERRORS } from './enums'
import { getBookedDays } from './index'
export type Booking = {
uuid: string
name: string
email: string
phone: string
street: string
zip: string
city: string
bill?: Bill
startDate: string
endDate: string
status?: BOOKING_STATUS
purpose?: string
org?: string
destination?: string
days?: string[]
}
export type BookingDocument = Booking &
mongoose.Document &
mongoose.SchemaTimestampsConfig
export type BookingModel = mongoose.Model<BookingDocument> & {
findBookedDays(uuidsToIngore?: string[]): Promise<string[]>
}
const BookingSchema = new mongoose.Schema<BookingDocument>(
{
// need a seperate uuid to be able to target a booking anonimously
uuid: {
type: String,
default: uuidv4,
index: true,
},
name: { type: String, required: true },
email: { type: String, required: true, minlength: 5 },
phone: { type: String, required: false },
street: { type: String, required: true },
zip: { type: String, required: true },
city: { type: String, required: true },
bill: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Bill',
required: false,
},
startDate: {
type: Date,
required: true,
get: dateFormatBackend,
validate: {
validator: function (v: Date): boolean {
return v >= nowInTz()
},
message: (props: { value: Date }): string =>
`${props.value} is in the past`,
},
},
endDate: {
type: Date,
required: false,
get: dateFormatBackend,
validate: {
validator: function (v: Date): boolean {
return v >= nowInTz()
},
message: (props: { value: Date }): string =>
`${props.value} is in the past`,
},
},
days: {
type: [String],
required: true,
validate: {
validator: async function (days: string[]): Promise<boolean> {
const booking = this as Booking
const uuid = booking.uuid && [booking.uuid]
const bookedDays = await getBookedDays(uuid)
const doubleBookedDays = days.filter((day: string): boolean =>
bookedDays.includes(day)
)
return doubleBookedDays.length === 0
},
message: (props: { value: string[] }): string =>
`At least one day is of ${props.value.join(',')} is already booked`,
type: VALIDATION_ERRORS.AT_LEAST_ONE_DAY_BOOKED,
},
},
status: {
type: String,
enum: Object.values(BOOKING_STATUS),
required: true,
default: 'requested',
},
purpose: { type: String, required: false },
org: { type: String, required: false },
destination: { type: String, required: false },
},
{
timestamps: true,
toJSON: { virtuals: true, getters: true },
toObject: { virtuals: true, getters: true },
}
)
BookingSchema.pre('validate', function (next: () => void): void {
const booking = this as BookingDocument
booking.days = getDays({
startDate: new Date(booking.startDate),
endDate: new Date(booking.endDate),
})
next()
})
BookingSchema.static('findBookedDays', async function (
uuidsToIngore: string[] = []
): Promise<string[]> {
const model = this as BookingModel
const now = nowInTz()
const bookedDays = await model
.find(
{
status: { $in: [BOOKING_STATUS.REQUESTED, BOOKING_STATUS.CONFIRMED] },
uuid: { $nin: uuidsToIngore },
// dateFormatBackend uses YYYY-MM-DD, which is startOfDay anyway
endDate: { $gt: dateFormatBackend(now) },
},
'days'
)
.exec()
return bookedDays
.map((b) => b.days)
.flat()
.sort()
})
export default <BookingModel>mongoose.models.Booking ||
mongoose.model<BookingDocument, BookingModel>('Booking', BookingSchema)