resolved patient sync issue
This commit is contained in:
@@ -57,7 +57,9 @@ app.use('/v1/rx', prescriptionRouter);
|
|||||||
app.use('/v1/prescriptions', prescriptionRouter);
|
app.use('/v1/prescriptions', prescriptionRouter);
|
||||||
app.use('/v1/ai', aiRouter);
|
app.use('/v1/ai', aiRouter);
|
||||||
app.use('/v1/integration', integrationRouter);
|
app.use('/v1/integration', integrationRouter);
|
||||||
|
app.use('/integration', integrationRouter);
|
||||||
app.use('/v1/patients', integrationRouter);
|
app.use('/v1/patients', integrationRouter);
|
||||||
|
app.use('/patients', integrationRouter);
|
||||||
app.use('/v1/upload', uploadRouter);
|
app.use('/v1/upload', uploadRouter);
|
||||||
app.use(viewerRouter);
|
app.use(viewerRouter);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ declare global {
|
|||||||
|
|
||||||
export const requireApiKey: RequestHandler = (req: Request, res: Response, next: NextFunction): void => {
|
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
|
// 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();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ export class IntegrationController {
|
|||||||
try {
|
try {
|
||||||
const payload = req.body;
|
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
|
// Basic validation
|
||||||
if (!payload.hospital_patient_id || !payload.full_name) {
|
if (!patientId || !fullName) {
|
||||||
return res.status(400).json({ error: 'Missing required patient fields' });
|
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
|
// Save or update the patient in Pharmacy DB
|
||||||
@@ -29,9 +35,41 @@ export class IntegrationController {
|
|||||||
// Endpoint to fetch all patients synced to Pharmacy DB
|
// Endpoint to fetch all patients synced to Pharmacy DB
|
||||||
static async getAllPatients(req: Request, res: Response) {
|
static async getAllPatients(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const tenantId = req.headers['x-tenant-id'] || req.tenantId || 'act_admin';
|
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;
|
||||||
const hospitalId = req.headers['x-hospital-id'] || req.hospitalId || '1';
|
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;
|
||||||
const patients = await IntegrationService.getAllPatients(String(tenantId), String(hospitalId));
|
|
||||||
|
// 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({
|
res.status(200).json({
|
||||||
message: 'Patients fetched successfully',
|
message: 'Patients fetched successfully',
|
||||||
data: patients
|
data: patients
|
||||||
@@ -159,18 +197,43 @@ export class IntegrationController {
|
|||||||
|
|
||||||
static async getAllPrescriptions(req: Request, res: Response) {
|
static async getAllPrescriptions(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const tenantId = req.headers['x-tenant-id'] || req.tenantId || 'act_admin';
|
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;
|
||||||
const hospitalId = req.headers['x-hospital-id'] || req.hospitalId || '1';
|
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();
|
const client = await require('../../db/pool').pool.connect();
|
||||||
try {
|
try {
|
||||||
let query: string;
|
let query: string;
|
||||||
let values: any[];
|
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;`;
|
query = `SELECT * FROM prescriptions ORDER BY created_at DESC;`;
|
||||||
values = [];
|
values = [];
|
||||||
} else {
|
} 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;`;
|
const hospId = hospitalId ? String(hospitalId) : String(tenantId);
|
||||||
values = [String(tenantId), String(hospitalId)];
|
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);
|
const result = await client.query(query, values);
|
||||||
res.status(200).json({ message: 'All prescriptions', data: result.rows });
|
res.status(200).json({ message: 'All prescriptions', data: result.rows });
|
||||||
|
|||||||
@@ -13,35 +13,38 @@ export class IntegrationService {
|
|||||||
const allergies = payload.allergies ? (typeof payload.allergies === 'string' ? payload.allergies : JSON.stringify(payload.allergies)) : null;
|
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';
|
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();
|
const client = await pool.connect();
|
||||||
try {
|
try {
|
||||||
const query = `
|
const checkRes = await client.query(
|
||||||
INSERT INTO patients (hospital_patient_id, full_name, contact_number, email, allergies, tenant_id)
|
'SELECT id FROM patients WHERE hospital_patient_id = $1 AND tenant_id = $2 LIMIT 1',
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
[hospitalPatientId, tenantId]
|
||||||
ON CONFLICT (hospital_patient_id)
|
);
|
||||||
DO UPDATE SET
|
|
||||||
full_name = EXCLUDED.full_name,
|
if (checkRes.rows.length > 0) {
|
||||||
contact_number = COALESCE(EXCLUDED.contact_number, patients.contact_number),
|
const updateRes = await client.query(
|
||||||
email = COALESCE(EXCLUDED.email, patients.email),
|
`UPDATE patients
|
||||||
allergies = COALESCE(EXCLUDED.allergies, patients.allergies),
|
SET full_name = $1,
|
||||||
tenant_id = EXCLUDED.tenant_id,
|
contact_number = COALESCE($2, contact_number),
|
||||||
|
email = COALESCE($3, email),
|
||||||
|
allergies = COALESCE($4, allergies),
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
RETURNING id, hospital_patient_id, full_name;
|
WHERE hospital_patient_id = $5 AND tenant_id = $6
|
||||||
`;
|
RETURNING id, hospital_patient_id, full_name, tenant_id`,
|
||||||
|
[fullName, contactNumber, email, allergies, hospitalPatientId, tenantId]
|
||||||
const values = [
|
);
|
||||||
hospitalPatientId,
|
return updateRes.rows[0];
|
||||||
fullName,
|
} else {
|
||||||
contactNumber,
|
const id = require('crypto').randomUUID();
|
||||||
email,
|
const insertRes = await client.query(
|
||||||
allergies,
|
`INSERT INTO patients (id, hospital_patient_id, full_name, contact_number, email, allergies, tenant_id)
|
||||||
tenantId
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||||
];
|
RETURNING id, hospital_patient_id, full_name, tenant_id`,
|
||||||
|
[id, hospitalPatientId, fullName, contactNumber, email, allergies, tenantId]
|
||||||
const result = await client.query(query, values);
|
);
|
||||||
return result.rows[0];
|
return insertRes.rows[0];
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
client.release();
|
client.release();
|
||||||
}
|
}
|
||||||
@@ -55,12 +58,12 @@ export class IntegrationService {
|
|||||||
try {
|
try {
|
||||||
let query: string;
|
let query: string;
|
||||||
let values: any[];
|
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;`;
|
query = `SELECT * FROM patients ORDER BY created_at DESC;`;
|
||||||
values = [];
|
values = [];
|
||||||
} else {
|
} else {
|
||||||
const hospId = hospitalId || '1';
|
const hospId = hospitalId && hospitalId !== 'undefined' && hospitalId !== 'null' ? hospitalId : tenantId;
|
||||||
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;`;
|
query = `SELECT * FROM patients WHERE tenant_id = $1 OR tenant_id = $2 ORDER BY created_at DESC;`;
|
||||||
values = [tenantId, hospId];
|
values = [tenantId, hospId];
|
||||||
}
|
}
|
||||||
const result = await client.query(query, values);
|
const result = await client.query(query, values);
|
||||||
|
|||||||
@@ -133,20 +133,32 @@ export class PrescriptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getPrescription(id: string, actorId: string, hospitalId?: string) {
|
async getPrescription(id: string, actorId: string, hospitalId?: string) {
|
||||||
const hospId = hospitalId || '1';
|
const hospId = hospitalId || actorId;
|
||||||
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]);
|
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];
|
return rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAllPrescriptions(actorId: string, hospitalId?: string) {
|
async getAllPrescriptions(actorId: string, hospitalId?: string) {
|
||||||
let query: string;
|
let query: string;
|
||||||
let values: any[];
|
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`;
|
query = `SELECT * FROM prescriptions ORDER BY created_at DESC`;
|
||||||
values = [];
|
values = [];
|
||||||
} else {
|
} else {
|
||||||
const hospId = hospitalId || '1';
|
let hospId = hospitalId || actorId;
|
||||||
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`;
|
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];
|
values = [actorId, hospId];
|
||||||
}
|
}
|
||||||
const { rows } = await pool.query(query, values);
|
const { rows } = await pool.query(query, values);
|
||||||
@@ -154,12 +166,18 @@ export class PrescriptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async confirmPrescription(id: string, confirmedData: any, actorId: string, hospitalId?: string) {
|
async confirmPrescription(id: string, confirmedData: any, actorId: string, hospitalId?: string) {
|
||||||
const hospId = hospitalId || '1';
|
const hospId = hospitalId || actorId;
|
||||||
// Human Gate 1: Validate that unconfirmed fields are explicitly reviewed
|
let rows: any[];
|
||||||
const { rows } = await pool.query(
|
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
|
||||||
`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)`,
|
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]
|
[id, actorId, hospId]
|
||||||
);
|
);
|
||||||
|
rows = res.rows;
|
||||||
|
}
|
||||||
const record = rows[0];
|
const record = rows[0];
|
||||||
|
|
||||||
if (!record) throw { status: 404, error: 'NOT_FOUND' };
|
if (!record) throw { status: 404, error: 'NOT_FOUND' };
|
||||||
@@ -184,11 +202,19 @@ export class PrescriptionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!actorId || actorId === 'all' || actorId === 'superadmin' || actorId === 'undefined' || actorId === 'null') {
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
UPDATE prescriptions
|
UPDATE prescriptions
|
||||||
SET status = 'confirmed', confirmed_data = $1
|
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)
|
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]);
|
`, [confirmedData, id, actorId, hospId]);
|
||||||
|
}
|
||||||
|
|
||||||
return { id, status: 'confirmed' };
|
return { id, status: 'confirmed' };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user