edit business
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddBusinessSlotStepMinutes1789046400000
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'AddBusinessSlotStepMinutes1789046400000';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "businesses"
|
||||||
|
ADD COLUMN IF NOT EXISTS "slotStepMinutes" integer NOT NULL DEFAULT 30
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "businesses" DROP COLUMN IF EXISTS "slotStepMinutes"
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class SetBusinessSlotStepDefault301789046500000
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'SetBusinessSlotStepDefault301789046500000';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "businesses"
|
||||||
|
ALTER COLUMN "slotStepMinutes" SET DEFAULT 30
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`
|
||||||
|
ALTER TABLE "businesses"
|
||||||
|
ALTER COLUMN "slotStepMinutes" SET DEFAULT 15
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,10 @@ export class BusinessEntity {
|
|||||||
@Column({ type: 'int', default: 1 })
|
@Column({ type: 'int', default: 1 })
|
||||||
concurrentCapacity!: number;
|
concurrentCapacity!: number;
|
||||||
|
|
||||||
|
/** Minutes between bookable slot start times (e.g. 30 → 10:00, 10:30, 11:00) */
|
||||||
|
@Column({ type: 'int', default: 30 })
|
||||||
|
slotStepMinutes!: number;
|
||||||
|
|
||||||
@ManyToOne(() => BusinessCategoryEntity, (category) => category.businesses, {
|
@ManyToOne(() => BusinessCategoryEntity, (category) => category.businesses, {
|
||||||
onDelete: 'RESTRICT',
|
onDelete: 'RESTRICT',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -388,13 +388,14 @@ export class BookingsService {
|
|||||||
|
|
||||||
const windows = await this.loadAvailabilityWindows(businessId);
|
const windows = await this.loadAvailabilityWindows(businessId);
|
||||||
const busy = await this.loadBusyIntervals(businessId, dateYmd);
|
const busy = await this.loadBusyIntervals(businessId, dateYmd);
|
||||||
const concurrentCapacity = await this.getConcurrentCapacity(businessId);
|
const { concurrentCapacity, slotStepMinutes } =
|
||||||
|
await this.getBusinessBookingSettings(businessId);
|
||||||
|
|
||||||
const slots = generateAvailableSlotStarts({
|
const slots = generateAvailableSlotStarts({
|
||||||
dateYmd,
|
dateYmd,
|
||||||
durationMinutes: totalDurationMinutes,
|
durationMinutes: totalDurationMinutes,
|
||||||
leadMinutes: this.minLeadMinutes,
|
leadMinutes: this.minLeadMinutes,
|
||||||
slotStepMinutes: 15,
|
slotStepMinutes,
|
||||||
windows,
|
windows,
|
||||||
busy,
|
busy,
|
||||||
concurrentCapacity,
|
concurrentCapacity,
|
||||||
@@ -403,13 +404,18 @@ export class BookingsService {
|
|||||||
return { date: dateYmd, slots, totalDurationMinutes };
|
return { date: dateYmd, slots, totalDurationMinutes };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getConcurrentCapacity(businessId: string): Promise<number> {
|
private async getBusinessBookingSettings(businessId: string): Promise<{
|
||||||
|
concurrentCapacity: number;
|
||||||
|
slotStepMinutes: number;
|
||||||
|
}> {
|
||||||
const business = await this.businessRepo.findOne({
|
const business = await this.businessRepo.findOne({
|
||||||
where: { id: businessId },
|
where: { id: businessId },
|
||||||
select: ['id', 'concurrentCapacity'],
|
select: ['id', 'concurrentCapacity', 'slotStepMinutes'],
|
||||||
});
|
});
|
||||||
const value = business?.concurrentCapacity ?? 1;
|
return {
|
||||||
return Math.max(1, value);
|
concurrentCapacity: Math.max(1, business?.concurrentCapacity ?? 1),
|
||||||
|
slotStepMinutes: Math.max(5, business?.slotStepMinutes ?? 30),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadAvailabilityWindows(
|
private async loadAvailabilityWindows(
|
||||||
@@ -658,12 +664,13 @@ export class BookingsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const concurrentCapacity = await this.getConcurrentCapacity(businessId);
|
const { concurrentCapacity, slotStepMinutes } =
|
||||||
|
await this.getBusinessBookingSettings(businessId);
|
||||||
const slots = generateAvailableSlotStarts({
|
const slots = generateAvailableSlotStarts({
|
||||||
dateYmd,
|
dateYmd,
|
||||||
durationMinutes,
|
durationMinutes,
|
||||||
leadMinutes: this.minLeadMinutes,
|
leadMinutes: this.minLeadMinutes,
|
||||||
slotStepMinutes: 15,
|
slotStepMinutes,
|
||||||
windows,
|
windows,
|
||||||
busy: await this.loadBusyIntervals(businessId, dateYmd, {
|
busy: await this.loadBusyIntervals(businessId, dateYmd, {
|
||||||
excludeBookingId,
|
excludeBookingId,
|
||||||
|
|||||||
@@ -91,4 +91,16 @@ export class UpdateBusinessDto {
|
|||||||
@Min(1)
|
@Min(1)
|
||||||
@Max(20)
|
@Max(20)
|
||||||
concurrentCapacity?: number;
|
concurrentCapacity?: number;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
example: 30,
|
||||||
|
description: 'Minutes between available booking slot start times',
|
||||||
|
minimum: 5,
|
||||||
|
maximum: 120,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(5)
|
||||||
|
@Max(120)
|
||||||
|
slotStepMinutes?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user