Compare commits
8 Commits
master
...
refactor/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c22e179b73 | ||
|
|
ec06282d56 | ||
|
|
f0e4a99032 | ||
|
|
d91cabaed0 | ||
|
|
d42e240a2a | ||
|
|
04e8393993 | ||
|
|
cd5456f7f5 | ||
|
|
456f9b071c |
@ -15,6 +15,7 @@
|
|||||||
"@strapi/plugin-graphql": "^5.15.0",
|
"@strapi/plugin-graphql": "^5.15.0",
|
||||||
"@strapi/plugin-users-permissions": "5.15.0",
|
"@strapi/plugin-users-permissions": "5.15.0",
|
||||||
"@strapi/strapi": "5.15.0",
|
"@strapi/strapi": "5.15.0",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"pg": "8.8.0",
|
"pg": "8.8.0",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0",
|
"react-dom": "^18.0.0",
|
||||||
|
|||||||
52
pnpm-lock.yaml
generated
52
pnpm-lock.yaml
generated
@ -20,6 +20,9 @@ importers:
|
|||||||
'@strapi/strapi':
|
'@strapi/strapi':
|
||||||
specifier: 5.15.0
|
specifier: 5.15.0
|
||||||
version: 5.15.0(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@swc/helpers@0.5.15)(@types/hoist-non-react-statics@3.3.5)(@types/node@20.17.9)(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(esbuild@0.25.5)(koa@2.16.1)(pg@8.8.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redux@4.2.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.36.0)(type-fest@4.29.0)
|
version: 5.15.0(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@swc/helpers@0.5.15)(@types/hoist-non-react-statics@3.3.5)(@types/node@20.17.9)(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(esbuild@0.25.5)(koa@2.16.1)(pg@8.8.0)(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(redux@4.2.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.36.0)(type-fest@4.29.0)
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.13
|
||||||
|
version: 1.11.13
|
||||||
pg:
|
pg:
|
||||||
specifier: 8.8.0
|
specifier: 8.8.0
|
||||||
version: 8.8.0
|
version: 8.8.0
|
||||||
@ -2808,6 +2811,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||||
engines: {node: '>=0.11'}
|
engines: {node: '>=0.11'}
|
||||||
|
|
||||||
|
dayjs@1.11.13:
|
||||||
|
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||||
|
|
||||||
debounce@1.2.1:
|
debounce@1.2.1:
|
||||||
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||||
|
|
||||||
@ -7789,7 +7795,7 @@ snapshots:
|
|||||||
'@strapi/design-system': 2.0.0-rc.24(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@strapi/icons@2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
'@strapi/design-system': 2.0.0-rc.24(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@strapi/icons@2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
'@strapi/icons': 2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
'@strapi/icons': 2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
'@strapi/permissions': 5.15.0
|
'@strapi/permissions': 5.15.0
|
||||||
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.4.4)
|
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)
|
||||||
'@strapi/typescript-utils': 5.15.0
|
'@strapi/typescript-utils': 5.15.0
|
||||||
'@strapi/utils': 5.15.0
|
'@strapi/utils': 5.15.0
|
||||||
'@testing-library/dom': 10.1.0
|
'@testing-library/dom': 10.1.0
|
||||||
@ -7982,7 +7988,7 @@ snapshots:
|
|||||||
'@strapi/database': 5.15.0(@types/node@20.17.9)(pg@8.8.0)
|
'@strapi/database': 5.15.0(@types/node@20.17.9)(pg@8.8.0)
|
||||||
'@strapi/design-system': 2.0.0-rc.24(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@strapi/icons@2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
'@strapi/design-system': 2.0.0-rc.24(@babel/runtime@7.26.0)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.5)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3))(@codemirror/language@6.10.5)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.0)(@strapi/icons@2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@types/react-dom@18.3.1)(@types/react@18.3.12)(codemirror@5.65.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
'@strapi/icons': 2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
'@strapi/icons': 2.0.0-rc.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||||
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.4.4)
|
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)
|
||||||
'@strapi/utils': 5.15.0
|
'@strapi/utils': 5.15.0
|
||||||
date-fns: 2.30.0
|
date-fns: 2.30.0
|
||||||
date-fns-tz: 2.0.1(date-fns@2.30.0)
|
date-fns-tz: 2.0.1(date-fns@2.30.0)
|
||||||
@ -8075,7 +8081,7 @@ snapshots:
|
|||||||
'@strapi/generators': 5.15.0
|
'@strapi/generators': 5.15.0
|
||||||
'@strapi/logger': 5.15.0
|
'@strapi/logger': 5.15.0
|
||||||
'@strapi/permissions': 5.15.0
|
'@strapi/permissions': 5.15.0
|
||||||
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.4.4)
|
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)
|
||||||
'@strapi/typescript-utils': 5.15.0
|
'@strapi/typescript-utils': 5.15.0
|
||||||
'@strapi/utils': 5.15.0
|
'@strapi/utils': 5.15.0
|
||||||
bcryptjs: 2.4.3
|
bcryptjs: 2.4.3
|
||||||
@ -8525,7 +8531,7 @@ snapshots:
|
|||||||
'@strapi/logger': 5.15.0
|
'@strapi/logger': 5.15.0
|
||||||
'@strapi/permissions': 5.15.0
|
'@strapi/permissions': 5.15.0
|
||||||
'@strapi/review-workflows': 5.15.0(2imnoqd43gbalynv3yjuponlpy)
|
'@strapi/review-workflows': 5.15.0(2imnoqd43gbalynv3yjuponlpy)
|
||||||
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.4.4)
|
'@strapi/types': 5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)
|
||||||
'@strapi/typescript-utils': 5.15.0
|
'@strapi/typescript-utils': 5.15.0
|
||||||
'@strapi/upload': 5.15.0(6nrgkysfwt52tfycqocmy4s2kq)
|
'@strapi/upload': 5.15.0(6nrgkysfwt52tfycqocmy4s2kq)
|
||||||
'@strapi/utils': 5.15.0
|
'@strapi/utils': 5.15.0
|
||||||
@ -8627,34 +8633,6 @@ snapshots:
|
|||||||
- webpack-dev-server
|
- webpack-dev-server
|
||||||
- webpack-plugin-serve
|
- webpack-plugin-serve
|
||||||
|
|
||||||
'@strapi/types@5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.4.4)':
|
|
||||||
dependencies:
|
|
||||||
'@casl/ability': 6.5.0
|
|
||||||
'@koa/cors': 5.0.0
|
|
||||||
'@koa/router': 12.0.2
|
|
||||||
'@strapi/database': 5.15.0(@types/node@20.17.9)(pg@8.8.0)
|
|
||||||
'@strapi/logger': 5.15.0
|
|
||||||
'@strapi/permissions': 5.15.0
|
|
||||||
'@strapi/utils': 5.15.0
|
|
||||||
commander: 8.3.0
|
|
||||||
koa: 2.16.1
|
|
||||||
koa-body: 6.0.1
|
|
||||||
node-schedule: 2.1.1
|
|
||||||
typedoc: 0.25.10(typescript@5.4.4)
|
|
||||||
typedoc-github-wiki-theme: 1.1.0(typedoc-plugin-markdown@3.17.1(typedoc@0.25.10(typescript@5.7.2)))(typedoc@0.25.10(typescript@5.7.2))
|
|
||||||
typedoc-plugin-markdown: 3.17.1(typedoc@0.25.10(typescript@5.7.2))
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@types/node'
|
|
||||||
- better-sqlite3
|
|
||||||
- mysql
|
|
||||||
- mysql2
|
|
||||||
- pg
|
|
||||||
- pg-native
|
|
||||||
- sqlite3
|
|
||||||
- supports-color
|
|
||||||
- tedious
|
|
||||||
- typescript
|
|
||||||
|
|
||||||
'@strapi/types@5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)':
|
'@strapi/types@5.15.0(@types/node@20.17.9)(pg@8.8.0)(typescript@5.7.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@casl/ability': 6.5.0
|
'@casl/ability': 6.5.0
|
||||||
@ -9908,6 +9886,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.0
|
'@babel/runtime': 7.26.0
|
||||||
|
|
||||||
|
dayjs@1.11.13: {}
|
||||||
|
|
||||||
debounce@1.2.1: {}
|
debounce@1.2.1: {}
|
||||||
|
|
||||||
debug@2.6.9:
|
debug@2.6.9:
|
||||||
@ -13142,14 +13122,6 @@ snapshots:
|
|||||||
handlebars: 4.7.8
|
handlebars: 4.7.8
|
||||||
typedoc: 0.25.10(typescript@5.7.2)
|
typedoc: 0.25.10(typescript@5.7.2)
|
||||||
|
|
||||||
typedoc@0.25.10(typescript@5.4.4):
|
|
||||||
dependencies:
|
|
||||||
lunr: 2.3.9
|
|
||||||
marked: 4.3.0
|
|
||||||
minimatch: 9.0.5
|
|
||||||
shiki: 0.14.7
|
|
||||||
typescript: 5.4.4
|
|
||||||
|
|
||||||
typedoc@0.25.10(typescript@5.7.2):
|
typedoc@0.25.10(typescript@5.7.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
lunr: 2.3.9
|
lunr: 2.3.9
|
||||||
|
|||||||
@ -11,15 +11,23 @@ const ERR_INVALID_MASTER = 'Некорректный мастер';
|
|||||||
const ERR_MISSING_CLIENT = 'Не указан клиент';
|
const ERR_MISSING_CLIENT = 'Не указан клиент';
|
||||||
const ERR_MISSING_SLOT = 'Не указан слот';
|
const ERR_MISSING_SLOT = 'Не указан слот';
|
||||||
const ERR_MISSING_SERVICE = 'Не указан сервис';
|
const ERR_MISSING_SERVICE = 'Не указан сервис';
|
||||||
|
const ERR_ORDER_OUT_OF_SLOT = 'Время заказа выходит за пределы слота';
|
||||||
|
const ERR_EXISTING_ORDER_OR_SLOT_NOT_FOUND = 'Существующий заказ или слот не найден';
|
||||||
|
const ERR_CANNOT_COMPLETE_BEFORE_START = 'Нельзя завершить запись до её наступления';
|
||||||
|
|
||||||
function timeToDate(time: string) {
|
const dayjs = require('dayjs');
|
||||||
return new Date(`1970-01-01T${time}Z`);
|
const utc = require('dayjs/plugin/utc');
|
||||||
|
const timezone = require('dayjs/plugin/timezone');
|
||||||
|
|
||||||
|
if (!dayjs.prototype.tz) {
|
||||||
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(timezone);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async beforeCreate(event) {
|
async beforeCreate(event) {
|
||||||
const { data } = event.params;
|
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 clientId = extractId(client);
|
||||||
const slotId = extractId(data.slot);
|
const slotId = extractId(data.slot);
|
||||||
|
|
||||||
@ -29,10 +37,10 @@ export default {
|
|||||||
if (!extractId(services)) throw new Error(ERR_MISSING_SERVICE);
|
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);
|
throw new Error(ERR_MISSING_TIME);
|
||||||
}
|
}
|
||||||
if (timeToDate(time_start) >= timeToDate(time_end)) {
|
if (new Date(datetime_end) <= new Date(datetime_start)) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +51,14 @@ export default {
|
|||||||
});
|
});
|
||||||
if (!slot) throw new Error(ERR_MISSING_SLOT);
|
if (!slot) throw new Error(ERR_MISSING_SLOT);
|
||||||
|
|
||||||
|
// Проверка, что заказ укладывается в рамки слота
|
||||||
|
if (
|
||||||
|
new Date(datetime_start) < new Date(slot.datetime_start) ||
|
||||||
|
new Date(datetime_end) > new Date(slot.datetime_end)
|
||||||
|
) {
|
||||||
|
throw new Error(ERR_ORDER_OUT_OF_SLOT);
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Слот не должен быть закрыт
|
// 1. Слот не должен быть закрыт
|
||||||
if (slot.state === 'closed') {
|
if (slot.state === 'closed') {
|
||||||
throw new Error(ERR_SLOT_CLOSED);
|
throw new Error(ERR_SLOT_CLOSED);
|
||||||
@ -96,13 +112,13 @@ export default {
|
|||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
documentId: { $ne: data.documentId },
|
documentId: { $ne: data.documentId },
|
||||||
time_start: { $lt: time_end },
|
datetime_start: { $lt: datetime_end },
|
||||||
time_end: { $gt: time_start },
|
datetime_end: { $gt: datetime_start },
|
||||||
slot: {
|
slot: {
|
||||||
id: { $eq: slotId },
|
id: { $eq: slotId },
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
$notIn: ['cancelled', 'completed'],
|
$notIn: ['cancelled'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
populate: ['slot'],
|
populate: ['slot'],
|
||||||
@ -116,15 +132,15 @@ export default {
|
|||||||
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, state } = data;
|
const { datetime_start, datetime_end, state } = data;
|
||||||
|
|
||||||
const existingOrder = await strapi.db.query('api::order.order').findOne({
|
const existingOrder = await strapi.db.query('api::order.order').findOne({
|
||||||
where: { id: entityId },
|
where: { id: entityId },
|
||||||
select: ['documentId', 'time_start', 'time_end'],
|
select: ['documentId', 'datetime_start', 'datetime_end'],
|
||||||
populate: ['slot', 'client'],
|
populate: ['slot', 'client'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (state && !time_start && !time_end) {
|
if (state && !datetime_start && !datetime_end) {
|
||||||
if (state === 'completed') {
|
if (state === 'completed') {
|
||||||
const clientId = extractId(existingOrder.client);
|
const clientId = extractId(existingOrder.client);
|
||||||
|
|
||||||
@ -144,26 +160,33 @@ export default {
|
|||||||
data.order_number = lastOrderNumber + 1;
|
data.order_number = lastOrderNumber + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const now = dayjs().tz('Europe/Moscow');
|
||||||
|
const orderStart = dayjs(existingOrder.datetime_start).tz('Europe/Moscow');
|
||||||
|
|
||||||
|
if (state === 'completed' && now.isBefore(orderStart, 'minute')) {
|
||||||
|
throw new Error(ERR_CANNOT_COMPLETE_BEFORE_START);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
existingOrder.time_start === time_start &&
|
existingOrder.datetime_start === datetime_start &&
|
||||||
existingOrder.time_end === time_end
|
existingOrder.datetime_end === datetime_end
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!time_start || !time_end) {
|
if (!datetime_start || !datetime_end) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeToDate(time_start) >= timeToDate(time_end)) {
|
if (new Date(datetime_end) <= new Date(datetime_start)) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existingOrder || !existingOrder.slot) {
|
if (!existingOrder || !existingOrder.slot) {
|
||||||
throw new Error('Существующий заказ или слот не найден');
|
throw new Error(ERR_EXISTING_ORDER_OR_SLOT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const slotId = existingOrder.slot.documentId;
|
const slotId = existingOrder.slot.documentId;
|
||||||
@ -173,8 +196,8 @@ export default {
|
|||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
id: { $ne: entityId },
|
id: { $ne: entityId },
|
||||||
time_start: { $lt: time_end },
|
datetime_start: { $lt: datetime_end },
|
||||||
time_end: { $gt: time_start },
|
datetime_end: { $gt: datetime_start },
|
||||||
slot: {
|
slot: {
|
||||||
documentId: { $eq: slotId },
|
documentId: { $eq: slotId },
|
||||||
},
|
},
|
||||||
|
|||||||
@ -51,17 +51,19 @@
|
|||||||
"target": "api::slot.slot",
|
"target": "api::slot.slot",
|
||||||
"inversedBy": "orders"
|
"inversedBy": "orders"
|
||||||
},
|
},
|
||||||
"time_start": {
|
|
||||||
"type": "time"
|
|
||||||
},
|
|
||||||
"time_end": {
|
|
||||||
"type": "time"
|
|
||||||
},
|
|
||||||
"services": {
|
"services": {
|
||||||
"type": "relation",
|
"type": "relation",
|
||||||
"relation": "manyToMany",
|
"relation": "manyToMany",
|
||||||
"target": "api::service.service",
|
"target": "api::service.service",
|
||||||
"inversedBy": "orders"
|
"inversedBy": "orders"
|
||||||
|
},
|
||||||
|
"datetime_start": {
|
||||||
|
"type": "datetime",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"datetime_end": {
|
||||||
|
"type": "datetime",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,12 @@
|
|||||||
import { extractId } from '../../../../utils';
|
import { extractId } from '../../../../utils';
|
||||||
|
const dayjs = require('dayjs');
|
||||||
|
const utc = require('dayjs/plugin/utc');
|
||||||
|
const timezone = require('dayjs/plugin/timezone');
|
||||||
|
|
||||||
|
if (!dayjs.prototype.tz) {
|
||||||
|
dayjs.extend(utc);
|
||||||
|
dayjs.extend(timezone);
|
||||||
|
}
|
||||||
|
|
||||||
const ERR_INVALID_TIME = 'Некорректное время';
|
const ERR_INVALID_TIME = 'Некорректное время';
|
||||||
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими слотами';
|
const ERR_OVERLAPPING_TIME = 'Время пересекается с другими слотами';
|
||||||
@ -16,15 +24,12 @@ const ERR_INACTIVE_MASTER = 'Мастер не активен';
|
|||||||
const ERR_INVALID_MASTER = 'Некорректный мастер';
|
const ERR_INVALID_MASTER = 'Некорректный мастер';
|
||||||
const ERR_PAST_SLOT = 'Нельзя создать слот в прошлом';
|
const ERR_PAST_SLOT = 'Нельзя создать слот в прошлом';
|
||||||
const ERR_SLOT_HAS_ORDERS = 'Нельзя удалить слот с активными заказами';
|
const ERR_SLOT_HAS_ORDERS = 'Нельзя удалить слот с активными заказами';
|
||||||
|
const ERR_RECORD_NOT_FOUND = 'Запись не найдена';
|
||||||
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 { master, date, time_start, time_end } = data;
|
const { master, datetime_start, datetime_end } = data;
|
||||||
|
|
||||||
// Проверка, что мастер существует и активен
|
// Проверка, что мастер существует и активен
|
||||||
const masterId = extractId(master);
|
const masterId = extractId(master);
|
||||||
@ -39,70 +44,67 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Проверка, что слот не создаётся в прошлом
|
// Проверка, что слот не создаётся в прошлом
|
||||||
if (date && time_start) {
|
if (datetime_start) {
|
||||||
const slotDate = timeToDate(date, time_start);
|
const now = dayjs().tz('Europe/Moscow');
|
||||||
if (slotDate < new Date()) {
|
const slotStart = dayjs(datetime_start).tz('Europe/Moscow');
|
||||||
|
if (slotStart.isBefore(now, 'minute')) {
|
||||||
throw new Error(ERR_PAST_SLOT);
|
throw new Error(ERR_PAST_SLOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Существующие проверки времени и пересечений ---
|
// Проверка валидности времени
|
||||||
if (!time_start || !time_end) {
|
if (!datetime_start || !datetime_end) {
|
||||||
throw new Error('Некорректное время');
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
if (timeToDate(date, time_start) >= timeToDate(date, time_end)) {
|
if (new Date(datetime_end) <= new Date(datetime_start)) {
|
||||||
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({
|
||||||
where: {
|
where: {
|
||||||
date,
|
|
||||||
documentId: { $ne: data.documentId },
|
documentId: { $ne: data.documentId },
|
||||||
time_start: { $lt: time_end },
|
datetime_start: { $lt: datetime_end },
|
||||||
time_end: { $gt: time_start },
|
datetime_end: { $gt: datetime_start },
|
||||||
master: masterId,
|
master: masterId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (overlappingEntities.length > 0) {
|
if (overlappingEntities.length > 0) {
|
||||||
throw new Error('Время пересекается с другими слотами');
|
throw new Error(ERR_OVERLAPPING_TIME);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
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 isTimeChange =
|
const isTimeChange = 'datetime_start' in data || 'datetime_end' in data;
|
||||||
'time_start' in data || 'time_end' in data || 'date' in data;
|
|
||||||
|
|
||||||
if (isTimeChange) {
|
if (isTimeChange) {
|
||||||
let date = data.date;
|
let datetime_start = data.datetime_start;
|
||||||
let time_start = data.time_start;
|
let datetime_end = data.datetime_end;
|
||||||
let time_end = data.time_end;
|
|
||||||
|
|
||||||
// Подтянуть недостающие значения из существующего слота
|
// Подтянуть недостающие значения из существующего слота
|
||||||
const existingSlot = await strapi.db.query('api::slot.slot').findOne({
|
const existingSlot = await strapi.db.query('api::slot.slot').findOne({
|
||||||
where: { id: entityId },
|
where: { id: entityId },
|
||||||
select: ['date', 'time_start', 'time_end'],
|
select: ['datetime_start', 'datetime_end'],
|
||||||
});
|
});
|
||||||
if (!date) date = existingSlot?.date;
|
if (!datetime_start) datetime_start = existingSlot?.datetime_start;
|
||||||
if (!time_start) time_start = existingSlot?.time_start;
|
if (!datetime_end) datetime_end = existingSlot?.datetime_end;
|
||||||
if (!time_end) time_end = existingSlot?.time_end;
|
|
||||||
|
|
||||||
// Проверка: оба времени должны быть определены
|
// Проверка: оба времени должны быть определены
|
||||||
if (!time_start || !time_end) {
|
if (!datetime_start || !datetime_end) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверка валидности времени
|
// Проверка валидности времени
|
||||||
if (timeToDate(date, time_start) >= timeToDate(date, time_end)) {
|
if (new Date(datetime_end) <= new Date(datetime_start)) {
|
||||||
throw new Error(ERR_INVALID_TIME);
|
throw new Error(ERR_INVALID_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingEntity = await strapi.db.query('api::slot.slot').findOne({
|
const existingEntity = await strapi.db.query('api::slot.slot').findOne({
|
||||||
where: { id: entityId },
|
where: { id: entityId },
|
||||||
select: ['date', 'documentId'],
|
select: ['documentId'],
|
||||||
populate: ['orders'],
|
populate: ['orders'],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -116,23 +118,20 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!existingEntity) {
|
if (!existingEntity) {
|
||||||
throw new Error('Запись не найдена');
|
throw new Error(ERR_RECORD_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { documentId } = existingEntity;
|
const { documentId } = existingEntity;
|
||||||
|
|
||||||
const overlappingEntities = await strapi.db
|
const overlappingEntities = await strapi.db
|
||||||
.query('api::slot.slot')
|
.query('api::slot.slot')
|
||||||
.findMany({
|
.findMany({
|
||||||
where: {
|
where: {
|
||||||
date,
|
|
||||||
id: { $ne: entityId },
|
id: { $ne: entityId },
|
||||||
documentId: { $ne: documentId },
|
documentId: { $ne: documentId },
|
||||||
time_start: { $lt: time_end },
|
datetime_start: { $lt: datetime_end },
|
||||||
time_end: { $gt: time_start },
|
datetime_end: { $gt: datetime_start },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (overlappingEntities.length > 0) {
|
if (overlappingEntities.length > 0) {
|
||||||
throw new Error(ERR_OVERLAPPING_TIME);
|
throw new Error(ERR_OVERLAPPING_TIME);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,14 +12,6 @@
|
|||||||
},
|
},
|
||||||
"pluginOptions": {},
|
"pluginOptions": {},
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"time_start": {
|
|
||||||
"type": "time",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"time_end": {
|
|
||||||
"type": "time",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"state": {
|
"state": {
|
||||||
"type": "enumeration",
|
"type": "enumeration",
|
||||||
"enum": [
|
"enum": [
|
||||||
@ -40,8 +32,13 @@
|
|||||||
"target": "api::customer.customer",
|
"target": "api::customer.customer",
|
||||||
"inversedBy": "slots"
|
"inversedBy": "slots"
|
||||||
},
|
},
|
||||||
"date": {
|
"datetime_end": {
|
||||||
"type": "date"
|
"type": "datetime",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"datetime_start": {
|
||||||
|
"type": "datetime",
|
||||||
|
"required": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
types/generated/contentTypes.d.ts
vendored
9
types/generated/contentTypes.d.ts
vendored
@ -470,6 +470,8 @@ export interface ApiOrderOrder extends Struct.CollectionTypeSchema {
|
|||||||
createdAt: Schema.Attribute.DateTime;
|
createdAt: Schema.Attribute.DateTime;
|
||||||
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
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;
|
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||||
localizations: Schema.Attribute.Relation<'oneToMany', 'api::order.order'> &
|
localizations: Schema.Attribute.Relation<'oneToMany', 'api::order.order'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
@ -490,8 +492,6 @@ export interface ApiOrderOrder extends Struct.CollectionTypeSchema {
|
|||||||
]
|
]
|
||||||
> &
|
> &
|
||||||
Schema.Attribute.DefaultTo<'created'>;
|
Schema.Attribute.DefaultTo<'created'>;
|
||||||
time_end: Schema.Attribute.Time;
|
|
||||||
time_start: Schema.Attribute.Time;
|
|
||||||
updatedAt: Schema.Attribute.DateTime;
|
updatedAt: Schema.Attribute.DateTime;
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
@ -577,7 +577,8 @@ export interface ApiSlotSlot extends Struct.CollectionTypeSchema {
|
|||||||
createdAt: Schema.Attribute.DateTime;
|
createdAt: Schema.Attribute.DateTime;
|
||||||
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
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;
|
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||||
localizations: Schema.Attribute.Relation<'oneToMany', 'api::slot.slot'> &
|
localizations: Schema.Attribute.Relation<'oneToMany', 'api::slot.slot'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
@ -585,8 +586,6 @@ export interface ApiSlotSlot extends Struct.CollectionTypeSchema {
|
|||||||
orders: Schema.Attribute.Relation<'oneToMany', 'api::order.order'>;
|
orders: Schema.Attribute.Relation<'oneToMany', 'api::order.order'>;
|
||||||
publishedAt: Schema.Attribute.DateTime;
|
publishedAt: Schema.Attribute.DateTime;
|
||||||
state: Schema.Attribute.Enumeration<['open', 'reserved', 'closed']>;
|
state: Schema.Attribute.Enumeration<['open', 'reserved', 'closed']>;
|
||||||
time_end: Schema.Attribute.Time & Schema.Attribute.Required;
|
|
||||||
time_start: Schema.Attribute.Time & Schema.Attribute.Required;
|
|
||||||
updatedAt: Schema.Attribute.DateTime;
|
updatedAt: Schema.Attribute.DateTime;
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user