fix(orders): incoming payload — optional fields use .nullish(), accept ISO date OR datetime

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
admin
2026-06-16 22:40:17 +03:00
parent 49ebe245d0
commit 49b13c6f41
+23 -17
View File
@@ -10,27 +10,33 @@ import { verifySiteKey } from '../sites/auth-verify.js';
const STATUSES = ['new', 'accepted', 'invoiced', 'paid', 'fulfilled', 'cancelled'] as const;
const VAT_VALUES = ['none', 'vat_0', 'vat_5', 'vat_7', 'vat_10', 'vat_20'] as const;
// Внешний (S2S) payload от сайта — толерантный
// Внешний (S2S) payload от сайта — толерантный. Optional поля = .nullish()
// (можно опустить в JSON, прислать null или валидное значение).
const isoDateOrDateTime = z.string().regex(
/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?)?$/,
'нужна дата ISO (YYYY-MM-DD) или datetime',
);
const IncomingItem = z.object({
name: z.string().min(1).max(500),
qty: z.coerce.number().positive().default(1),
unit: z.string().max(50).default('шт'),
priceRub: z.coerce.number().nonnegative(), // в рублях, чтобы сайту удобней
priceRub: z.coerce.number().nonnegative(),
vat: z.enum(VAT_VALUES).default('none'),
eventDate: z.string().datetime().nullable().optional(),
serviceSlug: z.string().optional(), // если сайт знает наш каталог
eventDate: isoDateOrDateTime.nullish(),
serviceSlug: z.string().optional(),
});
const IncomingOrder = z.object({
customerName: z.string().min(1).max(500),
customerEmail: optionalText(200),
customerPhone: optionalText(50),
customerInn: optionalText(20),
customerKpp: optionalText(20),
customerAddress: optionalText(1000),
customerEmail: z.string().email().nullish(),
customerPhone: z.string().max(50).nullish(),
customerInn: z.string().max(20).nullish(),
customerKpp: z.string().max(20).nullish(),
customerAddress: z.string().max(1000).nullish(),
customerKind: z.enum(['ul', 'ip', 'fl']).default('ul'),
notes: optionalText(2000),
acceptedOfferAt: z.string().datetime().nullable().optional(),
notes: z.string().max(2000).nullish(),
acceptedOfferAt: isoDateOrDateTime.nullish(),
items: z.array(IncomingItem).min(1),
});
@@ -161,17 +167,17 @@ export async function ordersRoutes(app: FastifyInstance) {
siteId: localSite?.id ?? null,
status: 'new',
customerName: data.customerName,
customerEmail: data.customerEmail,
customerPhone: data.customerPhone,
customerInn: data.customerInn,
customerKpp: data.customerKpp,
customerAddress: data.customerAddress,
customerEmail: data.customerEmail ?? null,
customerPhone: data.customerPhone ?? null,
customerInn: data.customerInn ?? null,
customerKpp: data.customerKpp ?? null,
customerAddress: data.customerAddress ?? null,
customerKind: data.customerKind,
totalCents: sums.totalCents,
vatCents: sums.vatCents,
currency: 'RUB',
acceptedOfferAt: data.acceptedOfferAt ? new Date(data.acceptedOfferAt) : null,
notes: data.notes,
notes: data.notes ?? null,
rawPayload: req.body as Prisma.InputJsonValue,
items: {
create: data.items.map((it, i) => {