lennnn
This commit is contained in:
@@ -29,9 +29,6 @@ export class OrderSchema extends PothosSchema {
|
|||||||
userId: t.exposeID('userId', {
|
userId: t.exposeID('userId', {
|
||||||
description: 'The ID of the user.',
|
description: 'The ID of the user.',
|
||||||
}),
|
}),
|
||||||
paymentId: t.exposeString('paymentId', {
|
|
||||||
description: 'The ID of the payment.',
|
|
||||||
}),
|
|
||||||
serviceId: t.exposeID('serviceId', {
|
serviceId: t.exposeID('serviceId', {
|
||||||
description: 'The ID of the service.',
|
description: 'The ID of the service.',
|
||||||
}),
|
}),
|
||||||
@@ -56,17 +53,17 @@ export class OrderSchema extends PothosSchema {
|
|||||||
user: t.relation('user', {
|
user: t.relation('user', {
|
||||||
description: 'The user who made the order.',
|
description: 'The user who made the order.',
|
||||||
}),
|
}),
|
||||||
payment: t.relation('payment', {
|
|
||||||
description: 'The payment for the order.',
|
|
||||||
}),
|
|
||||||
service: t.relation('service', {
|
service: t.relation('service', {
|
||||||
description: 'The service for the order.',
|
description: 'The service for the order.',
|
||||||
}),
|
}),
|
||||||
refundTicket: t.relation('refundTicket', {
|
refundTicket: t.relation('refundTicket', {
|
||||||
description: 'The refund ticket for the order.',
|
description: 'The refund ticket for the order.',
|
||||||
}),
|
}),
|
||||||
schedule: t.relation('schedule', {
|
payment: t.relation('payment', {
|
||||||
description: 'The schedule for the order.',
|
description: 'The payment for the order.',
|
||||||
|
}),
|
||||||
|
paymentId: t.exposeString('paymentId', {
|
||||||
|
description: 'The ID of the payment.',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
|
|||||||
level: 'warn',
|
level: 'warn',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
errorFormat: 'pretty',
|
errorFormat: 'colorless',
|
||||||
transactionOptions: {
|
transactionOptions: {
|
||||||
maxWait: 30 * 1000,
|
maxWait: 30 * 1000,
|
||||||
timeout: 60 * 1000,
|
timeout: 60 * 1000,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common'
|
import { Inject, Injectable, Logger } from '@nestjs/common'
|
||||||
import {
|
import {
|
||||||
Pothos,
|
Pothos,
|
||||||
PothosRef,
|
PothosRef,
|
||||||
@@ -10,6 +10,7 @@ import { PrismaService } from '../Prisma/prisma.service'
|
|||||||
import { ScheduleStatus } from '@prisma/client'
|
import { ScheduleStatus } from '@prisma/client'
|
||||||
import { ScheduleService } from './schedule.service'
|
import { ScheduleService } from './schedule.service'
|
||||||
import { AppConfigService } from '../AppConfig/appconfig.service'
|
import { AppConfigService } from '../AppConfig/appconfig.service'
|
||||||
|
import { forEach } from 'lodash'
|
||||||
|
|
||||||
export type ScheduleConfigType =
|
export type ScheduleConfigType =
|
||||||
| {
|
| {
|
||||||
@@ -63,10 +64,18 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
id: t.exposeID('id', {
|
id: t.exposeID('id', {
|
||||||
description: 'The ID of the schedule.',
|
description: 'The ID of the schedule.',
|
||||||
}),
|
}),
|
||||||
|
customerId: t.exposeID('customerId', {
|
||||||
|
description: 'The ID of the customer the schedule belongs to.',
|
||||||
|
nullable: true,
|
||||||
|
}),
|
||||||
managedServiceId: t.exposeID('managedServiceId', {
|
managedServiceId: t.exposeID('managedServiceId', {
|
||||||
description: 'The ID of the managed service the schedule belongs to.',
|
description: 'The ID of the managed service the schedule belongs to.',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
}),
|
}),
|
||||||
|
orderId: t.exposeID('orderId', {
|
||||||
|
description: 'The ID of the order the schedule belongs to.',
|
||||||
|
nullable: true,
|
||||||
|
}),
|
||||||
scheduleStart: t.expose('scheduleStart', {
|
scheduleStart: t.expose('scheduleStart', {
|
||||||
type: 'DateTime',
|
type: 'DateTime',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
@@ -75,6 +84,12 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
type: 'DateTime',
|
type: 'DateTime',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
}),
|
}),
|
||||||
|
slots: t.exposeIntList('slots', {
|
||||||
|
nullable: false,
|
||||||
|
}),
|
||||||
|
daysOfWeek: t.exposeIntList('daysOfWeek', {
|
||||||
|
nullable: false,
|
||||||
|
}),
|
||||||
dates: t.relation('dates', {
|
dates: t.relation('dates', {
|
||||||
description: 'The dates of the schedule.',
|
description: 'The dates of the schedule.',
|
||||||
}),
|
}),
|
||||||
@@ -132,6 +147,18 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
type: 'DateTime',
|
type: 'DateTime',
|
||||||
nullable: false,
|
nullable: false,
|
||||||
}),
|
}),
|
||||||
|
dayOfWeek: t.exposeInt('dayOfWeek', {
|
||||||
|
nullable: false,
|
||||||
|
}),
|
||||||
|
slot: t.exposeInt('slot', {
|
||||||
|
nullable: false,
|
||||||
|
}),
|
||||||
|
serviceId: t.exposeID('serviceId', {
|
||||||
|
nullable: false,
|
||||||
|
}),
|
||||||
|
orderId: t.exposeID('orderId', {
|
||||||
|
nullable: true,
|
||||||
|
}),
|
||||||
schedule: t.relation('schedule', {
|
schedule: t.relation('schedule', {
|
||||||
description: 'The schedule the schedule date belongs to.',
|
description: 'The schedule the schedule date belongs to.',
|
||||||
}),
|
}),
|
||||||
@@ -227,5 +254,78 @@ export class ScheduleSchema extends PothosSchema {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
/* overlapping case
|
||||||
|
46836288-bb2c-4da6-892b-a559a480cbf8,e9be51fd-2382-4e43-9988-74e76fde4b56,2024-11-22 00:00:00.000,2024-11-02 00:00:00.000,UNPUBLISHED,,"{3,5}",,"{2,4}"
|
||||||
|
d72a864e-2f41-45ab-9c9b-bf0512a31883,e9be51fd-2382-4e43-9988-74e76fde4b56,2024-11-22 00:00:00.000,2024-11-02 00:00:00.000,UNPUBLISHED,,"{3,5}",,"{2,4}"
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.builder.mutationFields((t) => ({
|
||||||
|
// Mutations
|
||||||
|
createSchedule: t.prismaField({
|
||||||
|
type: this.schedule(),
|
||||||
|
description: 'Create a new schedule.',
|
||||||
|
args: {
|
||||||
|
schedule: t.arg({
|
||||||
|
type: this.builder.generator.getCreateInput('Schedule', [
|
||||||
|
'id',
|
||||||
|
'status',
|
||||||
|
'customerId',
|
||||||
|
'orderId',
|
||||||
|
]),
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
resolve: async (query, _root, args, _ctx, _info) => {
|
||||||
|
Logger.log('args.schedule', args.schedule)
|
||||||
|
// check if there is any overlapping schedule
|
||||||
|
const overlappingSchedules = await this.prisma.schedule.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
scheduleStart: {
|
||||||
|
gte: args.schedule.scheduleStart,
|
||||||
|
},
|
||||||
|
scheduleEnd: {
|
||||||
|
lte: args.schedule.scheduleEnd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// check if is same managedServiceId
|
||||||
|
if (
|
||||||
|
overlappingSchedules.some(
|
||||||
|
(schedule) =>
|
||||||
|
schedule.managedServiceId ===
|
||||||
|
args.schedule.managedService.connect?.id,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Overlapping schedule with ${JSON.stringify(
|
||||||
|
overlappingSchedules.map((schedule) => schedule.id),
|
||||||
|
)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const schedule = await this.prisma.schedule.create({
|
||||||
|
...query,
|
||||||
|
data: args.schedule,
|
||||||
|
})
|
||||||
|
// generate schedule dates based on data and config
|
||||||
|
const scheduleDates =
|
||||||
|
await this.scheduleService.generateScheduleDates(schedule)
|
||||||
|
// update schedule with schedule dates
|
||||||
|
return await this.prisma.schedule.update({
|
||||||
|
...query,
|
||||||
|
where: { id: schedule.id },
|
||||||
|
data: {
|
||||||
|
dates: {
|
||||||
|
connect: scheduleDates.map((date) => ({ id: date.id })),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,18 +10,27 @@ import {
|
|||||||
ScheduleConfigTypeForCenter,
|
ScheduleConfigTypeForCenter,
|
||||||
ScheduleSlotType,
|
ScheduleSlotType,
|
||||||
} from './schedule.schema'
|
} from './schedule.schema'
|
||||||
import { Config } from '@prisma/client'
|
import { Config, Schedule, ScheduleDate } from '@prisma/client'
|
||||||
import { DateTime, Settings, Zone } from 'luxon'
|
import { DateTime, Settings, Zone } from 'luxon'
|
||||||
import * as _ from 'lodash'
|
import * as _ from 'lodash'
|
||||||
|
|
||||||
Settings.defaultLocale = 'en-US'
|
Settings.defaultLocale = 'en-US'
|
||||||
Settings.defaultZone = 'utc'
|
Settings.defaultZone = 'utc'
|
||||||
// Settings.defaultWeekSettings = {
|
Settings.defaultWeekSettings = {
|
||||||
// firstDay: 2,
|
firstDay: 2,
|
||||||
// minimalDays: 1,
|
minimalDays: 1,
|
||||||
// weekend: [6, 7],
|
weekend: [6, 7],
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
interface ScheduleDateInput {
|
||||||
|
scheduleId: string
|
||||||
|
start: string
|
||||||
|
end: string
|
||||||
|
dayOfWeek: number
|
||||||
|
slot: number
|
||||||
|
serviceId: string
|
||||||
|
orderId: string | null
|
||||||
|
}
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ScheduleService {
|
export class ScheduleService {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -60,8 +69,56 @@ export class ScheduleService {
|
|||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
async generateScheduleDates(schedule: Schedule): Promise<ScheduleDate[]> {
|
||||||
|
// generate schedule dates based on data and config
|
||||||
|
const config: Config[] = await this.appConfigService.getVisibleConfigs()
|
||||||
|
const daysOfWeeks = schedule.daysOfWeek
|
||||||
|
const slots = schedule.slots
|
||||||
|
const scheduleStart = schedule.scheduleStart
|
||||||
|
const scheduleEnd = schedule.scheduleEnd
|
||||||
|
const slotDuration = config.find((c) => c.key === 'SLOT_DURATION')?.value
|
||||||
|
const slotBreakDuration = config.find(
|
||||||
|
(c) => c.key === 'SLOT_BREAK_DURATION',
|
||||||
|
)?.value
|
||||||
|
const slotStartTime = config.find((c) => c.key === 'SLOT_START_TIME')?.value
|
||||||
|
|
||||||
|
const scheduleDates: ScheduleDateInput[] = []
|
||||||
|
// loop each day from scheduleStart to scheduleEnd
|
||||||
|
let date = DateTime.fromJSDate(scheduleStart)
|
||||||
|
while (date <= DateTime.fromJSDate(scheduleEnd)) {
|
||||||
|
// Check if the current date matches one of the specified days of the week
|
||||||
|
if (daysOfWeeks.includes(date.weekday)) {
|
||||||
|
// loop through slots
|
||||||
|
for (const slot of slots) {
|
||||||
|
const { startTime, endTime } = this.getSlotStartAndEndTime(
|
||||||
|
slot,
|
||||||
|
slotDuration ?? '',
|
||||||
|
slotBreakDuration ?? '',
|
||||||
|
slotStartTime ?? '',
|
||||||
|
)
|
||||||
|
scheduleDates.push({
|
||||||
|
scheduleId: schedule.id,
|
||||||
|
start: startTime.toISO() ?? '',
|
||||||
|
end: endTime.toISO() ?? '',
|
||||||
|
dayOfWeek: date.weekday,
|
||||||
|
slot: slot,
|
||||||
|
serviceId: schedule.managedServiceId,
|
||||||
|
orderId: schedule.orderId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Move to the next day
|
||||||
|
date = date.plus({ days: 1 })
|
||||||
|
}
|
||||||
|
const scheduleDatesCreated =
|
||||||
|
await this.prisma.scheduleDate.createManyAndReturn({
|
||||||
|
data: scheduleDates,
|
||||||
|
})
|
||||||
|
|
||||||
|
return scheduleDatesCreated
|
||||||
|
}
|
||||||
|
|
||||||
generateSlots(scheduleConfigFilled: ScheduleConfigType): ScheduleSlotType[] {
|
generateSlots(scheduleConfigFilled: ScheduleConfigType): ScheduleSlotType[] {
|
||||||
Logger.log(`Generating slots with config: ${scheduleConfigFilled}`)
|
|
||||||
const slots: ScheduleSlotType[] = []
|
const slots: ScheduleSlotType[] = []
|
||||||
const numberOfSlots = this.calculateNumberOfSlots(
|
const numberOfSlots = this.calculateNumberOfSlots(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -143,15 +200,18 @@ export class ScheduleService {
|
|||||||
slotBreakDuration: string,
|
slotBreakDuration: string,
|
||||||
slotStartTime: string,
|
slotStartTime: string,
|
||||||
) {
|
) {
|
||||||
const durationInMinutes = parseInt(slotDuration);
|
const durationInMinutes = parseInt(slotDuration)
|
||||||
const breakDurationInMinutes = parseInt(slotBreakDuration);
|
const breakDurationInMinutes = parseInt(slotBreakDuration)
|
||||||
|
|
||||||
const startTime = DateTime.fromISO(slotStartTime).plus({
|
const startTime = DateTime.fromISO(slotStartTime).plus({
|
||||||
minutes: (slotNumber - 1) * (durationInMinutes + breakDurationInMinutes),
|
minutes: (slotNumber - 1) * (durationInMinutes + breakDurationInMinutes),
|
||||||
});
|
})
|
||||||
|
|
||||||
const endTime = startTime.plus({ minutes: durationInMinutes })
|
const endTime = startTime.plus({ minutes: durationInMinutes })
|
||||||
return { startTime, endTime }
|
return {
|
||||||
|
startTime: startTime,
|
||||||
|
endTime: endTime,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processScheduleConfig(
|
processScheduleConfig(
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user