feat: refactor slot lifecycle methods to include master validation and enhance time checks
This commit is contained in:
parent
2686249cc9
commit
a447f4bf1c
@ -1,3 +1,5 @@
|
|||||||
|
import { extractId } from '../../../../utils';
|
||||||
|
|
||||||
const ERR_INVALID_TIME = 'Некорректное время';
|
const ERR_INVALID_TIME = 'Некорректное время';
|
||||||
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими заказами';
|
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими заказами';
|
||||||
const ERR_INACTIVE_CLIENT = 'Клиент не активен';
|
const ERR_INACTIVE_CLIENT = 'Клиент не активен';
|
||||||
@ -13,22 +15,6 @@ function timeToDate(time: string) {
|
|||||||
return new Date(`1970-01-01T${time}Z`);
|
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 {
|
export default {
|
||||||
async beforeCreate(event) {
|
async beforeCreate(event) {
|
||||||
const { data } = event.params;
|
const { data } = event.params;
|
||||||
@ -68,7 +54,6 @@ export default {
|
|||||||
where: { id: clientId },
|
where: { id: clientId },
|
||||||
populate: { masters: true },
|
populate: { masters: true },
|
||||||
});
|
});
|
||||||
console.log('🚀 ~ clientEntity ~ clientEntity:', clientEntity);
|
|
||||||
if (!clientEntity) throw new Error(ERR_MISSING_CLIENT);
|
if (!clientEntity) throw new Error(ERR_MISSING_CLIENT);
|
||||||
|
|
||||||
// Проверка активности клиента
|
// Проверка активности клиента
|
||||||
@ -121,6 +106,7 @@ export default {
|
|||||||
},
|
},
|
||||||
populate: ['slot'],
|
populate: ['slot'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (overlappingEntities.length > 0) {
|
if (overlappingEntities.length > 0) {
|
||||||
throw new Error(ERR_OVERLAPPING_TIME);
|
throw new Error(ERR_OVERLAPPING_TIME);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { extractId } from '../../../../utils';
|
||||||
|
|
||||||
const ERR_INVALID_TIME = 'Некорректное время';
|
const ERR_INVALID_TIME = 'Некорректное время';
|
||||||
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими слотами';
|
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими слотами';
|
||||||
const ERR_FORBIDDEN_SLOT_STATUS =
|
const ERR_FORBIDDEN_SLOT_STATUS =
|
||||||
@ -10,23 +12,47 @@ const FORBIDDEN_ORDER_STATES = [
|
|||||||
'cancelling',
|
'cancelling',
|
||||||
];
|
];
|
||||||
|
|
||||||
function timeToDate(time: string) {
|
const ERR_INACTIVE_MASTER = 'Мастер не активен';
|
||||||
return new Date(`1970-01-01T${time}Z`);
|
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 {
|
export default {
|
||||||
async beforeCreate(event) {
|
async beforeCreate(event) {
|
||||||
const { data } = event.params;
|
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) {
|
if (!time_start || !time_end) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error('Некорректное время');
|
||||||
}
|
}
|
||||||
|
if (timeToDate(date, time_start) >= timeToDate(date, time_end)) {
|
||||||
if (timeToDate(time_start) >= timeToDate(time_end)) {
|
throw new Error('Некорректное время');
|
||||||
throw new Error(ERR_INVALID_TIME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const overlappingEntities = await strapi.db
|
const overlappingEntities = await strapi.db
|
||||||
.query('api::slot.slot')
|
.query('api::slot.slot')
|
||||||
.findMany({
|
.findMany({
|
||||||
@ -35,24 +61,42 @@ export default {
|
|||||||
documentId: { $ne: data.documentId },
|
documentId: { $ne: data.documentId },
|
||||||
time_start: { $lt: time_end },
|
time_start: { $lt: time_end },
|
||||||
time_end: { $gt: time_start },
|
time_end: { $gt: time_start },
|
||||||
|
master: masterId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (overlappingEntities.length > 0) {
|
if (overlappingEntities.length > 0) {
|
||||||
throw new Error(ERR_OVERLAPPING_TIME);
|
throw new Error('Время пересекается с другими слотами');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async beforeUpdate(event) {
|
async beforeUpdate(event) {
|
||||||
const { data, where } = event.params;
|
const { data, where } = event.params;
|
||||||
const { id: entityId } = where;
|
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) {
|
if (!time_start || !time_end) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
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);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +119,7 @@ export default {
|
|||||||
throw new Error('Запись не найдена');
|
throw new Error('Запись не найдена');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { date, documentId } = existingEntity;
|
const { documentId } = existingEntity;
|
||||||
|
|
||||||
const overlappingEntities = await strapi.db
|
const overlappingEntities = await strapi.db
|
||||||
.query('api::slot.slot')
|
.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);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
13
src/utils.ts
Normal file
13
src/utils.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user