@floristcloud/api-lib 1.2.136 → 1.2.138

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.
@@ -7,6 +7,10 @@ const PriceCorrectionAnalyzeRequestSchema = zod_1.z
7
7
  .object({
8
8
  productUUIDList: zod_1.z.array(zod_1.z.string().uuid()).optional(),
9
9
  consignmentUUID: zod_1.z.string().uuid().optional(),
10
+ // Поставка на сотни товаров даёт десятки тысяч позиций, из которых после правки прайса
11
+ // меняются единицы. По умолчанию отдаём только те, где цена реально расходится; полный
12
+ // список — режим «показать всё».
13
+ onlyChanged: zod_1.z.boolean().optional().default(true),
10
14
  })
11
15
  .refine(data => { var _a; return ((_a = data.productUUIDList) === null || _a === void 0 ? void 0 : _a.length) || data.consignmentUUID; }, {
12
16
  message: 'validation.productListOrConsignment',
@@ -4,14 +4,37 @@ exports.PriceCorrectionExecuteContractCommand = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const schemas_1 = require("../../../schemas");
6
6
  const PRICE_CORRECTION_BATCH_LIMIT = 50;
7
- const PriceCorrectionExecuteRequestSchema = zod_1.z.object({
8
- orderProductUUIDList: zod_1.z.array(zod_1.z.string().uuid()).min(1).max(PRICE_CORRECTION_BATCH_LIMIT),
7
+ /**
8
+ * Две формы адресации, ровно одна на запрос.
9
+ *
10
+ * `orderProductUUIDList` — точечная правка выбранных позиций (экран заказа), лимит остаётся.
11
+ * Scope (`consignmentUUID` / `productUUIDList` + `excludeOrderProductUUIDList`) — «выровнять всё,
12
+ * кроме снятого»: набор собирает сервер тем же правилом, что и analyze, поэтому размер запроса не
13
+ * зависит от числа позиций и нарезка на пачки на клиенте не нужна.
14
+ */
15
+ const PriceCorrectionExecuteRequestSchema = zod_1.z
16
+ .object({
17
+ orderProductUUIDList: zod_1.z.array(zod_1.z.string().uuid()).min(1).max(PRICE_CORRECTION_BATCH_LIMIT).optional(),
18
+ consignmentUUID: zod_1.z.string().uuid().optional(),
19
+ productUUIDList: zod_1.z.array(zod_1.z.string().uuid()).min(1).optional(),
20
+ excludeOrderProductUUIDList: zod_1.z.array(zod_1.z.string().uuid()).optional(),
21
+ onlyChanged: zod_1.z.boolean().optional().default(true),
9
22
  withDiscount: zod_1.z.boolean(),
10
- });
23
+ })
24
+ .refine(data => {
25
+ var _a, _b;
26
+ const isListForm = Boolean((_a = data.orderProductUUIDList) === null || _a === void 0 ? void 0 : _a.length);
27
+ const isScopeForm = Boolean(data.consignmentUUID) || Boolean((_b = data.productUUIDList) === null || _b === void 0 ? void 0 : _b.length);
28
+ return isListForm !== isScopeForm;
29
+ }, { message: 'validation.orderProductListOrScope' });
11
30
  const PriceCorrectionExecuteResponseSchema = zod_1.z.object({
12
31
  message: zod_1.z.string().optional(),
13
32
  data: zod_1.z.object({
33
+ // Форма `orderProductUUIDList` — строка на каждую позицию запроса. Scope-форма — только
34
+ // строки, потребовавшие внимания (ошибки и пропуски): успешных там тысячи, и клиент всё
35
+ // равно перезапрашивает analyze, чтобы обновить таблицу.
14
36
  results: zod_1.z.array(schemas_1.PriceCorrectionExecuteResultItemSchema),
37
+ orders: zod_1.z.array(schemas_1.PriceCorrectionExecuteOrderItemSchema),
15
38
  successCount: zod_1.z.number(),
16
39
  skippedCount: zod_1.z.number(),
17
40
  }),
@@ -552,6 +552,25 @@ exports.ERRORS = {
552
552
  message: 'Source and destination orders must differ',
553
553
  httpCode: 400,
554
554
  },
555
+ // Статус заказа проверяется до отправки пакета и ещё раз внутри транзакции под блокировкой.
556
+ // Этот код — про вторую проверку: статус сменили, пока пакет ждал строку заказа.
557
+ ORDER_PRODUCT_PRICE_APPLY_ORDER_STATUS_CHANGED: {
558
+ code: 'OP026',
559
+ message: 'Order status changed and no longer allows price change',
560
+ httpCode: 409,
561
+ },
562
+ ORDER_PRODUCT_PRICE_CORRECTION_ALREADY_RUNNING: {
563
+ code: 'OP027',
564
+ message: 'Price correction for this consignment is already in progress',
565
+ httpCode: 409,
566
+ },
567
+ // Redis недоступен, то есть исключительность прогона гарантировать нечем. Коррекция поставки
568
+ // переписывает цены во всех заказах партии — тут отказ безопаснее, чем прогон вслепую.
569
+ ORDER_PRODUCT_PRICE_CORRECTION_LOCK_UNAVAILABLE: {
570
+ code: 'OP028',
571
+ message: 'Cannot guarantee exclusive price correction right now, try again later',
572
+ httpCode: 503,
573
+ },
555
574
  //TRANSACTION
556
575
  TRANSACTION_NOT_CREATED: { code: 'TR001', message: 'Failed to transaction product', httpCode: 500 },
557
576
  TRANSACTION_FETCH_FAILED: { code: 'TR002', message: 'Error when retrieving a transaction', httpCode: 500 },
@@ -6,6 +6,8 @@ var UserRoleEnum;
6
6
  UserRoleEnum["ADMIN"] = "ADMIN";
7
7
  UserRoleEnum["MANAGER"] = "MANAGER";
8
8
  UserRoleEnum["EMPLOYEE"] = "EMPLOYEE";
9
+ // Warehouse worker: the same panel access as EMPLOYEE, but always sees every client of the company.
10
+ UserRoleEnum["WAREHOUSE_EMPLOYEE"] = "WAREHOUSE_EMPLOYEE";
9
11
  UserRoleEnum["BUYER"] = "BUYER";
10
12
  })(UserRoleEnum || (exports.UserRoleEnum = UserRoleEnum = {}));
11
13
  var UserPanelRoleEnum;
@@ -13,5 +15,11 @@ var UserPanelRoleEnum;
13
15
  UserPanelRoleEnum["ADMIN"] = "ADMIN";
14
16
  UserPanelRoleEnum["MANAGER"] = "MANAGER";
15
17
  UserPanelRoleEnum["EMPLOYEE"] = "EMPLOYEE";
18
+ UserPanelRoleEnum["WAREHOUSE_EMPLOYEE"] = "WAREHOUSE_EMPLOYEE";
16
19
  })(UserPanelRoleEnum || (exports.UserPanelRoleEnum = UserPanelRoleEnum = {}));
