PPPAIMT-Crest/backend/src/schemas/application.js

70 lines
2.7 KiB
JavaScript

import { z } from 'zod';
import { stripHtml } from '../services/sanitize.js';
const panPattern = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
const ifscPattern = /^[A-Z]{4}0[A-Z0-9]{6}$/;
const gstinPattern = /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/;
const accountPattern = /^[0-9]{9,18}$/;
const emptyToUndefined = (value) => (value === '' || value === null ? undefined : value);
/**
* @param {z.ZodTypeAny} schema
*/
function text(schema) {
return z.preprocess((value) => (typeof value === 'string' ? stripHtml(value) : value), schema);
}
export const applicationSchema = z.object({
agreement_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
full_name: text(z.string().trim().min(1)),
age: z.number().int().min(21).max(100),
address: text(z.string().trim().min(1)),
registration_no: text(z.string().trim().min(1)),
medical_council: text(z.string().trim().min(1)),
pan: text(z.string().regex(panPattern)),
gstin: z.preprocess(emptyToUndefined, text(z.string().regex(gstinPattern)).optional()),
bank_name: text(z.string().trim().min(1)),
account_holder: text(z.string().trim().min(1)),
account_number: text(z.string().regex(accountPattern)),
ifsc: text(z.string().regex(ifscPattern)),
});
const emptyToNull = (value) => (value === '' ? null : value);
const optionalText = (schema) => z.preprocess(emptyToNull, schema.nullable().optional());
export const draftApplicationSchema = z
.object({
agreement_date: optionalText(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)),
full_name: optionalText(text(z.string().trim().min(1))),
age: z.preprocess((value) => {
if (value === '' || value === null) {
return null;
}
if (typeof value === 'string' && /^\d+$/.test(value)) {
return Number(value);
}
return value;
}, z.number().int().min(21).max(100).nullable().optional()),
address: optionalText(text(z.string().trim().min(1))),
registration_no: optionalText(text(z.string().trim().min(1))),
medical_council: optionalText(text(z.string().trim().min(1))),
pan: optionalText(text(z.string().regex(panPattern))),
gstin: optionalText(text(z.string().regex(gstinPattern))),
bank_name: optionalText(text(z.string().trim().min(1))),
account_holder: optionalText(text(z.string().trim().min(1))),
account_number: optionalText(text(z.string().regex(accountPattern))),
ifsc: optionalText(text(z.string().regex(ifscPattern))),
accepted: z.boolean().optional(),
/** Optional cancelled-cheque image as a data URL, or null to remove. */
cancelled_cheque: z.union([z.string().min(1), z.null()]).optional(),
})
.strict();
export const finalizeApplicationSchema = applicationSchema.extend({
accepted: z.literal(true),
});