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:
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user