17
- exports.USER_PANEL_ROLE_LIST = [UserRoleEnum.ADMIN, UserRoleEnum.MANAGER, UserRoleEnum.EMPLOYEE];
20
+ exports.USER_PANEL_ROLE_LIST = [
21
+ UserRoleEnum.ADMIN,
22
+ UserRoleEnum.MANAGER,
23
+ UserRoleEnum.EMPLOYEE,
24
+ UserRoleEnum.WAREHOUSE_EMPLOYEE,
25
+ ];
@@ -9,7 +9,11 @@ const userRole_enum_1 = require("../../enum/userRole.enum");
9
9
  * it always bypasses UI restrictions, so any ADMIN entry in a policy is dead
10
10
  * config. The admin panel must not offer it, and the sanitizer drops it.
11
11
  */
12
- exports.CONFIGURABLE_PANEL_ROLE_LIST = [userRole_enum_1.UserPanelRoleEnum.MANAGER, userRole_enum_1.UserPanelRoleEnum.EMPLOYEE];
12
+ exports.CONFIGURABLE_PANEL_ROLE_LIST = [
13
+ userRole_enum_1.UserPanelRoleEnum.MANAGER,
14
+ userRole_enum_1.UserPanelRoleEnum.EMPLOYEE,
15
+ userRole_enum_1.UserPanelRoleEnum.WAREHOUSE_EMPLOYEE,
16
+ ];
13
17
  /**
14
18
  * A sparse map of permission key -> boolean.
15
19
  * Semantics are default-allow: a missing key means "allowed", so in practice
@@ -30,6 +30,7 @@ __exportStar(require("./order-product/order-product-list.schema"), exports);
30
30
  __exportStar(require("./order-product/order-product-multiplicity-optiom.schema"), exports);
31
31
  __exportStar(require("./order-product/price-correction-analyze-item.schema"), exports);
32
32
  __exportStar(require("./order-product/price-correction-execute-result-item.schema"), exports);
33
+ __exportStar(require("./order-product/price-correction-execute-order-item.schema"), exports);
33
34
  __exportStar(require("./transaction/transaction.schema"), exports);
34
35
  __exportStar(require("./transaction/transaction-list.schema"), exports);
35
36
  __exportStar(require("./cash-flow-item/cash-flow-item.schema"), exports);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PriceCorrectionExecuteOrderItemSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ // Итог коррекции по одному заказу. Ради него ответ scope-формы не обязан нести строку на позицию:
6
+ // на тысяче позиций постраничный разбор клиенту не нужен, нужна сводка «в каком заказе что вышло».
7
+ exports.PriceCorrectionExecuteOrderItemSchema = zod_1.z.object({
8
+ orderUUID: zod_1.z.string(),
9
+ orderNumber: zod_1.z.number(),
10
+ successCount: zod_1.z.number(),
11
+ skippedCount: zod_1.z.number(),
12
+ });
@@ -5,6 +5,10 @@ const PriceCorrectionAnalyzeRequestSchema = z
5
5
  .object({
6
6
  productUUIDList: z.array(z.string().uuid()).optional(),
7
7
  consignmentUUID: z.string().uuid().optional(),
8
+ // Поставка на сотни товаров даёт десятки тысяч позиций, из которых после правки прайса
9
+ // меняются единицы. По умолчанию отдаём только те, где цена реально расходится; полный
10
+ // список — режим «показать всё».
11
+ onlyChanged: z.boolean().optional().default(true),
8
12
  })
9
13
  .refine(data => data.productUUIDList?.length || data.consignmentUUID, {
10
14
  message: 'validation.productListOrConsignment',
@@ -1,17 +1,44 @@
1
1
  import { z } from 'zod';
2
- import { PriceCorrectionExecuteResultItemSchema } from '../../../schemas';
2
+ import { PriceCorrectionExecuteOrderItemSchema, PriceCorrectionExecuteResultItemSchema } from '../../../schemas';
3
3
 
4
4
  const PRICE_CORRECTION_BATCH_LIMIT = 50;
5
5
 
6
- const PriceCorrectionExecuteRequestSchema = z.object({
7
- orderProductUUIDList: z.array(z.string().uuid()).min(1).max(PRICE_CORRECTION_BATCH_LIMIT),
8
- withDiscount: z.boolean(),
9
- });
6
+ /**
7
+ * Две формы адресации, ровно одна на запрос.
8
+ *
9
+ * `orderProductUUIDList` — точечная правка выбранных позиций (экран заказа), лимит остаётся.
10
+ * Scope (`consignmentUUID` / `productUUIDList` + `excludeOrderProductUUIDList`) — «выровнять всё,
11
+ * кроме снятого»: набор собирает сервер тем же правилом, что и analyze, поэтому размер запроса не
12
+ * зависит от числа позиций и нарезка на пачки на клиенте не нужна.
13
+ */
14
+ const PriceCorrectionExecuteRequestSchema = z
15
+ .object({
16
+ orderProductUUIDList: z.array(z.string().uuid()).min(1).max(PRICE_CORRECTION_BATCH_LIMIT).optional(),
17
+
18
+ consignmentUUID: z.string().uuid().optional(),
19
+ productUUIDList: z.array(z.string().uuid()).min(1).optional(),
20
+ excludeOrderProductUUIDList: z.array(z.string().uuid()).optional(),
21
+ onlyChanged: z.boolean().optional().default(true),
22
+
23
+ withDiscount: z.boolean(),
24
+ })
25
+ .refine(
26
+ data => {
27
+ const isListForm = Boolean(data.orderProductUUIDList?.length);
28
+ const isScopeForm = Boolean(data.consignmentUUID) || Boolean(data.productUUIDList?.length);
29
+ return isListForm !== isScopeForm;
30
+ },
31
+ { message: 'validation.orderProductListOrScope' },
32
+ );
10
33
 
