@floristcloud/api-lib 1.2.189 → 1.2.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/report/get-write-off-breakdown.query.js +10 -0
- package/build/commands/report/get-write-off-by-client.query.js +10 -0
- package/build/commands/report/get-write-off-summary.query.js +4 -76
- package/build/commands/report/index.js +2 -0
- package/build/constant/error.js +7 -0
- package/build/constant/report.constant.js +7 -1
- package/build/enum/index.js +1 -0
- package/build/enum/write-off-breakdown-group-by.enum.js +18 -0
- package/build/schemas/report/get-write-off-breakdown-request.schema.js +26 -0
- package/build/schemas/report/get-write-off-breakdown-response-data.schema.js +12 -0
- package/build/schemas/report/get-write-off-breakdown-response.schema.js +9 -0
- package/build/schemas/report/get-write-off-by-client-request.schema.js +29 -0
- package/build/schemas/report/get-write-off-by-client-response-data.schema.js +12 -0
- package/build/schemas/report/get-write-off-by-client-response.schema.js +9 -0
- package/build/schemas/report/get-write-off-summary-request.schema.js +34 -0
- package/build/schemas/report/get-write-off-summary-response-data.schema.js +12 -0
- package/build/schemas/report/get-write-off-summary-response.schema.js +9 -0
- package/build/schemas/report/index.js +25 -0
- package/build/schemas/report/write-off-breakdown-category-row.schema.js +9 -0
- package/build/schemas/report/write-off-breakdown-consignment-row.schema.js +11 -0
- package/build/schemas/report/write-off-breakdown-consignment.schema.js +11 -0
- package/build/schemas/report/write-off-breakdown-grower-row.schema.js +9 -0
- package/build/schemas/report/write-off-breakdown-reason-row.schema.js +9 -0
- package/build/schemas/report/write-off-breakdown-row-base.schema.js +21 -0
- package/build/schemas/report/write-off-breakdown-row.schema.js +15 -0
- package/build/schemas/report/write-off-breakdown-totals.schema.js +18 -0
- package/build/schemas/report/write-off-by-client-row.schema.js +21 -0
- package/build/schemas/report/write-off-by-client-totals.schema.js +20 -0
- package/build/schemas/report/write-off-report-page.schema.js +13 -0
- package/build/schemas/report/write-off-summary-by-client.schema.js +18 -0
- package/build/schemas/report/write-off-summary-client-row.schema.js +13 -0
- package/build/schemas/report/write-off-summary-reason-row.schema.js +12 -0
- package/build/schemas/report/write-off-summary-totals.schema.js +18 -0
- package/build/schemas/report/write-off-summary-warehouse.schema.js +12 -0
- package/commands/report/get-write-off-breakdown.query.ts +11 -0
- package/commands/report/get-write-off-by-client.query.ts +11 -0
- package/commands/report/get-write-off-summary.query.ts +3 -82
- package/commands/report/index.ts +2 -0
- package/constant/error.ts +7 -0
- package/constant/report.constant.ts +8 -0
- package/enum/index.ts +1 -0
- package/enum/write-off-breakdown-group-by.enum.ts +14 -0
- package/package.json +1 -1
- package/schemas/report/get-write-off-breakdown-request.schema.ts +26 -0
- package/schemas/report/get-write-off-breakdown-response-data.schema.ts +10 -0
- package/schemas/report/get-write-off-breakdown-response.schema.ts +7 -0
- package/schemas/report/get-write-off-by-client-request.schema.ts +27 -0
- package/schemas/report/get-write-off-by-client-response-data.schema.ts +10 -0
- package/schemas/report/get-write-off-by-client-response.schema.ts +7 -0
- package/schemas/report/get-write-off-summary-request.schema.ts +36 -0
- package/schemas/report/get-write-off-summary-response-data.schema.ts +10 -0
- package/schemas/report/get-write-off-summary-response.schema.ts +7 -0
- package/schemas/report/index.ts +25 -0
- package/schemas/report/write-off-breakdown-category-row.schema.ts +7 -0
- package/schemas/report/write-off-breakdown-consignment-row.schema.ts +9 -0
- package/schemas/report/write-off-breakdown-consignment.schema.ts +9 -0
- package/schemas/report/write-off-breakdown-grower-row.schema.ts +7 -0
- package/schemas/report/write-off-breakdown-reason-row.schema.ts +7 -0
- package/schemas/report/write-off-breakdown-row-base.schema.ts +19 -0
- package/schemas/report/write-off-breakdown-row.schema.ts +13 -0
- package/schemas/report/write-off-breakdown-totals.schema.ts +16 -0
- package/schemas/report/write-off-by-client-row.schema.ts +19 -0
- package/schemas/report/write-off-by-client-totals.schema.ts +18 -0
- package/schemas/report/write-off-report-page.schema.ts +11 -0
- package/schemas/report/write-off-summary-by-client.schema.ts +16 -0
- package/schemas/report/write-off-summary-client-row.schema.ts +11 -0
- package/schemas/report/write-off-summary-reason-row.schema.ts +10 -0
- package/schemas/report/write-off-summary-totals.schema.ts +16 -0
- package/schemas/report/write-off-summary-warehouse.schema.ts +10 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WriteOffSummaryTotalsSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const money_schema_1 = require("../money.schema");
|
|
6
|
+
/**
|
|
7
|
+
* totals = warehouse + byClient, считается по всем строкам — усечение `clientLimit` на итоги
|
|
8
|
+
* не влияет.
|
|
9
|
+
*/
|
|
10
|
+
exports.WriteOffSummaryTotalsSchema = zod_1.z.object({
|
|
11
|
+
units: zod_1.z.number(),
|
|
12
|
+
cost: money_schema_1.MoneySchema,
|
|
13
|
+
// Себестоимость прихода партий периода и доля списаний от неё в процентах — только на
|
|
14
|
+
// оси CONSIGNMENT_DELIVERY_DATE, иначе null: на оси даты списания товар мог прийти
|
|
15
|
+
// раньше периода, и делить его на приход периода некорректно.
|
|
16
|
+
receivedCost: money_schema_1.MoneySchema.nullable(),
|
|
17
|
+
share: zod_1.z.number().nullable(),
|
|
18
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WriteOffSummaryWarehouseSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const money_schema_1 = require("../money.schema");
|
|
6
|
+
const write_off_summary_reason_row_schema_1 = require("./write-off-summary-reason-row.schema");
|
|
7
|
+
/** Списания без заказа: до этого отчёта они не попадали ни в одну сводку. */
|
|
8
|
+
exports.WriteOffSummaryWarehouseSchema = zod_1.z.object({
|
|
9
|
+
units: zod_1.z.number(),
|
|
10
|
+
cost: money_schema_1.MoneySchema,
|
|
11
|
+
byReason: write_off_summary_reason_row_schema_1.WriteOffSummaryReasonRowSchema.array(),
|
|
12
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import { GetWriteOffBreakdownRequestSchema } from '../../schemas/report/get-write-off-breakdown-request.schema';
|
|
3
|
+
import { GetWriteOffBreakdownResponseSchema } from '../../schemas/report/get-write-off-breakdown-response.schema';
|
|
4
|
+
|
|
5
|
+
export namespace GetWriteOffBreakdownContractQuery {
|
|
6
|
+
export const RequestSchema = GetWriteOffBreakdownRequestSchema;
|
|
7
|
+
export type Request = z.infer<typeof RequestSchema>;
|
|
8
|
+
|
|
9
|
+
export const ResponseSchema = GetWriteOffBreakdownResponseSchema;
|
|
10
|
+
export type Response = z.infer<typeof ResponseSchema>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import { GetWriteOffByClientRequestSchema } from '../../schemas/report/get-write-off-by-client-request.schema';
|
|
3
|
+
import { GetWriteOffByClientResponseSchema } from '../../schemas/report/get-write-off-by-client-response.schema';
|
|
4
|
+
|
|
5
|
+
export namespace GetWriteOffByClientContractQuery {
|
|
6
|
+
export const RequestSchema = GetWriteOffByClientRequestSchema;
|
|
7
|
+
export type Request = z.infer<typeof RequestSchema>;
|
|
8
|
+
|
|
9
|
+
export const ResponseSchema = GetWriteOffByClientResponseSchema;
|
|
10
|
+
export type Response = z.infer<typeof ResponseSchema>;
|
|
11
|
+
}
|
|
@@ -1,85 +1,6 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { WriteOffReportDateBasisEnum } from '../../enum/write-off-report-date-basis.enum';
|
|
5
|
-
import {
|
|
6
|
-
REPORT_EXCLUDE_CLIENT_LIMIT,
|
|
7
|
-
REPORT_WRITE_OFF_CLIENT_LIMIT_DEFAULT,
|
|
8
|
-
REPORT_WRITE_OFF_CLIENT_LIMIT_MAX,
|
|
9
|
-
} from '../../constant/report.constant';
|
|
10
|
-
|
|
11
|
-
const GetWriteOffSummaryRequestSchema = z
|
|
12
|
-
.object({
|
|
13
|
-
startDate: z.coerce.date(),
|
|
14
|
-
endDate: z.coerce.date(),
|
|
15
|
-
dateBasis: z.nativeEnum(WriteOffReportDateBasisEnum).default(WriteOffReportDateBasisEnum.WRITE_OFF_DATE),
|
|
16
|
-
// Тот же формат, что в ReportOrderFilterSchema: CSV или повтор параметра. Применяется
|
|
17
|
-
// только к разрезу по клиентам — у складских списаний клиента нет.
|
|
18
|
-
excludeClientUUID: z
|
|
19
|
-
.union([z.string(), z.array(z.string())])
|
|
20
|
-
.transform(value => (Array.isArray(value) ? value : value.split(',')).map(item => item.trim()).filter(Boolean))
|
|
21
|
-
.pipe(z.array(z.string().uuid()).max(REPORT_EXCLUDE_CLIENT_LIMIT))
|
|
22
|
-
.optional(),
|
|
23
|
-
clientLimit: z.coerce
|
|
24
|
-
.number()
|
|
25
|
-
.int()
|
|
26
|
-
.positive()
|
|
27
|
-
.max(REPORT_WRITE_OFF_CLIENT_LIMIT_MAX)
|
|
28
|
-
.default(REPORT_WRITE_OFF_CLIENT_LIMIT_DEFAULT),
|
|
29
|
-
})
|
|
30
|
-
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
31
|
-
message: 'validation.dateRange',
|
|
32
|
-
path: ['endDate', 'startDate'],
|
|
33
|
-
})
|
|
34
|
-
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
35
|
-
message: 'validation.reportPeriodMax',
|
|
36
|
-
path: ['endDate', 'startDate'],
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const WriteOffSummaryReasonRowSchema = z.object({
|
|
40
|
-
// Свободный текст из справочника причин, схлопнут только по краевым пробелам (TRIM).
|
|
41
|
-
// Регистр не нормализуем: «Брак» и «брак» останутся разными строками.
|
|
42
|
-
reason: z.string(),
|
|
43
|
-
units: z.number(),
|
|
44
|
-
cost: MoneySchema,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const WriteOffSummaryClientRowSchema = z.object({
|
|
48
|
-
clientUUID: z.string(),
|
|
49
|
-
clientName: z.string(),
|
|
50
|
-
orderCount: z.number(),
|
|
51
|
-
units: z.number(),
|
|
52
|
-
cost: MoneySchema,
|
|
53
|
-
orderAmount: MoneySchema,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const GetWriteOffSummaryResponseSchema = z.object({
|
|
57
|
-
message: z.string().optional(),
|
|
58
|
-
data: z.object({
|
|
59
|
-
// totals = warehouse + byClient, считается по всем строкам — усечение `clientLimit`
|
|
60
|
-
// на итоги не влияет.
|
|
61
|
-
totals: z.object({
|
|
62
|
-
units: z.number(),
|
|
63
|
-
cost: MoneySchema,
|
|
64
|
-
}),
|
|
65
|
-
// Списания без заказа: до этого отчёта они не попадали ни в одну сводку.
|
|
66
|
-
warehouse: z.object({
|
|
67
|
-
units: z.number(),
|
|
68
|
-
cost: MoneySchema,
|
|
69
|
-
byReason: WriteOffSummaryReasonRowSchema.array(),
|
|
70
|
-
}),
|
|
71
|
-
// Списания по заказу: клиенту они уже вернулись на баланс, то есть это выручка,
|
|
72
|
-
// которой по факту не было.
|
|
73
|
-
byClient: z.object({
|
|
74
|
-
units: z.number(),
|
|
75
|
-
cost: MoneySchema,
|
|
76
|
-
orderAmount: MoneySchema,
|
|
77
|
-
rows: WriteOffSummaryClientRowSchema.array(),
|
|
78
|
-
// Сколько клиентов всего — чтобы отличить усечённый ответ от полного.
|
|
79
|
-
rowsTotal: z.number(),
|
|
80
|
-
}),
|
|
81
|
-
}),
|
|
82
|
-
});
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import { GetWriteOffSummaryRequestSchema } from '../../schemas/report/get-write-off-summary-request.schema';
|
|
3
|
+
import { GetWriteOffSummaryResponseSchema } from '../../schemas/report/get-write-off-summary-response.schema';
|
|
83
4
|
|
|
84
5
|
export namespace GetWriteOffSummaryContractQuery {
|
|
85
6
|
export const RequestSchema = GetWriteOffSummaryRequestSchema;
|
package/commands/report/index.ts
CHANGED
|
@@ -13,6 +13,8 @@ export * from './get-report-by-client-weeks.query';
|
|
|
13
13
|
export * from './get-daily-orders-summary.query';
|
|
14
14
|
export * from './get-cash-flow.query';
|
|
15
15
|
export * from './get-write-off-summary.query';
|
|
16
|
+
export * from './get-write-off-breakdown.query';
|
|
17
|
+
export * from './get-write-off-by-client.query';
|
|
16
18
|
export * from './get-inventory-stock.query';
|
|
17
19
|
export * from './get-inventory-turnover.query';
|
|
18
20
|
export * from './get-sales-by-client.query';
|
package/constant/error.ts
CHANGED
|
@@ -1548,6 +1548,8 @@ export const ERRORS = {
|
|
|
1548
1548
|
REPORT_EXPORT_FAILED: { code: 'REP002', message: 'Failed to export report', httpCode: 500 },
|
|
1549
1549
|
REPORT_NO_DATA: { code: 'REP003', message: 'No data available for the selected period', httpCode: 404 },
|
|
1550
1550
|
WRITE_OFF_SUMMARY_REPORT_FAILED: { code: 'REP004', message: 'Failed to build write-off summary report', httpCode: 500 },
|
|
1551
|
+
WRITE_OFF_BREAKDOWN_REPORT_FAILED: { code: 'REP005', message: 'Failed to build write-off breakdown report', httpCode: 500 },
|
|
1552
|
+
WRITE_OFF_BY_CLIENT_REPORT_FAILED: { code: 'REP006', message: 'Failed to build write-off by client report', httpCode: 500 },
|
|
1551
1553
|
STATEMENT_PERIOD_TOO_LONG: { code: 'REP004', message: 'Statement period must not exceed 1 year', httpCode: 400 },
|
|
1552
1554
|
|
|
1553
1555
|
// PAYMENT INVOICE
|
|
@@ -1844,6 +1846,10 @@ export const ERRORS = {
|
|
|
1844
1846
|
message: 'Product stock cannot be recomputed: it is not in the 1C sync consignment',
|
|
1845
1847
|
httpCode: 409,
|
|
1846
1848
|
},
|
|
1849
|
+
// Файл обмена больше ONE_C_MAX_EXCHANGE_FILE_BYTES: тело сбрасывается без чтения в память.
|
|
1850
|
+
ONE_C_EXCHANGE_FILE_TOO_LARGE: { code: 'OC024', message: '1C exchange file exceeds the size limit', httpCode: 413 },
|
|
1851
|
+
// Приём каталога выключен на интеграции (CompanyIntegration.isCatalogExchangeEnabled), обмен заказами продолжается.
|
|
1852
|
+
ONE_C_CATALOG_EXCHANGE_DISABLED: { code: 'OC025', message: '1C catalog exchange is disabled for this company', httpCode: 409 },
|
|
1847
1853
|
|
|
1848
1854
|
// COMPANY INTEGRATION (общая запись интеграции компании)
|
|
1849
1855
|
COMPANY_INTEGRATION_FETCH_FAILED: { code: 'CINT001', message: 'Failed to read company integration', httpCode: 500 },
|
|
@@ -1855,6 +1861,7 @@ export const ERRORS = {
|
|
|
1855
1861
|
message: 'The unit is not among the saved units of the company integration',
|
|
1856
1862
|
httpCode: 400,
|
|
1857
1863
|
},
|
|
1864
|
+
INTEGRATION_WAREHOUSES_NOT_SAVED: { code: 'CINT006', message: 'Failed to save integration warehouses', httpCode: 500 },
|
|
1858
1865
|
|
|
1859
1866
|
// OZEXPORT INTEGRATION (FC-513)
|
|
1860
1867
|
OZEXPORT_AUTH_FAILED: { code: 'OZ001', message: 'ozexport rejected the username or password', httpCode: 400 },
|
|
@@ -13,3 +13,11 @@ export const REPORT_EXCLUDE_CLIENT_LIMIT = 500;
|
|
|
13
13
|
export const REPORT_WRITE_OFF_CLIENT_LIMIT_DEFAULT = 50;
|
|
14
14
|
|
|
15
15
|
export const REPORT_WRITE_OFF_CLIENT_LIMIT_MAX = 500;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Страница строк в разрезах вкладки «Списания» (`breakdown`, `by-client`). Итоги считаются по всем
|
|
19
|
+
* строкам независимо от `limit`/`offset`; полный список фронт выбирает постранично через `offset`.
|
|
20
|
+
*/
|
|
21
|
+
export const REPORT_WRITE_OFF_BREAKDOWN_LIMIT_DEFAULT = 50;
|
|
22
|
+
|
|
23
|
+
export const REPORT_WRITE_OFF_BREAKDOWN_LIMIT_MAX = 500;
|
package/enum/index.ts
CHANGED
|
@@ -148,6 +148,7 @@ export * from './userRole.enum';
|
|
|
148
148
|
export * from './userStatus.enum';
|
|
149
149
|
export * from './webshop-main-display-mode.enum';
|
|
150
150
|
export * from './webshop-mode.enum';
|
|
151
|
+
export * from './write-off-breakdown-group-by.enum';
|
|
151
152
|
export * from './write-off-report-date-basis.enum';
|
|
152
153
|
export * from './write-off-status.enum';
|
|
153
154
|
export * from './one-c-stock-conflict-status.enum';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Разрез отчёта `/report/write-off/breakdown` — по какому признаку группируются списания.
|
|
3
|
+
*
|
|
4
|
+
* `CONSIGNMENT` — партия (база процента — приход партии целиком, на любой оси периода).
|
|
5
|
+
* `GROWER` — производитель `product.grower`, без учёта регистра и краевых пробелов.
|
|
6
|
+
* `CATEGORY` — категория, снимок `write_off.category_uuid` на момент списания.
|
|
7
|
+
* `REASON` — причина, свободный текст без краевых пробелов; процента у причин нет.
|
|
8
|
+
*/
|
|
9
|
+
export enum WriteOffBreakdownGroupByEnum {
|
|
10
|
+
CONSIGNMENT = 'CONSIGNMENT',
|
|
11
|
+
GROWER = 'GROWER',
|
|
12
|
+
CATEGORY = 'CATEGORY',
|
|
13
|
+
REASON = 'REASON',
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { dateRangeCheck, isStatementPeriodWithinLimit } from '../../helpers/date.helper';
|
|
3
|
+
import { WriteOffBreakdownGroupByEnum } from '../../enum/write-off-breakdown-group-by.enum';
|
|
4
|
+
import { WriteOffReportDateBasisEnum } from '../../enum/write-off-report-date-basis.enum';
|
|
5
|
+
import { ReportOrderFilterSchema } from './report-order-filter.schema';
|
|
6
|
+
import { WriteOffReportPageSchema } from './write-off-report-page.schema';
|
|
7
|
+
|
|
8
|
+
export const GetWriteOffBreakdownRequestSchema = WriteOffReportPageSchema.extend({
|
|
9
|
+
startDate: z.coerce.date(),
|
|
10
|
+
endDate: z.coerce.date(),
|
|
11
|
+
// Дефолт тот же, что у write-off/summary: один и тот же запрос без оси не должен давать разную
|
|
12
|
+
// выборку на двух эндпоинтах. Вкладка «Списания» передаёт ось явно.
|
|
13
|
+
dateBasis: z.nativeEnum(WriteOffReportDateBasisEnum).default(WriteOffReportDateBasisEnum.WRITE_OFF_DATE),
|
|
14
|
+
groupBy: z.nativeEnum(WriteOffBreakdownGroupByEnum),
|
|
15
|
+
excludeClientUUID: ReportOrderFilterSchema.shape.excludeClientUUID.describe(
|
|
16
|
+
'Clients whose order write-offs are dropped. Warehouse write-offs have no client and stay.',
|
|
17
|
+
),
|
|
18
|
+
})
|
|
19
|
+
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
20
|
+
message: 'validation.dateRange',
|
|
21
|
+
path: ['endDate', 'startDate'],
|
|
22
|
+
})
|
|
23
|
+
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
24
|
+
message: 'validation.reportPeriodMax',
|
|
25
|
+
path: ['endDate', 'startDate'],
|
|
26
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownRowSchema } from './write-off-breakdown-row.schema';
|
|
3
|
+
import { WriteOffBreakdownTotalsSchema } from './write-off-breakdown-totals.schema';
|
|
4
|
+
|
|
5
|
+
export const GetWriteOffBreakdownResponseDataSchema = z.object({
|
|
6
|
+
totals: WriteOffBreakdownTotalsSchema,
|
|
7
|
+
rows: WriteOffBreakdownRowSchema.array(),
|
|
8
|
+
// Строк во всём разрезе — чтобы отличить страницу от полного списка.
|
|
9
|
+
rowsTotal: z.number(),
|
|
10
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { GetWriteOffBreakdownResponseDataSchema } from './get-write-off-breakdown-response-data.schema';
|
|
3
|
+
|
|
4
|
+
export const GetWriteOffBreakdownResponseSchema = z.object({
|
|
5
|
+
message: z.string().optional(),
|
|
6
|
+
data: GetWriteOffBreakdownResponseDataSchema,
|
|
7
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { dateRangeCheck, isStatementPeriodWithinLimit } from '../../helpers/date.helper';
|
|
3
|
+
import { ReportOrderDateBasisEnum } from '../../enum/report-order-date-basis.enum';
|
|
4
|
+
import { ReportOrderFilterSchema } from './report-order-filter.schema';
|
|
5
|
+
import { WriteOffReportPageSchema } from './write-off-report-page.schema';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Ось разреза — заказы периода, а не списания: списание по заказу попадает сюда независимо от
|
|
9
|
+
* своей даты, иначе процент делил бы брак по заказу прошлого месяца на выручку текущего.
|
|
10
|
+
*
|
|
11
|
+
* Параметр назван `orderDateBasis`, а не `dateBasis`, как в отчётах продаж: остальные запросы
|
|
12
|
+
* вкладки шлют `dateBasis` с осью списаний, и общее имя позволило бы молча перепутать перечисления.
|
|
13
|
+
*/
|
|
14
|
+
export const GetWriteOffByClientRequestSchema = WriteOffReportPageSchema.extend({
|
|
15
|
+
startDate: z.coerce.date(),
|
|
16
|
+
endDate: z.coerce.date(),
|
|
17
|
+
orderDateBasis: z.nativeEnum(ReportOrderDateBasisEnum).default(ReportOrderDateBasisEnum.CREATED_AT),
|
|
18
|
+
excludeClientUUID: ReportOrderFilterSchema.shape.excludeClientUUID,
|
|
19
|
+
})
|
|
20
|
+
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
21
|
+
message: 'validation.dateRange',
|
|
22
|
+
path: ['endDate', 'startDate'],
|
|
23
|
+
})
|
|
24
|
+
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
25
|
+
message: 'validation.reportPeriodMax',
|
|
26
|
+
path: ['endDate', 'startDate'],
|
|
27
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffByClientRowSchema } from './write-off-by-client-row.schema';
|
|
3
|
+
import { WriteOffByClientTotalsSchema } from './write-off-by-client-totals.schema';
|
|
4
|
+
|
|
5
|
+
export const GetWriteOffByClientResponseDataSchema = z.object({
|
|
6
|
+
totals: WriteOffByClientTotalsSchema,
|
|
7
|
+
rows: WriteOffByClientRowSchema.array(),
|
|
8
|
+
// Клиентов со списаниями всего — чтобы отличить страницу от полного списка.
|
|
9
|
+
rowsTotal: z.number(),
|
|
10
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { GetWriteOffByClientResponseDataSchema } from './get-write-off-by-client-response-data.schema';
|
|
3
|
+
|
|
4
|
+
export const GetWriteOffByClientResponseSchema = z.object({
|
|
5
|
+
message: z.string().optional(),
|
|
6
|
+
data: GetWriteOffByClientResponseDataSchema,
|
|
7
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { dateRangeCheck, isStatementPeriodWithinLimit } from '../../helpers/date.helper';
|
|
3
|
+
import { WriteOffReportDateBasisEnum } from '../../enum/write-off-report-date-basis.enum';
|
|
4
|
+
import {
|
|
5
|
+
REPORT_EXCLUDE_CLIENT_LIMIT,
|
|
6
|
+
REPORT_WRITE_OFF_CLIENT_LIMIT_DEFAULT,
|
|
7
|
+
REPORT_WRITE_OFF_CLIENT_LIMIT_MAX,
|
|
8
|
+
} from '../../constant/report.constant';
|
|
9
|
+
|
|
10
|
+
export const GetWriteOffSummaryRequestSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
startDate: z.coerce.date(),
|
|
13
|
+
endDate: z.coerce.date(),
|
|
14
|
+
dateBasis: z.nativeEnum(WriteOffReportDateBasisEnum).default(WriteOffReportDateBasisEnum.WRITE_OFF_DATE),
|
|
15
|
+
// Тот же формат, что в ReportOrderFilterSchema: CSV или повтор параметра. Применяется
|
|
16
|
+
// только к разрезу по клиентам — у складских списаний клиента нет.
|
|
17
|
+
excludeClientUUID: z
|
|
18
|
+
.union([z.string(), z.array(z.string())])
|
|
19
|
+
.transform(value => (Array.isArray(value) ? value : value.split(',')).map(item => item.trim()).filter(Boolean))
|
|
20
|
+
.pipe(z.array(z.string().uuid()).max(REPORT_EXCLUDE_CLIENT_LIMIT))
|
|
21
|
+
.optional(),
|
|
22
|
+
clientLimit: z.coerce
|
|
23
|
+
.number()
|
|
24
|
+
.int()
|
|
25
|
+
.positive()
|
|
26
|
+
.max(REPORT_WRITE_OFF_CLIENT_LIMIT_MAX)
|
|
27
|
+
.default(REPORT_WRITE_OFF_CLIENT_LIMIT_DEFAULT),
|
|
28
|
+
})
|
|
29
|
+
.refine(value => dateRangeCheck(value.startDate, value.endDate), {
|
|
30
|
+
message: 'validation.dateRange',
|
|
31
|
+
path: ['endDate', 'startDate'],
|
|
32
|
+
})
|
|
33
|
+
.refine(value => isStatementPeriodWithinLimit(value.startDate, value.endDate), {
|
|
34
|
+
message: 'validation.reportPeriodMax',
|
|
35
|
+
path: ['endDate', 'startDate'],
|
|
36
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffSummaryByClientSchema } from './write-off-summary-by-client.schema';
|
|
3
|
+
import { WriteOffSummaryTotalsSchema } from './write-off-summary-totals.schema';
|
|
4
|
+
import { WriteOffSummaryWarehouseSchema } from './write-off-summary-warehouse.schema';
|
|
5
|
+
|
|
6
|
+
export const GetWriteOffSummaryResponseDataSchema = z.object({
|
|
7
|
+
totals: WriteOffSummaryTotalsSchema,
|
|
8
|
+
warehouse: WriteOffSummaryWarehouseSchema,
|
|
9
|
+
byClient: WriteOffSummaryByClientSchema,
|
|
10
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { GetWriteOffSummaryResponseDataSchema } from './get-write-off-summary-response-data.schema';
|
|
3
|
+
|
|
4
|
+
export const GetWriteOffSummaryResponseSchema = z.object({
|
|
5
|
+
message: z.string().optional(),
|
|
6
|
+
data: GetWriteOffSummaryResponseDataSchema,
|
|
7
|
+
});
|
package/schemas/report/index.ts
CHANGED
|
@@ -6,3 +6,28 @@ export * from './daily-orders-summary-by-day-item.schema';
|
|
|
6
6
|
export * from './daily-orders-summary-by-responsible-item.schema';
|
|
7
7
|
export * from './get-daily-orders-summary-response-data.schema';
|
|
8
8
|
export * from './get-daily-orders-summary-response.schema';
|
|
9
|
+
export * from './get-write-off-summary-request.schema';
|
|
10
|
+
export * from './write-off-summary-reason-row.schema';
|
|
11
|
+
export * from './write-off-summary-client-row.schema';
|
|
12
|
+
export * from './write-off-summary-totals.schema';
|
|
13
|
+
export * from './write-off-summary-warehouse.schema';
|
|
14
|
+
export * from './write-off-summary-by-client.schema';
|
|
15
|
+
export * from './get-write-off-summary-response-data.schema';
|
|
16
|
+
export * from './get-write-off-summary-response.schema';
|
|
17
|
+
export * from './write-off-report-page.schema';
|
|
18
|
+
export * from './get-write-off-breakdown-request.schema';
|
|
19
|
+
export * from './write-off-breakdown-totals.schema';
|
|
20
|
+
export * from './write-off-breakdown-row-base.schema';
|
|
21
|
+
export * from './write-off-breakdown-consignment.schema';
|
|
22
|
+
export * from './write-off-breakdown-consignment-row.schema';
|
|
23
|
+
export * from './write-off-breakdown-grower-row.schema';
|
|
24
|
+
export * from './write-off-breakdown-category-row.schema';
|
|
25
|
+
export * from './write-off-breakdown-reason-row.schema';
|
|
26
|
+
export * from './write-off-breakdown-row.schema';
|
|
27
|
+
export * from './get-write-off-breakdown-response-data.schema';
|
|
28
|
+
export * from './get-write-off-breakdown-response.schema';
|
|
29
|
+
export * from './get-write-off-by-client-request.schema';
|
|
30
|
+
export * from './write-off-by-client-totals.schema';
|
|
31
|
+
export * from './write-off-by-client-row.schema';
|
|
32
|
+
export * from './get-write-off-by-client-response-data.schema';
|
|
33
|
+
export * from './get-write-off-by-client-response.schema';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownGroupByEnum } from '../../enum/write-off-breakdown-group-by.enum';
|
|
3
|
+
import { WriteOffBreakdownRowBaseSchema } from './write-off-breakdown-row-base.schema';
|
|
4
|
+
|
|
5
|
+
export const WriteOffBreakdownCategoryRowSchema = WriteOffBreakdownRowBaseSchema.extend({
|
|
6
|
+
groupBy: z.literal(WriteOffBreakdownGroupByEnum.CATEGORY),
|
|
7
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownGroupByEnum } from '../../enum/write-off-breakdown-group-by.enum';
|
|
3
|
+
import { WriteOffBreakdownConsignmentSchema } from './write-off-breakdown-consignment.schema';
|
|
4
|
+
import { WriteOffBreakdownRowBaseSchema } from './write-off-breakdown-row-base.schema';
|
|
5
|
+
|
|
6
|
+
export const WriteOffBreakdownConsignmentRowSchema = WriteOffBreakdownRowBaseSchema.extend({
|
|
7
|
+
groupBy: z.literal(WriteOffBreakdownGroupByEnum.CONSIGNMENT),
|
|
8
|
+
consignment: WriteOffBreakdownConsignmentSchema,
|
|
9
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownGroupByEnum } from '../../enum/write-off-breakdown-group-by.enum';
|
|
3
|
+
import { WriteOffBreakdownRowBaseSchema } from './write-off-breakdown-row-base.schema';
|
|
4
|
+
|
|
5
|
+
export const WriteOffBreakdownGrowerRowSchema = WriteOffBreakdownRowBaseSchema.extend({
|
|
6
|
+
groupBy: z.literal(WriteOffBreakdownGroupByEnum.GROWER),
|
|
7
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownGroupByEnum } from '../../enum/write-off-breakdown-group-by.enum';
|
|
3
|
+
import { WriteOffBreakdownRowBaseSchema } from './write-off-breakdown-row-base.schema';
|
|
4
|
+
|
|
5
|
+
export const WriteOffBreakdownReasonRowSchema = WriteOffBreakdownRowBaseSchema.extend({
|
|
6
|
+
groupBy: z.literal(WriteOffBreakdownGroupByEnum.REASON),
|
|
7
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Общая часть строки разреза списаний.
|
|
6
|
+
*
|
|
7
|
+
* `key` — устойчивый идентификатор группы (uuid партии или категории, нормализованный
|
|
8
|
+
* производитель, причина); у групп «Без категории» и «Не указан» — пустая строка.
|
|
9
|
+
* `receivedCost` — база процента, `share` — доля списаний от неё в процентах с округлением на бэке;
|
|
10
|
+
* `null` там, где процент не считается, и при нулевой базе.
|
|
11
|
+
*/
|
|
12
|
+
export const WriteOffBreakdownRowBaseSchema = z.object({
|
|
13
|
+
key: z.string(),
|
|
14
|
+
label: z.string(),
|
|
15
|
+
units: z.number(),
|
|
16
|
+
cost: MoneySchema,
|
|
17
|
+
receivedCost: MoneySchema.nullable(),
|
|
18
|
+
share: z.number().nullable(),
|
|
19
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { WriteOffBreakdownCategoryRowSchema } from './write-off-breakdown-category-row.schema';
|
|
3
|
+
import { WriteOffBreakdownConsignmentRowSchema } from './write-off-breakdown-consignment-row.schema';
|
|
4
|
+
import { WriteOffBreakdownGrowerRowSchema } from './write-off-breakdown-grower-row.schema';
|
|
5
|
+
import { WriteOffBreakdownReasonRowSchema } from './write-off-breakdown-reason-row.schema';
|
|
6
|
+
|
|
7
|
+
/** Строка разреза: форма зависит от `groupBy`, поэтому фронт сужает тип по дискриминатору. */
|
|
8
|
+
export const WriteOffBreakdownRowSchema = z.discriminatedUnion('groupBy', [
|
|
9
|
+
WriteOffBreakdownConsignmentRowSchema,
|
|
10
|
+
WriteOffBreakdownGrowerRowSchema,
|
|
11
|
+
WriteOffBreakdownCategoryRowSchema,
|
|
12
|
+
WriteOffBreakdownReasonRowSchema,
|
|
13
|
+
]);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Итог разреза по всем строкам, независимо от `limit`/`offset`.
|
|
6
|
+
*
|
|
7
|
+
* `receivedCost` — себестоимость прихода всех партий периода, `share` — доля списаний от него в
|
|
8
|
+
* процентах. Обе величины есть только на оси `CONSIGNMENT_DELIVERY_DATE`: на оси даты списания
|
|
9
|
+
* товар мог прийти раньше периода, и делить его на приход периода некорректно.
|
|
10
|
+
*/
|
|
11
|
+
export const WriteOffBreakdownTotalsSchema = z.object({
|
|
12
|
+
units: z.number(),
|
|
13
|
+
cost: MoneySchema,
|
|
14
|
+
receivedCost: MoneySchema.nullable(),
|
|
15
|
+
share: z.number().nullable(),
|
|
16
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Клиент со списаниями по заказам периода.
|
|
6
|
+
*
|
|
7
|
+
* `revenue` — выручка всех заказов клиента за период (товары по цене продажи, без доставки и
|
|
8
|
+
* услуг), `share` — `writeOffOrderAmount ÷ revenue` в процентах; `null` при нулевой выручке.
|
|
9
|
+
*/
|
|
10
|
+
export const WriteOffByClientRowSchema = z.object({
|
|
11
|
+
clientUUID: z.string(),
|
|
12
|
+
clientName: z.string(),
|
|
13
|
+
revenue: MoneySchema,
|
|
14
|
+
ordersWithWriteOff: z.number(),
|
|
15
|
+
writeOffOrderAmount: MoneySchema,
|
|
16
|
+
cost: MoneySchema,
|
|
17
|
+
units: z.number(),
|
|
18
|
+
share: z.number().nullable(),
|
|
19
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Итог разреза по клиентам.
|
|
6
|
+
*
|
|
7
|
+
* `revenue` — выручка ВСЕХ клиентов периода, а не только тех, у кого были списания: `share` здесь —
|
|
8
|
+
* доля брака по клиентам вообще. Она не равна ни сумме, ни среднему долей по строкам.
|
|
9
|
+
* Списания (`writeOffOrderAmount`, `cost`, `units`) суммируются по всем строкам независимо от
|
|
10
|
+
* `limit`/`offset`.
|
|
11
|
+
*/
|
|
12
|
+
export const WriteOffByClientTotalsSchema = z.object({
|
|
13
|
+
revenue: MoneySchema,
|
|
14
|
+
writeOffOrderAmount: MoneySchema,
|
|
15
|
+
cost: MoneySchema,
|
|
16
|
+
units: z.number(),
|
|
17
|
+
share: z.number().nullable(),
|
|
18
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { REPORT_WRITE_OFF_BREAKDOWN_LIMIT_DEFAULT, REPORT_WRITE_OFF_BREAKDOWN_LIMIT_MAX } from '../../constant/report.constant';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Страница строк разреза списаний. Итоги от неё не зависят; чтобы выбрать список целиком (выгрузка
|
|
6
|
+
* в Excel), фронт идёт по `offset` с шагом `limit`, пока не наберёт `rowsTotal`.
|
|
7
|
+
*/
|
|
8
|
+
export const WriteOffReportPageSchema = z.object({
|
|
9
|
+
limit: z.coerce.number().int().positive().max(REPORT_WRITE_OFF_BREAKDOWN_LIMIT_MAX).default(REPORT_WRITE_OFF_BREAKDOWN_LIMIT_DEFAULT),
|
|
10
|
+
offset: z.coerce.number().int().nonnegative().default(0),
|
|
11
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
import { WriteOffSummaryClientRowSchema } from './write-off-summary-client-row.schema';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Списания по заказу: клиенту они уже вернулись на баланс, то есть это выручка, которой по факту
|
|
7
|
+
* не было.
|
|
8
|
+
*/
|
|
9
|
+
export const WriteOffSummaryByClientSchema = z.object({
|
|
10
|
+
units: z.number(),
|
|
11
|
+
cost: MoneySchema,
|
|
12
|
+
orderAmount: MoneySchema,
|
|
13
|
+
rows: WriteOffSummaryClientRowSchema.array(),
|
|
14
|
+
// Сколько клиентов всего — чтобы отличить усечённый ответ от полного.
|
|
15
|
+
rowsTotal: z.number(),
|
|
16
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
export const WriteOffSummaryClientRowSchema = z.object({
|
|
5
|
+
clientUUID: z.string(),
|
|
6
|
+
clientName: z.string(),
|
|
7
|
+
orderCount: z.number(),
|
|
8
|
+
units: z.number(),
|
|
9
|
+
cost: MoneySchema,
|
|
10
|
+
orderAmount: MoneySchema,
|
|
11
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
export const WriteOffSummaryReasonRowSchema = z.object({
|
|
5
|
+
// Свободный текст из справочника причин, схлопнут только по краевым пробелам (TRIM).
|
|
6
|
+
// Регистр не нормализуем: «Брак» и «брак» останутся разными строками.
|
|
7
|
+
reason: z.string(),
|
|
8
|
+
units: z.number(),
|
|
9
|
+
cost: MoneySchema,
|
|
10
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { MoneySchema } from '../money.schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* totals = warehouse + byClient, считается по всем строкам — усечение `clientLimit` на итоги
|
|
6
|
+
* не влияет.
|
|
7
|
+
*/
|
|
8
|
+
export const WriteOffSummaryTotalsSchema = z.object({
|
|
9
|
+
units: z.number(),
|
|
10
|
+
cost: MoneySchema,
|
|
11
|
+
// Себестоимость прихода партий периода и доля списаний от неё в процентах — только на
|
|
12
|
+
// оси CONSIGNMENT_DELIVERY_DATE, иначе null: на оси даты списания товар мог прийти
|
|
13
|
+
// раньше периода, и делить его на приход периода некорректно.
|
|
14
|
+
receivedCost: MoneySchema.nullable(),
|
|
15
|
+
share: z.number().nullable(),
|
|
16
|
+
});
|