resolved patient sync issue

This commit is contained in:
2026-09-19 11:14:50 +05:30
parent f7d76c915b
commit a1675428cf
5 changed files with 149 additions and 55 deletions
+2
View File
@@ -57,7 +57,9 @@ app.use('/v1/rx', prescriptionRouter);
app.use('/v1/prescriptions', prescriptionRouter);
app.use('/v1/ai', aiRouter);
app.use('/v1/integration', integrationRouter);
app.use('/integration', integrationRouter);
app.use('/v1/patients', integrationRouter);
app.use('/patients', integrationRouter);
app.use('/v1/upload', uploadRouter);
app.use(viewerRouter);
+1 -1
View File
@@ -16,7 +16,7 @@ declare global {
export const requireApiKey: RequestHandler = (req: Request, res: Response, next: NextFunction): void => {
// Allow /v1/integration, /v1/patients, /health, and /db-viewer routes to validate via their own auth or be publicly checked
if (req.originalUrl.startsWith('/v1/integration') || req.originalUrl.startsWith('/v1/patients') || req.originalUrl === '/health' || req.originalUrl.startsWith('/health') || req.originalUrl.startsWith('/db-viewer') || req.originalUrl.startsWith('/api/db')) {
if (req.originalUrl.startsWith('/v1/integration') || req.originalUrl.startsWith('/integration') || req.originalUrl.startsWith('/v1/patients') || req.originalUrl.startsWith('/patients') || req.originalUrl === '/health' || req.originalUrl.startsWith('/health') || req.originalUrl.startsWith('/db-viewer') || req.originalUrl.startsWith('/api/db')) {
return next();
}
+73 -10
View File
@@ -8,9 +8,15 @@ export class IntegrationController {
try {
const payload = req.body;
const patientId = payload.hospital_patient_id || payload.patient_id || payload.patientId || payload.id;
const fullName = payload.full_name || payload.patient_name || payload.patientName || payload.name;
// Basic validation
if (!payload.hospital_patient_id || !payload.full_name) {
return res.status(400).json({ error: 'Missing required patient fields' });
if (!patientId || !fullName) {
return res.status(400).json({
error: 'Missing required patient fields',
details: 'Please include hospital_patient_id (or patient_id) and full_name (or name) in your JSON request body.'
});
}
// Save or update the patient in Pharmacy DB
@@ -29,9 +35,41 @@ export class IntegrationController {
// Endpoint to fetch all patients synced to Pharmacy DB
static async getAllPatients(req: Request, res: Response) {
try {
const tenantId = req.headers['x-tenant-id'] || req.tenantId || 'act_admin';
const hospitalId = req.headers['x-hospital-id'] || req.hospitalId || '1';
const patients = await IntegrationService.getAllPatients(String(tenantId), String(hospitalId));
let tenantId = (req.body?.tenant_id || req.body?.tenantId || req.query?.tenant_id || req.query?.tenantId || req.headers['x-tenant-id'] || req.tenantId || req.body?.hospital_id || req.body?.hospitalId || req.query?.hospital_id || req.query?.hospitalId || req.headers['x-hospital-id'] || req.hospitalId) as string | undefined;
let hospitalId = (req.body?.hospital_id || req.body?.hospitalId || req.query?.hospital_id || req.query?.hospitalId || req.headers['x-hospital-id'] || req.hospitalId) as string | undefined;
// Extract from Bearer token if headers not explicitly passed
if (req.headers.authorization) {
try {
const token = req.headers.authorization.replace('Bearer ', '').trim();
const jwt = require('jsonwebtoken');
const secret = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
const decoded = jwt.verify(token, secret) as any;
if (decoded) {
tenantId = (tenantId && tenantId !== 'act_admin') ? tenantId : (decoded.tenant_id || decoded.tenantId || tenantId);
hospitalId = (hospitalId && hospitalId !== 'undefined' && hospitalId !== 'null') ? hospitalId : (decoded.hospital_id || decoded.hospitalId || hospitalId);
}
} catch (e) {}
}
// If tenantId is a branch tenant_id (e.g. 'pharm_mkkhospital_...'), fetch its parent hospital_id from users DB
if (tenantId && tenantId.startsWith('pharm_') && (!hospitalId || hospitalId === 'undefined' || hospitalId === 'null')) {
try {
const userRes = await require('../../db/pool').pool.query(
'SELECT hospital_id FROM users WHERE tenant_id = $1 LIMIT 1',
[tenantId]
);
if (userRes.rows.length > 0 && userRes.rows[0].hospital_id) {
hospitalId = String(userRes.rows[0].hospital_id);
}
} catch (e) {}
}
const patients = await IntegrationService.getAllPatients(
tenantId ? String(tenantId) : '',
hospitalId ? String(hospitalId) : undefined
);
res.status(200).json({
message: 'Patients fetched successfully',
data: patients
@@ -159,18 +197,43 @@ export class IntegrationController {
static async getAllPrescriptions(req: Request, res: Response) {
try {
const tenantId = req.headers['x-tenant-id'] || req.tenantId || 'act_admin';
const hospitalId = req.headers['x-hospital-id'] || req.hospitalId || '1';
let tenantId = (req.body?.tenant_id || req.body?.tenantId || req.query?.tenant_id || req.query?.tenantId || req.headers['x-tenant-id'] || req.tenantId || req.body?.hospital_id || req.body?.hospitalId || req.query?.hospital_id || req.query?.hospitalId || req.headers['x-hospital-id'] || req.hospitalId) as string | undefined;
let hospitalId = (req.body?.hospital_id || req.body?.hospitalId || req.query?.hospital_id || req.query?.hospitalId || req.headers['x-hospital-id'] || req.hospitalId) as string | undefined;
if (req.headers.authorization) {
try {
const token = req.headers.authorization.replace('Bearer ', '').trim();
const jwt = require('jsonwebtoken');
const secret = process.env.JWT_SECRET || 'super_secret_pharmacy_key_2026';
const decoded = jwt.verify(token, secret) as any;
if (decoded) {
tenantId = (tenantId && tenantId !== 'act_admin') ? tenantId : (decoded.tenant_id || decoded.tenantId || tenantId);
hospitalId = (hospitalId && hospitalId !== 'undefined' && hospitalId !== 'null') ? hospitalId : (decoded.hospital_id || decoded.hospitalId || hospitalId);
}
} catch (e) {}
}
if (tenantId && tenantId.startsWith('pharm_') && (!hospitalId || hospitalId === 'undefined' || hospitalId === 'null')) {
try {
const clientPool = require('../../db/pool').pool;
const r = await clientPool.query('SELECT hospital_id FROM users WHERE tenant_id = $1 LIMIT 1', [tenantId]);
if (r.rows.length > 0 && r.rows[0].hospital_id) {
hospitalId = String(r.rows[0].hospital_id);
}
} catch (e) {}
}
const client = await require('../../db/pool').pool.connect();
try {
let query: string;
let values: any[];
if (!tenantId || tenantId === 'act_admin' || tenantId === 'all' || tenantId === 'undefined' || tenantId === 'null') {
if (!tenantId || tenantId === 'all' || tenantId === 'superadmin' || tenantId === 'undefined' || tenantId === 'null') {
query = `SELECT * FROM prescriptions ORDER BY created_at DESC;`;
values = [];
} else {
query = `SELECT * FROM prescriptions WHERE tenant_id = $1 OR tenant_id = $2 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL ORDER BY created_at DESC;`;
values = [String(tenantId), String(hospitalId)];
const hospId = hospitalId ? String(hospitalId) : String(tenantId);
query = `SELECT * FROM prescriptions WHERE tenant_id = $1 OR tenant_id = $2 ORDER BY created_at DESC;`;
values = [String(tenantId), hospId];
}
const result = await client.query(query, values);
res.status(200).json({ message: 'All prescriptions', data: result.rows });
+31 -28
View File
@@ -13,35 +13,38 @@ export class IntegrationService {
const allergies = payload.allergies ? (typeof payload.allergies === 'string' ? payload.allergies : JSON.stringify(payload.allergies)) : null;
const tenantId = payload.hospital_id || payload.tenant_id || payload.tenantId ? String(payload.hospital_id || payload.tenant_id || payload.tenantId) : 'act_admin';
console.log('[IntegrationService] Syncing patient from Hospital API:', hospitalPatientId, fullName);
console.log('[IntegrationService] Syncing patient from Hospital API:', hospitalPatientId, fullName, 'Tenant:', tenantId);
const client = await pool.connect();
try {
const query = `
INSERT INTO patients (hospital_patient_id, full_name, contact_number, email, allergies, tenant_id)
VALUES ($1, $2, $3, $4, $5, $6)
ON CONFLICT (hospital_patient_id)
DO UPDATE SET
full_name = EXCLUDED.full_name,
contact_number = COALESCE(EXCLUDED.contact_number, patients.contact_number),
email = COALESCE(EXCLUDED.email, patients.email),
allergies = COALESCE(EXCLUDED.allergies, patients.allergies),
tenant_id = EXCLUDED.tenant_id,
updated_at = CURRENT_TIMESTAMP
RETURNING id, hospital_patient_id, full_name;
`;
const values = [
hospitalPatientId,
fullName,
contactNumber,
email,
allergies,
tenantId
];
const checkRes = await client.query(
'SELECT id FROM patients WHERE hospital_patient_id = $1 AND tenant_id = $2 LIMIT 1',
[hospitalPatientId, tenantId]
);
const result = await client.query(query, values);
return result.rows[0];
if (checkRes.rows.length > 0) {
const updateRes = await client.query(
`UPDATE patients
SET full_name = $1,
contact_number = COALESCE($2, contact_number),
email = COALESCE($3, email),
allergies = COALESCE($4, allergies),
updated_at = CURRENT_TIMESTAMP
WHERE hospital_patient_id = $5 AND tenant_id = $6
RETURNING id, hospital_patient_id, full_name, tenant_id`,
[fullName, contactNumber, email, allergies, hospitalPatientId, tenantId]
);
return updateRes.rows[0];
} else {
const id = require('crypto').randomUUID();
const insertRes = await client.query(
`INSERT INTO patients (id, hospital_patient_id, full_name, contact_number, email, allergies, tenant_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, hospital_patient_id, full_name, tenant_id`,
[id, hospitalPatientId, fullName, contactNumber, email, allergies, tenantId]
);
return insertRes.rows[0];
}
} finally {
client.release();
}
@@ -55,12 +58,12 @@ export class IntegrationService {
try {
let query: string;
let values: any[];
if (!tenantId || tenantId === 'act_admin' || tenantId === 'all' || tenantId === 'undefined' || tenantId === 'null') {
if (!tenantId || tenantId === 'all' || tenantId === 'superadmin' || tenantId === 'undefined' || tenantId === 'null') {
query = `SELECT * FROM patients ORDER BY created_at DESC;`;
values = [];
} else {
const hospId = hospitalId || '1';
query = `SELECT * FROM patients WHERE tenant_id = $1 OR tenant_id = $2 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL ORDER BY created_at DESC;`;
const hospId = hospitalId && hospitalId !== 'undefined' && hospitalId !== 'null' ? hospitalId : tenantId;
query = `SELECT * FROM patients WHERE tenant_id = $1 OR tenant_id = $2 ORDER BY created_at DESC;`;
values = [tenantId, hospId];
}
const result = await client.query(query, values);
+42 -16
View File
@@ -133,20 +133,32 @@ export class PrescriptionService {
}
async getPrescription(id: string, actorId: string, hospitalId?: string) {
const hospId = hospitalId || '1';
const { rows } = await pool.query(`SELECT * FROM prescriptions WHERE id = $1 AND (tenant_id = $2 OR tenant_id = $3 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL)`, [id, actorId, hospId]);
const hospId = hospitalId || actorId;
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
const { rows } = await pool.query(`SELECT * FROM prescriptions WHERE id = $1`, [id]);
return rows[0];
}
const { rows } = await pool.query(`SELECT * FROM prescriptions WHERE id = $1 AND (tenant_id = $2 OR tenant_id = $3)`, [id, actorId, hospId]);
return rows[0];
}
async getAllPrescriptions(actorId: string, hospitalId?: string) {
let query: string;
let values: any[];
if (!actorId || actorId === 'act_admin' || actorId === 'all' || actorId === 'undefined' || actorId === 'null') {
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
query = `SELECT * FROM prescriptions ORDER BY created_at DESC`;
values = [];
} else {
const hospId = hospitalId || '1';
query = `SELECT * FROM prescriptions WHERE tenant_id = $1 OR tenant_id = $2 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL ORDER BY created_at DESC`;
let hospId = hospitalId || actorId;
if (actorId.startsWith('pharm_') && hospId === actorId) {
try {
const userRes = await pool.query('SELECT hospital_id FROM users WHERE tenant_id = $1 LIMIT 1', [actorId]);
if (userRes.rows.length > 0 && userRes.rows[0].hospital_id) {
hospId = String(userRes.rows[0].hospital_id);
}
} catch (e) {}
}
query = `SELECT * FROM prescriptions WHERE tenant_id = $1 OR tenant_id = $2 ORDER BY created_at DESC`;
values = [actorId, hospId];
}
const { rows } = await pool.query(query, values);
@@ -154,12 +166,18 @@ export class PrescriptionService {
}
async confirmPrescription(id: string, confirmedData: any, actorId: string, hospitalId?: string) {
const hospId = hospitalId || '1';
// Human Gate 1: Validate that unconfirmed fields are explicitly reviewed
const { rows } = await pool.query(
`SELECT status, extracted_data FROM prescriptions WHERE id = $1 AND (tenant_id = $2 OR tenant_id = $3 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL)`,
[id, actorId, hospId]
);
const hospId = hospitalId || actorId;
let rows: any[];
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
const res = await pool.query(`SELECT status, extracted_data FROM prescriptions WHERE id = $1`, [id]);
rows = res.rows;
} else {
const res = await pool.query(
`SELECT status, extracted_data FROM prescriptions WHERE id = $1 AND (tenant_id = $2 OR tenant_id = $3)`,
[id, actorId, hospId]
);
rows = res.rows;
}
const record = rows[0];
if (!record) throw { status: 404, error: 'NOT_FOUND' };
@@ -184,11 +202,19 @@ export class PrescriptionService {
}
}
await pool.query(`
UPDATE prescriptions
SET status = 'confirmed', confirmed_data = $1
WHERE id = $2 AND (tenant_id = $3 OR tenant_id = $4 OR tenant_id = '1' OR tenant_id = 'act_admin' OR tenant_id IS NULL)
`, [confirmedData, id, actorId, hospId]);
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
await pool.query(`
UPDATE prescriptions
SET status = 'confirmed', confirmed_data = $1
WHERE id = $2
`, [confirmedData, id]);
} else {
await pool.query(`
UPDATE prescriptions
SET status = 'confirmed', confirmed_data = $1
WHERE id = $2 AND (tenant_id = $3 OR tenant_id = $4)
`, [confirmedData, id, actorId, hospId]);
}
return { id, status: 'confirmed' };
}