11
34
  const PriceCorrectionExecuteResponseSchema = z.object({
12
35
  message: z.string().optional(),
13
36
  data: z.object({
37
+ // Форма `orderProductUUIDList` — строка на каждую позицию запроса. Scope-форма — только
38
+ // строки, потребовавшие внимания (ошибки и пропуски): успешных там тысячи, и клиент всё
39
+ // равно перезапрашивает analyze, чтобы обновить таблицу.
14
40
  results: z.array(PriceCorrectionExecuteResultItemSchema),
41
+ orders: z.array(PriceCorrectionExecuteOrderItemSchema),
15
42
  successCount: z.number(),
16
43
  skippedCount: z.number(),
17
44
  }),
package/constant/error.ts CHANGED
@@ -570,6 +570,25 @@ export const ERRORS = {
570
570
  message: 'Source and destination orders must differ',
571
571
  httpCode: 400,
572
572
  },
573
+ // Статус заказа проверяется до отправки пакета и ещё раз внутри транзакции под блокировкой.
574
+ // Этот код — про вторую проверку: статус сменили, пока пакет ждал строку заказа.
575
+ ORDER_PRODUCT_PRICE_APPLY_ORDER_STATUS_CHANGED: {
576
+ code: 'OP026',
577
+ message: 'Order status changed and no longer allows price change',
578
+ httpCode: 409,
579
+ },
580
+ ORDER_PRODUCT_PRICE_CORRECTION_ALREADY_RUNNING: {
581
+ code: 'OP027',
582
+ message: 'Price correction for this consignment is already in progress',
583
+ httpCode: 409,
584
+ },
585
+ // Redis недоступен, то есть исключительность прогона гарантировать нечем. Коррекция поставки
586
+ // переписывает цены во всех заказах партии — тут отказ безопаснее, чем прогон вслепую.
587
+ ORDER_PRODUCT_PRICE_CORRECTION_LOCK_UNAVAILABLE: {
588
+ code: 'OP028',
589
+ message: 'Cannot guarantee exclusive price correction right now, try again later',
590
+ httpCode: 503,
591
+ },
573
592
 
574
593
  //TRANSACTION
575
594
  TRANSACTION_NOT_CREATED: { code: 'TR001', message: 'Failed to transaction product', httpCode: 500 },
@@ -2,6 +2,8 @@ export enum UserRoleEnum {
2
2
  ADMIN = 'ADMIN',
3
3
  MANAGER = 'MANAGER',
4
4
  EMPLOYEE = 'EMPLOYEE',
5
+ // Warehouse worker: the same panel access as EMPLOYEE, but always sees every client of the company.
6
+ WAREHOUSE_EMPLOYEE = 'WAREHOUSE_EMPLOYEE',
5
7
  BUYER = 'BUYER',
6
8
  }
7
9
 
@@ -9,6 +11,12 @@ export enum UserPanelRoleEnum {
9
11
  ADMIN = 'ADMIN',
10
12
  MANAGER = 'MANAGER',
11
13
  EMPLOYEE = 'EMPLOYEE',
14
+ WAREHOUSE_EMPLOYEE = 'WAREHOUSE_EMPLOYEE',
12
15
  }
13
16
 
14
- export const USER_PANEL_ROLE_LIST: UserRoleEnum[] = [UserRoleEnum.ADMIN, UserRoleEnum.MANAGER, UserRoleEnum.EMPLOYEE];
17
+ export const USER_PANEL_ROLE_LIST: UserRoleEnum[] = [
18
+ UserRoleEnum.ADMIN,
19
+ UserRoleEnum.MANAGER,
20
+ UserRoleEnum.EMPLOYEE,
21
+ UserRoleEnum.WAREHOUSE_EMPLOYEE,
22
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floristcloud/api-lib",
3
- "version": "1.2.136",
3
+ "version": "1.2.138",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -7,7 +7,11 @@ import { UserPanelRoleEnum } from '../../enum/userRole.enum';
7
7
  * it always bypasses UI restrictions, so any ADMIN entry in a policy is dead
8
8
  * config. The admin panel must not offer it, and the sanitizer drops it.
9
9
  */
10
- export const CONFIGURABLE_PANEL_ROLE_LIST: UserPanelRoleEnum[] = [UserPanelRoleEnum.MANAGER, UserPanelRoleEnum.EMPLOYEE];
10
+ export const CONFIGURABLE_PANEL_ROLE_LIST: UserPanelRoleEnum[] = [
11
+ UserPanelRoleEnum.MANAGER,
12
+ UserPanelRoleEnum.EMPLOYEE,
13
+ UserPanelRoleEnum.WAREHOUSE_EMPLOYEE,
14
+ ];
11
15
 
12
16
  /**
13
17
  * A sparse map of permission key -> boolean.
package/schemas/index.ts CHANGED
@@ -14,6 +14,7 @@ export * from './order-product/order-product-list.schema';
14
14
  export * from './order-product/order-product-multiplicity-optiom.schema';
15
15
  export * from './order-product/price-correction-analyze-item.schema';
16
16
  export * from './order-product/price-correction-execute-result-item.schema';
17
+ export * from './order-product/price-correction-execute-order-item.schema';
17
18
  export * from './transaction/transaction.schema';
18
19
  export * from './transaction/transaction-list.schema';
19
20
  export * from './cash-flow-item/cash-flow-item.schema';
@@ -0,0 +1,10 @@
1
+ import { z } from 'zod';
2
+
3
+ // Итог коррекции по одному заказу. Ради него ответ scope-формы не обязан нести строку на позицию:
4
+ // на тысяче позиций постраничный разбор клиенту не нужен, нужна сводка «в каком заказе что вышло».
5
+ export const PriceCorrectionExecuteOrderItemSchema = z.object({
6
+ orderUUID: z.string(),
7
+ orderNumber: z.number(),
8
+ successCount: z.number(),
9
+ skippedCount: z.number(),
10
+ });