diff --git a/src/api/order/content-types/order/lifecycles.ts b/src/api/order/content-types/order/lifecycles.ts index 8845921..3b2ae8c 100644 --- a/src/api/order/content-types/order/lifecycles.ts +++ b/src/api/order/content-types/order/lifecycles.ts @@ -1,3 +1,5 @@ +import { extractId } from '../../../../utils'; + const ERR_INVALID_TIME = 'Некорректное время'; const ERR_OVERLAPPING_TIME = 'Время пересекается с другими заказами'; const ERR_INACTIVE_CLIENT = 'Клиент не активен'; @@ -13,22 +15,6 @@ function timeToDate(time: string) { return new Date(`1970-01-01T${time}Z`); } -function extractId(input: any): number | null { - if (typeof input === 'number') { - return input; - } - - if (input?.id) { - return input.id; - } - - if (input?.set?.[0]?.id) { - return input.set[0].id; - } - - return null; -} - export default { async beforeCreate(event) { const { data } = event.params; @@ -68,7 +54,6 @@ export default { where: { id: clientId }, populate: { masters: true }, }); - console.log('🚀 ~ clientEntity ~ clientEntity:', clientEntity); if (!clientEntity) throw new Error(ERR_MISSING_CLIENT); // Проверка активности клиента @@ -121,6 +106,7 @@ export default { }, populate: ['slot'], }); + if (overlappingEntities.length > 0) { throw new Error(ERR_OVERLAPPING_TIME); } diff --git a/src/api/slot/content-types/slot/lifecycles.ts b/src/api/slot/content-types/slot/lifecycles.ts index 02ab373..65d8286 100644 --- a/src/api/slot/content-types/slot/lifecycles.ts +++ b/src/api/slot/content-types/slot/lifecycles.ts @@ -1,3 +1,5 @@ +import { extractId } from '../../../../utils'; + const ERR_INVALID_TIME = 'Некорректное время'; const ERR_OVERLAPPING_TIME = 'Время пересекается с другими слотами'; const ERR_FORBIDDEN_SLOT_STATUS = @@ -10,23 +12,47 @@ const FORBIDDEN_ORDER_STATES = [ 'cancelling', ]; -function timeToDate(time: string) { - return new Date(`1970-01-01T${time}Z`); +const ERR_INACTIVE_MASTER = 'Мастер не активен'; +const ERR_INVALID_MASTER = 'Некорректный мастер'; +const ERR_PAST_SLOT = 'Нельзя создать слот в прошлом'; +const ERR_SLOT_HAS_ORDERS = 'Нельзя удалить слот с активными заказами'; + +function timeToDate(date, time) { + return new Date(`${date}T${time}:00Z`); } export default { async beforeCreate(event) { const { data } = event.params; - const { time_start, time_end, date } = data; + const { master, date, time_start, time_end } = data; + // Проверка, что мастер существует и активен + const masterId = extractId(master); + const masterEntity = await strapi.db + .query('api::customer.customer') + .findOne({ + where: { id: masterId }, + }); + if (!masterEntity) throw new Error(ERR_INVALID_MASTER); + if (!masterEntity.active || masterEntity.role !== 'master') { + throw new Error(ERR_INACTIVE_MASTER); + } + + // Проверка, что слот не создаётся в прошлом + if (date && time_start) { + const slotDate = timeToDate(date, time_start); + if (slotDate < new Date()) { + throw new Error(ERR_PAST_SLOT); + } + } + + // --- Существующие проверки времени и пересечений --- if (!time_start || !time_end) { - throw new Error(ERR_INVALID_TIME); + throw new Error('Некорректное время'); } - - if (timeToDate(time_start) >= timeToDate(time_end)) { - throw new Error(ERR_INVALID_TIME); + if (timeToDate(date, time_start) >= timeToDate(date, time_end)) { + throw new Error('Некорректное время'); } - const overlappingEntities = await strapi.db .query('api::slot.slot') .findMany({ @@ -35,24 +61,42 @@ export default { documentId: { $ne: data.documentId }, time_start: { $lt: time_end }, time_end: { $gt: time_start }, + master: masterId, }, }); - if (overlappingEntities.length > 0) { - throw new Error(ERR_OVERLAPPING_TIME); + throw new Error('Время пересекается с другими слотами'); } }, async beforeUpdate(event) { const { data, where } = event.params; const { id: entityId } = where; - const { time_start, time_end } = data; - if (time_start && time_end) { + // Если меняется хотя бы одно из полей времени или дата + const isTimeChange = + 'time_start' in data || 'time_end' in data || 'date' in data; + + if (isTimeChange) { + let date = data.date; + let time_start = data.time_start; + let time_end = data.time_end; + + // Подтянуть недостающие значения из существующего слота + const existingSlot = await strapi.db.query('api::slot.slot').findOne({ + where: { id: entityId }, + select: ['date', 'time_start', 'time_end'], + }); + if (!date) date = existingSlot?.date; + if (!time_start) time_start = existingSlot?.time_start; + if (!time_end) time_end = existingSlot?.time_end; + + // Проверка: оба времени должны быть определены if (!time_start || !time_end) { throw new Error(ERR_INVALID_TIME); } - if (timeToDate(time_start) >= timeToDate(time_end)) { + // Проверка валидности времени + if (timeToDate(date, time_start) >= timeToDate(date, time_end)) { throw new Error(ERR_INVALID_TIME); } @@ -75,7 +119,7 @@ export default { throw new Error('Запись не найдена'); } - const { date, documentId } = existingEntity; + const { documentId } = existingEntity; const overlappingEntities = await strapi.db .query('api::slot.slot') @@ -94,4 +138,15 @@ export default { } } }, + async beforeDelete(event) { + const { where } = event.params; + const slotId = where.id; + const slot = await strapi.db.query('api::slot.slot').findOne({ + where: { id: slotId }, + populate: ['orders'], + }); + if (slot?.orders?.length) { + throw new Error(ERR_SLOT_HAS_ORDERS); + } + }, }; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..3cf6be5 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,13 @@ +// Универсальная функция для извлечения id из разных форматов входных данных +export function extractId(input) { + if (typeof input === 'number' || typeof input === 'string') { + return input; + } + if (input?.id) { + return input.id; + } + if (input?.set?.[0]?.id) { + return input.set[0].id; + } + return null; +}