edit business

This commit is contained in:
2026-06-02 11:35:54 +03:30
parent 0d90703827
commit 49bf3118c9
5 changed files with 72 additions and 8 deletions
@@ -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
`);
}
}
+4
View File
@@ -53,6 +53,10 @@ export class BusinessEntity {
@Column({ type: 'int', default: 1 })
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, {
onDelete: 'RESTRICT',
})
+15 -8
View File
@@ -388,13 +388,14 @@ export class BookingsService {
const windows = await this.loadAvailabilityWindows(businessId);
const busy = await this.loadBusyIntervals(businessId, dateYmd);
const concurrentCapacity = await this.getConcurrentCapacity(businessId);
const { concurrentCapacity, slotStepMinutes } =
await this.getBusinessBookingSettings(businessId);
const slots = generateAvailableSlotStarts({
dateYmd,
durationMinutes: totalDurationMinutes,
leadMinutes: this.minLeadMinutes,
slotStepMinutes: 15,
slotStepMinutes,
windows,
busy,
concurrentCapacity,
@@ -403,13 +404,18 @@ export class BookingsService {
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({
where: { id: businessId },
select: ['id', 'concurrentCapacity'],
select: ['id', 'concurrentCapacity', 'slotStepMinutes'],
});
const value = business?.concurrentCapacity ?? 1;
return Math.max(1, value);
return {
concurrentCapacity: Math.max(1, business?.concurrentCapacity ?? 1),
slotStepMinutes: Math.max(5, business?.slotStepMinutes ?? 30),
};
}
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({
dateYmd,
durationMinutes,
leadMinutes: this.minLeadMinutes,
slotStepMinutes: 15,
slotStepMinutes,
windows,
busy: await this.loadBusyIntervals(businessId, dateYmd, {
excludeBookingId,
@@ -91,4 +91,16 @@ export class UpdateBusinessDto {
@Min(1)
@Max(20)
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;
}