@floristcloud/api-lib 1.2.103 → 1.2.105

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.
@@ -25,6 +25,9 @@ const GetOrderListRequestSchema = zod_1.z
25
25
  city: zod_1.z.string().optional(),
26
26
  responsibleUserUUID: zod_1.z.string().optional(),
27
27
  group: zod_1.z.string().optional(),
28
+ // Только заказы, ещё не включённые ни в один счёт на оплату — список для пикера при
29
+ // выставлении счёта. Комбинируется с остальными критериями обычным AND.
30
+ withoutPaymentInvoice: zod_1.z.coerce.boolean().optional(),
28
31
  })
29
32
  .refine(value => {
30
33
  const { startDate, endDate } = value;
@@ -3,10 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CreatePaymentInvoiceContractCommand = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const payment_invoice_schema_1 = require("../../schemas/payment-invoice/payment-invoice.schema");
6
- const CreatePaymentInvoiceRequestSchema = zod_1.z.object({
6
+ // Потолок числа заказов в счёте. Это защита от абсурдного запроса, а не бизнес-правило: счёт за месяц
7
+ // клиенту с ежедневными поставками — это уже 30 строк, поэтому 10-20 отрезали бы живые сценарии.
8
+ // Сто строк — это 3-4 страницы таблицы, документ такого размера собирается и читается нормально.
9
+ const MAX_INVOICE_ORDER_COUNT = 100;
10
+ // Счёт выставляется одним из двух способов:
11
+ // - по заказам: orderUUIDs непустой, итог считается на бэке как сумма totalCost этих заказов,
12
+ // а amount — необязательная правка итога (например скидка по счёту), она уходит отдельной строкой;
13
+ // - вручную: orderUUIDs пуст, тогда amount обязателен и становится единственной строкой счёта.
14
+ const CreatePaymentInvoiceRequestSchema = zod_1.z
15
+ .object({
7
16
  clientUUID: zod_1.z.uuid(),
8
17
  invoiceDate: zod_1.z.coerce.date(),
9
- amount: zod_1.z.coerce.number().positive(),
18
+ orderUUIDs: zod_1.z.uuid().array().max(MAX_INVOICE_ORDER_COUNT).optional(),
19
+ amount: zod_1.z.coerce.number().positive().optional(),
20
+ })
21
+ .refine(value => { var _a; return Boolean((_a = value.orderUUIDs) === null || _a === void 0 ? void 0 : _a.length) || value.amount !== undefined; }, {
22
+ message: 'Either orderUUIDs or amount must be provided',
23
+ path: ['amount'],
10
24
  });
11
25
  const CreatePaymentInvoiceResponseSchema = zod_1.z.object({
12
26
  message: zod_1.z.string().optional(),
@@ -18,3 +18,4 @@ __exportStar(require("./create-payment-invoice.command"), exports);
18
18
  __exportStar(require("./get-payment-invoice-list.query"), exports);
19
19
  __exportStar(require("./get-payment-invoice.query"), exports);
20
20
  __exportStar(require("./delete-payment-invoice.command"), exports);
21
+ __exportStar(require("./pay-payment-invoice.command"), exports);
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PayPaymentInvoiceContractCommand = void 0;
4
+ const zod_1 = require("zod");
5
+ const payment_invoice_schema_1 = require("../../schemas/payment-invoice/payment-invoice.schema");
6
+ // Оплата по счёту. amount не передан — гасим остаток целиком; передан — частичная оплата.
7
+ const PayPaymentInvoiceRequestSchema = zod_1.z.object({
8
+ moneyAccountUUID: zod_1.z.uuid(),
9
+ amount: zod_1.z.coerce.number().positive().optional(),
10
+ comment: zod_1.z.string().optional(),
11
+ cashFlowItemUUID: zod_1.z.uuid().nullable().optional(),
12
+ createdAt: zod_1.z.coerce.date().optional(),
13
+ });
14
+ const PayPaymentInvoiceResponseSchema = zod_1.z.object({
15
+ message: zod_1.z.string().optional(),
16
+ data: payment_invoice_schema_1.PaymentInvoiceSchema,
17
+ });
18
+ var PayPaymentInvoiceContractCommand;
19
+ (function (PayPaymentInvoiceContractCommand) {
20
+ PayPaymentInvoiceContractCommand.RequestSchema = PayPaymentInvoiceRequestSchema;
21
+ PayPaymentInvoiceContractCommand.ResponseSchema = PayPaymentInvoiceResponseSchema;
22
+ })(PayPaymentInvoiceContractCommand || (exports.PayPaymentInvoiceContractCommand = PayPaymentInvoiceContractCommand = {}));
@@ -379,6 +379,11 @@ exports.ERRORS = {
379
379
  ORDER_MODIFY_NOT_ALLOWED: { code: 'O018', message: 'Modifying the order is not allowed', httpCode: 500 },
380
380
  ORDER_SPLIT_ERROR: { code: 'O015', message: 'Error splitting orders', httpCode: 500 },
381
381
  ORDER_PAYMENT_NOT_EMPTY: { code: 'O016', message: 'Order not deleted, the order contains payments', httpCode: 400 },
382
+ ORDER_IN_PAYMENT_INVOICE: {
383
+ code: 'O039',
384
+ message: 'Order not deleted, the order is included in a payment invoice',
385
+ httpCode: 409,
386
+ },
382
387
  ORDER_SPLIT_NOT_COMPLETELY_ERROR: { code: 'O019', message: 'Error splitting orders, not all orders were split', httpCode: 500 },
383
388
  ORDER_TOTAL_COST_NEGATIVE: { code: 'O020', message: 'Order total cost is negative', httpCode: 400 },
384
389
  ORDER_SUBTOTAL_NEGATIVE: { code: 'O021', message: 'Order subtotal is negative', httpCode: 400 },
@@ -1107,6 +1112,29 @@ exports.ERRORS = {
1107
1112
  PAYMENT_INVOICE_NOT_FOUND: { code: 'PINV003', message: 'Payment invoice not found', httpCode: 404 },
1108
1113
  PAYMENT_INVOICE_FETCH_FAILED: { code: 'PINV004', message: 'Failed to fetch payment invoices', httpCode: 500 },
1109
1114
  PAYMENT_INVOICE_DELETE_FAILED: { code: 'PINV005', message: 'Failed to delete payment invoice', httpCode: 500 },
1115
+ PAYMENT_INVOICE_ORDERS_NOT_FOUND: {
1116
+ code: 'PINV006',
1117
+ message: 'Some of the requested orders were not found for this client',
1118
+ httpCode: 404,
1119
+ },
1120
+ PAYMENT_INVOICE_ORDER_ALREADY_INVOICED: {
1121
+ code: 'PINV007',
1122
+ message: 'Some of the requested orders are already included in another invoice',
1123
+ httpCode: 409,
1124
+ },
1125
+ PAYMENT_INVOICE_AMOUNT_INVALID: { code: 'PINV008', message: 'Payment invoice amount must be positive', httpCode: 400 },
1126
+ PAYMENT_INVOICE_ALREADY_PAID: { code: 'PINV009', message: 'Payment invoice is already fully paid', httpCode: 409 },
1127
+ PAYMENT_INVOICE_PAYMENT_EXCEEDS_REMAINING: {
1128
+ code: 'PINV010',
1129
+ message: 'Payment amount exceeds the remaining amount of the invoice',
1130
+ httpCode: 400,
1131
+ },
1132
+ PAYMENT_INVOICE_PAYMENT_FAILED: { code: 'PINV011', message: 'Failed to register payment for the invoice', httpCode: 500 },
1133
+ PAYMENT_INVOICE_HAS_PAYMENTS: {
1134
+ code: 'PINV012',
1135
+ message: 'Payment invoice has registered payments and cannot be deleted',
1136
+ httpCode: 409,
1137
+ },
1110
1138
  // AUTH GATEWAY
1111
1139
  AUTH_GATEWAY_VOICE_FAILED: { code: 'AG001', message: 'Failed to send code via voice call', httpCode: 500 },
1112
1140
  AUTH_GATEWAY_TELEGRAM_FAILED: { code: 'AG002', message: 'Failed to send code via Telegram', httpCode: 500 },
@@ -110,3 +110,4 @@ __exportStar(require("./consignment-transfer-direction.enum"), exports);
110
110
  __exportStar(require("./legal-identifier-field.enum"), exports);
111
111
  __exportStar(require("./permission-key.enum"), exports);
112
112
  __exportStar(require("./cash-flow-item-status.enum"), exports);
113
+ __exportStar(require("./payment-invoice-status.enum"), exports);
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PaymentInvoiceStatusEnum = void 0;
4
+ // Статус счёта — производный: считается из суммы живых платежей, привязанных к счёту.
5
+ // В БД не хранится, поэтому не имеет пары в Prisma-энамах и не нуждается в prisma-enum-bridge.
6
+ var PaymentInvoiceStatusEnum;
7
+ (function (PaymentInvoiceStatusEnum) {
8
+ PaymentInvoiceStatusEnum["ISSUED"] = "ISSUED";
9
+ PaymentInvoiceStatusEnum["PARTIALLY_PAID"] = "PARTIALLY_PAID";
10
+ PaymentInvoiceStatusEnum["PAID"] = "PAID";
11
+ })(PaymentInvoiceStatusEnum || (exports.PaymentInvoiceStatusEnum = PaymentInvoiceStatusEnum = {}));
@@ -32,15 +32,20 @@ exports.OrderSchema = zod_1.z.object({
32
32
  totalProductNumber: zod_1.z.number(),
33
33
  invoiceSendAt: zod_1.z.date().nullable(),
34
34
  deliveryRouteUUID: zod_1.z.uuid().nullable(),
35
+ // Счёт на оплату, в который включён заказ. Сам счёт (номер, файл, статус оплаты) читается
36
+ // отдельным запросом GET /payment-invoice/:uuid — здесь только ссылка, чтобы не утяжелять список заказов.
37
+ paymentInvoiceUUID: zod_1.z.uuid().nullable(),
35
38
  completedAt: zod_1.z.date().nullable(),
36
39
  paymentDueAt: zod_1.z.date().nullable(),
37
40
  createdAt: zod_1.z.date(),
38
41
  updatedAt: zod_1.z.date(),
39
42
  deletedAt: zod_1.z.date().nullable(),
40
43
  });
44
+ // Витрина клиента не показывает внутреннюю кухню счетов, поэтому ссылка на счёт сюда не попадает.
41
45
  exports.OrderForClientSchema = exports.OrderSchema.omit({
42
46
  comment: true,
43
47
  tag: true,
48
+ paymentInvoiceUUID: true,
44
49
  });
45
50
  exports.OrderExtendedSchema = exports.OrderSchema.extend({
46
51
  client: client_1.ClientSchema,
@@ -2,13 +2,29 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PaymentInvoiceSchema = void 0;
4
4
  const zod_1 = require("zod");
5
+ const payment_invoice_status_enum_1 = require("../../enum/payment-invoice-status.enum");
6
+ // Строка счёта. Для счёта, собранного из заказов, каждая строка — снимок заказа на момент
7
+ // выставления: заказ потом может измениться, а выставленный счёт обязан остаться прежним.
8
+ const PaymentInvoiceItemSchema = zod_1.z.object({
9
+ orderUUID: zod_1.z.string().nullable(),
10
+ orderId: zod_1.z.number().nullable(),
11
+ orderDate: zod_1.z.coerce.date().nullable(),
12
+ description: zod_1.z.string(),
13
+ amount: zod_1.z.coerce.number(),
14
+ });
5
15
  exports.PaymentInvoiceSchema = zod_1.z.object({
6
16
  uuid: zod_1.z.string(),
7
17
  number: zod_1.z.string(),
8
18
  invoiceDate: zod_1.z.coerce.date(),
9
19
  amount: zod_1.z.coerce.number(),
20
+ paidAmount: zod_1.z.coerce.number(),
21
+ remainingAmount: zod_1.z.coerce.number(),
22
+ status: zod_1.z.nativeEnum(payment_invoice_status_enum_1.PaymentInvoiceStatusEnum),
10
23
  clientUUID: zod_1.z.string(),
11
24
  clientName: zod_1.z.string(),
25
+ contractNumber: zod_1.z.string().nullable(),
26
+ contractDate: zod_1.z.coerce.date().nullable(),
27
+ items: PaymentInvoiceItemSchema.array(),
12
28
  fileUrl: zod_1.z.string().nullable(),
13
29
  createdAt: zod_1.z.coerce.date(),
14
30
  });
@@ -16,6 +16,10 @@ exports.TransactionSchema = zod_1.z.object({
16
16
  type: zod_1.z.nativeEnum(enum_1.TransactionTypeEnum),
17
17
  createdByUserUUID: zod_1.z.string(),
18
18
  cashFlowItemUUID: zod_1.z.string().nullable(),
19
+ // Счёт на оплату, по которому проведён платёж. Заполнен только у платежей, созданных через
20
+ // POST /payment-invoice/:uuid/payment — по нему панель предупреждает, что удаление платежа
21
+ // вернёт счёт в неоплаченные.
22
+ paymentInvoiceUUID: zod_1.z.string().nullable(),
19
23
  createdAt: zod_1.z.date(),
20
24
  updatedAt: zod_1.z.date(),
21
25
  deletedAt: zod_1.z.date().nullable(),
@@ -25,7 +29,8 @@ exports.TransactionExtendedSchema = exports.TransactionSchema.extend({
25
29
  client: client_1.ClientSchema.nullable(),
26
30
  order: order_schema_1.OrderSchema.nullable(),
27
31
  });
28
- exports.TransactionForClientExtendedSchema = exports.TransactionSchema.extend({
32
+ // Витрина клиента не показывает внутреннюю кухню счетов — так же, как OrderForClientSchema.
33
+ exports.TransactionForClientExtendedSchema = exports.TransactionSchema.omit({ paymentInvoiceUUID: true }).extend({
29
34
  moneyAccount: money_account_schema_1.MoneyAccountSchema,
30
35
  client: client_1.ClientSchema.nullable(),
31
36
  order: order_schema_1.OrderForClientSchema.nullable(),
@@ -23,6 +23,9 @@ const GetOrderListRequestSchema = z
23
23
  city: z.string().optional(),
24
24
  responsibleUserUUID: z.string().optional(),
25
25
  group: z.string().optional(),
26
+ // Только заказы, ещё не включённые ни в один счёт на оплату — список для пикера при
27
+ // выставлении счёта. Комбинируется с остальными критериями обычным AND.
28
+ withoutPaymentInvoice: z.coerce.boolean().optional(),
26
29
  })
27
30
  .refine(
28
31
  value => {
@@ -1,11 +1,26 @@
1
1
  import { z } from 'zod';
2
2
  import { PaymentInvoiceSchema } from '../../schemas/payment-invoice/payment-invoice.schema';
3
3
 
4
- const CreatePaymentInvoiceRequestSchema = z.object({
5
- clientUUID: z.uuid(),
6
- invoiceDate: z.coerce.date(),
7
- amount: z.coerce.number().positive(),
8
- });
4
+ // Потолок числа заказов в счёте. Это защита от абсурдного запроса, а не бизнес-правило: счёт за месяц
5
+ // клиенту с ежедневными поставками — это уже 30 строк, поэтому 10-20 отрезали бы живые сценарии.
6
+ // Сто строк — это 3-4 страницы таблицы, документ такого размера собирается и читается нормально.
7
+ const MAX_INVOICE_ORDER_COUNT = 100;
8
+
9
+ // Счёт выставляется одним из двух способов:
10
+ // - по заказам: orderUUIDs непустой, итог считается на бэке как сумма totalCost этих заказов,
11
+ // а amount — необязательная правка итога (например скидка по счёту), она уходит отдельной строкой;
12
+ // - вручную: orderUUIDs пуст, тогда amount обязателен и становится единственной строкой счёта.
13
+ const CreatePaymentInvoiceRequestSchema = z
14
+ .object({
15
+ clientUUID: z.uuid(),
16
+ invoiceDate: z.coerce.date(),
17
+ orderUUIDs: z.uuid().array().max(MAX_INVOICE_ORDER_COUNT).optional(),
18
+ amount: z.coerce.number().positive().optional(),
19
+ })
20
+ .refine(value => Boolean(value.orderUUIDs?.length) || value.amount !== undefined, {
21
+ message: 'Either orderUUIDs or amount must be provided',
22
+ path: ['amount'],
23
+ });
9
24
 
10
25
  const CreatePaymentInvoiceResponseSchema = z.object({
11
26
  message: z.string().optional(),
@@ -2,3 +2,4 @@ export * from './create-payment-invoice.command';
2
2
  export * from './get-payment-invoice-list.query';
3
3
  export * from './get-payment-invoice.query';
4
4
  export * from './delete-payment-invoice.command';
5
+ export * from './pay-payment-invoice.command';
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ import { PaymentInvoiceSchema } from '../../schemas/payment-invoice/payment-invoice.schema';
3
+
4
+ // Оплата по счёту. amount не передан — гасим остаток целиком; передан — частичная оплата.
5
+ const PayPaymentInvoiceRequestSchema = z.object({
6
+ moneyAccountUUID: z.uuid(),
7
+ amount: z.coerce.number().positive().optional(),
8
+ comment: z.string().optional(),
9
+ cashFlowItemUUID: z.uuid().nullable().optional(),
10
+ createdAt: z.coerce.date().optional(),
11
+ });
12
+
13
+ const PayPaymentInvoiceResponseSchema = z.object({
14
+ message: z.string().optional(),
15
+ data: PaymentInvoiceSchema,
16
+ });
17
+
18
+ export namespace PayPaymentInvoiceContractCommand {
19
+ export const RequestSchema = PayPaymentInvoiceRequestSchema;
20
+ export type Request = z.infer<typeof RequestSchema>;
21
+
22
+ export const ResponseSchema = PayPaymentInvoiceResponseSchema;
23
+ export type Response = z.infer<typeof ResponseSchema>;
24
+ }
package/constant/error.ts CHANGED
@@ -393,6 +393,11 @@ export const ERRORS = {
393
393
  ORDER_MODIFY_NOT_ALLOWED: { code: 'O018', message: 'Modifying the order is not allowed', httpCode: 500 },
394
394
  ORDER_SPLIT_ERROR: { code: 'O015', message: 'Error splitting orders', httpCode: 500 },
395
395
  ORDER_PAYMENT_NOT_EMPTY: { code: 'O016', message: 'Order not deleted, the order contains payments', httpCode: 400 },
396
+ ORDER_IN_PAYMENT_INVOICE: {
397
+ code: 'O039',
398
+ message: 'Order not deleted, the order is included in a payment invoice',
399
+ httpCode: 409,
400
+ },
396
401
  ORDER_SPLIT_NOT_COMPLETELY_ERROR: { code: 'O019', message: 'Error splitting orders, not all orders were split', httpCode: 500 },
397
402
  ORDER_TOTAL_COST_NEGATIVE: { code: 'O020', message: 'Order total cost is negative', httpCode: 400 },
398
403
  ORDER_SUBTOTAL_NEGATIVE: { code: 'O021', message: 'Order subtotal is negative', httpCode: 400 },
@@ -1155,6 +1160,29 @@ export const ERRORS = {
1155
1160
  PAYMENT_INVOICE_NOT_FOUND: { code: 'PINV003', message: 'Payment invoice not found', httpCode: 404 },
1156
1161
  PAYMENT_INVOICE_FETCH_FAILED: { code: 'PINV004', message: 'Failed to fetch payment invoices', httpCode: 500 },
1157
1162
  PAYMENT_INVOICE_DELETE_FAILED: { code: 'PINV005', message: 'Failed to delete payment invoice', httpCode: 500 },
1163
+ PAYMENT_INVOICE_ORDERS_NOT_FOUND: {
1164
+ code: 'PINV006',
1165
+ message: 'Some of the requested orders were not found for this client',
1166
+ httpCode: 404,
1167
+ },
1168
+ PAYMENT_INVOICE_ORDER_ALREADY_INVOICED: {
1169
+ code: 'PINV007',
1170
+ message: 'Some of the requested orders are already included in another invoice',
1171
+ httpCode: 409,
1172
+ },
1173
+ PAYMENT_INVOICE_AMOUNT_INVALID: { code: 'PINV008', message: 'Payment invoice amount must be positive', httpCode: 400 },
1174
+ PAYMENT_INVOICE_ALREADY_PAID: { code: 'PINV009', message: 'Payment invoice is already fully paid', httpCode: 409 },
1175
+ PAYMENT_INVOICE_PAYMENT_EXCEEDS_REMAINING: {
1176
+ code: 'PINV010',
1177
+ message: 'Payment amount exceeds the remaining amount of the invoice',
1178
+ httpCode: 400,
1179
+ },
1180
+ PAYMENT_INVOICE_PAYMENT_FAILED: { code: 'PINV011', message: 'Failed to register payment for the invoice', httpCode: 500 },
1181
+ PAYMENT_INVOICE_HAS_PAYMENTS: {
1182
+ code: 'PINV012',
1183
+ message: 'Payment invoice has registered payments and cannot be deleted',
1184
+ httpCode: 409,
1185
+ },
1158
1186
 
1159
1187
  // AUTH GATEWAY
1160
1188
  AUTH_GATEWAY_VOICE_FAILED: { code: 'AG001', message: 'Failed to send code via voice call', httpCode: 500 },
package/enum/index.ts CHANGED
@@ -94,3 +94,4 @@ export * from './consignment-transfer-direction.enum';
94
94
  export * from './legal-identifier-field.enum';
95
95
  export * from './permission-key.enum';
96
96
  export * from './cash-flow-item-status.enum';
97
+ export * from './payment-invoice-status.enum';
@@ -0,0 +1,7 @@
1
+ // Статус счёта — производный: считается из суммы живых платежей, привязанных к счёту.
2
+ // В БД не хранится, поэтому не имеет пары в Prisma-энамах и не нуждается в prisma-enum-bridge.
3
+ export enum PaymentInvoiceStatusEnum {
4
+ ISSUED = 'ISSUED',
5
+ PARTIALLY_PAID = 'PARTIALLY_PAID',
6
+ PAID = 'PAID',
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floristcloud/api-lib",
3
- "version": "1.2.103",
3
+ "version": "1.2.105",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {
@@ -34,6 +34,9 @@ export const OrderSchema = z.object({
34
34
  totalProductNumber: z.number(),
35
35
  invoiceSendAt: z.date().nullable(),
36
36
  deliveryRouteUUID: z.uuid().nullable(),
37
+ // Счёт на оплату, в который включён заказ. Сам счёт (номер, файл, статус оплаты) читается
38
+ // отдельным запросом GET /payment-invoice/:uuid — здесь только ссылка, чтобы не утяжелять список заказов.
39
+ paymentInvoiceUUID: z.uuid().nullable(),
37
40
 
38
41
  completedAt: z.date().nullable(),
39
42
  paymentDueAt: z.date().nullable(),
@@ -43,9 +46,11 @@ export const OrderSchema = z.object({
43
46
  deletedAt: z.date().nullable(),
44
47
  });
45
48
 
49
+ // Витрина клиента не показывает внутреннюю кухню счетов, поэтому ссылка на счёт сюда не попадает.
46
50
  export const OrderForClientSchema = OrderSchema.omit({
47
51
  comment: true,
48
52
  tag: true,
53
+ paymentInvoiceUUID: true,
49
54
  });
50
55
 
51
56
  export const OrderExtendedSchema = OrderSchema.extend({
@@ -1,12 +1,29 @@
1
1
  import { z } from 'zod';
2
+ import { PaymentInvoiceStatusEnum } from '../../enum/payment-invoice-status.enum';
3
+
4
+ // Строка счёта. Для счёта, собранного из заказов, каждая строка — снимок заказа на момент
5
+ // выставления: заказ потом может измениться, а выставленный счёт обязан остаться прежним.
6
+ const PaymentInvoiceItemSchema = z.object({
7
+ orderUUID: z.string().nullable(),
8
+ orderId: z.number().nullable(),
9
+ orderDate: z.coerce.date().nullable(),
10
+ description: z.string(),
11
+ amount: z.coerce.number(),
12
+ });
2
13
 
3
14
  export const PaymentInvoiceSchema = z.object({
4
15
  uuid: z.string(),
5
16
  number: z.string(),
6
17
  invoiceDate: z.coerce.date(),
7
18
  amount: z.coerce.number(),
19
+ paidAmount: z.coerce.number(),
20
+ remainingAmount: z.coerce.number(),
21
+ status: z.nativeEnum(PaymentInvoiceStatusEnum),
8
22
  clientUUID: z.string(),
9
23
  clientName: z.string(),
24
+ contractNumber: z.string().nullable(),
25
+ contractDate: z.coerce.date().nullable(),
26
+ items: PaymentInvoiceItemSchema.array(),
10
27
  fileUrl: z.string().nullable(),
11
28
  createdAt: z.coerce.date(),
12
29
  });
@@ -18,6 +18,10 @@ export const TransactionSchema = z.object({
18
18
  createdByUserUUID: z.string(),
19
19
 
20
20
  cashFlowItemUUID: z.string().nullable(),
21
+ // Счёт на оплату, по которому проведён платёж. Заполнен только у платежей, созданных через
22
+ // POST /payment-invoice/:uuid/payment — по нему панель предупреждает, что удаление платежа
23
+ // вернёт счёт в неоплаченные.
24
+ paymentInvoiceUUID: z.string().nullable(),
21
25
 
22
26
  createdAt: z.date(),
23
27
  updatedAt: z.date(),
@@ -30,7 +34,8 @@ export const TransactionExtendedSchema = TransactionSchema.extend({
30
34
  order: OrderSchema.nullable(),
31
35
  });
32
36
 
33
- export const TransactionForClientExtendedSchema = TransactionSchema.extend({
37
+ // Витрина клиента не показывает внутреннюю кухню счетов — так же, как OrderForClientSchema.
38
+ export const TransactionForClientExtendedSchema = TransactionSchema.omit({ paymentInvoiceUUID: true }).extend({
34
39
  moneyAccount: MoneyAccountSchema,
35
40
  client: ClientSchema.nullable(),
36
41
  order: OrderForClientSchema.nullable(),