@floristcloud/api-lib 1.2.131 → 1.2.133

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.
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AnalyzeConsignmentProductEnrichmentContractQuery = void 0;
4
+ const zod_1 = require("zod");
5
+ const query_boolean_helper_1 = require("../../helpers/query-boolean.helper");
6
+ const consignment_product_enrichment_constant_1 = require("../../constant/consignment-product-enrichment.constant");
7
+ const product_enrichment_field_enum_1 = require("../../enum/product-enrichment-field.enum");
8
+ const product_enrichment_field_state_enum_1 = require("../../enum/product-enrichment-field-state.enum");
9
+ const AnalyzeConsignmentProductEnrichmentRequestSchema = zod_1.z.object({
10
+ page: zod_1.z.coerce.number().int().min(1).default(1),
11
+ count: zod_1.z.coerce
12
+ .number()
13
+ .int()
14
+ .min(1)
15
+ .max(consignment_product_enrichment_constant_1.CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.MAX_PAGE_SIZE)
16
+ .default(consignment_product_enrichment_constant_1.CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.DEFAULT_PAGE_SIZE),
17
+ // Показывать только товары, у которых есть хотя бы одно поле к заполнению или конфликт.
18
+ // По умолчанию false: пользователь видит всю поставку и понимает, почему строк меньше, чем товаров.
19
+ // Не `z.coerce.boolean()`: в query приходит текст, а `Boolean('false') === true` — флаг нельзя
20
+ // было бы выключить явным значением.
21
+ isOnlyChanged: query_boolean_helper_1.queryBooleanSchema,
22
+ });
23
+ const ProductEnrichmentFieldSchema = zod_1.z.object({
24
+ current: zod_1.z.string().nullable(),
25
+ proposed: zod_1.z.string().nullable(),
26
+ state: zod_1.z.nativeEnum(product_enrichment_field_state_enum_1.ProductEnrichmentFieldStateEnum),
27
+ });
28
+ const ProductEnrichmentItemSchema = zod_1.z.object({
29
+ productUUID: zod_1.z.string(),
30
+ productName: zod_1.z.string(),
31
+ article: zod_1.z.string().nullable(),
32
+ size: zod_1.z.string().nullable(),
33
+ categoryName: zod_1.z.string().nullable(),
34
+ imageUUID: zod_1.z.string(),
35
+ imageName: zod_1.z.string(),
36
+ imageUrl: zod_1.z.string(),
37
+ fields: zod_1.z.record(zod_1.z.nativeEnum(product_enrichment_field_enum_1.ProductEnrichmentFieldEnum), ProductEnrichmentFieldSchema),
38
+ });
39
+ const ProductEnrichmentFieldStatsSchema = zod_1.z.object({
40
+ fillable: zod_1.z.number(),
41
+ conflicts: zod_1.z.number(),
42
+ });
43
+ const ProductEnrichmentSummarySchema = zod_1.z.object({
44
+ // Всего товаров в поставке.
45
+ totalProducts: zod_1.z.number(),
46
+ // Из них с привязанной карточкой — только они попадают в таблицу.
47
+ linkedProducts: zod_1.z.number(),
48
+ // Товаров, у которых есть хотя бы одно поле к заполнению.
49
+ fillableProducts: zod_1.z.number(),
50
+ // Товаров, у которых есть хотя бы один конфликт.
51
+ conflictProducts: zod_1.z.number(),
52
+ byField: zod_1.z.record(zod_1.z.nativeEnum(product_enrichment_field_enum_1.ProductEnrichmentFieldEnum), ProductEnrichmentFieldStatsSchema),
53
+ });
54
+ const AnalyzeConsignmentProductEnrichmentResponseSchema = zod_1.z.object({
55
+ data: zod_1.z.object({
56
+ items: zod_1.z.array(ProductEnrichmentItemSchema),
57
+ total: zod_1.z.number(),
58
+ page: zod_1.z.number(),
59
+ count: zod_1.z.number(),
60
+ // Считается по всей поставке, а не по текущей странице: иначе счётчики прыгали бы при листании.
61
+ summary: ProductEnrichmentSummarySchema,
62
+ }),
63
+ });
64
+ var AnalyzeConsignmentProductEnrichmentContractQuery;
65
+ (function (AnalyzeConsignmentProductEnrichmentContractQuery) {
66
+ AnalyzeConsignmentProductEnrichmentContractQuery.RequestSchema = AnalyzeConsignmentProductEnrichmentRequestSchema;
67
+ AnalyzeConsignmentProductEnrichmentContractQuery.ResponseSchema = AnalyzeConsignmentProductEnrichmentResponseSchema;
68
+ })(AnalyzeConsignmentProductEnrichmentContractQuery || (exports.AnalyzeConsignmentProductEnrichmentContractQuery = AnalyzeConsignmentProductEnrichmentContractQuery = {}));
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExecuteConsignmentProductEnrichmentContractCommand = void 0;
4
+ const zod_1 = require("zod");
5
+ const error_message_schema_1 = require("../../schemas/error-message.schema");
6
+ const consignment_product_enrichment_constant_1 = require("../../constant/consignment-product-enrichment.constant");
7
+ const product_enrichment_field_enum_1 = require("../../enum/product-enrichment-field.enum");
8
+ const ExecuteConsignmentProductEnrichmentItemSchema = zod_1.z.object({
9
+ productUUID: zod_1.z.string().uuid(),
10
+ // Какие поля переносим из карточки в этот товар. Пустой список — товар пропускается.
11
+ fields: zod_1.z.array(zod_1.z.nativeEnum(product_enrichment_field_enum_1.ProductEnrichmentFieldEnum)).min(1),
12
+ });
13
+ const ExecuteConsignmentProductEnrichmentRequestSchema = zod_1.z.object({
14
+ // Фронт шлёт пачками по EXECUTE_BATCH_SIZE и сам собирает итог: одна поставка может
15
+ // содержать до 1200 товаров, и одним запросом это отправлять не нужно.
16
+ // Повторы `productUUID` допустимы и схлопываются на сервере в объединение полей: товар
17
+ // обновляется и логируется один раз, сколько бы записей о нём ни пришло.
18
+ items: zod_1.z.array(ExecuteConsignmentProductEnrichmentItemSchema).min(1).max(consignment_product_enrichment_constant_1.CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.EXECUTE_BATCH_SIZE),
19
+ });
20
+ const ExecuteConsignmentProductEnrichmentResponseSchema = zod_1.z.object({
21
+ data: zod_1.z.object({
22
+ success: zod_1.z.boolean(),
23
+ // Товаров, у которых изменилось хотя бы одно поле.
24
+ successCount: zod_1.z.number(),
25
+ // Товаров, где менять было нечего: значение уже совпадало или карточка опустела с момента разбора.
26
+ skippedCount: zod_1.z.number(),
27
+ // Товаров, которые кто-то изменил между разбором и применением. Отдельно от skipped, потому
28
+ // что это единственный исход, требующий действия: пользователю нужно перечитать разбор.
29
+ conflictedCount: zod_1.z.number(),
30
+ failedCount: zod_1.z.number(),
31
+ errors: zod_1.z.array(error_message_schema_1.ErrorMessageSchema.extend({
32
+ uuid: zod_1.z.string(),
33
+ })),
34
+ }),
35
+ });
36
+ var ExecuteConsignmentProductEnrichmentContractCommand;
37
+ (function (ExecuteConsignmentProductEnrichmentContractCommand) {
38
+ ExecuteConsignmentProductEnrichmentContractCommand.RequestSchema = ExecuteConsignmentProductEnrichmentRequestSchema;
39
+ ExecuteConsignmentProductEnrichmentContractCommand.ResponseSchema = ExecuteConsignmentProductEnrichmentResponseSchema;
40
+ })(ExecuteConsignmentProductEnrichmentContractCommand || (exports.ExecuteConsignmentProductEnrichmentContractCommand = ExecuteConsignmentProductEnrichmentContractCommand = {}));
@@ -29,3 +29,5 @@ __exportStar(require("./get-access-clients-consignment.query"), exports);
29
29
  __exportStar(require("./get-consignment-filters-values.query"), exports);
