feat: refactor order and slot lifecycles to use datetime fields for time validation
This commit is contained in:
parent
07b4239038
commit
456f9b071c
@ -12,14 +12,10 @@ const ERR_MISSING_CLIENT = 'Не указан клиент';
|
||||
const ERR_MISSING_SLOT = 'Не указан слот';
|
||||
const ERR_MISSING_SERVICE = 'Не указан сервис';
|
||||
|
||||
function timeToDate(time: string) {
|
||||
return new Date(`1970-01-01T${time}Z`);
|
||||
}
|
||||
|
||||
export default {
|
||||
async beforeCreate(event) {
|
||||
const { data } = event.params;
|
||||
const { time_start, time_end, client, services } = data;
|
||||
const { datetime_start, datetime_end, client, services } = data;
|
||||
const clientId = extractId(client);
|
||||
const slotId = extractId(data.slot);
|
||||
|
||||
@ -29,10 +25,10 @@ export default {
|
||||
if (!extractId(services)) throw new Error(ERR_MISSING_SERVICE);
|
||||
|
||||
// Проверка корректности времени заказа.
|
||||
if (!time_start || !time_end) {
|
||||
if (!datetime_start || !datetime_end) {
|
||||
throw new Error(ERR_MISSING_TIME);
|
||||
}
|
||||
if (timeToDate(time_start) >= timeToDate(time_end)) {
|
||||
if (new Date(datetime_start) >= new Date(datetime_end)) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
|
||||
@ -96,8 +92,8 @@ export default {
|
||||
.findMany({
|
||||
where: {
|
||||
documentId: { $ne: data.documentId },
|
||||
time_start: { $lt: time_end },
|
||||
time_end: { $gt: time_start },
|
||||
datetime_start: { $lt: datetime_end },
|
||||
datetime_end: { $gt: datetime_start },
|
||||
slot: {
|
||||
id: { $eq: slotId },
|
||||
},
|
||||
@ -116,15 +112,15 @@ export default {
|
||||
async beforeUpdate(event) {
|
||||
const { data, where } = event.params;
|
||||
const { id: entityId } = where;
|
||||
const { time_start, time_end, state } = data;
|
||||
const { datetime_start, datetime_end, state } = data;
|
||||
|
||||
const existingOrder = await strapi.db.query('api::order.order').findOne({
|
||||
where: { id: entityId },
|
||||
select: ['documentId', 'time_start', 'time_end'],
|
||||
select: ['documentId', 'datetime_start', 'datetime_end'],
|
||||
populate: ['slot', 'client'],
|
||||
});
|
||||
|
||||
if (state && !time_start && !time_end) {
|
||||
if (state && !datetime_start && !datetime_end) {
|
||||
if (state === 'completed') {
|
||||
const clientId = extractId(existingOrder.client);
|
||||
|
||||
@ -148,17 +144,17 @@ export default {
|
||||
}
|
||||
|
||||
if (
|
||||
existingOrder.time_start === time_start &&
|
||||
existingOrder.time_end === time_end
|
||||
existingOrder.datetime_start === datetime_start &&
|
||||
existingOrder.datetime_end === datetime_end
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!time_start || !time_end) {
|
||||
if (!datetime_start || !datetime_end) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
|
||||
if (timeToDate(time_start) >= timeToDate(time_end)) {
|
||||
if (new Date(datetime_start) >= new Date(datetime_end)) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
|
||||
@ -173,8 +169,8 @@ export default {
|
||||
.findMany({
|
||||
where: {
|
||||
id: { $ne: entityId },
|
||||
time_start: { $lt: time_end },
|
||||
time_end: { $gt: time_start },
|
||||
datetime_start: { $lt: datetime_end },
|
||||
datetime_end: { $gt: datetime_start },
|
||||
slot: {
|
||||
documentId: { $eq: slotId },
|
||||
},
|
||||
|
||||
@ -62,6 +62,14 @@
|
||||
"relation": "manyToMany",
|
||||
"target": "api::service.service",
|
||||
"inversedBy": "orders"
|
||||
},
|
||||
"datetime_start": {
|
||||
"type": "datetime",
|
||||
"required": true
|
||||
},
|
||||
"datetime_end": {
|
||||
"type": "datetime",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,14 +17,10 @@ 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 { master, date, time_start, time_end } = data;
|
||||
const { master, datetime_start, datetime_end } = data;
|
||||
|
||||
// Проверка, что мастер существует и активен
|
||||
const masterId = extractId(master);
|
||||
@ -39,70 +35,63 @@ export default {
|
||||
}
|
||||
|
||||
// Проверка, что слот не создаётся в прошлом
|
||||
if (date && time_start) {
|
||||
const slotDate = timeToDate(date, time_start);
|
||||
if (slotDate < new Date()) {
|
||||
throw new Error(ERR_PAST_SLOT);
|
||||
}
|
||||
if (datetime_start && new Date(datetime_start) < new Date()) {
|
||||
throw new Error(ERR_PAST_SLOT);
|
||||
}
|
||||
|
||||
// --- Существующие проверки времени и пересечений ---
|
||||
if (!time_start || !time_end) {
|
||||
throw new Error('Некорректное время');
|
||||
// Проверка валидности времени
|
||||
if (!datetime_start || !datetime_end) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
if (timeToDate(date, time_start) >= timeToDate(date, time_end)) {
|
||||
throw new Error('Некорректное время');
|
||||
if (new Date(datetime_start) >= new Date(datetime_end)) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
const overlappingEntities = await strapi.db
|
||||
.query('api::slot.slot')
|
||||
.findMany({
|
||||
where: {
|
||||
date,
|
||||
documentId: { $ne: data.documentId },
|
||||
time_start: { $lt: time_end },
|
||||
time_end: { $gt: time_start },
|
||||
datetime_start: { $lt: datetime_end },
|
||||
datetime_end: { $gt: datetime_start },
|
||||
master: masterId,
|
||||
},
|
||||
});
|
||||
if (overlappingEntities.length > 0) {
|
||||
throw new Error('Время пересекается с другими слотами');
|
||||
throw new Error(ERR_OVERLAPPING_TIME);
|
||||
}
|
||||
},
|
||||
async beforeUpdate(event) {
|
||||
const { data, where } = event.params;
|
||||
const { id: entityId } = where;
|
||||
|
||||
// Если меняется хотя бы одно из полей времени или дата
|
||||
const isTimeChange =
|
||||
'time_start' in data || 'time_end' in data || 'date' in data;
|
||||
// Если меняется хотя бы одно из полей времени
|
||||
const isTimeChange = 'datetime_start' in data || 'datetime_end' in data;
|
||||
|
||||
if (isTimeChange) {
|
||||
let date = data.date;
|
||||
let time_start = data.time_start;
|
||||
let time_end = data.time_end;
|
||||
let datetime_start = data.datetime_start;
|
||||
let datetime_end = data.datetime_end;
|
||||
|
||||
// Подтянуть недостающие значения из существующего слота
|
||||
const existingSlot = await strapi.db.query('api::slot.slot').findOne({
|
||||
where: { id: entityId },
|
||||
select: ['date', 'time_start', 'time_end'],
|
||||
select: ['datetime_start', 'datetime_end'],
|
||||
});
|
||||
if (!date) date = existingSlot?.date;
|
||||
if (!time_start) time_start = existingSlot?.time_start;
|
||||
if (!time_end) time_end = existingSlot?.time_end;
|
||||
if (!datetime_start) datetime_start = existingSlot?.datetime_start;
|
||||
if (!datetime_end) datetime_end = existingSlot?.datetime_end;
|
||||
|
||||
// Проверка: оба времени должны быть определены
|
||||
if (!time_start || !time_end) {
|
||||
if (!datetime_start || !datetime_end) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
|
||||
// Проверка валидности времени
|
||||
if (timeToDate(date, time_start) >= timeToDate(date, time_end)) {
|
||||
if (new Date(datetime_start) >= new Date(datetime_end)) {
|
||||
throw new Error(ERR_INVALID_TIME);
|
||||
}
|
||||
|
||||
const existingEntity = await strapi.db.query('api::slot.slot').findOne({
|
||||
where: { id: entityId },
|
||||
select: ['date', 'documentId'],
|
||||
select: ['documentId'],
|
||||
populate: ['orders'],
|
||||
});
|
||||
|
||||
@ -120,19 +109,16 @@ export default {
|
||||
}
|
||||
|
||||
const { documentId } = existingEntity;
|
||||
|
||||
const overlappingEntities = await strapi.db
|
||||
.query('api::slot.slot')
|
||||
.findMany({
|
||||
where: {
|
||||
date,
|
||||
id: { $ne: entityId },
|
||||
documentId: { $ne: documentId },
|
||||
time_start: { $lt: time_end },
|
||||
time_end: { $gt: time_start },
|
||||
datetime_start: { $lt: datetime_end },
|
||||
datetime_end: { $gt: datetime_start },
|
||||
},
|
||||
});
|
||||
|
||||
if (overlappingEntities.length > 0) {
|
||||
throw new Error(ERR_OVERLAPPING_TIME);
|
||||
}
|
||||
|
||||
@ -42,6 +42,14 @@
|
||||
},
|
||||
"date": {
|
||||
"type": "date"
|
||||
},
|
||||
"datetime_end": {
|
||||
"type": "datetime",
|
||||
"required": true
|
||||
},
|
||||
"datetime_start": {
|
||||
"type": "datetime",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
types/generated/contentTypes.d.ts
vendored
4
types/generated/contentTypes.d.ts
vendored
@ -470,6 +470,8 @@ export interface ApiOrderOrder extends Struct.CollectionTypeSchema {
|
||||
createdAt: Schema.Attribute.DateTime;
|
||||
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||
Schema.Attribute.Private;
|
||||
datetime_end: Schema.Attribute.DateTime & Schema.Attribute.Required;
|
||||
datetime_start: Schema.Attribute.DateTime & Schema.Attribute.Required;
|
||||
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||
localizations: Schema.Attribute.Relation<'oneToMany', 'api::order.order'> &
|
||||
Schema.Attribute.Private;
|
||||
@ -578,6 +580,8 @@ export interface ApiSlotSlot extends Struct.CollectionTypeSchema {
|
||||
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||
Schema.Attribute.Private;
|
||||
date: Schema.Attribute.Date;
|
||||
datetime_end: Schema.Attribute.DateTime & Schema.Attribute.Required;
|
||||
datetime_start: Schema.Attribute.DateTime & Schema.Attribute.Required;
|
||||
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||
localizations: Schema.Attribute.Relation<'oneToMany', 'api::slot.slot'> &
|
||||
Schema.Attribute.Private;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user