@floristcloud/api-lib 1.2.154 → 1.2.156

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.
Files changed (39) hide show
  1. package/build/commands/order/create-order.command.js +3 -0
  2. package/build/commands/order/get-order-list.query.js +2 -2
  3. package/build/commands/order/group/pin-group-order-planned-shipment-date.command.js +21 -0
  4. package/build/commands/order/index.js +1 -0
  5. package/build/commands/order/update-order.command.js +3 -0
  6. package/build/commands/order/webshop/update-cart.command.js +5 -0
  7. package/build/commands/pre-order-product/group/create-standing-order-positions.command.js +40 -0
  8. package/build/commands/pre-order-product/index.js +1 -0
  9. package/build/constant/error.js +8 -0
  10. package/build/constant/index.js +1 -0
  11. package/build/constant/standing-order-positions.constant.js +7 -0
  12. package/build/enum/index.js +1 -0
  13. package/build/enum/order-updated-field.enum.js +1 -0
  14. package/build/enum/standing-order-position-source.enum.js +9 -0
  15. package/build/schemas/calendar-day.schema.js +27 -0
  16. package/build/schemas/index.js +1 -0
  17. package/build/schemas/order/order.schema.js +4 -0
  18. package/build/schemas/order-attachment/order-attachment.schema.js +3 -2
  19. package/build/schemas/order-delivery/order-delivery.schema.js +5 -0
  20. package/commands/order/create-order.command.ts +4 -1
  21. package/commands/order/get-order-list.query.ts +2 -2
  22. package/commands/order/group/pin-group-order-planned-shipment-date.command.ts +23 -0
  23. package/commands/order/index.ts +1 -0
  24. package/commands/order/update-order.command.ts +4 -1
  25. package/commands/order/webshop/update-cart.command.ts +6 -1
  26. package/commands/pre-order-product/group/create-standing-order-positions.command.ts +46 -0
  27. package/commands/pre-order-product/index.ts +1 -0
  28. package/constant/error.ts +8 -0
  29. package/constant/index.ts +1 -0
  30. package/constant/standing-order-positions.constant.ts +4 -0
  31. package/enum/index.ts +1 -0
  32. package/enum/order-updated-field.enum.ts +1 -0
  33. package/enum/standing-order-position-source.enum.ts +5 -0
  34. package/package.json +1 -1
  35. package/schemas/calendar-day.schema.ts +26 -0
  36. package/schemas/index.ts +1 -0
  37. package/schemas/order/order.schema.ts +4 -0
  38. package/schemas/order-attachment/order-attachment.schema.ts +3 -2
  39. package/schemas/order-delivery/order-delivery.schema.ts +5 -0
@@ -14,6 +14,9 @@ const CreateOrderRequestSchema = zod_1.z.object({
14
14
  comment: zod_1.z.string().nullable(),
15
15
  clientComment: zod_1.z.string().nullable(),
16
16
  tag: zod_1.z.string().nullable(),
17
+ // Дата отгрузки/выдачи — одна для самовывоза и доставки. Приоритет над устаревшим
18
+ // `delivery.deliveryDate`.
19
+ plannedShipmentDate: schemas_1.CalendarDaySchema.nullable().optional(),
17
20
  delivery: schemas_1.OrderDeliverySchema.omit({ uuid: true }).nullable(),
18
21
  });