30
30
  __exportStar(require("./execute-consignment-product-update.command"), exports);
31
31
  __exportStar(require("./determine-consignment-product-images.command"), exports);
32
+ __exportStar(require("./analyze-consignment-product-enrichment.query"), exports);
33
+ __exportStar(require("./execute-consignment-product-enrichment.command"), exports);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS = void 0;
4
+ // Лимиты операции «получить характеристики из номенклатуры».
5
+ // Анализ читает всю поставку целиком (потолок задан лимитами импорта: MAX_ROWS_WITHOUT_IMAGES = 1200),
6
+ // но наружу отдаёт страницами, а применение принимает пачками — чтобы ни ответ, ни запрос не росли
7
+ // вместе с размером поставки.
8
+ exports.CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS = {
9
+ DEFAULT_PAGE_SIZE: 50,
10
+ MAX_PAGE_SIZE: 200,
11
+ EXECUTE_BATCH_SIZE: 100,
12
+ };
@@ -25,3 +25,4 @@ __exportStar(require("./oksm"), exports);
25
25
  __exportStar(require("./okei"), exports);
26
26
  __exportStar(require("./contact-phone.constant"), exports);
27
27
  __exportStar(require("./settings.constant"), exports);
28
+ __exportStar(require("./consignment-product-enrichment.constant"), exports);
@@ -122,3 +122,5 @@ __exportStar(require("./transfer-document-state.enum"), exports);
122
122
  __exportStar(require("./transfer-document-kind.enum"), exports);
