fix(insuranceId space issue)
This commit is contained in:
@@ -3,6 +3,7 @@ import type { Request, Response } from "express";
|
|||||||
import { storage } from "../storage";
|
import { storage } from "../storage";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { insertPatientSchema, updatePatientSchema } from "@repo/db/types";
|
import { insertPatientSchema, updatePatientSchema } from "@repo/db/types";
|
||||||
|
import { normalizeInsuranceId } from "../utils/helpers";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@@ -160,6 +161,18 @@ router.get(
|
|||||||
// Create a new patient
|
// Create a new patient
|
||||||
router.post("/", async (req: Request, res: Response): Promise<any> => {
|
router.post("/", async (req: Request, res: Response): Promise<any> => {
|
||||||
try {
|
try {
|
||||||
|
const body: any = { ...req.body, userId: req.user!.id };
|
||||||
|
|
||||||
|
// Normalize insuranceId early and return clear error if invalid
|
||||||
|
try {
|
||||||
|
const normalized = normalizeInsuranceId(body.insuranceId);
|
||||||
|
body.insuranceId = normalized;
|
||||||
|
} catch (err: any) {
|
||||||
|
return res.status(400).json({
|
||||||
|
message: "Invalid insuranceId",
|
||||||
|
details: err?.message ?? "Invalid insuranceId format",
|
||||||
|
});
|
||||||
|
}
|
||||||
// Validate request body
|
// Validate request body
|
||||||
const patientData = insertPatientSchema.parse({
|
const patientData = insertPatientSchema.parse({
|
||||||
...req.body,
|
...req.body,
|
||||||
@@ -169,7 +182,7 @@ router.post("/", async (req: Request, res: Response): Promise<any> => {
|
|||||||
// Check for duplicate insuranceId if it's provided
|
// Check for duplicate insuranceId if it's provided
|
||||||
if (patientData.insuranceId) {
|
if (patientData.insuranceId) {
|
||||||
const existingPatient = await storage.getPatientByInsuranceId(
|
const existingPatient = await storage.getPatientByInsuranceId(
|
||||||
patientData.insuranceId
|
patientData.insuranceId as string
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingPatient) {
|
if (existingPatient) {
|
||||||
@@ -201,6 +214,18 @@ router.put(
|
|||||||
try {
|
try {
|
||||||
const patientIdParam = req.params.id;
|
const patientIdParam = req.params.id;
|
||||||
|
|
||||||
|
// Normalize incoming insuranceId (if present)
|
||||||
|
try {
|
||||||
|
if (req.body.insuranceId !== undefined) {
|
||||||
|
req.body.insuranceId = normalizeInsuranceId(req.body.insuranceId);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
return res.status(400).json({
|
||||||
|
message: "Invalid insuranceId",
|
||||||
|
details: err?.message ?? "Invalid insuranceId format",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that patientIdParam exists and is a valid number
|
// Ensure that patientIdParam exists and is a valid number
|
||||||
if (!patientIdParam) {
|
if (!patientIdParam) {
|
||||||
return res.status(400).json({ message: "Patient ID is required" });
|
return res.status(400).json({ message: "Patient ID is required" });
|
||||||
@@ -223,7 +248,7 @@ router.put(
|
|||||||
patientData.insuranceId !== existingPatient.insuranceId
|
patientData.insuranceId !== existingPatient.insuranceId
|
||||||
) {
|
) {
|
||||||
const duplicatePatient = await storage.getPatientByInsuranceId(
|
const duplicatePatient = await storage.getPatientByInsuranceId(
|
||||||
patientData.insuranceId
|
patientData.insuranceId as string
|
||||||
);
|
);
|
||||||
if (duplicatePatient && duplicatePatient.id !== patientId) {
|
if (duplicatePatient && duplicatePatient.id !== patientId) {
|
||||||
return res.status(409).json({
|
return res.status(409).json({
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export const patientsStorage: IStorage = {
|
|||||||
try {
|
try {
|
||||||
return await db.patient.update({
|
return await db.patient.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: updateData,
|
data: updateData as Patient,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Patient with ID ${id} not found`);
|
throw new Error(`Patient with ID ${id} not found`);
|
||||||
|
|||||||
27
apps/Backend/src/utils/helpers.ts
Normal file
27
apps/Backend/src/utils/helpers.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export function normalizeInsuranceId(raw: unknown): string | undefined {
|
||||||
|
if (raw === undefined || raw === null) return undefined;
|
||||||
|
|
||||||
|
// Accept numbers too (e.g. 12345), but prefer strings
|
||||||
|
let s: string;
|
||||||
|
if (typeof raw === "number") {
|
||||||
|
s = String(raw);
|
||||||
|
} else if (typeof raw === "string") {
|
||||||
|
s = raw;
|
||||||
|
} else {
|
||||||
|
// Not acceptable type
|
||||||
|
throw new Error("Insurance ID must be a numeric string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all whitespace
|
||||||
|
const cleaned = s.replace(/\s+/g, "");
|
||||||
|
|
||||||
|
// If empty after cleaning, treat as undefined
|
||||||
|
if (cleaned === "") return undefined;
|
||||||
|
|
||||||
|
// Only digits allowed (since you said it's numeric)
|
||||||
|
if (!/^\d+$/.test(cleaned)) {
|
||||||
|
throw new Error("Insurance ID must contain only digits.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
@@ -1,14 +1,44 @@
|
|||||||
import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
|
||||||
import {z} from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export type Patient = z.infer<typeof PatientUncheckedCreateInputObjectSchema>;
|
export type Patient = z.infer<typeof PatientUncheckedCreateInputObjectSchema>;
|
||||||
|
|
||||||
|
export const insuranceIdSchema = z.preprocess(
|
||||||
|
(val) => {
|
||||||
|
if (val === undefined || val === null) return undefined;
|
||||||
|
|
||||||
|
// Accept numbers and strings
|
||||||
|
if (typeof val === "number") {
|
||||||
|
return String(val).replace(/\s+/g, "");
|
||||||
|
}
|
||||||
|
if (typeof val === "string") {
|
||||||
|
const cleaned = val.replace(/\s+/g, "");
|
||||||
|
if (cleaned === "") return undefined;
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
},
|
||||||
|
// After preprocess, require digits-only string (or optional nullable)
|
||||||
|
z
|
||||||
|
.string()
|
||||||
|
.regex(/^\d+$/, { message: "Insurance ID must contain only digits" })
|
||||||
|
.min(1)
|
||||||
|
.max(32)
|
||||||
|
.optional()
|
||||||
|
.nullable()
|
||||||
|
);
|
||||||
|
|
||||||
export const insertPatientSchema = (
|
export const insertPatientSchema = (
|
||||||
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||||
).omit({
|
)
|
||||||
id: true,
|
.omit({
|
||||||
createdAt: true,
|
id: true,
|
||||||
});
|
createdAt: true,
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
insuranceId: insuranceIdSchema, // enforce numeric insuranceId
|
||||||
|
});
|
||||||
|
|
||||||
export type InsertPatient = z.infer<typeof insertPatientSchema>;
|
export type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||||
|
|
||||||
export const updatePatientSchema = (
|
export const updatePatientSchema = (
|
||||||
@@ -19,6 +49,9 @@ export const updatePatientSchema = (
|
|||||||
createdAt: true,
|
createdAt: true,
|
||||||
userId: true,
|
userId: true,
|
||||||
})
|
})
|
||||||
.partial();
|
.partial()
|
||||||
|
.extend({
|
||||||
|
insuranceId: insuranceIdSchema, // enforce numeric insuranceId
|
||||||
|
});
|
||||||
|
|
||||||
export type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
export type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||||
Reference in New Issue
Block a user