19
22
  const CreateOrderResponseSchema = zod_1.z.object({
@@ -24,8 +24,8 @@ const GetOrderListRequestSchema = zod_1.z
24
24
  * ссылок и MCP-клиентов: трактуется как период из одного дня (from = to = deliveryDate).
25
25
  */
26
26
  deliveryDate: zod_1.z.coerce.date().optional(),
27
- // Календарные даты, а не моменты: сутки разворачиваются на бэке по таймзоне компании
28
- // (`resolveLocalDayPeriod`), поэтому здесь достаточно дня, закодированного полночью UTC.
27
+ // Период даты отгрузки/выдачи заказа (`plannedShipmentDate`) — и доставка, и самовывоз; имена
28
+ // остались от фильтра по дате доставки. Календарные дни `YYYY-MM-DD`, обе границы включительно.
29
29
  deliveryDateFrom: zod_1.z.coerce.date().optional(),
30
30
  deliveryDateTo: zod_1.z.coerce.date().optional(),
31
31
  byProductSearchQuery: zod_1.z.string().optional(),
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PinGroupOrderPlannedShipmentDateContractCommand = void 0;
4
+ const zod_1 = require("zod");
5
+ // Подтянуть дату отгрузки/выдачи выбранных заказов к приходу товара в пути — например, после того как
6
+ // сдвинули дату прихода задержавшейся партии. Заказы целиком в наличии не меняются.
7
+ const PinGroupOrderPlannedShipmentDateRequestSchema = zod_1.z.object({
8
+ orderUUIDList: zod_1.z.array(zod_1.z.uuid()).min(1),
9
+ });
10
+ const PinGroupOrderPlannedShipmentDateResponseSchema = zod_1.z.object({
11
+ message: zod_1.z.string().optional(),
12
+ data: zod_1.z.object({
13
+ // Сколько заказов получили новую дату.
14
+ updatedCount: zod_1.z.number(),
15
+ }),
16
+ });
17
+ var PinGroupOrderPlannedShipmentDateContractCommand;
18
+ (function (PinGroupOrderPlannedShipmentDateContractCommand) {
19
+ PinGroupOrderPlannedShipmentDateContractCommand.RequestSchema = PinGroupOrderPlannedShipmentDateRequestSchema;
20
+ PinGroupOrderPlannedShipmentDateContractCommand.ResponseSchema = PinGroupOrderPlannedShipmentDateResponseSchema;
21
+ })(PinGroupOrderPlannedShipmentDateContractCommand || (exports.PinGroupOrderPlannedShipmentDateContractCommand = PinGroupOrderPlannedShipmentDateContractCommand = {}));
@@ -34,6 +34,7 @@ __exportStar(require("./group/split-group-order.command"), exports);
34
34
  __exportStar(require("./group/merge-orders.command"), exports);
35
35
  __exportStar(require("./group/get-group-order-invoice-info.query"), exports);
36
36
  __exportStar(require("./group/send-group-order-invoice.command"), exports);
37
+ __exportStar(require("./group/pin-group-order-planned-shipment-date.command"), exports);
37
38
  //webshop
38
39
  __exportStar(require("./webshop/get-order-for-client.query"), exports);
39
40
  __exportStar(require("./webshop/create-cart.command"), exports);
@@ -19,6 +19,9 @@ const UpdateOrderRequestSchema = zod_1.z.object({
19
19
  deliveryVatAmount: money_schema_1.MoneySchema,
20
20
  completedAt: zod_1.z.coerce.date().nullable().optional(),
21
21
  paymentDueAt: zod_1.z.coerce.date().nullable().optional(),
22
+ // Дата отгрузки/выдачи — одна для самовывоза и доставки. Не раньше прихода товара в пути
23
+ // (O061, в errorData — самый ранний день). Не передано — не меняется, null — снять.
24
+ plannedShipmentDate: schemas_1.CalendarDaySchema.nullable().optional(),
22
25
  });
23
26
  const UpdateOrderResponseSchema = zod_1.z.object({
24
27
  message: zod_1.z.string().optional(),
@@ -6,8 +6,13 @@ const schemas_1 = require("../../../schemas");
6
6
  const UpdateCartRequestSchema = zod_1.z.object({
7
7
  clientComment: zod_1.z.string().nullable().optional().default(null),
8
8
  branchUUID: zod_1.z.uuid().optional().nullable().default(null),
9
+ // Дата выдачи/доставки заказа — одна для самовывоза и доставки. Относится к товару в наличии:
10
+ // заказы с товаром в пути после оформления получают дату прихода. Имеет приоритет над
11
+ // устаревшим `delivery.deliveryDate`.
12
+ plannedShipmentDate: schemas_1.CalendarDaySchema.nullable().optional(),
9
13
  delivery: zod_1.z
10
14
  .object({
15
+ /** @deprecated Используйте `plannedShipmentDate` — единую дату для самовывоза и доставки. */
11
16
  deliveryDate: zod_1.z.coerce.date().optional().nullable().default(null),
12
17
  address: zod_1.z.string().nullable().optional().default(null),
13
18
  time: zod_1.z.string().nullable().optional().default(null),
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CreateStandingOrderPositionsContractCommand = void 0;
4
+ const zod_1 = require("zod");
5
+ const schemas_1 = require("../../../schemas");
6
+ const standing_order_position_source_enum_1 = require("../../../enum/standing-order-position-source.enum");
7
+ const standing_order_positions_constant_1 = require("../../../constant/standing-order-positions.constant");
8
+ // size: явное значение перекрывает источник (null — без размера); если поле не передано,
9
+ // размер источника переносится только для категорий с isSizeSeparate.
10
+ const StandingOrderPositionBaseSchema = zod_1.z.object({
11
+ quantity: zod_1.z.number().int().positive(),
12
+ size: zod_1.z.string().nullable().optional(),
13
+ });
14
+ const StandingOrderPositionSchema = zod_1.z.discriminatedUnion('source', [
15
+ StandingOrderPositionBaseSchema.extend({
16
+ source: zod_1.z.literal(standing_order_position_source_enum_1.StandingOrderPositionSourceEnum.PRE_ORDER_COLLECTION_ITEM),
17
+ preOrderCollectionItemUUID: zod_1.z.uuid(),
18
+ }),
19
+ StandingOrderPositionBaseSchema.extend({
20
+ source: zod_1.z.literal(standing_order_position_source_enum_1.StandingOrderPositionSourceEnum.PRODUCT),
21
+ productUUID: zod_1.z.uuid(),
22
+ }),
23
+ StandingOrderPositionBaseSchema.extend({
24
+ source: zod_1.z.literal(standing_order_position_source_enum_1.StandingOrderPositionSourceEnum.PRODUCT_IMAGE),
25
+ productImageUUID: zod_1.z.uuid(),
26
+ }),
27
+ ]);
28
+ const CreateStandingOrderPositionsRequestSchema = zod_1.z.object({
29
+ positions: zod_1.z.array(StandingOrderPositionSchema).min(1).max(standing_order_positions_constant_1.STANDING_ORDER_POSITIONS_LIMITS.MAX_POSITIONS_PER_REQUEST),
30
+ });
31
+ const CreateStandingOrderPositionsResponseSchema = zod_1.z.object({
32
+ message: zod_1.z.string().optional(),
33
+ data: zod_1.z.array(schemas_1.PreOrderProductSchema),
34
+ });
35
+ var CreateStandingOrderPositionsContractCommand;
36
+ (function (CreateStandingOrderPositionsContractCommand) {
37
+ CreateStandingOrderPositionsContractCommand.PositionSchema = StandingOrderPositionSchema;
38
+ CreateStandingOrderPositionsContractCommand.RequestSchema = CreateStandingOrderPositionsRequestSchema;
39
+ CreateStandingOrderPositionsContractCommand.ResponseSchema = CreateStandingOrderPositionsResponseSchema;
40
+ })(CreateStandingOrderPositionsContractCommand || (exports.CreateStandingOrderPositionsContractCommand = CreateStandingOrderPositionsContractCommand = {}));
@@ -32,3 +32,4 @@ __exportStar(require("./create-manually-pre-order-product-for-standing-order.com
32
32
  __exportStar(require("./get-pre-order-products-by-pre-order-list.command"), exports);
33
33
  __exportStar(require("./set-pre-order-product-quantity-webshop.command"), exports);
34
34
  __exportStar(require("./group/move-group-pre-order-product.command"), exports);
35
+ __exportStar(require("./group/create-standing-order-positions.command"), exports);
@@ -429,6 +429,14 @@ exports.ERRORS = {
429
429
  ORDER_MERGE_HAS_DISTRIBUTION: { code: 'O032', message: 'Order has products with distribution', httpCode: 400 },
430
430
  ORDER_MERGE_HAS_PAYMENT_INVOICE: { code: 'O040', message: 'Order is included in a payment invoice', httpCode: 400 },
431
431
  ORDER_MERGE_HAS_TRANSFER_DOCUMENT: { code: 'O060', message: 'Order is included in a transfer document', httpCode: 400 },
432
+ // Дата отгрузки/выдачи раньше прихода товара на склад. В errorData — самый ранний допустимый день
433
+ // (`YYYY-MM-DD`), чтобы UI мог его подсказать, а не отправлял угадывать.
434
+ ORDER_PLANNED_SHIPMENT_DATE_BEFORE_ARRIVAL: {
435
+ code: 'O061',
436
+ message: 'Planned shipment date cannot be earlier than the arrival of the goods',
437
+ httpCode: 400,
438
+ },
439
+ ORDER_PLANNED_SHIPMENT_DATE_NOT_UPDATED: { code: 'O062', message: 'Failed to update order planned shipment date', httpCode: 500 },
432
440
  ORDER_MERGE_BALANCE_STATUS_NOT_ALLOWED: {
433
441
  code: 'O033',
434
442
  message: 'Order status is balance affecting, merge not allowed',
@@ -29,3 +29,4 @@ __exportStar(require("./consignment-product-enrichment.constant"), exports);
29
29
  __exportStar(require("./attribute-normalization.constant"), exports);
30
30
  __exportStar(require("./product-image-merge.constant"), exports);
31
31
  __exportStar(require("./client-group-actions.constant"), exports);
32
+ __exportStar(require("./standing-order-positions.constant"), exports);
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STANDING_ORDER_POSITIONS_LIMITS = void 0;
4
+ exports.STANDING_ORDER_POSITIONS_LIMITS = {
5
+ // Все позиции пакета создаются в одной транзакции: предел держит её короткой.
6
+ MAX_POSITIONS_PER_REQUEST: 200,
7
+ };
@@ -144,3 +144,4 @@ __exportStar(require("./client-updated-field.enum"), exports);
144
144
  __exportStar(require("./collection-item-updated-field.enum"), exports);
145
145
  __exportStar(require("./action-log-operation.enum"), exports);
146
146
  __exportStar(require("./collection-item-multiplicity-option-operation.enum"), exports);
147
+ __exportStar(require("./standing-order-position-source.enum"), exports);
@@ -20,4 +20,5 @@ var OrderUpdatedFieldEnum;
20
20
  OrderUpdatedFieldEnum["IS_PAID"] = "IS_PAID";
21
21
  OrderUpdatedFieldEnum["COMPLETED_AT"] = "COMPLETED_AT";
22
22
  OrderUpdatedFieldEnum["PAYMENT_DUE_AT"] = "PAYMENT_DUE_AT";
23
+ OrderUpdatedFieldEnum["PLANNED_SHIPMENT_DATE"] = "PLANNED_SHIPMENT_DATE";
23
24
  })(OrderUpdatedFieldEnum || (exports.OrderUpdatedFieldEnum = OrderUpdatedFieldEnum = {}));
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StandingOrderPositionSourceEnum = void 0;
4
+ var StandingOrderPositionSourceEnum;
5
+ (function (StandingOrderPositionSourceEnum) {
6
+ StandingOrderPositionSourceEnum["PRE_ORDER_COLLECTION_ITEM"] = "PRE_ORDER_COLLECTION_ITEM";
7
+ StandingOrderPositionSourceEnum["PRODUCT"] = "PRODUCT";
8
+ StandingOrderPositionSourceEnum["PRODUCT_IMAGE"] = "PRODUCT_IMAGE";
9
+ })(StandingOrderPositionSourceEnum || (exports.StandingOrderPositionSourceEnum = StandingOrderPositionSourceEnum = {}));
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CalendarDaySchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * Единственное описание календарного дня в контрактах — даты без времени и таймзоны.
7
+ *
8
+ * День пересекает границу строкой `YYYY-MM-DD` в обе стороны. Не `Date` и не ISO-момент: момент
9
+ * требует таймзоны, чтобы из него вернуть день, а у клиента (браузер, устройство) она своя и
10
+ * с таймзоной компании не обязана совпадать — `2026-09-24T21:00:00Z` это 25-е в Москве и 24-е в
11
+ * Лондоне. Строка дня однозначна везде.
12
+ *
13
+ * Сервер хранит такие поля колонкой `DATE`; в JS они живут как полночь UTC, читать и писать их —
14
+ * только через `calendar-day.helper.ts` на бэке.
15
+ *
16
+ * Модификаторы `.optional()` и `.nullable()` работают напрямую.
17
+ */
18
+ exports.CalendarDaySchema = zod_1.z
19
+ .string()
20
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
21
+ .refine(isExistingCalendarDay, { message: 'Invalid calendar day' });
22
+ // Регэксп пропускает `2026-02-30`: такой день парсер либо отвергает, либо переносит на март —
23
+ // в обоих случаях строка обратно не совпадёт.
24
+ function isExistingCalendarDay(value) {
25
+ const date = new Date(`${value}T00:00:00.000Z`);
26
+ return !Number.isNaN(date.getTime()) && date.toISOString().startsWith(value);
27
+ }
@@ -106,6 +106,7 @@ __exportStar(require("./account-deletion-request/account-deletion-request.schema
106
106
  __exportStar(require("./consignment-transfer"), exports);
107
107
  __exportStar(require("./api-error.schema"), exports);
108
108
  __exportStar(require("./access-policy"), exports);
109
+ __exportStar(require("./calendar-day.schema"), exports);
109
110
  __exportStar(require("./money.schema"), exports);
110
111
  __exportStar(require("./non-negative-money.schema"), exports);
111
112
  __exportStar(require("./positive-money.schema"), exports);
@@ -8,6 +8,7 @@ const branch_schema_1 = require("../branch/branch.schema");
8
8
  const order_delivery_schema_1 = require("../order-delivery/order-delivery.schema");
9
9
  const delivery_route_schema_1 = require("../delivery-route/delivery-route.schema");
10
10
  const money_schema_1 = require("../money.schema");
11
+ const calendar_day_schema_1 = require("../calendar-day.schema");
11
12
  exports.OrderSchema = zod_1.z.object({
12
13
  uuid: zod_1.z.uuid(),
13
14
  orderNumber: zod_1.z.number(),
@@ -42,6 +43,9 @@ exports.OrderSchema = zod_1.z.object({
42
43
  transferDocumentUUID: zod_1.z.uuid().nullable(),
43
44
  completedAt: zod_1.z.date().nullable(),
44
45
  paymentDueAt: zod_1.z.date().nullable(),
46
+ // План отгрузки/выдачи — одна дата для самовывоза и доставки, календарный день `YYYY-MM-DD`.
47
+ // Не путать с `completedAt` (факт отгрузки) и датой прихода партии.
48
+ plannedShipmentDate: calendar_day_schema_1.CalendarDaySchema.nullable(),
45
49
  createdAt: zod_1.z.date(),
46
50
  updatedAt: zod_1.z.date(),
47
51
  deletedAt: zod_1.z.date().nullable(),
@@ -4,8 +4,9 @@ exports.DeleteOrderAttachmentResponseSchema = exports.GetOrderAttachmentListResp
4
4
  const zod_1 = require("zod");
5
5
  const order_attachment_type_enum_1 = require("../../enum/order-attachment-type.enum");
6
6
  /**
7
- * Ссылка на файл в бакете (для FILE её отдаёт `PUT /files/upload-file`) или внешний адрес (для LINK).
8
- * Схему не сужаем до домена CDN: в LINK кладут ссылки на сторонние сервисы доставки.
7
+ * Ссылка на файл в бакете (для FILE её отдаёт загрузка в file-api, `PUT /api/v2/files/upload`) или
8
+ * внешний адрес (для LINK). Схему не сужаем до домена CDN: в LINK кладут ссылки на сторонние
9
+ * сервисы доставки.
9
10
  *
10
11
  * Протокол только http/https: `z.url()` сам по себе принимает и `javascript:`, и `data:text/html`,
11
12
  * а вложение с включённой видимостью витрина отдаёт клиенту и рисует ссылкой — то есть сотрудник
@@ -6,6 +6,11 @@ const enum_1 = require("../../enum");
6
6
  exports.OrderDeliverySchema = zod_1.z.object({
7
7
  uuid: zod_1.z.uuid(),
8
8
  city: zod_1.z.string().nullable(),
9
+ /**
10
+ * @deprecated Дата доставки живёт в заказе: `Order.plannedShipmentDate` (`YYYY-MM-DD`) — одна для
11
+ * самовывоза и доставки. Поле принимается и отдаётся ради совместимости: запись перекладывается
12
+ * в заказ, чтение возвращает дату заказа.
13
+ */
9
14
  deliveryDate: zod_1.z.coerce.date().nullable(),
10
15
  address: zod_1.z.string().nullable(),
11
16
  time: zod_1.z.string().nullable(),
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { OrderTypeEnum } from '../../enum';
3
- import { OrderDeliverySchema, OrderSchema } from '../../schemas';
3
+ import { CalendarDaySchema, OrderDeliverySchema, OrderSchema } from '../../schemas';
4
4
  import { MoneySchema } from '../../schemas/money.schema';
5
5
 
6
6
  const CreateOrderRequestSchema = z.object({
@@ -12,6 +12,9 @@ const CreateOrderRequestSchema = z.object({
12
12
  comment: z.string().nullable(),
13
13
  clientComment: z.string().nullable(),
14
14
  tag: z.string().nullable(),
15
+ // Дата отгрузки/выдачи — одна для самовывоза и доставки. Приоритет над устаревшим
16
+ // `delivery.deliveryDate`.
17
+ plannedShipmentDate: CalendarDaySchema.nullable().optional(),
15
18
  delivery: OrderDeliverySchema.omit({ uuid: true }).nullable(),
16
19
  });
17
20
 
@@ -22,8 +22,8 @@ const GetOrderListRequestSchema = z
22
22
  * ссылок и MCP-клиентов: трактуется как период из одного дня (from = to = deliveryDate).
23
23
  */
24
24
  deliveryDate: z.coerce.date().optional(),
25
- // Календарные даты, а не моменты: сутки разворачиваются на бэке по таймзоне компании
26
- // (`resolveLocalDayPeriod`), поэтому здесь достаточно дня, закодированного полночью UTC.
25
+ // Период даты отгрузки/выдачи заказа (`plannedShipmentDate`) — и доставка, и самовывоз; имена
26
+ // остались от фильтра по дате доставки. Календарные дни `YYYY-MM-DD`, обе границы включительно.
27
27
  deliveryDateFrom: z.coerce.date().optional(),
28
28
  deliveryDateTo: z.coerce.date().optional(),
29
29
  byProductSearchQuery: z.string().optional(),
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod';
2
+
3
+ // Подтянуть дату отгрузки/выдачи выбранных заказов к приходу товара в пути — например, после того как
4
+ // сдвинули дату прихода задержавшейся партии. Заказы целиком в наличии не меняются.
5
+ const PinGroupOrderPlannedShipmentDateRequestSchema = z.object({
6
+ orderUUIDList: z.array(z.uuid()).min(1),
7
+ });
8
+
9
+ const PinGroupOrderPlannedShipmentDateResponseSchema = z.object({
10
+ message: z.string().optional(),
11
+ data: z.object({
12
+ // Сколько заказов получили новую дату.
13
+ updatedCount: z.number(),
14
+ }),
15
+ });
16
+
17
+ export namespace PinGroupOrderPlannedShipmentDateContractCommand {
18
+ export const RequestSchema = PinGroupOrderPlannedShipmentDateRequestSchema;
19
+ export type Request = z.infer<typeof RequestSchema>;
20
+
21
+ export const ResponseSchema = PinGroupOrderPlannedShipmentDateResponseSchema;
22
+ export type Response = z.infer<typeof ResponseSchema>;
23
+ }
@@ -18,6 +18,7 @@ export * from './group/split-group-order.command';
18
18
  export * from './group/merge-orders.command';
19
19
  export * from './group/get-group-order-invoice-info.query';
20
20
  export * from './group/send-group-order-invoice.command';
21
+ export * from './group/pin-group-order-planned-shipment-date.command';
21
22
  //webshop
22
23
  export * from './webshop/get-order-for-client.query';
23
24
  export * from './webshop/create-cart.command';
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { OrderStatusEnum, OrderTypeEnum } from '../../enum';
3
- import { OrderSchema } from '../../schemas';
3
+ import { CalendarDaySchema, OrderSchema } from '../../schemas';
4
4
  import { MoneySchema } from '../../schemas/money.schema';
5
5
 
6
6
  const UpdateOrderRequestSchema = z.object({
@@ -17,6 +17,9 @@ const UpdateOrderRequestSchema = z.object({
17
17
  deliveryVatAmount: MoneySchema,
18
18
  completedAt: z.coerce.date().nullable().optional(),
19
19
  paymentDueAt: z.coerce.date().nullable().optional(),
20
+ // Дата отгрузки/выдачи — одна для самовывоза и доставки. Не раньше прихода товара в пути
21
+ // (O061, в errorData — самый ранний день). Не передано — не меняется, null — снять.
22
+ plannedShipmentDate: CalendarDaySchema.nullable().optional(),
20
23
  });
21
24
 
22
25
  const UpdateOrderResponseSchema = z.object({
@@ -1,11 +1,16 @@
1
1
  import { z } from 'zod';
2
- import { OrderForClientSchema } from '../../../schemas';
2
+ import { CalendarDaySchema, OrderForClientSchema } from '../../../schemas';
3
3
 
4
4
  const UpdateCartRequestSchema = z.object({
5
5
  clientComment: z.string().nullable().optional().default(null),
6
6
  branchUUID: z.uuid().optional().nullable().default(null),
7
+ // Дата выдачи/доставки заказа — одна для самовывоза и доставки. Относится к товару в наличии:
8
+ // заказы с товаром в пути после оформления получают дату прихода. Имеет приоритет над
9
+ // устаревшим `delivery.deliveryDate`.
10
+ plannedShipmentDate: CalendarDaySchema.nullable().optional(),
7
11
  delivery: z
8
12
  .object({
13
+ /** @deprecated Используйте `plannedShipmentDate` — единую дату для самовывоза и доставки. */
9
14
  deliveryDate: z.coerce.date().optional().nullable().default(null),
10
15
  address: z.string().nullable().optional().default(null),
11
16
  time: z.string().nullable().optional().default(null),
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod';
2
+ import { PreOrderProductSchema } from '../../../schemas';
3
+ import { StandingOrderPositionSourceEnum } from '../../../enum/standing-order-position-source.enum';
4
+ import { STANDING_ORDER_POSITIONS_LIMITS } from '../../../constant/standing-order-positions.constant';
5
+
6
+ // size: явное значение перекрывает источник (null — без размера); если поле не передано,
7
+ // размер источника переносится только для категорий с isSizeSeparate.
8
+ const StandingOrderPositionBaseSchema = z.object({
9
+ quantity: z.number().int().positive(),
10
+ size: z.string().nullable().optional(),
11
+ });
12
+
13
+ const StandingOrderPositionSchema = z.discriminatedUnion('source', [
14
+ StandingOrderPositionBaseSchema.extend({
15
+ source: z.literal(StandingOrderPositionSourceEnum.PRE_ORDER_COLLECTION_ITEM),
16
+ preOrderCollectionItemUUID: z.uuid(),
17
+ }),
18
+ StandingOrderPositionBaseSchema.extend({
19
+ source: z.literal(StandingOrderPositionSourceEnum.PRODUCT),
20
+ productUUID: z.uuid(),
21
+ }),
22
+ StandingOrderPositionBaseSchema.extend({
23
+ source: z.literal(StandingOrderPositionSourceEnum.PRODUCT_IMAGE),
24
+ productImageUUID: z.uuid(),
25
+ }),
26
+ ]);
27
+
28
+ const CreateStandingOrderPositionsRequestSchema = z.object({
29
+ positions: z.array(StandingOrderPositionSchema).min(1).max(STANDING_ORDER_POSITIONS_LIMITS.MAX_POSITIONS_PER_REQUEST),
30
+ });
31
+
32
+ const CreateStandingOrderPositionsResponseSchema = z.object({
33
+ message: z.string().optional(),
34
+ data: z.array(PreOrderProductSchema),
35
+ });
36
+
37
+ export namespace CreateStandingOrderPositionsContractCommand {
38
+ export const PositionSchema = StandingOrderPositionSchema;
39
+ export type Position = z.infer<typeof PositionSchema>;
40
+
41
+ export const RequestSchema = CreateStandingOrderPositionsRequestSchema;
42
+ export type Request = z.infer<typeof RequestSchema>;
43
+
44
+ export const ResponseSchema = CreateStandingOrderPositionsResponseSchema;
45
+ export type Response = z.infer<typeof ResponseSchema>;
46
+ }
@@ -16,3 +16,4 @@ export * from './create-manually-pre-order-product-for-standing-order.command';
16
16
  export * from './get-pre-order-products-by-pre-order-list.command';
17
17
  export * from './set-pre-order-product-quantity-webshop.command';
18
18
  export * from './group/move-group-pre-order-product.command';
19
+ export * from './group/create-standing-order-positions.command';
package/constant/error.ts CHANGED
@@ -443,6 +443,14 @@ export const ERRORS = {
443
443
  ORDER_MERGE_HAS_DISTRIBUTION: { code: 'O032', message: 'Order has products with distribution', httpCode: 400 },
444
444
  ORDER_MERGE_HAS_PAYMENT_INVOICE: { code: 'O040', message: 'Order is included in a payment invoice', httpCode: 400 },
445
445
  ORDER_MERGE_HAS_TRANSFER_DOCUMENT: { code: 'O060', message: 'Order is included in a transfer document', httpCode: 400 },
446
+ // Дата отгрузки/выдачи раньше прихода товара на склад. В errorData — самый ранний допустимый день
447
+ // (`YYYY-MM-DD`), чтобы UI мог его подсказать, а не отправлял угадывать.
448
+ ORDER_PLANNED_SHIPMENT_DATE_BEFORE_ARRIVAL: {
449
+ code: 'O061',
450
+ message: 'Planned shipment date cannot be earlier than the arrival of the goods',
451
+ httpCode: 400,
452
+ },
453
+ ORDER_PLANNED_SHIPMENT_DATE_NOT_UPDATED: { code: 'O062', message: 'Failed to update order planned shipment date', httpCode: 500 },
446
454
  ORDER_MERGE_BALANCE_STATUS_NOT_ALLOWED: {
447
455
  code: 'O033',
448
456
  message: 'Order status is balance affecting, merge not allowed',
package/constant/index.ts CHANGED
@@ -13,3 +13,4 @@ export * from './consignment-product-enrichment.constant';
13
13
  export * from './attribute-normalization.constant';
14
14
  export * from './product-image-merge.constant';
15
15
  export * from './client-group-actions.constant';
16
+ export * from './standing-order-positions.constant';
@@ -0,0 +1,4 @@
1
+ export const STANDING_ORDER_POSITIONS_LIMITS = {
2
+ // Все позиции пакета создаются в одной транзакции: предел держит её короткой.
3
+ MAX_POSITIONS_PER_REQUEST: 200,
4
+ } as const;
package/enum/index.ts CHANGED
@@ -128,3 +128,4 @@ export * from './client-updated-field.enum';
128
128
  export * from './collection-item-updated-field.enum';
129
129
  export * from './action-log-operation.enum';
130
130
  export * from './collection-item-multiplicity-option-operation.enum';
131
+ export * from './standing-order-position-source.enum';
@@ -16,4 +16,5 @@ export enum OrderUpdatedFieldEnum {
16
16
  IS_PAID = 'IS_PAID',
17
17
  COMPLETED_AT = 'COMPLETED_AT',
18
18
  PAYMENT_DUE_AT = 'PAYMENT_DUE_AT',
19
+ PLANNED_SHIPMENT_DATE = 'PLANNED_SHIPMENT_DATE',
19
20
  }
@@ -0,0 +1,5 @@
1
+ export enum StandingOrderPositionSourceEnum {
2
+ PRE_ORDER_COLLECTION_ITEM = 'PRE_ORDER_COLLECTION_ITEM',
3
+ PRODUCT = 'PRODUCT',
4
+ PRODUCT_IMAGE = 'PRODUCT_IMAGE',
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floristcloud/api-lib",
3
- "version": "1.2.154",
3
+ "version": "1.2.156",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Единственное описание календарного дня в контрактах — даты без времени и таймзоны.
5
+ *
6
+ * День пересекает границу строкой `YYYY-MM-DD` в обе стороны. Не `Date` и не ISO-момент: момент
7
+ * требует таймзоны, чтобы из него вернуть день, а у клиента (браузер, устройство) она своя и
8
+ * с таймзоной компании не обязана совпадать — `2026-09-24T21:00:00Z` это 25-е в Москве и 24-е в
9
+ * Лондоне. Строка дня однозначна везде.
10
+ *
11
+ * Сервер хранит такие поля колонкой `DATE`; в JS они живут как полночь UTC, читать и писать их —
12
+ * только через `calendar-day.helper.ts` на бэке.
13
+ *
14
+ * Модификаторы `.optional()` и `.nullable()` работают напрямую.
15
+ */
16
+ export const CalendarDaySchema = z
17
+ .string()
18
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
19
+ .refine(isExistingCalendarDay, { message: 'Invalid calendar day' });
20
+
21
+ // Регэксп пропускает `2026-02-30`: такой день парсер либо отвергает, либо переносит на март —
22
+ // в обоих случаях строка обратно не совпадёт.
23
+ function isExistingCalendarDay(value: string): boolean {
24
+ const date = new Date(`${value}T00:00:00.000Z`);
25
+ return !Number.isNaN(date.getTime()) && date.toISOString().startsWith(value);
26
+ }
package/schemas/index.ts CHANGED
@@ -90,6 +90,7 @@ export * from './account-deletion-request/account-deletion-request.schema';
90
90
  export * from './consignment-transfer';
91
91
  export * from './api-error.schema';
92
92
  export * from './access-policy';
93
+ export * from './calendar-day.schema';
93
94
  export * from './money.schema';
94
95
  export * from './non-negative-money.schema';
95
96
  export * from './positive-money.schema';
@@ -5,6 +5,7 @@ import { BranchSchema } from '../branch/branch.schema';
5
5
  import { OrderDeliverySchema } from '../order-delivery/order-delivery.schema';
6
6
  import { DeliveryRouteSchema } from '../delivery-route/delivery-route.schema';
7
7
  import { MoneySchema } from '../money.schema';
8
+ import { CalendarDaySchema } from '../calendar-day.schema';
8
9
 
9
10
  export const OrderSchema = z.object({
10
11
  uuid: z.uuid(),
@@ -45,6 +46,9 @@ export const OrderSchema = z.object({
45
46
 
46
47
  completedAt: z.date().nullable(),
47
48
  paymentDueAt: z.date().nullable(),
49
+ // План отгрузки/выдачи — одна дата для самовывоза и доставки, календарный день `YYYY-MM-DD`.
50
+ // Не путать с `completedAt` (факт отгрузки) и датой прихода партии.
51
+ plannedShipmentDate: CalendarDaySchema.nullable(),
48
52
 
49
53
  createdAt: z.date(),
50
54
  updatedAt: z.date(),
@@ -2,8 +2,9 @@ import { z } from 'zod';
2
2
  import { OrderAttachmentTypeEnum } from '../../enum/order-attachment-type.enum';
3
3
 
4
4
  /**
5
- * Ссылка на файл в бакете (для FILE её отдаёт `PUT /files/upload-file`) или внешний адрес (для LINK).
6
- * Схему не сужаем до домена CDN: в LINK кладут ссылки на сторонние сервисы доставки.
5
+ * Ссылка на файл в бакете (для FILE её отдаёт загрузка в file-api, `PUT /api/v2/files/upload`) или
6
+ * внешний адрес (для LINK). Схему не сужаем до домена CDN: в LINK кладут ссылки на сторонние
7
+ * сервисы доставки.
7
8
  *
8
9
  * Протокол только http/https: `z.url()` сам по себе принимает и `javascript:`, и `data:text/html`,
9
10
  * а вложение с включённой видимостью витрина отдаёт клиенту и рисует ссылкой — то есть сотрудник
@@ -4,6 +4,11 @@ import { OrderDeliveryStatusEnum } from '../../enum';
4
4
  export const OrderDeliverySchema = z.object({
5
5
  uuid: z.uuid(),
6
6
  city: z.string().nullable(),
7
+ /**
8
+ * @deprecated Дата доставки живёт в заказе: `Order.plannedShipmentDate` (`YYYY-MM-DD`) — одна для
9
+ * самовывоза и доставки. Поле принимается и отдаётся ради совместимости: запись перекладывается
10
+ * в заказ, чтение возвращает дату заказа.
11
+ */
7
12
  deliveryDate: z.coerce.date().nullable(),
8
13
  address: z.string().nullable(),
9
14
  time: z.string().nullable(),