@floristcloud/api-lib 1.2.139 → 1.2.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/order-product/group/price-correction-analyze.command.js +27 -1
- package/build/commands/order-product/group/price-correction-execute.command.js +10 -0
- package/build/commands/report/get-user-activity-timeline.query.js +51 -0
- package/build/commands/report/get-user-activity.query.js +30 -0
- package/build/commands/report/index.js +2 -0
- package/build/enum/action-log-type.enum.js +4 -0
- package/build/enum/index.js +3 -0
- package/build/enum/user-activity-action-type.enum.js +46 -0
- package/build/enum/user-activity-section.enum.js +16 -0
- package/build/enum/user-activity-source.enum.js +20 -0
- package/build/enum/userRole.enum.js +9 -1
- package/build/schemas/action-log/action-log-payload.schema.js +2 -0
- package/build/schemas/action-log/action-log.schema.js +30 -0
- package/build/schemas/action-log/import-transactions-action-log.schema.js +16 -0
- package/build/schemas/action-log/index.js +1 -0
- package/build/schemas/action-log/order-product-move-action-log.schema.js +1 -0
- package/build/schemas/index.js +3 -0
- package/build/schemas/report/user-activity-by-section.schema.js +21 -0
- package/build/schemas/report/user-activity-summary-row.schema.js +28 -0
- package/build/schemas/report/user-activity-timeline-row.schema.js +29 -0
- package/commands/order-product/group/price-correction-analyze.command.ts +32 -1
- package/commands/order-product/group/price-correction-execute.command.ts +12 -0
- package/commands/report/get-user-activity-timeline.query.ts +54 -0
- package/commands/report/get-user-activity.query.ts +32 -0
- package/commands/report/index.ts +2 -0
- package/enum/action-log-type.enum.ts +4 -0
- package/enum/index.ts +3 -0
- package/enum/user-activity-action-type.enum.ts +42 -0
- package/enum/user-activity-section.enum.ts +12 -0
- package/enum/user-activity-source.enum.ts +16 -0
- package/enum/userRole.enum.ts +9 -0
- package/package.json +1 -1
- package/schemas/action-log/action-log-payload.schema.ts +2 -0
- package/schemas/action-log/action-log.schema.ts +35 -0
- package/schemas/action-log/import-transactions-action-log.schema.ts +14 -0
- package/schemas/action-log/index.ts +1 -0
- package/schemas/action-log/order-product-move-action-log.schema.ts +1 -0
- package/schemas/index.ts +3 -0
- package/schemas/report/user-activity-by-section.schema.ts +19 -0
- package/schemas/report/user-activity-summary-row.schema.ts +26 -0
- package/schemas/report/user-activity-timeline-row.schema.ts +27 -0
|
@@ -9,10 +9,24 @@ const PriceCorrectionAnalyzeRequestSchema = zod_1.z
|
|
|
9
9
|
.object({
|
|
10
10
|
productUUIDList: zod_1.z.array(zod_1.z.string().uuid()).optional(),
|
|
11
11
|
consignmentUUID: zod_1.z.string().uuid().optional(),
|
|
12
|
+
/**
|
|
13
|
+
* Режим цены — обязателен и без умолчания, как в execute.
|
|
14
|
+
*
|
|
15
|
+
* Раньше analyze этого поля не знал и считал `onlyChanged` по OR обоих режимов: строка
|
|
16
|
+
* попадала в выдачу, если разошлась цена со скидкой ИЛИ без. На клиенте с фиксированной
|
|
17
|
+
* скидкой это выносило в предпросмотр каждую его позицию — цена строки законно ниже
|
|
18
|
+
* каталожной ровно на скидку, и «без скидок» это выглядит расхождением. Execute при этом
|
|
19
|
+
* считает предикат в выбранном режиме, то есть предпросмотр и применение расходились на
|
|
20
|
+
* целую партию.
|
|
21
|
+
*/
|
|
22
|
+
withDiscount: zod_1.z.boolean(),
|
|
12
23
|
// Поставка на сотни товаров даёт десятки тысяч позиций, из которых после правки прайса
|
|
13
24
|
// меняются единицы. По умолчанию отдаём только те, где цена реально расходится; полный
|
|
14
25
|
// список — режим «показать всё».
|
|
15
26
|
onlyChanged: zod_1.z.boolean().optional().default(true),
|
|
27
|
+
// Позиции, которые применить нельзя (статус заказа, заказ в передаточном документе).
|
|
28
|
+
// `true` — скрыть их из выдачи; сколько их всего, видно по `skippedTotal`.
|
|
29
|
+
excludeSkipped: zod_1.z.boolean().optional().default(false),
|
|
16
30
|
// Страница обязательна по смыслу, а не по схеме: строк здесь товары × заказы, то есть
|
|
17
31
|
// десятки тысяч на партию, и безлимитная выдача кладёт запрос независимо от того, сколько
|
|
18
32
|
// из неё нарисует таблица. Усечение при этом урезает только предпросмотр — execute в
|
|
@@ -33,8 +47,20 @@ const PriceCorrectionAnalyzeResponseSchema = zod_1.z.object({
|
|
|
33
47
|
message: zod_1.z.string().optional(),
|
|
34
48
|
data: zod_1.z.object({
|
|
35
49
|
items: zod_1.z.array(schemas_1.PriceCorrectionAnalyzeItemSchema),
|
|
36
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Размер выдачи, а не страницы: по нему листают. Учитывает ВСЕ фильтры запроса, включая
|
|
52
|
+
* `excludeSkipped`.
|
|
53
|
+
*/
|
|
37
54
|
total: zod_1.z.number(),
|
|
55
|
+
/**
|
|
56
|
+
* Сколько строк набора применить нельзя — статус заказа или заказ в передаточном документе.
|
|
57
|
+
*
|
|
58
|
+
* Считается по набору после `onlyChanged`, но ДО `excludeSkipped`: иначе при скрытых
|
|
59
|
+
* непроходных строках это всегда был бы ноль и шапку «столько-то не пройдёт» стало бы не из
|
|
60
|
+
* чего собрать. Отсюда: применимых строк — `total` при `excludeSkipped: true` и
|
|
61
|
+
* `total − skippedTotal` при `false`.
|
|
62
|
+
*/
|
|
63
|
+
skippedTotal: zod_1.z.number(),
|
|
38
64
|
}),
|
|
39
65
|
});
|
|
40
66
|
var PriceCorrectionAnalyzeContractCommand;
|
|
@@ -42,7 +42,17 @@ const PriceCorrectionExecuteResponseSchema = zod_1.z.object({
|
|
|
42
42
|
// равно перезапрашивает analyze, чтобы обновить таблицу.
|
|
43
43
|
results: zod_1.z.array(schemas_1.PriceCorrectionExecuteResultItemSchema),
|
|
44
44
|
orders: zod_1.z.array(schemas_1.PriceCorrectionExecuteOrderItemSchema),
|
|
45
|
+
/**
|
|
46
|
+
* «Строк в нужной цене после прогона», а не «строк изменено».
|
|
47
|
+
*
|
|
48
|
+
* Применение сходящееся: позиция, уже стоящая в нужной цене, — успех, а не отказ, поэтому
|
|
49
|
+
* повтор запроса и ретрай после таймаута безопасны и дают тот же ответ. Из-за этого при
|
|
50
|
+
* `onlyChanged: false` в счётчик попадают и нетронутые позиции. При `onlyChanged: true`
|
|
51
|
+
* таких строк здесь не будет: они отсеиваются до обработки и в счётчики не идут вовсе.
|
|
52
|
+
*/
|
|
45
53
|
successCount: zod_1.z.number(),
|
|
54
|
+
// Строки, которые прогон не применил: статус заказа, заказ в передаточном документе, отказ
|
|
55
|
+
// применения. Каждая из них есть в `results` — с точностью до потолка (см. `isTruncated`).
|
|
46
56
|
skippedCount: zod_1.z.number(),
|
|
47
57
|
// `results` в scope-форме обрезан по лимиту. Полные числа — в счётчиках и в `orders`.
|
|
48
58
|
isTruncated: zod_1.z.boolean(),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetUserActivityTimelineContractQuery = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const user_activity_section_enum_1 = require("../../enum/user-activity-section.enum");
|
|
6
|
+
const date_helper_1 = require("../../helpers/date.helper");
|
|
7
|
+
const query_boolean_helper_1 = require("../../helpers/query-boolean.helper");
|
|
8
|
+
const user_activity_timeline_row_schema_1 = require("../../schemas/report/user-activity-timeline-row.schema");
|
|
9
|
+
const DEFAULT_PAGE = 1;
|
|
10
|
+
// Верхняя граница страницы: page × count должно помещаться в int4 OFFSET, а лента длиннее
|
|
11
|
+
// MAX_PAGE × MAX_COUNT строк за период невозможна.
|
|
12
|
+
const MAX_PAGE = 10000;
|
|
13
|
+
const DEFAULT_COUNT = 50;
|
|
14
|
+
const MAX_COUNT = 500;
|
|
15
|
+
// Лента действий одного сотрудника за период, в хронологическом порядке.
|
|
16
|
+
//
|
|
17
|
+
// Пагинация обязательная: у активного сотрудника за день до нескольких тысяч строк, поэтому
|
|
18
|
+
// page/count всегда применяются — со значениями по умолчанию, если не переданы.
|
|
19
|
+
const GetUserActivityTimelineRequestSchema = zod_1.z
|
|
20
|
+
.object({
|
|
21
|
+
userUUID: zod_1.z.uuid(),
|
|
22
|
+
startDate: zod_1.z.coerce.date(),
|
|
23
|
+
endDate: zod_1.z.coerce.date(),
|
|
24
|
+
section: zod_1.z.nativeEnum(user_activity_section_enum_1.UserActivitySectionEnum).optional().describe('Keep only the rows of this section.'),
|
|
25
|
+
// «Добавление позиции в заказ» — самый частый тип строк (тысячи в день). По умолчанию
|
|
26
|
+
// выключен, чтобы не забивать ленту; в сводке позиции считаются всегда (positionsAdded).
|
|
27
|
+
includeOrderProductRows: query_boolean_helper_1.queryBooleanSchema.describe('Pass "true" to include "position added to order" rows.'),
|
|
28
|
+
page: zod_1.z.coerce.number().int().positive().max(MAX_PAGE).default(DEFAULT_PAGE),
|
|
29
|
+
count: zod_1.z.coerce.number().int().positive().max(MAX_COUNT).default(DEFAULT_COUNT),
|
|
30
|
+
})
|
|
31
|
+
.refine(value => (0, date_helper_1.dateRangeCheck)(value.startDate, value.endDate), {
|
|
32
|
+
message: 'validation.dateRange',
|
|
33
|
+
path: ['endDate', 'startDate'],
|
|
34
|
+
})
|
|
35
|
+
.refine(value => (0, date_helper_1.isStatementPeriodWithinLimit)(value.startDate, value.endDate), {
|
|
36
|
+
message: 'validation.reportPeriodMax',
|
|
37
|
+
path: ['endDate', 'startDate'],
|
|
38
|
+
});
|
|
39
|
+
const GetUserActivityTimelineResponseSchema = zod_1.z.object({
|
|
40
|
+
message: zod_1.z.string().optional(),
|
|
41
|
+
data: zod_1.z.object({
|
|
42
|
+
list: zod_1.z.array(user_activity_timeline_row_schema_1.UserActivityTimelineRowSchema),
|
|
43
|
+
// Число строк во всей ленте за период — не размер страницы.
|
|
44
|
+
total: zod_1.z.number(),
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
var GetUserActivityTimelineContractQuery;
|
|
48
|
+
(function (GetUserActivityTimelineContractQuery) {
|
|
49
|
+
GetUserActivityTimelineContractQuery.RequestSchema = GetUserActivityTimelineRequestSchema;
|
|
50
|
+
GetUserActivityTimelineContractQuery.ResponseSchema = GetUserActivityTimelineResponseSchema;
|
|
51
|
+
})(GetUserActivityTimelineContractQuery || (exports.GetUserActivityTimelineContractQuery = GetUserActivityTimelineContractQuery = {}));
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GetUserActivityContractQuery = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const date_helper_1 = require("../../helpers/date.helper");
|
|
6
|
+
const user_activity_summary_row_schema_1 = require("../../schemas/report/user-activity-summary-row.schema");
|
|
7
|
+
// Сводка «Активность сотрудников»: строка на каждого активного сотрудника компании за период.
|
|
8
|
+
// Период — календарные даты, разворачиваются в границы суток таймзоны компании на бэке.
|
|
9
|
+
const GetUserActivityRequestSchema = zod_1.z
|
|
10
|
+
.object({
|
|
11
|
+
startDate: zod_1.z.coerce.date(),
|
|
12
|
+
endDate: zod_1.z.coerce.date(),
|
|
13
|
+
})
|
|
14
|
+
.refine(value => (0, date_helper_1.dateRangeCheck)(value.startDate, value.endDate), {
|
|
15
|
+
message: 'validation.dateRange',
|
|
16
|
+
path: ['endDate', 'startDate'],
|
|
17
|
+
})
|
|
18
|
+
.refine(value => (0, date_helper_1.isStatementPeriodWithinLimit)(value.startDate, value.endDate), {
|
|
19
|
+
message: 'validation.reportPeriodMax',
|
|
20
|
+
path: ['endDate', 'startDate'],
|
|
21
|
+
});
|
|
22
|
+
const GetUserActivityResponseSchema = zod_1.z.object({
|
|
23
|
+
message: zod_1.z.string().optional(),
|
|
24
|
+
data: zod_1.z.array(user_activity_summary_row_schema_1.UserActivitySummaryRowSchema),
|
|
25
|
+
});
|
|
26
|
+
var GetUserActivityContractQuery;
|
|
27
|
+
(function (GetUserActivityContractQuery) {
|
|
28
|
+
GetUserActivityContractQuery.RequestSchema = GetUserActivityRequestSchema;
|
|
29
|
+
GetUserActivityContractQuery.ResponseSchema = GetUserActivityResponseSchema;
|
|
30
|
+
})(GetUserActivityContractQuery || (exports.GetUserActivityContractQuery = GetUserActivityContractQuery = {}));
|
|
@@ -32,3 +32,5 @@ __exportStar(require("./get-write-off-summary.query"), exports);
|
|
|
32
32
|
__exportStar(require("./get-inventory-stock.query"), exports);
|
|
33
33
|
__exportStar(require("./get-inventory-turnover.query"), exports);
|
|
34
34
|
__exportStar(require("./get-sales-by-client.query"), exports);
|
|
35
|
+
__exportStar(require("./get-user-activity.query"), exports);
|
|
36
|
+
__exportStar(require("./get-user-activity-timeline.query"), exports);
|
|
@@ -14,6 +14,10 @@ var ActionLogTypeEnum;
|
|
|
14
14
|
ActionLogTypeEnum["MERGE_ORDER"] = "MERGE_ORDER";
|
|
15
15
|
ActionLogTypeEnum["ADD_PAYMENT"] = "ADD_PAYMENT";
|
|
16
16
|
ActionLogTypeEnum["DELETE_PAYMENT"] = "DELETE_PAYMENT";
|
|
17
|
+
// Применение банковского платежа к клиенту: та же форма payload, что у ADD_PAYMENT.
|
|
18
|
+
ActionLogTypeEnum["APPLY_BANK_PAYMENT"] = "APPLY_BANK_PAYMENT";
|
|
19
|
+
// Импорт выписки: одна агрегатная запись на пакет, а не строка на платёж.
|
|
20
|
+
ActionLogTypeEnum["IMPORT_TRANSACTIONS"] = "IMPORT_TRANSACTIONS";
|
|
17
21
|
ActionLogTypeEnum["CREATE_PREORDER"] = "CREATE_PREORDER";
|
|
18
22
|
ActionLogTypeEnum["DELETE_PREORDER"] = "DELETE_PREORDER";
|
|
19
23
|
ActionLogTypeEnum["DELETE_PREORDER_ITEM"] = "DELETE_PREORDER_ITEM";
|
package/build/enum/index.js
CHANGED
|
@@ -129,3 +129,6 @@ __exportStar(require("./normalization-target.enum"), exports);
|
|
|
129
129
|
__exportStar(require("./normalization-value-state.enum"), exports);
|
|
130
130
|
__exportStar(require("./normalization-decision-action.enum"), exports);
|
|
131
131
|
__exportStar(require("./product-image-duplicate-mode.enum"), exports);
|
|
132
|
+
__exportStar(require("./user-activity-section.enum"), exports);
|
|
133
|
+
__exportStar(require("./user-activity-action-type.enum"), exports);
|
|
134
|
+
__exportStar(require("./user-activity-source.enum"), exports);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivityActionTypeEnum = void 0;
|
|
4
|
+
// Тип строки ленты активности сотрудника.
|
|
5
|
+
//
|
|
6
|
+
// Значения журнальных типов совпадают со строками ActionLogTypeEnum — фронт может использовать
|
|
7
|
+
// одну и ту же локализацию. Сверх журнала есть синтетические типы: действия, которых в журнале
|
|
8
|
+
// нет и которые восстанавливаются из самих таблиц (создание заказа, добавление позиции, списание,
|
|
9
|
+
// документы, смены). CREATE_ORDER здесь именно синтетический: в журнал этот тип никогда не пишется.
|
|
10
|
+
var UserActivityActionTypeEnum;
|
|
11
|
+
(function (UserActivityActionTypeEnum) {
|
|
12
|
+
// Заказы
|
|
13
|
+
UserActivityActionTypeEnum["CREATE_ORDER"] = "CREATE_ORDER";
|
|
14
|
+
UserActivityActionTypeEnum["ADD_ORDER_ITEM"] = "ADD_ORDER_ITEM";
|
|
15
|
+
UserActivityActionTypeEnum["UPDATE_ORDER_STATUS"] = "UPDATE_ORDER_STATUS";
|
|
16
|
+
UserActivityActionTypeEnum["DELETE_ORDER"] = "DELETE_ORDER";
|
|
17
|
+
UserActivityActionTypeEnum["DELETE_ORDER_ITEM"] = "DELETE_ORDER_ITEM";
|
|
18
|
+
UserActivityActionTypeEnum["UPDATE_ORDER_ITEM"] = "UPDATE_ORDER_ITEM";
|
|
19
|
+
UserActivityActionTypeEnum["VERIFY_ORDER_ITEM"] = "VERIFY_ORDER_ITEM";
|
|
20
|
+
UserActivityActionTypeEnum["MOVE_ORDER_ITEM"] = "MOVE_ORDER_ITEM";
|
|
21
|
+
UserActivityActionTypeEnum["MERGE_ORDER"] = "MERGE_ORDER";
|
|
22
|
+
// Деньги
|
|
23
|
+
UserActivityActionTypeEnum["ADD_PAYMENT"] = "ADD_PAYMENT";
|
|
24
|
+
UserActivityActionTypeEnum["DELETE_PAYMENT"] = "DELETE_PAYMENT";
|
|
25
|
+
UserActivityActionTypeEnum["APPLY_BANK_PAYMENT"] = "APPLY_BANK_PAYMENT";
|
|
26
|
+
UserActivityActionTypeEnum["IMPORT_TRANSACTIONS"] = "IMPORT_TRANSACTIONS";
|
|
27
|
+
// Предзаказы
|
|
28
|
+
UserActivityActionTypeEnum["CREATE_PREORDER"] = "CREATE_PREORDER";
|
|
29
|
+
UserActivityActionTypeEnum["DELETE_PREORDER"] = "DELETE_PREORDER";
|
|
30
|
+
UserActivityActionTypeEnum["UPDATE_PREORDER_ITEM"] = "UPDATE_PREORDER_ITEM";
|
|
31
|
+
UserActivityActionTypeEnum["DELETE_PREORDER_ITEM"] = "DELETE_PREORDER_ITEM";
|
|
32
|
+
// Поставки
|
|
33
|
+
UserActivityActionTypeEnum["UPDATE_CONSIGNMENT_ITEM"] = "UPDATE_CONSIGNMENT_ITEM";
|
|
34
|
+
UserActivityActionTypeEnum["CREATE_CONSIGNMENT_ITEMS"] = "CREATE_CONSIGNMENT_ITEMS";
|
|
35
|
+
UserActivityActionTypeEnum["DELETE_CONSIGNMENT_ITEMS"] = "DELETE_CONSIGNMENT_ITEMS";
|
|
36
|
+
UserActivityActionTypeEnum["GROUP_UPDATE_CONSIGNMENT_ITEMS"] = "GROUP_UPDATE_CONSIGNMENT_ITEMS";
|
|
37
|
+
UserActivityActionTypeEnum["TRANSFER_CONSIGNMENT_ITEMS"] = "TRANSFER_CONSIGNMENT_ITEMS";
|
|
38
|
+
// Списания
|
|
39
|
+
UserActivityActionTypeEnum["CREATE_WRITE_OFF"] = "CREATE_WRITE_OFF";
|
|
40
|
+
// Документы
|
|
41
|
+
UserActivityActionTypeEnum["CREATE_PAYMENT_INVOICE"] = "CREATE_PAYMENT_INVOICE";
|
|
42
|
+
UserActivityActionTypeEnum["CREATE_TRANSFER_DOCUMENT"] = "CREATE_TRANSFER_DOCUMENT";
|
|
43
|
+
// Смены
|
|
44
|
+
UserActivityActionTypeEnum["CHECK_IN"] = "CHECK_IN";
|
|
45
|
+
UserActivityActionTypeEnum["CHECK_OUT"] = "CHECK_OUT";
|
|
46
|
+
})(UserActivityActionTypeEnum || (exports.UserActivityActionTypeEnum = UserActivityActionTypeEnum = {}));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivitySectionEnum = void 0;
|
|
4
|
+
// Разделы отчёта «Активность сотрудников»: фиксированный набор, по нему режется и сводка (bySection),
|
|
5
|
+
// и лента (фильтр section). Каталожные операции (нормализация атрибутов, слияние картинок) в отчёт
|
|
6
|
+
// не входят — это обслуживание справочника, а не работа с клиентом за день.
|
|
7
|
+
var UserActivitySectionEnum;
|
|
8
|
+
(function (UserActivitySectionEnum) {
|
|
9
|
+
UserActivitySectionEnum["ORDERS"] = "ORDERS";
|
|
10
|
+
UserActivitySectionEnum["MONEY"] = "MONEY";
|
|
11
|
+
UserActivitySectionEnum["PRE_ORDERS"] = "PRE_ORDERS";
|
|
12
|
+
UserActivitySectionEnum["CONSIGNMENTS"] = "CONSIGNMENTS";
|
|
13
|
+
UserActivitySectionEnum["WRITE_OFFS"] = "WRITE_OFFS";
|
|
14
|
+
UserActivitySectionEnum["DOCUMENTS"] = "DOCUMENTS";
|
|
15
|
+
UserActivitySectionEnum["SHIFTS"] = "SHIFTS";
|
|
16
|
+
})(UserActivitySectionEnum || (exports.UserActivitySectionEnum = UserActivitySectionEnum = {}));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivitySourceEnum = void 0;
|
|
4
|
+
// Откуда взята строка ленты: из журнала действий (action_log) или восстановлена из таблицы сущности.
|
|
5
|
+
// Деньги — только журнал: ручные, банковские платежи и импорт пишутся туда, история дозаписана миграцией.
|
|
6
|
+
//
|
|
7
|
+
// Каждый источник несёт ТОЧНЫЙ момент события (created_at, applied_at, check_in_at, check_out_at),
|
|
8
|
+
// поэтому признака точности времени в строке нет. Единственный приблизительный источник —
|
|
9
|
+
// подтверждение позиции по order_product.updated_at — не используется: то же действие есть в журнале
|
|
10
|
+
// с точным временем.
|
|
11
|
+
var UserActivitySourceEnum;
|
|
12
|
+
(function (UserActivitySourceEnum) {
|
|
13
|
+
UserActivitySourceEnum["JOURNAL"] = "JOURNAL";
|
|
14
|
+
UserActivitySourceEnum["ORDER"] = "ORDER";
|
|
15
|
+
UserActivitySourceEnum["ORDER_PRODUCT"] = "ORDER_PRODUCT";
|
|
16
|
+
UserActivitySourceEnum["WRITE_OFF"] = "WRITE_OFF";
|
|
17
|
+
UserActivitySourceEnum["PAYMENT_INVOICE"] = "PAYMENT_INVOICE";
|
|
18
|
+
UserActivitySourceEnum["TRANSFER_DOCUMENT"] = "TRANSFER_DOCUMENT";
|
|
19
|
+
UserActivitySourceEnum["WORK_SESSION"] = "WORK_SESSION";
|
|
20
|
+
})(UserActivitySourceEnum || (exports.UserActivitySourceEnum = UserActivitySourceEnum = {}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.USER_PANEL_ROLE_LIST = exports.UserPanelRoleEnum = exports.UserRoleEnum = void 0;
|
|
3
|
+
exports.CLIENT_SCOPE_BYPASS_ROLE_LIST = exports.USER_PANEL_ROLE_LIST = exports.UserPanelRoleEnum = exports.UserRoleEnum = void 0;
|
|
4
4
|
var UserRoleEnum;
|
|
5
5
|
(function (UserRoleEnum) {
|
|
6
6
|
UserRoleEnum["ADMIN"] = "ADMIN";
|
|
@@ -23,3 +23,11 @@ exports.USER_PANEL_ROLE_LIST = [
|
|
|
23
23
|
UserRoleEnum.EMPLOYEE,
|
|
24
24
|
UserRoleEnum.WAREHOUSE_EMPLOYEE,
|
|
25
25
|
];
|
|
26
|
+
/**
|
|
27
|
+
* Roles that always see and manage every client of the company, regardless of the employee client scope
|
|
28
|
+
* mode: ADMIN / MANAGER by rank, WAREHOUSE_EMPLOYEE because warehouse work spans all clients. Shared by
|
|
29
|
+
* EmployeeClientScopeService (read and write scoping) and by ClientResponsibleGuard, which applies the
|
|
30
|
+
* same list itself — the guard protects routes where a missing role check would expose client details,
|
|
31
|
+
* so it does not delegate the decision.
|
|
32
|
+
*/
|
|
33
|
+
exports.CLIENT_SCOPE_BYPASS_ROLE_LIST = [UserRoleEnum.ADMIN, UserRoleEnum.MANAGER, UserRoleEnum.WAREHOUSE_EMPLOYEE];
|
|
@@ -22,6 +22,7 @@ const group_update_consignment_items_action_log_schema_1 = require("./group-upda
|
|
|
22
22
|
const transfer_consignment_items_action_log_schema_1 = require("./transfer-consignment-items-action-log.schema");
|
|
23
23
|
const normalize_attributes_action_log_schema_1 = require("./normalize-attributes-action-log.schema");
|
|
24
24
|
const merge_product_images_action_log_schema_1 = require("./merge-product-images-action-log.schema");
|
|
25
|
+
const import_transactions_action_log_schema_1 = require("./import-transactions-action-log.schema");
|
|
25
26
|
exports.ActionLogPayloadSchema = zod_1.z.union([
|
|
26
27
|
order_delete_action_log_payload_schema_1.OrderDeleteActionLogPayloadSchema,
|
|
27
28
|
order_merge_action_log_schema_1.OrderMergeActionLogSchema,
|
|
@@ -43,5 +44,6 @@ exports.ActionLogPayloadSchema = zod_1.z.union([
|
|
|
43
44
|
transfer_consignment_items_action_log_schema_1.TransferConsignmentItemsActionLogSchema,
|
|
44
45
|
normalize_attributes_action_log_schema_1.NormalizeAttributesActionLogSchema,
|
|
45
46
|
merge_product_images_action_log_schema_1.MergeProductImagesActionLogSchema,
|
|
47
|
+
import_transactions_action_log_schema_1.ImportTransactionsActionLogSchema,
|
|
46
48
|
zod_1.z.null(),
|
|
47
49
|
]);
|
|
@@ -22,6 +22,9 @@ const consignment_items_create_action_log_schema_1 = require("./consignment-item
|
|
|
22
22
|
const consignment_items_delete_action_log_schema_1 = require("./consignment-items-delete-action-log.schema");
|
|
23
23
|
const group_update_consignment_items_action_log_schema_1 = require("./group-update-consignment-items-action-log.schema");
|
|
24
24
|
const transfer_consignment_items_action_log_schema_1 = require("./transfer-consignment-items-action-log.schema");
|
|
25
|
+
const normalize_attributes_action_log_schema_1 = require("./normalize-attributes-action-log.schema");
|
|
26
|
+
const merge_product_images_action_log_schema_1 = require("./merge-product-images-action-log.schema");
|
|
27
|
+
const import_transactions_action_log_schema_1 = require("./import-transactions-action-log.schema");
|
|
25
28
|
const BaseActionFields = zod_1.z.object({
|
|
26
29
|
uuid: zod_1.z.string(),
|
|
27
30
|
companyUUID: zod_1.z.string(),
|
|
@@ -150,7 +153,34 @@ exports.ActionLogSchema = zod_1.z.discriminatedUnion('actionType', [
|
|
|
150
153
|
payload: transfer_consignment_items_action_log_schema_1.TransferConsignmentItemsActionLogSchema,
|
|
151
154
|
})
|
|
152
155
|
.merge(BaseActionFields),
|
|
156
|
+
zod_1.z
|
|
157
|
+
.object({
|
|
158
|
+
actionType: zod_1.z.literal(enum_1.ActionLogTypeEnum.NORMALIZE_ATTRIBUTES),
|
|
159
|
+
payload: normalize_attributes_action_log_schema_1.NormalizeAttributesActionLogSchema,
|
|
160
|
+
})
|
|
161
|
+
.merge(BaseActionFields),
|
|
162
|
+
zod_1.z
|
|
163
|
+
.object({
|
|
164
|
+
actionType: zod_1.z.literal(enum_1.ActionLogTypeEnum.MERGE_PRODUCT_IMAGES),
|
|
165
|
+
payload: merge_product_images_action_log_schema_1.MergeProductImagesActionLogSchema,
|
|
166
|
+
})
|
|
167
|
+
.merge(BaseActionFields),
|
|
168
|
+
zod_1.z
|
|
169
|
+
.object({
|
|
170
|
+
actionType: zod_1.z.literal(enum_1.ActionLogTypeEnum.APPLY_BANK_PAYMENT),
|
|
171
|
+
payload: transaction_add_action_log_schema_1.TransactionAddActionLogSchema,
|
|
172
|
+
})
|
|
173
|
+
.merge(BaseActionFields),
|
|
174
|
+
zod_1.z
|
|
175
|
+
.object({
|
|
176
|
+
actionType: zod_1.z.literal(enum_1.ActionLogTypeEnum.IMPORT_TRANSACTIONS),
|
|
177
|
+
payload: import_transactions_action_log_schema_1.ImportTransactionsActionLogSchema,
|
|
178
|
+
})
|
|
179
|
+
.merge(BaseActionFields),
|
|
153
180
|
]);
|
|
181
|
+
// Именно Record, а не массив: пустой массив присваивается `T[]` при любом T и ничего не ловит.
|
|
182
|
+
const assertEveryActionTypeIsCovered = {};
|
|
183
|
+
void assertEveryActionTypeIsCovered;
|
|
154
184
|
exports.CreateActionLogSchema = zod_1.z.object({
|
|
155
185
|
actionType: zod_1.z.nativeEnum(enum_1.ActionLogTypeEnum),
|
|
156
186
|
entityUUID: zod_1.z.string(),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImportTransactionsActionLogSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const action_log_money_schema_1 = require("./action-log-money.schema");
|
|
6
|
+
// Импорт выписки — одна агрегатная запись на пакет: сколько платежей создано, приход и расход
|
|
7
|
+
// отдельно (в одном файле бывают оба направления), даты операций и uuid созданных транзакций —
|
|
8
|
+
// по ним пакет восстановим, хотя у самой записи сущности нет. Построчный лог был бы шумом.
|
|
9
|
+
exports.ImportTransactionsActionLogSchema = zod_1.z.object({
|
|
10
|
+
count: zod_1.z.coerce.number(),
|
|
11
|
+
incomeAmount: action_log_money_schema_1.ActionLogMoneySchema,
|
|
12
|
+
expenseAmount: action_log_money_schema_1.ActionLogMoneySchema,
|
|
13
|
+
dateFrom: zod_1.z.coerce.date().nullable(),
|
|
14
|
+
dateTo: zod_1.z.coerce.date().nullable(),
|
|
15
|
+
transactionUUIDs: zod_1.z.array(zod_1.z.string()),
|
|
16
|
+
});
|
|
@@ -38,3 +38,4 @@ __exportStar(require("./consignment-items-delete-action-log.schema"), exports);
|
|
|
38
38
|
__exportStar(require("./group-update-consignment-items-action-log.schema"), exports);
|
|
39
39
|
__exportStar(require("./transfer-consignment-items-action-log.schema"), exports);
|
|
40
40
|
__exportStar(require("./action-log-money.schema"), exports);
|
|
41
|
+
__exportStar(require("./import-transactions-action-log.schema"), exports);
|
package/build/schemas/index.js
CHANGED
|
@@ -109,3 +109,6 @@ __exportStar(require("./attribute-normalization/normalization-value.schema"), ex
|
|
|
109
109
|
__exportStar(require("./attribute-normalization/normalization-summary.schema"), exports);
|
|
110
110
|
__exportStar(require("./attribute-normalization/normalization-decision.schema"), exports);
|
|
111
111
|
__exportStar(require("./attribute-normalization/product-image-duplicate-forecast.schema"), exports);
|
|
112
|
+
__exportStar(require("./report/user-activity-by-section.schema"), exports);
|
|
113
|
+
__exportStar(require("./report/user-activity-summary-row.schema"), exports);
|
|
114
|
+
__exportStar(require("./report/user-activity-timeline-row.schema"), exports);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivityBySectionSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
// Счётчики действий по разделам отчёта «Активность сотрудников». Набор ключей фиксированный —
|
|
6
|
+
// панель рисует таблицу с постоянными колонками, а не map по типам действий.
|
|
7
|
+
//
|
|
8
|
+
// Каждый счётчик, кроме смен, равен числу строк ленты того же раздела за тот же период — по цифре
|
|
9
|
+
// можно провалиться и увидеть ровно столько строк. Исключений два, оба намеренные: `shifts` считает
|
|
10
|
+
// СМЕНЫ (начатые в периоде), а лента показывает приход и уход отдельными строками, поэтому строк
|
|
11
|
+
// там больше; и позиции заказов в счётчики не входят вовсе — они отдельным полем positionsAdded,
|
|
12
|
+
// потому что лента по умолчанию их не показывает.
|
|
13
|
+
exports.UserActivityBySectionSchema = zod_1.z.object({
|
|
14
|
+
orders: zod_1.z.number(),
|
|
15
|
+
money: zod_1.z.number(),
|
|
16
|
+
preOrders: zod_1.z.number(),
|
|
17
|
+
consignments: zod_1.z.number(),
|
|
18
|
+
writeOffs: zod_1.z.number(),
|
|
19
|
+
documents: zod_1.z.number(),
|
|
20
|
+
shifts: zod_1.z.number(),
|
|
21
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivitySummaryRowSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const money_schema_1 = require("../money.schema");
|
|
6
|
+
const user_activity_by_section_schema_1 = require("./user-activity-by-section.schema");
|
|
7
|
+
// Строка сводки: один сотрудник за период. Сотрудники без действий тоже присутствуют — с нулями:
|
|
8
|
+
// «кто ничего не делал» и есть половина ценности сводки.
|
|
9
|
+
exports.UserActivitySummaryRowSchema = zod_1.z.object({
|
|
10
|
+
userUUID: zod_1.z.string(),
|
|
11
|
+
userName: zod_1.z.string(),
|
|
12
|
+
// Сумма разделов БЕЗ смен: приход и уход — рамка дня, а не работа.
|
|
13
|
+
actionsTotal: zod_1.z.number(),
|
|
14
|
+
bySection: user_activity_by_section_schema_1.UserActivityBySectionSchema,
|
|
15
|
+
ordersCreated: zod_1.z.number(),
|
|
16
|
+
// Принятые сотрудником деньги за период: только приход (INCOME, PREPAYMENT) по ручным и
|
|
17
|
+
// банковским платежам из журнала. Расход и возврат — действия, но не сумма; пакет импорта
|
|
18
|
+
// выписки — тоже действие без суммы (он не «принят» сотрудником).
|
|
19
|
+
paymentsAmount: money_schema_1.MoneySchema,
|
|
20
|
+
positionsAdded: zod_1.z.number(),
|
|
21
|
+
// Первое и последнее действие за период. Смены не считаются (это рамка дня), а добавление позиций
|
|
22
|
+
// считается: сотрудник, который весь день собирал заказы, показывает actionsTotal 0 при непустых
|
|
23
|
+
// отметках — его работа целиком в positionsAdded.
|
|
24
|
+
firstActionAt: zod_1.z.date().nullable(),
|
|
25
|
+
lastActionAt: zod_1.z.date().nullable(),
|
|
26
|
+
// Часы смен, закрытых в периоде (как у строки CHECK_OUT в ленте); null — ни одна смена в периоде не закрыта.
|
|
27
|
+
workHours: zod_1.z.number().nullable(),
|
|
28
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserActivityTimelineRowSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const user_activity_action_type_enum_1 = require("../../enum/user-activity-action-type.enum");
|
|
6
|
+
const user_activity_section_enum_1 = require("../../enum/user-activity-section.enum");
|
|
7
|
+
const user_activity_source_enum_1 = require("../../enum/user-activity-source.enum");
|
|
8
|
+
const money_schema_1 = require("../money.schema");
|
|
9
|
+
// Строка ленты активности сотрудника. Текст действия (action, details) рендерит сервер на языке
|
|
10
|
+
// компании — панель показывает его как есть. Клиент, номер заказа, сумма и количество вынесены в
|
|
11
|
+
// отдельные колонки, в details остаётся то, что в колонки не ложится (товар, статусы, причина).
|
|
12
|
+
//
|
|
13
|
+
// Телефон клиента здесь намеренно НЕ отдаётся: строка описывает действие сотрудника, клиент в ней
|
|
14
|
+
// нужен для опознания и уже опознан парой clientUUID + clientName — карточку с контактами панель
|
|
15
|
+
// открывает по uuid. Отчёт, где телефон это рабочий инструмент, — отдельный (/report/sales/clients).
|
|
16
|
+
exports.UserActivityTimelineRowSchema = zod_1.z.object({
|
|
17
|
+
at: zod_1.z.date(),
|
|
18
|
+
section: zod_1.z.nativeEnum(user_activity_section_enum_1.UserActivitySectionEnum),
|
|
19
|
+
actionType: zod_1.z.nativeEnum(user_activity_action_type_enum_1.UserActivityActionTypeEnum),
|
|
20
|
+
action: zod_1.z.string(),
|
|
21
|
+
clientUUID: zod_1.z.string().nullable(),
|
|
22
|
+
clientName: zod_1.z.string().nullable(),
|
|
23
|
+
orderId: zod_1.z.number().nullable(),
|
|
24
|
+
amount: money_schema_1.MoneySchema.nullable(),
|
|
25
|
+
quantity: zod_1.z.number().nullable(),
|
|
26
|
+
details: zod_1.z.string().nullable(),
|
|
27
|
+
source: zod_1.z.nativeEnum(user_activity_source_enum_1.UserActivitySourceEnum),
|
|
28
|
+
entityUUID: zod_1.z.string(),
|
|
29
|
+
});
|
|
@@ -8,11 +8,28 @@ const PriceCorrectionAnalyzeRequestSchema = z
|
|
|
8
8
|
.object({
|
|
9
9
|
productUUIDList: z.array(z.string().uuid()).optional(),
|
|
10
10
|
consignmentUUID: z.string().uuid().optional(),
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Режим цены — обязателен и без умолчания, как в execute.
|
|
14
|
+
*
|
|
15
|
+
* Раньше analyze этого поля не знал и считал `onlyChanged` по OR обоих режимов: строка
|
|
16
|
+
* попадала в выдачу, если разошлась цена со скидкой ИЛИ без. На клиенте с фиксированной
|
|
17
|
+
* скидкой это выносило в предпросмотр каждую его позицию — цена строки законно ниже
|
|
18
|
+
* каталожной ровно на скидку, и «без скидок» это выглядит расхождением. Execute при этом
|
|
19
|
+
* считает предикат в выбранном режиме, то есть предпросмотр и применение расходились на
|
|
20
|
+
* целую партию.
|
|
21
|
+
*/
|
|
22
|
+
withDiscount: z.boolean(),
|
|
23
|
+
|
|
11
24
|
// Поставка на сотни товаров даёт десятки тысяч позиций, из которых после правки прайса
|
|
12
25
|
// меняются единицы. По умолчанию отдаём только те, где цена реально расходится; полный
|
|
13
26
|
// список — режим «показать всё».
|
|
14
27
|
onlyChanged: z.boolean().optional().default(true),
|
|
15
28
|
|
|
29
|
+
// Позиции, которые применить нельзя (статус заказа, заказ в передаточном документе).
|
|
30
|
+
// `true` — скрыть их из выдачи; сколько их всего, видно по `skippedTotal`.
|
|
31
|
+
excludeSkipped: z.boolean().optional().default(false),
|
|
32
|
+
|
|
16
33
|
// Страница обязательна по смыслу, а не по схеме: строк здесь товары × заказы, то есть
|
|
17
34
|
// десятки тысяч на партию, и безлимитная выдача кладёт запрос независимо от того, сколько
|
|
18
35
|
// из неё нарисует таблица. Усечение при этом урезает только предпросмотр — execute в
|
|
@@ -34,8 +51,22 @@ const PriceCorrectionAnalyzeResponseSchema = z.object({
|
|
|
34
51
|
message: z.string().optional(),
|
|
35
52
|
data: z.object({
|
|
36
53
|
items: z.array(PriceCorrectionAnalyzeItemSchema),
|
|
37
|
-
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Размер выдачи, а не страницы: по нему листают. Учитывает ВСЕ фильтры запроса, включая
|
|
57
|
+
* `excludeSkipped`.
|
|
58
|
+
*/
|
|
38
59
|
total: z.number(),
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Сколько строк набора применить нельзя — статус заказа или заказ в передаточном документе.
|
|
63
|
+
*
|
|
64
|
+
* Считается по набору после `onlyChanged`, но ДО `excludeSkipped`: иначе при скрытых
|
|
65
|
+
* непроходных строках это всегда был бы ноль и шапку «столько-то не пройдёт» стало бы не из
|
|
66
|
+
* чего собрать. Отсюда: применимых строк — `total` при `excludeSkipped: true` и
|
|
67
|
+
* `total − skippedTotal` при `false`.
|
|
68
|
+
*/
|
|
69
|
+
skippedTotal: z.number(),
|
|
39
70
|
}),
|
|
40
71
|
});
|
|
41
72
|
|
|
@@ -47,7 +47,19 @@ const PriceCorrectionExecuteResponseSchema = z.object({
|
|
|
47
47
|
// равно перезапрашивает analyze, чтобы обновить таблицу.
|
|
48
48
|
results: z.array(PriceCorrectionExecuteResultItemSchema),
|
|
49
49
|
orders: z.array(PriceCorrectionExecuteOrderItemSchema),
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* «Строк в нужной цене после прогона», а не «строк изменено».
|
|
53
|
+
*
|
|
54
|
+
* Применение сходящееся: позиция, уже стоящая в нужной цене, — успех, а не отказ, поэтому
|
|
55
|
+
* повтор запроса и ретрай после таймаута безопасны и дают тот же ответ. Из-за этого при
|
|
56
|
+
* `onlyChanged: false` в счётчик попадают и нетронутые позиции. При `onlyChanged: true`
|
|
57
|
+
* таких строк здесь не будет: они отсеиваются до обработки и в счётчики не идут вовсе.
|
|
58
|
+
*/
|
|
50
59
|
successCount: z.number(),
|
|
60
|
+
|
|
61
|
+
// Строки, которые прогон не применил: статус заказа, заказ в передаточном документе, отказ
|
|
62
|
+
// применения. Каждая из них есть в `results` — с точностью до потолка (см. `isTruncated`).
|
|
51
63
|
skippedCount: z.number(),
|
|
52
64
|
// `results` в scope-форме обрезан по лимиту. Полные числа — в счётчиках и в `orders`.
|
|
53
65
|
isTruncated: z.boolean(),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UserActivitySectionEnum } from '../../enum/user-activity-section.enum';
|
|
3
|
+
import { dateRangeCheck, isStatementPeriodWithinLimit } from '../../helpers/date.helper';
|
|
4
|
+
import { queryBooleanSchema } from '../../helpers/query-boolean.helper';
|
|
5
|
+
import { UserActivityTimelineRowSchema } from '../../schemas/report/user-activity-timeline-row.schema';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PAGE = 1;
|
|
8
|
+
// Верхняя граница страницы: page × count должно помещаться в int4 OFFSET, а лента длиннее
|
|
9
|
+
// MAX_PAGE × MAX_COUNT строк за период невозможна.
|
|
10
|
+
const MAX_PAGE = 10000;
|
|
11
|
+
const DEFAULT_COUNT = 50;
|
|
12
|
+
const MAX_COUNT = 500;
|
|
13
|
+
|
|
14
|
+
// Лента действий одного сотрудника за период, в хронологическом порядке.
|
|
15
|
+
//
|
|
16
|
+
// Пагинация обязательная: у активного сотрудника за день до нескольких тысяч строк, поэтому
|
|
17
|
+
// page/count всегда применяются — со значениями по умолчанию, если не переданы.
|
|
18
|
+
const GetUserActivityTimelineRequestSchema = z
|
|
19
|
+
.object({
|
|
20
|
+
userUUID: z.uuid(),
|
|
21
|
+
startDate: z.coerce.date(),
|
|
22
|
+
endDate: z.coerce.date(),
|
|
23
|
+
section: z.nativeEnum(UserActivitySectionEnum).optional().describe('Keep only the rows of this section.'),
|
|
24
|
+
// «Добавление позиции в заказ» — самый частый тип строк (тысячи в день). По умолчанию
|
|
25
|
+
// выключен, чтобы не забивать ленту; в сводке позиции считаются всегда (positionsAdded).
|
|
26
|
+
includeOrderProductRows: queryBooleanSchema.describe('Pass "true" to include "position added to order" rows.'),
|
|
27
|
+
page: z.coerce.number().int().positive().max(MAX_PAGE).default(DEFAULT_PAGE),
|
|
28
|
+
count: z.coerce.number().int().positive().max(MAX_COUNT).default(DEFAULT_COUNT),
|
|
29
|
+
})
|
|
30
|
+
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
31
|
+
message: 'validation.dateRange',
|
|
32
|
+
path: ['endDate', 'startDate'],
|
|
33
|
+
})
|
|
34
|
+
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
35
|
+
message: 'validation.reportPeriodMax',
|
|
36
|
+
path: ['endDate', 'startDate'],
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const GetUserActivityTimelineResponseSchema = z.object({
|
|
40
|
+
message: z.string().optional(),
|
|
41
|
+
data: z.object({
|
|
42
|
+
list: z.array(UserActivityTimelineRowSchema),
|
|
43
|
+
// Число строк во всей ленте за период — не размер страницы.
|
|
44
|
+
total: z.number(),
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export namespace GetUserActivityTimelineContractQuery {
|
|
49
|
+
export const RequestSchema = GetUserActivityTimelineRequestSchema;
|
|
50
|
+
export type Request = z.infer<typeof RequestSchema>;
|
|
51
|
+
|
|
52
|
+
export const ResponseSchema = GetUserActivityTimelineResponseSchema;
|
|
53
|
+
export type Response = z.infer<typeof ResponseSchema>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { dateRangeCheck, isStatementPeriodWithinLimit } from '../../helpers/date.helper';
|
|
3
|
+
import { UserActivitySummaryRowSchema } from '../../schemas/report/user-activity-summary-row.schema';
|
|
4
|
+
|
|
5
|
+
// Сводка «Активность сотрудников»: строка на каждого активного сотрудника компании за период.
|
|
6
|
+
// Период — календарные даты, разворачиваются в границы суток таймзоны компании на бэке.
|
|
7
|
+
const GetUserActivityRequestSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
startDate: z.coerce.date(),
|
|
10
|
+
endDate: z.coerce.date(),
|
|
11
|
+
})
|
|
12
|
+
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
13
|
+
message: 'validation.dateRange',
|
|
14
|
+
path: ['endDate', 'startDate'],
|
|
15
|
+
})
|
|
16
|
+
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
17
|
+
message: 'validation.reportPeriodMax',
|
|
18
|
+
path: ['endDate', 'startDate'],
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const GetUserActivityResponseSchema = z.object({
|
|
22
|
+
message: z.string().optional(),
|
|
23
|
+
data: z.array(UserActivitySummaryRowSchema),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export namespace GetUserActivityContractQuery {
|
|
27
|
+
export const RequestSchema = GetUserActivityRequestSchema;
|
|
28
|
+
export type Request = z.infer<typeof RequestSchema>;
|
|
29
|
+
|
|
30
|
+
export const ResponseSchema = GetUserActivityResponseSchema;
|
|
31
|
+
export type Response = z.infer<typeof ResponseSchema>;
|
|
32
|
+
}
|
package/commands/report/index.ts
CHANGED
|
@@ -16,3 +16,5 @@ export * from './get-write-off-summary.query';
|
|
|
16
16
|
export * from './get-inventory-stock.query';
|
|
17
17
|
export * from './get-inventory-turnover.query';
|
|
18
18
|
export * from './get-sales-by-client.query';
|
|
19
|
+
export * from './get-user-activity.query';
|
|
20
|
+
export * from './get-user-activity-timeline.query';
|
|
@@ -10,6 +10,10 @@ export enum ActionLogTypeEnum {
|
|
|
10
10
|
MERGE_ORDER = 'MERGE_ORDER',
|
|
11
11
|
ADD_PAYMENT = 'ADD_PAYMENT',
|
|
12
12
|
DELETE_PAYMENT = 'DELETE_PAYMENT',
|
|
13
|
+
// Применение банковского платежа к клиенту: та же форма payload, что у ADD_PAYMENT.
|
|
14
|
+
APPLY_BANK_PAYMENT = 'APPLY_BANK_PAYMENT',
|
|
15
|
+
// Импорт выписки: одна агрегатная запись на пакет, а не строка на платёж.
|
|
16
|
+
IMPORT_TRANSACTIONS = 'IMPORT_TRANSACTIONS',
|
|
13
17
|
CREATE_PREORDER = 'CREATE_PREORDER',
|
|
14
18
|
DELETE_PREORDER = 'DELETE_PREORDER',
|
|
15
19
|
DELETE_PREORDER_ITEM = 'DELETE_PREORDER_ITEM',
|
package/enum/index.ts
CHANGED
|
@@ -113,3 +113,6 @@ export * from './normalization-target.enum';
|
|
|
113
113
|
export * from './normalization-value-state.enum';
|
|
114
114
|
export * from './normalization-decision-action.enum';
|
|
115
115
|
export * from './product-image-duplicate-mode.enum';
|
|
116
|
+
export * from './user-activity-section.enum';
|
|
117
|
+
export * from './user-activity-action-type.enum';
|
|
118
|
+
export * from './user-activity-source.enum';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Тип строки ленты активности сотрудника.
|
|
2
|
+
//
|
|
3
|
+
// Значения журнальных типов совпадают со строками ActionLogTypeEnum — фронт может использовать
|
|
4
|
+
// одну и ту же локализацию. Сверх журнала есть синтетические типы: действия, которых в журнале
|
|
5
|
+
// нет и которые восстанавливаются из самих таблиц (создание заказа, добавление позиции, списание,
|
|
6
|
+
// документы, смены). CREATE_ORDER здесь именно синтетический: в журнал этот тип никогда не пишется.
|
|
7
|
+
export enum UserActivityActionTypeEnum {
|
|
8
|
+
// Заказы
|
|
9
|
+
CREATE_ORDER = 'CREATE_ORDER',
|
|
10
|
+
ADD_ORDER_ITEM = 'ADD_ORDER_ITEM',
|
|
11
|
+
UPDATE_ORDER_STATUS = 'UPDATE_ORDER_STATUS',
|
|
12
|
+
DELETE_ORDER = 'DELETE_ORDER',
|
|
13
|
+
DELETE_ORDER_ITEM = 'DELETE_ORDER_ITEM',
|
|
14
|
+
UPDATE_ORDER_ITEM = 'UPDATE_ORDER_ITEM',
|
|
15
|
+
VERIFY_ORDER_ITEM = 'VERIFY_ORDER_ITEM',
|
|
16
|
+
MOVE_ORDER_ITEM = 'MOVE_ORDER_ITEM',
|
|
17
|
+
MERGE_ORDER = 'MERGE_ORDER',
|
|
18
|
+
// Деньги
|
|
19
|
+
ADD_PAYMENT = 'ADD_PAYMENT',
|
|
20
|
+
DELETE_PAYMENT = 'DELETE_PAYMENT',
|
|
21
|
+
APPLY_BANK_PAYMENT = 'APPLY_BANK_PAYMENT',
|
|
22
|
+
IMPORT_TRANSACTIONS = 'IMPORT_TRANSACTIONS',
|
|
23
|
+
// Предзаказы
|
|
24
|
+
CREATE_PREORDER = 'CREATE_PREORDER',
|
|
25
|
+
DELETE_PREORDER = 'DELETE_PREORDER',
|
|
26
|
+
UPDATE_PREORDER_ITEM = 'UPDATE_PREORDER_ITEM',
|
|
27
|
+
DELETE_PREORDER_ITEM = 'DELETE_PREORDER_ITEM',
|
|
28
|
+
// Поставки
|
|
29
|
+
UPDATE_CONSIGNMENT_ITEM = 'UPDATE_CONSIGNMENT_ITEM',
|
|
30
|
+
CREATE_CONSIGNMENT_ITEMS = 'CREATE_CONSIGNMENT_ITEMS',
|
|
31
|
+
DELETE_CONSIGNMENT_ITEMS = 'DELETE_CONSIGNMENT_ITEMS',
|
|
32
|
+
GROUP_UPDATE_CONSIGNMENT_ITEMS = 'GROUP_UPDATE_CONSIGNMENT_ITEMS',
|
|
33
|
+
TRANSFER_CONSIGNMENT_ITEMS = 'TRANSFER_CONSIGNMENT_ITEMS',
|
|
34
|
+
// Списания
|
|
35
|
+
CREATE_WRITE_OFF = 'CREATE_WRITE_OFF',
|
|
36
|
+
// Документы
|
|
37
|
+
CREATE_PAYMENT_INVOICE = 'CREATE_PAYMENT_INVOICE',
|
|
38
|
+
CREATE_TRANSFER_DOCUMENT = 'CREATE_TRANSFER_DOCUMENT',
|
|
39
|
+
// Смены
|
|
40
|
+
CHECK_IN = 'CHECK_IN',
|
|
41
|
+
CHECK_OUT = 'CHECK_OUT',
|
|
42
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Разделы отчёта «Активность сотрудников»: фиксированный набор, по нему режется и сводка (bySection),
|
|
2
|
+
// и лента (фильтр section). Каталожные операции (нормализация атрибутов, слияние картинок) в отчёт
|
|
3
|
+
// не входят — это обслуживание справочника, а не работа с клиентом за день.
|
|
4
|
+
export enum UserActivitySectionEnum {
|
|
5
|
+
ORDERS = 'ORDERS',
|
|
6
|
+
MONEY = 'MONEY',
|
|
7
|
+
PRE_ORDERS = 'PRE_ORDERS',
|
|
8
|
+
CONSIGNMENTS = 'CONSIGNMENTS',
|
|
9
|
+
WRITE_OFFS = 'WRITE_OFFS',
|
|
10
|
+
DOCUMENTS = 'DOCUMENTS',
|
|
11
|
+
SHIFTS = 'SHIFTS',
|
|
12
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Откуда взята строка ленты: из журнала действий (action_log) или восстановлена из таблицы сущности.
|
|
2
|
+
// Деньги — только журнал: ручные, банковские платежи и импорт пишутся туда, история дозаписана миграцией.
|
|
3
|
+
//
|
|
4
|
+
// Каждый источник несёт ТОЧНЫЙ момент события (created_at, applied_at, check_in_at, check_out_at),
|
|
5
|
+
// поэтому признака точности времени в строке нет. Единственный приблизительный источник —
|
|
6
|
+
// подтверждение позиции по order_product.updated_at — не используется: то же действие есть в журнале
|
|
7
|
+
// с точным временем.
|
|
8
|
+
export enum UserActivitySourceEnum {
|
|
9
|
+
JOURNAL = 'JOURNAL',
|
|
10
|
+
ORDER = 'ORDER',
|
|
11
|
+
ORDER_PRODUCT = 'ORDER_PRODUCT',
|
|
12
|
+
WRITE_OFF = 'WRITE_OFF',
|
|
13
|
+
PAYMENT_INVOICE = 'PAYMENT_INVOICE',
|
|
14
|
+
TRANSFER_DOCUMENT = 'TRANSFER_DOCUMENT',
|
|
15
|
+
WORK_SESSION = 'WORK_SESSION',
|
|
16
|
+
}
|
package/enum/userRole.enum.ts
CHANGED
|
@@ -20,3 +20,12 @@ export const USER_PANEL_ROLE_LIST: UserRoleEnum[] = [
|
|
|
20
20
|
UserRoleEnum.EMPLOYEE,
|
|
21
21
|
UserRoleEnum.WAREHOUSE_EMPLOYEE,
|
|
22
22
|
];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Roles that always see and manage every client of the company, regardless of the employee client scope
|
|
26
|
+
* mode: ADMIN / MANAGER by rank, WAREHOUSE_EMPLOYEE because warehouse work spans all clients. Shared by
|
|
27
|
+
* EmployeeClientScopeService (read and write scoping) and by ClientResponsibleGuard, which applies the
|
|
28
|
+
* same list itself — the guard protects routes where a missing role check would expose client details,
|
|
29
|
+
* so it does not delegate the decision.
|
|
30
|
+
*/
|
|
31
|
+
export const CLIENT_SCOPE_BYPASS_ROLE_LIST: UserRoleEnum[] = [UserRoleEnum.ADMIN, UserRoleEnum.MANAGER, UserRoleEnum.WAREHOUSE_EMPLOYEE];
|
package/package.json
CHANGED
|
@@ -19,6 +19,7 @@ import { GroupUpdateConsignmentItemsActionLogSchema } from './group-update-consi
|
|
|
19
19
|
import { TransferConsignmentItemsActionLogSchema } from './transfer-consignment-items-action-log.schema';
|
|
20
20
|
import { NormalizeAttributesActionLogSchema } from './normalize-attributes-action-log.schema';
|
|
21
21
|
import { MergeProductImagesActionLogSchema } from './merge-product-images-action-log.schema';
|
|
22
|
+
import { ImportTransactionsActionLogSchema } from './import-transactions-action-log.schema';
|
|
22
23
|
|
|
23
24
|
export const ActionLogPayloadSchema = z.union([
|
|
24
25
|
OrderDeleteActionLogPayloadSchema,
|
|
@@ -41,5 +42,6 @@ export const ActionLogPayloadSchema = z.union([
|
|
|
41
42
|
TransferConsignmentItemsActionLogSchema,
|
|
42
43
|
NormalizeAttributesActionLogSchema,
|
|
43
44
|
MergeProductImagesActionLogSchema,
|
|
45
|
+
ImportTransactionsActionLogSchema,
|
|
44
46
|
z.null(),
|
|
45
47
|
]);
|
|
@@ -19,6 +19,9 @@ import { ConsignmentItemsCreateActionLogSchema } from './consignment-items-creat
|
|
|
19
19
|
import { ConsignmentItemsDeleteActionLogSchema } from './consignment-items-delete-action-log.schema';
|
|
20
20
|
import { GroupUpdateConsignmentItemsActionLogSchema } from './group-update-consignment-items-action-log.schema';
|
|
21
21
|
import { TransferConsignmentItemsActionLogSchema } from './transfer-consignment-items-action-log.schema';
|
|
22
|
+
import { NormalizeAttributesActionLogSchema } from './normalize-attributes-action-log.schema';
|
|
23
|
+
import { MergeProductImagesActionLogSchema } from './merge-product-images-action-log.schema';
|
|
24
|
+
import { ImportTransactionsActionLogSchema } from './import-transactions-action-log.schema';
|
|
22
25
|
|
|
23
26
|
const BaseActionFields = z.object({
|
|
24
27
|
uuid: z.string(),
|
|
@@ -149,8 +152,40 @@ export const ActionLogSchema = z.discriminatedUnion('actionType', [
|
|
|
149
152
|
payload: TransferConsignmentItemsActionLogSchema,
|
|
150
153
|
})
|
|
151
154
|
.merge(BaseActionFields),
|
|
155
|
+
z
|
|
156
|
+
.object({
|
|
157
|
+
actionType: z.literal(ActionLogTypeEnum.NORMALIZE_ATTRIBUTES),
|
|
158
|
+
payload: NormalizeAttributesActionLogSchema,
|
|
159
|
+
})
|
|
160
|
+
.merge(BaseActionFields),
|
|
161
|
+
z
|
|
162
|
+
.object({
|
|
163
|
+
actionType: z.literal(ActionLogTypeEnum.MERGE_PRODUCT_IMAGES),
|
|
164
|
+
payload: MergeProductImagesActionLogSchema,
|
|
165
|
+
})
|
|
166
|
+
.merge(BaseActionFields),
|
|
167
|
+
z
|
|
168
|
+
.object({
|
|
169
|
+
actionType: z.literal(ActionLogTypeEnum.APPLY_BANK_PAYMENT),
|
|
170
|
+
payload: TransactionAddActionLogSchema,
|
|
171
|
+
})
|
|
172
|
+
.merge(BaseActionFields),
|
|
173
|
+
z
|
|
174
|
+
.object({
|
|
175
|
+
actionType: z.literal(ActionLogTypeEnum.IMPORT_TRANSACTIONS),
|
|
176
|
+
payload: ImportTransactionsActionLogSchema,
|
|
177
|
+
})
|
|
178
|
+
.merge(BaseActionFields),
|
|
152
179
|
]);
|
|
153
180
|
|
|
181
|
+
// Забытый в союзе тип роняет ZodError-ом всю страницу истории, а не одну строку: ActionLogResponseModel
|
|
182
|
+
// парсит каждую запись этой схемой. Так уже случалось с NORMALIZE_ATTRIBUTES и MERGE_PRODUCT_IMAGES,
|
|
183
|
+
// поэтому пропуск обязан ломать компиляцию.
|
|
184
|
+
type UncoveredActionLogType = Exclude<ActionLogTypeEnum, z.infer<typeof ActionLogSchema>['actionType']>;
|
|
185
|
+
// Именно Record, а не массив: пустой массив присваивается `T[]` при любом T и ничего не ловит.
|
|
186
|
+
const assertEveryActionTypeIsCovered: Record<UncoveredActionLogType, true> = {};
|
|
187
|
+
void assertEveryActionTypeIsCovered;
|
|
188
|
+
|
|
154
189
|
export const CreateActionLogSchema = z.object({
|
|
155
190
|
actionType: z.nativeEnum(ActionLogTypeEnum),
|
|
156
191
|
entityUUID: z.string(),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ActionLogMoneySchema } from './action-log-money.schema';
|
|
3
|
+
|
|
4
|
+
// Импорт выписки — одна агрегатная запись на пакет: сколько платежей создано, приход и расход
|
|
5
|
+
// отдельно (в одном файле бывают оба направления), даты операций и uuid созданных транзакций —
|
|
6
|
+
// по ним пакет восстановим, хотя у самой записи сущности нет. Построчный лог был бы шумом.
|
|
7
|
+
export const ImportTransactionsActionLogSchema = z.object({
|
|
8
|
+
count: z.coerce.number(),
|
|
9
|
+
incomeAmount: ActionLogMoneySchema,
|
|
10
|
+
expenseAmount: ActionLogMoneySchema,
|
|
11
|
+
dateFrom: z.coerce.date().nullable(),
|
|
12
|
+
dateTo: z.coerce.date().nullable(),
|
|
13
|
+
transactionUUIDs: z.array(z.string()),
|
|
14
|
+
});
|
|
@@ -22,3 +22,4 @@ export * from './consignment-items-delete-action-log.schema';
|
|
|
22
22
|
export * from './group-update-consignment-items-action-log.schema';
|
|
23
23
|
export * from './transfer-consignment-items-action-log.schema';
|
|
24
24
|
export * from './action-log-money.schema';
|
|
25
|
+
export * from './import-transactions-action-log.schema';
|
package/schemas/index.ts
CHANGED
|
@@ -93,3 +93,6 @@ export * from './attribute-normalization/normalization-value.schema';
|
|
|
93
93
|
export * from './attribute-normalization/normalization-summary.schema';
|
|
94
94
|
export * from './attribute-normalization/normalization-decision.schema';
|
|
95
95
|
export * from './attribute-normalization/product-image-duplicate-forecast.schema';
|
|
96
|
+
export * from './report/user-activity-by-section.schema';
|
|
97
|
+
export * from './report/user-activity-summary-row.schema';
|
|
98
|
+
export * from './report/user-activity-timeline-row.schema';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// Счётчики действий по разделам отчёта «Активность сотрудников». Набор ключей фиксированный —
|
|
4
|
+
// панель рисует таблицу с постоянными колонками, а не map по типам действий.
|
|
5
|
+
//
|
|
6
|
+
// Каждый счётчик, кроме смен, равен числу строк ленты того же раздела за тот же период — по цифре
|
|
7
|
+
// можно провалиться и увидеть ровно столько строк. Исключений два, оба намеренные: `shifts` считает
|
|
8
|
+
// СМЕНЫ (начатые в периоде), а лента показывает приход и уход отдельными строками, поэтому строк
|
|
9
|
+
// там больше; и позиции заказов в счётчики не входят вовсе — они отдельным полем positionsAdded,
|
|
10
|
+
// потому что лента по умолчанию их не показывает.
|
|
11
|
+
export const UserActivityBySectionSchema = z.object({
|
|
12
|
+
orders: z.number(),
|
|
13
|
+
money: z.number(),
|
|
14
|
+
preOrders: z.number(),
|
|
15
|
+
consignments: z.number(),
|
|
16
|
+
writeOffs: z.number(),
|
|
17
|
+
documents: z.number(),
|
|
18
|
+
shifts: z.number(),
|
|
19
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
import { UserActivityBySectionSchema } from './user-activity-by-section.schema';
|
|
4
|
+
|
|
5
|
+
// Строка сводки: один сотрудник за период. Сотрудники без действий тоже присутствуют — с нулями:
|
|
6
|
+
// «кто ничего не делал» и есть половина ценности сводки.
|
|
7
|
+
export const UserActivitySummaryRowSchema = z.object({
|
|
8
|
+
userUUID: z.string(),
|
|
9
|
+
userName: z.string(),
|
|
10
|
+
// Сумма разделов БЕЗ смен: приход и уход — рамка дня, а не работа.
|
|
11
|
+
actionsTotal: z.number(),
|
|
12
|
+
bySection: UserActivityBySectionSchema,
|
|
13
|
+
ordersCreated: z.number(),
|
|
14
|
+
// Принятые сотрудником деньги за период: только приход (INCOME, PREPAYMENT) по ручным и
|
|
15
|
+
// банковским платежам из журнала. Расход и возврат — действия, но не сумма; пакет импорта
|
|
16
|
+
// выписки — тоже действие без суммы (он не «принят» сотрудником).
|
|
17
|
+
paymentsAmount: MoneySchema,
|
|
18
|
+
positionsAdded: z.number(),
|
|
19
|
+
// Первое и последнее действие за период. Смены не считаются (это рамка дня), а добавление позиций
|
|
20
|
+
// считается: сотрудник, который весь день собирал заказы, показывает actionsTotal 0 при непустых
|
|
21
|
+
// отметках — его работа целиком в positionsAdded.
|
|
22
|
+
firstActionAt: z.date().nullable(),
|
|
23
|
+
lastActionAt: z.date().nullable(),
|
|
24
|
+
// Часы смен, закрытых в периоде (как у строки CHECK_OUT в ленте); null — ни одна смена в периоде не закрыта.
|
|
25
|
+
workHours: z.number().nullable(),
|
|
26
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UserActivityActionTypeEnum } from '../../enum/user-activity-action-type.enum';
|
|
3
|
+
import { UserActivitySectionEnum } from '../../enum/user-activity-section.enum';
|
|
4
|
+
import { UserActivitySourceEnum } from '../../enum/user-activity-source.enum';
|
|
5
|
+
import { MoneySchema } from '../money.schema';
|
|
6
|
+
|
|
7
|
+
// Строка ленты активности сотрудника. Текст действия (action, details) рендерит сервер на языке
|
|
8
|
+
// компании — панель показывает его как есть. Клиент, номер заказа, сумма и количество вынесены в
|
|
9
|
+
// отдельные колонки, в details остаётся то, что в колонки не ложится (товар, статусы, причина).
|
|
10
|
+
//
|
|
11
|
+
// Телефон клиента здесь намеренно НЕ отдаётся: строка описывает действие сотрудника, клиент в ней
|
|
12
|
+
// нужен для опознания и уже опознан парой clientUUID + clientName — карточку с контактами панель
|
|
13
|
+
// открывает по uuid. Отчёт, где телефон это рабочий инструмент, — отдельный (/report/sales/clients).
|
|
14
|
+
export const UserActivityTimelineRowSchema = z.object({
|
|
15
|
+
at: z.date(),
|
|
16
|
+
section: z.nativeEnum(UserActivitySectionEnum),
|
|
17
|
+
actionType: z.nativeEnum(UserActivityActionTypeEnum),
|
|
18
|
+
action: z.string(),
|
|
19
|
+
clientUUID: z.string().nullable(),
|
|
20
|
+
clientName: z.string().nullable(),
|
|
21
|
+
orderId: z.number().nullable(),
|
|
22
|
+
amount: MoneySchema.nullable(),
|
|
23
|
+
quantity: z.number().nullable(),
|
|
24
|
+
details: z.string().nullable(),
|
|
25
|
+
source: z.nativeEnum(UserActivitySourceEnum),
|
|
26
|
+
entityUUID: z.string(),
|
|
27
|
+
});
|