123
123
  __exportStar(require("./transfer-document-item-kind.enum"), exports);
124
124
  __exportStar(require("./write-off-report-date-basis.enum"), exports);
125
+ __exportStar(require("./product-enrichment-field.enum"), exports);
126
+ __exportStar(require("./product-enrichment-field-state.enum"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProductEnrichmentFieldStateEnum = void 0;
4
+ // Состояние одного поля товара относительно привязанной карточки номенклатуры.
5
+ // Определяет и подсказку в интерфейсе («собственные данные» / «из номенклатуры»), и то,
6
+ // отмечен ли чекбокс по умолчанию: предзаполняется только FILLABLE.
7
+ var ProductEnrichmentFieldStateEnum;
8
+ (function (ProductEnrichmentFieldStateEnum) {
9
+ // У товара пусто, в карточке значение есть — это и есть «дополнить».
10
+ ProductEnrichmentFieldStateEnum["FILLABLE"] = "FILLABLE";
11
+ // Оба заполнены и различаются. Показываем, но по умолчанию не отмечаем.
12
+ ProductEnrichmentFieldStateEnum["CONFLICT"] = "CONFLICT";
13
+ // Значения совпадают — брать нечего.
14
+ ProductEnrichmentFieldStateEnum["SAME"] = "SAME";
15
+ // В карточке пусто — источника нет.
16
+ ProductEnrichmentFieldStateEnum["NO_SOURCE"] = "NO_SOURCE";
17
+ })(ProductEnrichmentFieldStateEnum || (exports.ProductEnrichmentFieldStateEnum = ProductEnrichmentFieldStateEnum = {}));
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRODUCT_ENRICHMENT_FIELD_LIST = exports.ProductEnrichmentFieldEnum = void 0;
4
+ // Поля товара, которые можно дополнить из привязанной карточки номенклатуры (ProductImage).
5
+ // Значения совпадают с именами полей Product и ProductImage — это ключи, по которым фронт
6
+ // собирает решения, поэтому переименование значения ломает контракт.
7
+ var ProductEnrichmentFieldEnum;
8
+ (function (ProductEnrichmentFieldEnum) {
9
+ ProductEnrichmentFieldEnum["COLOR"] = "color";
10
+ ProductEnrichmentFieldEnum["COUNTRY"] = "country";
11
+ ProductEnrichmentFieldEnum["GROWER"] = "grower";
12
+ })(ProductEnrichmentFieldEnum || (exports.ProductEnrichmentFieldEnum = ProductEnrichmentFieldEnum = {}));
13
+ const PRODUCT_ENRICHMENT_FIELD_KEYS = Object.keys(ProductEnrichmentFieldEnum);
14
+ exports.PRODUCT_ENRICHMENT_FIELD_LIST = PRODUCT_ENRICHMENT_FIELD_KEYS.map(key => ProductEnrichmentFieldEnum[key]);
@@ -3,12 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PreOrderProductInfoSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const action_log_money_schema_1 = require("./action-log-money.schema");
6
+ /**
7
+ * Снимок позиции предзаказа в журнале аудита.
8
+ *
9
+ * `name` и `price` — nullable: у позиции предзаказа наименование и цена появляются не сразу
10
+ * (в БД `pre_order_product.name` и `price` тоже nullable), и журнал фиксирует позицию такой,
11
+ * какой она была на момент события. Записи с `null` уже лежат в `action_log.payload`.
12
+ */
6
13
  exports.PreOrderProductInfoSchema = zod_1.z.object({
7
14
  preOrderUUID: zod_1.z.string(),
8
15
  preOrderProductUUID: zod_1.z.string(),
9
- name: zod_1.z.string(),
16
+ name: zod_1.z.string().nullable(),
10
17
  categoryName: zod_1.z.string().nullable(),
11
18
  quantity: zod_1.z.coerce.number(),
12
- price: action_log_money_schema_1.ActionLogMoneySchema,
19
+ price: action_log_money_schema_1.ActionLogMoneySchema.nullable(),
13
20
  comment: zod_1.z.string().nullable(),
14
21
  });
@@ -7,6 +7,7 @@ const action_log_money_schema_1 = require("./action-log-money.schema");
7
7
  exports.PreOrderProductUpdateActionLogSchema = zod_1.z.object({
8
8
  preOrderProduct: pre_order_product_info_schema_1.PreOrderProductInfoSchema,
9
9
  newQuantity: zod_1.z.coerce.number(),
10
- newPrice: action_log_money_schema_1.ActionLogMoneySchema,
10
+ // Цену позиции предзаказа можно снять — новое значение бывает null, см. PreOrderProductInfoSchema.
11
+ newPrice: action_log_money_schema_1.ActionLogMoneySchema.nullable(),
11
12
  newComment: zod_1.z.string().nullable(),
12
13
  });
@@ -91,6 +91,23 @@ exports.ProductExtendedForClientSchema = exports.ProductForClientSchema.merge(zo
91
91
  isInStock: zod_1.z.boolean(),
92
92
  }),
93
93
  multiplicityOptions: zod_1.z.array(product_multiplicity_option_for_client_schema_1.ProductMultiplicityOptionForClientSchema).optional(),
94
+ /**
95
+ * Правила клиента, снижающие **базовую цену товара**, — и только они.
96
+ *
97
+ * Правило, оставляющее цену как есть, в список не попадает: это не скидка, а «скидка» с
98
+ * той же ценой рядом. Так выходит у правила от себестоимости, когда «себестоимость +
99
+ * наценка» вышла выше прайса, и у скидки в 0 %. Отбор общий с панелью
100
+ * (`GetProductDiscountPriceContractQuery`) — правила отбираются одинаково.
101
+ *
102
+ * Список считается от базовой цены товара, поэтому пустой список **не означает**, что у
103
+ * клиента нет скидочных цен по этому товару вовсе: у кратности своя база, и правило может
104
+ * снижать цену фасовки, не снижая цену штуки. Цена каждой фасовки — в её собственных
105
+ * `optionPriceWithDiscount` и `isDiscountApplicable`, там результат уже посчитан от нужной
106
+ * базы. Пример: прайс 50, фасовка 80, «себестоимость + наценка» 60 — список пуст, а
107
+ * фасовка идёт по 60.
108
+ *
109
+ * Порядок не зафиксирован — лучшую цену базового тарифа берите из `priceWithBestDiscount`.
110
+ */
94
111
  discountDataList: zod_1.z
95
112
  .object({
96
113
  discountRulesUUID: zod_1.z.string(),
@@ -0,0 +1,78 @@
1
+ import { z } from 'zod';
2
+ import { queryBooleanSchema } from '../../helpers/query-boolean.helper';
3
+ import { CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS } from '../../constant/consignment-product-enrichment.constant';
4
+ import { ProductEnrichmentFieldEnum } from '../../enum/product-enrichment-field.enum';
5
+ import { ProductEnrichmentFieldStateEnum } from '../../enum/product-enrichment-field-state.enum';
6
+
7
+ const AnalyzeConsignmentProductEnrichmentRequestSchema = z.object({
8
+ page: z.coerce.number().int().min(1).default(1),
9
+ count: z.coerce
10
+ .number()
11
+ .int()
12
+ .min(1)
13
+ .max(CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.MAX_PAGE_SIZE)
14
+ .default(CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.DEFAULT_PAGE_SIZE),
15
+ // Показывать только товары, у которых есть хотя бы одно поле к заполнению или конфликт.
16
+ // По умолчанию false: пользователь видит всю поставку и понимает, почему строк меньше, чем товаров.
17
+ // Не `z.coerce.boolean()`: в query приходит текст, а `Boolean('false') === true` — флаг нельзя
18
+ // было бы выключить явным значением.
19
+ isOnlyChanged: queryBooleanSchema,
20
+ });
21
+
22
+ const ProductEnrichmentFieldSchema = z.object({
23
+ current: z.string().nullable(),
24
+ proposed: z.string().nullable(),
25
+ state: z.nativeEnum(ProductEnrichmentFieldStateEnum),
26
+ });
27
+
28
+ const ProductEnrichmentItemSchema = z.object({
29
+ productUUID: z.string(),
30
+ productName: z.string(),
31
+ article: z.string().nullable(),
32
+ size: z.string().nullable(),
33
+ categoryName: z.string().nullable(),
34
+ imageUUID: z.string(),
35
+ imageName: z.string(),
36
+ imageUrl: z.string(),
37
+ fields: z.record(z.nativeEnum(ProductEnrichmentFieldEnum), ProductEnrichmentFieldSchema),
38
+ });
39
+
40
+ const ProductEnrichmentFieldStatsSchema = z.object({
41
+ fillable: z.number(),
42
+ conflicts: z.number(),
43
+ });
44
+
45
+ const ProductEnrichmentSummarySchema = z.object({
46
+ // Всего товаров в поставке.
47
+ totalProducts: z.number(),
48
+ // Из них с привязанной карточкой — только они попадают в таблицу.
49
+ linkedProducts: z.number(),
50
+ // Товаров, у которых есть хотя бы одно поле к заполнению.
51
+ fillableProducts: z.number(),
52
+ // Товаров, у которых есть хотя бы один конфликт.
53
+ conflictProducts: z.number(),
54
+ byField: z.record(z.nativeEnum(ProductEnrichmentFieldEnum), ProductEnrichmentFieldStatsSchema),
55
+ });
56
+
57
+ const AnalyzeConsignmentProductEnrichmentResponseSchema = z.object({
58
+ data: z.object({
59
+ items: z.array(ProductEnrichmentItemSchema),
60
+ total: z.number(),
61
+ page: z.number(),
62
+ count: z.number(),
63
+ // Считается по всей поставке, а не по текущей странице: иначе счётчики прыгали бы при листании.
64
+ summary: ProductEnrichmentSummarySchema,
65
+ }),
66
+ });
67
+
68
+ export namespace AnalyzeConsignmentProductEnrichmentContractQuery {
69
+ export const RequestSchema = AnalyzeConsignmentProductEnrichmentRequestSchema;
70
+ export type Request = z.infer<typeof RequestSchema>;
71
+
72
+ export const ResponseSchema = AnalyzeConsignmentProductEnrichmentResponseSchema;
73
+ export type Response = z.infer<typeof ResponseSchema>;
74
+
75
+ export type Item = z.infer<typeof ProductEnrichmentItemSchema>;
76
+ export type FieldDiff = z.infer<typeof ProductEnrichmentFieldSchema>;
77
+ export type Summary = z.infer<typeof ProductEnrichmentSummarySchema>;
78
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ import { ErrorMessageSchema } from '../../schemas/error-message.schema';
3
+ import { CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS } from '../../constant/consignment-product-enrichment.constant';
4
+ import { ProductEnrichmentFieldEnum } from '../../enum/product-enrichment-field.enum';
5
+
6
+ const ExecuteConsignmentProductEnrichmentItemSchema = z.object({
7
+ productUUID: z.string().uuid(),
8
+ // Какие поля переносим из карточки в этот товар. Пустой список — товар пропускается.
9
+ fields: z.array(z.nativeEnum(ProductEnrichmentFieldEnum)).min(1),
10
+ });
11
+
12
+ const ExecuteConsignmentProductEnrichmentRequestSchema = z.object({
13
+ // Фронт шлёт пачками по EXECUTE_BATCH_SIZE и сам собирает итог: одна поставка может
14
+ // содержать до 1200 товаров, и одним запросом это отправлять не нужно.
15
+ // Повторы `productUUID` допустимы и схлопываются на сервере в объединение полей: товар
16
+ // обновляется и логируется один раз, сколько бы записей о нём ни пришло.
17
+ items: z.array(ExecuteConsignmentProductEnrichmentItemSchema).min(1).max(CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS.EXECUTE_BATCH_SIZE),
18
+ });
19
+
20
+ const ExecuteConsignmentProductEnrichmentResponseSchema = z.object({
21
+ data: z.object({
22
+ success: z.boolean(),
23
+ // Товаров, у которых изменилось хотя бы одно поле.
24
+ successCount: z.number(),
25
+ // Товаров, где менять было нечего: значение уже совпадало или карточка опустела с момента разбора.
26
+ skippedCount: z.number(),
27
+ // Товаров, которые кто-то изменил между разбором и применением. Отдельно от skipped, потому
28
+ // что это единственный исход, требующий действия: пользователю нужно перечитать разбор.
29
+ conflictedCount: z.number(),
30
+ failedCount: z.number(),
31
+ errors: z.array(
32
+ ErrorMessageSchema.extend({
33
+ uuid: z.string(),
34
+ }),
35
+ ),
36
+ }),
37
+ });
38
+
39
+ export namespace ExecuteConsignmentProductEnrichmentContractCommand {
40
+ export const RequestSchema = ExecuteConsignmentProductEnrichmentRequestSchema;
41
+ export type Request = z.infer<typeof RequestSchema>;
42
+
43
+ export const ResponseSchema = ExecuteConsignmentProductEnrichmentResponseSchema;
44
+ export type Response = z.infer<typeof ResponseSchema>;
45
+
46
+ export type Item = z.infer<typeof ExecuteConsignmentProductEnrichmentItemSchema>;
47
+ }
@@ -13,3 +13,5 @@ export * from './get-access-clients-consignment.query';
13
13
  export * from './get-consignment-filters-values.query';
14
14
  export * from './execute-consignment-product-update.command';
15
15
  export * from './determine-consignment-product-images.command';
16
+ export * from './analyze-consignment-product-enrichment.query';
17
+ export * from './execute-consignment-product-enrichment.command';
@@ -0,0 +1,9 @@
1
+ // Лимиты операции «получить характеристики из номенклатуры».
2
+ // Анализ читает всю поставку целиком (потолок задан лимитами импорта: MAX_ROWS_WITHOUT_IMAGES = 1200),
3
+ // но наружу отдаёт страницами, а применение принимает пачками — чтобы ни ответ, ни запрос не росли
4
+ // вместе с размером поставки.
5
+ export const CONSIGNMENT_PRODUCT_ENRICHMENT_LIMITS = {
6
+ DEFAULT_PAGE_SIZE: 50,
7
+ MAX_PAGE_SIZE: 200,
8
+ EXECUTE_BATCH_SIZE: 100,
9
+ } as const;
package/constant/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from './oksm';
9
9
  export * from './okei';
10
10
  export * from './contact-phone.constant';
11
11
  export * from './settings.constant';
12
+ export * from './consignment-product-enrichment.constant';
package/enum/index.ts CHANGED
@@ -106,3 +106,5 @@ export * from './transfer-document-state.enum';
106
106
  export * from './transfer-document-kind.enum';
107
107
  export * from './transfer-document-item-kind.enum';
108
108
  export * from './write-off-report-date-basis.enum';
109
+ export * from './product-enrichment-field.enum';
110
+ export * from './product-enrichment-field-state.enum';
@@ -0,0 +1,13 @@
1
+ // Состояние одного поля товара относительно привязанной карточки номенклатуры.
2
+ // Определяет и подсказку в интерфейсе («собственные данные» / «из номенклатуры»), и то,
3
+ // отмечен ли чекбокс по умолчанию: предзаполняется только FILLABLE.
4
+ export enum ProductEnrichmentFieldStateEnum {
5
+ // У товара пусто, в карточке значение есть — это и есть «дополнить».
6
+ FILLABLE = 'FILLABLE',
7
+ // Оба заполнены и различаются. Показываем, но по умолчанию не отмечаем.
8
+ CONFLICT = 'CONFLICT',
9
+ // Значения совпадают — брать нечего.
10
+ SAME = 'SAME',
11
+ // В карточке пусто — источника нет.
12
+ NO_SOURCE = 'NO_SOURCE',
13
+ }
@@ -0,0 +1,14 @@
1
+ // Поля товара, которые можно дополнить из привязанной карточки номенклатуры (ProductImage).
2
+ // Значения совпадают с именами полей Product и ProductImage — это ключи, по которым фронт
3
+ // собирает решения, поэтому переименование значения ломает контракт.
4
+ export enum ProductEnrichmentFieldEnum {
5
+ COLOR = 'color',
6
+ COUNTRY = 'country',
7
+ GROWER = 'grower',
8
+ }
9
+
10
+ const PRODUCT_ENRICHMENT_FIELD_KEYS = Object.keys(ProductEnrichmentFieldEnum) as Array<keyof typeof ProductEnrichmentFieldEnum>;
11
+
12
+ export const PRODUCT_ENRICHMENT_FIELD_LIST: readonly ProductEnrichmentFieldEnum[] = PRODUCT_ENRICHMENT_FIELD_KEYS.map(
13
+ key => ProductEnrichmentFieldEnum[key],
14
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floristcloud/api-lib",
3
- "version": "1.2.131",
3
+ "version": "1.2.133",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -1,12 +1,19 @@
1
1
  import { z } from 'zod';
2
2
  import { ActionLogMoneySchema } from './action-log-money.schema';
3
3
 
4
+ /**
5
+ * Снимок позиции предзаказа в журнале аудита.
6
+ *
7
+ * `name` и `price` — nullable: у позиции предзаказа наименование и цена появляются не сразу
8
+ * (в БД `pre_order_product.name` и `price` тоже nullable), и журнал фиксирует позицию такой,
9
+ * какой она была на момент события. Записи с `null` уже лежат в `action_log.payload`.
10
+ */
4
11
  export const PreOrderProductInfoSchema = z.object({
5
12
  preOrderUUID: z.string(),
6
13
  preOrderProductUUID: z.string(),
7
- name: z.string(),
14
+ name: z.string().nullable(),
8
15
  categoryName: z.string().nullable(),
9
16
  quantity: z.coerce.number(),
10
- price: ActionLogMoneySchema,
17
+ price: ActionLogMoneySchema.nullable(),
11
18
  comment: z.string().nullable(),
12
19
  });
@@ -5,6 +5,7 @@ import { ActionLogMoneySchema } from './action-log-money.schema';
5
5
  export const PreOrderProductUpdateActionLogSchema = z.object({
6
6
  preOrderProduct: PreOrderProductInfoSchema,
7
7
  newQuantity: z.coerce.number(),
8
- newPrice: ActionLogMoneySchema,
8
+ // Цену позиции предзаказа можно снять — новое значение бывает null, см. PreOrderProductInfoSchema.
9
+ newPrice: ActionLogMoneySchema.nullable(),
9
10
  newComment: z.string().nullable(),
10
11
  });
@@ -98,6 +98,23 @@ export const ProductExtendedForClientSchema = ProductForClientSchema.merge(
98
98
  isInStock: z.boolean(),
99
99
  }),
100
100
  multiplicityOptions: z.array(ProductMultiplicityOptionForClientSchema).optional(),
101
+ /**
102
+ * Правила клиента, снижающие **базовую цену товара**, — и только они.
103
+ *
104
+ * Правило, оставляющее цену как есть, в список не попадает: это не скидка, а «скидка» с
105
+ * той же ценой рядом. Так выходит у правила от себестоимости, когда «себестоимость +
106
+ * наценка» вышла выше прайса, и у скидки в 0 %. Отбор общий с панелью
107
+ * (`GetProductDiscountPriceContractQuery`) — правила отбираются одинаково.
108
+ *
109
+ * Список считается от базовой цены товара, поэтому пустой список **не означает**, что у
110
+ * клиента нет скидочных цен по этому товару вовсе: у кратности своя база, и правило может
111
+ * снижать цену фасовки, не снижая цену штуки. Цена каждой фасовки — в её собственных
112
+ * `optionPriceWithDiscount` и `isDiscountApplicable`, там результат уже посчитан от нужной
113
+ * базы. Пример: прайс 50, фасовка 80, «себестоимость + наценка» 60 — список пуст, а
114
+ * фасовка идёт по 60.
115
+ *
116
+ * Порядок не зафиксирован — лучшую цену базового тарифа берите из `priceWithBestDiscount`.
117
+ */
101
118
  discountDataList: z
102
119
  .object({
103
120
  discountRulesUUID: z.string(),