diff --git a/packages/graphql/api/base.ts b/packages/graphql/api/base.ts index 1b7992c..f928e9e 100644 --- a/packages/graphql/api/base.ts +++ b/packages/graphql/api/base.ts @@ -3,8 +3,8 @@ import { getClientWithToken } from '../apollo/client'; import * as GQL from '../types'; const ERRORS = { + CUSTOMER_NOT_FOUND: 'Customer not found', MISSING_TELEGRAM_ID: 'Missing telegram id', - NOT_FOUND_CUSTOMER: 'Customer not found', }; type UserProfile = { @@ -32,7 +32,7 @@ export class BaseService { const customer = result.data.customers.at(0); - if (!customer) throw new Error(ERRORS.NOT_FOUND_CUSTOMER); + if (!customer) throw new Error(ERRORS.CUSTOMER_NOT_FOUND); return { customer }; } diff --git a/packages/graphql/api/slots.test.js b/packages/graphql/api/slots.test.js index 3fde691..18735e6 100644 --- a/packages/graphql/api/slots.test.js +++ b/packages/graphql/api/slots.test.js @@ -78,10 +78,21 @@ describe('SlotsService', () => { it('should successfully update slot when user has permission', async () => { const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValueOnce(mockGetSlotResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ data: { slots: [] } }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: mockMutate, @@ -93,20 +104,58 @@ describe('SlotsService', () => { await expect(result).resolves.toBe(mockMutationResult.data); }); + it('should successfully update slot when time is not changing', async () => { + const variablesWithoutTime = { + data: { + state: GQL.Enum_Slot_State.Closed, + }, + documentId: 'slot-123', + }; + + const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: mockMutate, + query: mockQuery, + }); + + const result = slotsService.updateSlot(variablesWithoutTime); + + await expect(result).resolves.toBe(mockMutationResult.data); + }); + it('should throw error when user does not have permission', async () => { const unrelatedCustomer = { ...mockCustomer, documentId: 'different-customer-123', }; - const mockQuery = vi - .fn() - .mockResolvedValueOnce({ - data: { customers: [unrelatedCustomer] }, - }) - .mockResolvedValueOnce({ - data: { slot: mockSlot }, // slot принадлежит другому пользователю - }); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve({ + data: { customers: [unrelatedCustomer] }, + }); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: mockSlot }, // slot принадлежит другому пользователю + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: vi.fn(), @@ -119,12 +168,19 @@ describe('SlotsService', () => { }); it('should throw error when slot does not exist', async () => { - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValueOnce({ - data: { slot: null }, // slot не найден - }); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: null }, // slot не найден + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: vi.fn(), @@ -133,12 +189,18 @@ describe('SlotsService', () => { const result = slotsService.updateSlot(mockVariables); - await expect(result).rejects.toThrow(); + await expect(result).rejects.toThrow(ERRORS.SLOT_NOT_FOUND); }); it('should throw error when customer is not found', async () => { - const mockQuery = vi.fn().mockResolvedValue({ - data: { customers: [] }, // пользователь не найден + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve({ + data: { customers: [] }, // пользователь не найден + }); + } + + return Promise.resolve({ data: {} }); }); mockGetClientWithToken.mockResolvedValue({ @@ -150,67 +212,469 @@ describe('SlotsService', () => { await expect(result).rejects.toThrow('Customer not found'); }); - }); - describe('checkPermission', () => { - const mockVariables = { - documentId: 'slot-123', - }; - - it('should not throw error when user has permission', async () => { - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValueOnce(mockGetSlotResult); - - mockGetClientWithToken.mockResolvedValue({ - query: mockQuery, - }); - - const result = slotsService.checkPermission(mockVariables); - - await expect(result).resolves.toBeUndefined(); - }); - - it('should throw error when user does not have permission', async () => { - const unrelatedCustomer = { - ...mockCustomer, - documentId: 'different-customer-123', + it('should throw error when datetime_start is missing', async () => { + const variablesWithMissingStart = { + data: { + datetime_end: '2024-01-01T11:00:00Z', + }, + documentId: 'slot-123', }; - const mockQuery = vi - .fn() - .mockResolvedValueOnce({ - data: { customers: [unrelatedCustomer] }, - }) - .mockResolvedValueOnce({ - data: { slot: mockSlot }, // slot принадлежит другому пользователю - }); + const slotWithoutStart = { + ...mockSlot, + datetime_start: null, + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithoutStart }, + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), query: mockQuery, }); - const result = slotsService.checkPermission(mockVariables); + const result = slotsService.updateSlot(variablesWithMissingStart); - await expect(result).rejects.toThrow(ERRORS.NO_PERMISSION); + await expect(result).rejects.toThrow(ERRORS.INVALID_TIME); }); - it('should throw error when slot does not exist', async () => { - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValueOnce({ - data: { slot: null }, // slot не найден - }); + it('should throw error when datetime_end is missing', async () => { + const variablesWithMissingEnd = { + data: { + datetime_start: '2024-01-01T10:00:00Z', + }, + documentId: 'slot-123', + }; + + const slotWithoutEnd = { + ...mockSlot, + datetime_end: null, + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithoutEnd }, + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), query: mockQuery, }); - const result = slotsService.checkPermission(mockVariables); + const result = slotsService.updateSlot(variablesWithMissingEnd); - await expect(result).rejects.toThrow(); + await expect(result).rejects.toThrow(ERRORS.INVALID_TIME); + }); + + it('should throw error when datetime_end is before datetime_start', async () => { + const variablesWithInvalidTime = { + data: { + datetime_end: '2024-01-01T10:00:00Z', + datetime_start: '2024-01-01T11:00:00Z', + }, + documentId: 'slot-123', + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(variablesWithInvalidTime); + + await expect(result).rejects.toThrow(ERRORS.INVALID_TIME); + }); + + it('should throw error when datetime_end equals datetime_start', async () => { + const variablesWithEqualTime = { + data: { + datetime_end: '2024-01-01T10:00:00Z', + datetime_start: '2024-01-01T10:00:00Z', + }, + documentId: 'slot-123', + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(variablesWithEqualTime); + + await expect(result).rejects.toThrow(ERRORS.INVALID_TIME); + }); + + it('should throw error when slot has scheduled orders', async () => { + const slotWithScheduledOrders = { + ...mockSlot, + orders: [ + { + datetime_end: '2024-01-01T10:30:00Z', + datetime_start: '2024-01-01T10:00:00Z', + documentId: 'order-123', + state: GQL.Enum_Order_State.Scheduled, + }, + ], + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithScheduledOrders }, + }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS); + }); + + it('should throw error when slot has approved orders', async () => { + const slotWithApprovedOrders = { + ...mockSlot, + orders: [ + { + datetime_end: '2024-01-01T10:30:00Z', + datetime_start: '2024-01-01T10:00:00Z', + documentId: 'order-123', + state: GQL.Enum_Order_State.Approved, + }, + ], + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithApprovedOrders }, + }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS); + }); + + it('should throw error when slot has completed orders', async () => { + const slotWithCompletedOrders = { + ...mockSlot, + orders: [ + { + datetime_end: '2024-01-01T10:30:00Z', + datetime_start: '2024-01-01T10:00:00Z', + documentId: 'order-123', + state: GQL.Enum_Order_State.Completed, + }, + ], + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithCompletedOrders }, + }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS); + }); + + it('should throw error when slot has cancelled orders', async () => { + const slotWithCancelledOrders = { + ...mockSlot, + orders: [ + { + datetime_end: '2024-01-01T10:30:00Z', + datetime_start: '2024-01-01T10:00:00Z', + documentId: 'order-123', + state: GQL.Enum_Order_State.Cancelled, + }, + ], + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithCancelledOrders }, + }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).rejects.toThrow(ERRORS.HAS_ORDERS); + }); + + it('should allow update when slot has non-forbidden order states', async () => { + const slotWithNonForbiddenOrders = { + ...mockSlot, + orders: [ + { + datetime_end: '2024-01-01T10:30:00Z', + datetime_start: '2024-01-01T10:00:00Z', + documentId: 'order-123', + state: GQL.Enum_Order_State.Draft, // не запрещенное состояние + }, + ], + }; + + const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithNonForbiddenOrders }, + }); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ data: { slots: [] } }); // нет пересекающихся слотов + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: mockMutate, + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).resolves.toBe(mockMutationResult.data); + }); + + it('should throw error when time overlaps with other slots', async () => { + const overlappingSlot = { + datetime_end: '2024-01-01T11:30:00Z', + datetime_start: '2024-01-01T10:30:00Z', + documentId: 'slot-456', + master: mockCustomer, + orders: [], + state: GQL.Enum_Slot_State.Open, + }; + + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ + data: { slots: [overlappingSlot] }, + }); // есть пересекающиеся слоты + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: vi.fn(), + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).rejects.toThrow(ERRORS.OVERLAPPING_TIME); + }); + + it('should allow update when time does not overlap with other slots', async () => { + const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ + data: { slots: [] }, + }); // нет пересекающихся слотов + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: mockMutate, + query: mockQuery, + }); + + const result = slotsService.updateSlot(mockVariables); + + await expect(result).resolves.toBe(mockMutationResult.data); + }); + + it('should use existing slot times when only datetime_start is provided', async () => { + const variablesWithOnlyStart = { + data: { + datetime_start: '2024-01-01T09:00:00Z', + }, + documentId: 'slot-123', + }; + + const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ data: { slots: [] } }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: mockMutate, + query: mockQuery, + }); + + const result = slotsService.updateSlot(variablesWithOnlyStart); + + await expect(result).resolves.toBe(mockMutationResult.data); + }); + + it('should use existing slot times when only datetime_end is provided', async () => { + const variablesWithOnlyEnd = { + data: { + datetime_end: '2024-01-01T12:00:00Z', + }, + documentId: 'slot-123', + }; + + const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + if (query === GQL.GetSlotsDocument) { + return Promise.resolve({ data: { slots: [] } }); + } + + return Promise.resolve({ data: {} }); + }); + + mockGetClientWithToken.mockResolvedValue({ + mutate: mockMutate, + query: mockQuery, + }); + + const result = slotsService.updateSlot(variablesWithOnlyEnd); + + await expect(result).resolves.toBe(mockMutationResult.data); }); }); @@ -230,10 +694,17 @@ describe('SlotsService', () => { it('should successfully delete slot when no orders', async () => { const mockMutate = vi.fn().mockResolvedValue(mockMutationResult); - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValue(mockGetSlotResult); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve(mockGetSlotResult); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: mockMutate, @@ -258,12 +729,19 @@ describe('SlotsService', () => { ], }; - const mockQuery = vi - .fn() - .mockResolvedValueOnce(mockGetCustomerResult) - .mockResolvedValue({ - data: { slot: slotWithOrders }, // slot с заказами - }); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve(mockGetCustomerResult); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: slotWithOrders }, // slot с заказами + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: vi.fn(), @@ -281,14 +759,21 @@ describe('SlotsService', () => { documentId: 'different-customer-123', }; - const mockQuery = vi - .fn() - .mockResolvedValueOnce({ - data: { customers: [unrelatedCustomer] }, - }) - .mockResolvedValueOnce({ - data: { slot: mockSlot }, // slot принадлежит другому пользователю - }); + const mockQuery = vi.fn().mockImplementation(({ query }) => { + if (query === GQL.GetCustomerDocument) { + return Promise.resolve({ + data: { customers: [unrelatedCustomer] }, + }); + } + + if (query === GQL.GetSlotDocument) { + return Promise.resolve({ + data: { slot: mockSlot }, // slot принадлежит другому пользователю + }); + } + + return Promise.resolve({ data: {} }); + }); mockGetClientWithToken.mockResolvedValue({ mutate: vi.fn(), diff --git a/packages/graphql/api/slots.ts b/packages/graphql/api/slots.ts index b762488..86df0b0 100644 --- a/packages/graphql/api/slots.ts +++ b/packages/graphql/api/slots.ts @@ -1,3 +1,5 @@ +/* eslint-disable canonical/id-match */ +/* eslint-disable @typescript-eslint/naming-convention */ import { getClientWithToken } from '../apollo/client'; import * as GQL from '../types'; import { BaseService } from './base'; @@ -7,13 +9,23 @@ import { getMinutes } from '@repo/utils/datetime-format'; import dayjs from 'dayjs'; export const ERRORS = { - HAS_ORDERS: 'Slot has orders', - MISSING_DATE: 'Missing date', - MISSING_SERVICE: 'Missing service', - MISSING_SERVICE_ID: 'Missing service id', - NO_PERMISSION: 'No permission', + HAS_ORDERS: 'Слот имеет активные заказы', + INVALID_TIME: 'Некорректное время', + MISSING_DATE: 'Не указана дата', + MISSING_SERVICE_ID: 'Не указана услуга', + NO_PERMISSION: 'Нет доступа', + OVERLAPPING_TIME: 'Время пересекается с другими слотами', + SERVICE_NOT_FOUND: 'Слот не найден', + SLOT_NOT_FOUND: 'Слот не найден', }; +const FORBIDDEN_ORDER_STATES: GQL.Enum_Order_State[] = [ + GQL.Enum_Order_State.Scheduled, + GQL.Enum_Order_State.Approved, + GQL.Enum_Order_State.Completed, + GQL.Enum_Order_State.Cancelled, +]; + export class SlotsService extends BaseService { async createSlot(variables: VariablesOf) { const { customer } = await this._getUser(); @@ -86,7 +98,7 @@ export class SlotsService extends BaseService { documentId: context.service.documentId.eq, }); - if (!service) throw new Error(ERRORS.MISSING_SERVICE); + if (!service) throw new Error(ERRORS.SERVICE_NOT_FOUND); const serviceDuration = getMinutes(service.duration); @@ -149,6 +161,7 @@ export class SlotsService extends BaseService { async updateSlot(variables: VariablesOf) { await this.checkPermission(variables); + await this.checkIsTimeChanging(variables); const { mutate } = await getClientWithToken(); @@ -163,6 +176,55 @@ export class SlotsService extends BaseService { return mutationResult.data; } + private async checkIsTimeChanging(variables: VariablesOf) { + const { slot } = await this.getSlot({ documentId: variables.documentId }); + + if (!slot) throw new Error(ERRORS.SLOT_NOT_FOUND); + + const isTimeChanging = variables?.data?.datetime_start || variables?.data?.datetime_end; + + if (!isTimeChanging) return; + + let datetime_start = variables.data.datetime_start; + let datetime_end = variables.data.datetime_end; + + if (!datetime_start) datetime_start = slot?.datetime_start; + if (!datetime_end) datetime_end = slot?.datetime_end; + + // Проверка: оба времени должны быть определены + if (!datetime_start || !datetime_end) { + throw new Error(ERRORS.INVALID_TIME); + } + + // Проверка валидности времени + if (new Date(datetime_end) <= new Date(datetime_start)) { + throw new Error(ERRORS.INVALID_TIME); + } + + const orders = slot?.orders; + + if ( + orders?.length && + orders?.some((order) => order?.state && FORBIDDEN_ORDER_STATES.includes(order.state)) + ) { + throw new Error(ERRORS.HAS_ORDERS); + } + + const { documentId } = slot; + + const overlappingEntities = await this.getSlots({ + filters: { + datetime_end: { gt: datetime_start }, + datetime_start: { lt: datetime_end }, + documentId: { not: { eq: documentId } }, + }, + }); + + if (overlappingEntities?.slots?.length) { + throw new Error(ERRORS.OVERLAPPING_TIME); + } + } + private async checkPermission( variables: Pick, 'documentId'>, ) { @@ -170,6 +232,8 @@ export class SlotsService extends BaseService { const { slot } = await this.getSlot({ documentId: variables.documentId }); + if (!slot) throw new Error(ERRORS.SLOT_NOT_FOUND); + if (slot?.master?.documentId !== customer?.documentId) throw new Error(ERRORS.NO_PERMISSION); } }