480 lines
18 KiB
TypeScript
480 lines
18 KiB
TypeScript
/**
|
|
* Claims Routes - FULL CRUD
|
|
*
|
|
* All claim-related tables are leaf nodes (no children), can be deleted directly:
|
|
* - Claim Status (VT, LT, UV, LF, VF, OP, NV)
|
|
* - Claim Type (EF, VF, RE, SC, QA, CC, PC, OF, VC)
|
|
* - Confidence levels
|
|
* - Interpretation (source concordance)
|
|
*/
|
|
|
|
import { Router, Request, Response } from 'express';
|
|
import { query, queryOne, transaction } from '../config/database';
|
|
import { ApiResponse } from '../types';
|
|
import { PoolClient } from 'pg';
|
|
import { internalError } from '../config/error-response';
|
|
|
|
const router = Router();
|
|
|
|
// ============================================================================
|
|
// HELPER FUNCTIONS
|
|
// ============================================================================
|
|
|
|
const createParameter = async (client: PoolClient, parameterType: number): Promise<number> => {
|
|
const maxResult = await client.query('SELECT COALESCE(MAX(parameter_id), 0) + 1 as next_id FROM parameter');
|
|
const nextParamId = maxResult.rows[0].next_id;
|
|
|
|
await client.query(`
|
|
INSERT INTO parameter (parameter_id, parameter_type, valid_from, valid_to, created_date, updated_date)
|
|
VALUES ($1, $2, '2026-01-01', '2999-01-01', CURRENT_DATE, CURRENT_DATE)
|
|
`, [nextParamId, parameterType]);
|
|
|
|
return nextParamId;
|
|
};
|
|
|
|
const getNextId = async (client: PoolClient, table: string, idColumn: string): Promise<number> => {
|
|
const result = await client.query(`SELECT COALESCE(MAX(${idColumn}), 0) + 1 as next_id FROM ${table}`);
|
|
return result.rows[0].next_id;
|
|
};
|
|
|
|
// ============================================================================
|
|
// CLAIM STATUS (Verification Outcome)
|
|
// Leaf node - no children
|
|
// ============================================================================
|
|
|
|
interface ClaimStatus {
|
|
claim_id: number;
|
|
claim_code: string;
|
|
claim_name: string;
|
|
claim_color: string;
|
|
start_range: number | null;
|
|
end_range: number | null;
|
|
parameter_id: number;
|
|
}
|
|
|
|
router.get('/status', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await query<ClaimStatus>(
|
|
'SELECT claim_id, claim_code, claim_name, claim_color, start_range, end_range, parameter_id FROM claim ORDER BY claim_id'
|
|
);
|
|
res.json({ success: true, data, count: data.length });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.get('/status/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await queryOne<ClaimStatus>(
|
|
'SELECT claim_id, claim_code, claim_name, claim_color, start_range, end_range, parameter_id FROM claim WHERE claim_id = $1',
|
|
[req.params.id]
|
|
);
|
|
if (!data) {
|
|
return res.status(404).json({ success: false, error: 'Claim status nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.post('/status', async (req: Request, res: Response) => {
|
|
try {
|
|
const { claim_code, claim_name, claim_color, start_range, end_range } = req.body;
|
|
if (!claim_code || !claim_name) {
|
|
return res.status(400).json({ success: false, error: 'Câmpuri obligatorii: claim_code, claim_name' });
|
|
}
|
|
|
|
const result = await transaction(async (client) => {
|
|
const parameterId = await createParameter(client, 10); // Parameter type for claims
|
|
const id = await getNextId(client, 'claim', 'claim_id');
|
|
const insertResult = await client.query(`
|
|
INSERT INTO claim (claim_id, claim_code, claim_name, claim_color, start_range, end_range, parameter_id)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING claim_id, claim_code, claim_name, claim_color, start_range, end_range, parameter_id
|
|
`, [id, claim_code, claim_name, claim_color || '#808080', start_range, end_range, parameterId]);
|
|
return insertResult.rows[0];
|
|
});
|
|
|
|
res.status(201).json({ success: true, data: result, message: 'Claim status creat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.put('/status/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const { claim_code, claim_name, claim_color, start_range, end_range } = req.body;
|
|
const result = await queryOne<ClaimStatus>(`
|
|
UPDATE claim
|
|
SET claim_code = COALESCE($1, claim_code),
|
|
claim_name = COALESCE($2, claim_name),
|
|
claim_color = COALESCE($3, claim_color),
|
|
start_range = COALESCE($4, start_range),
|
|
end_range = COALESCE($5, end_range)
|
|
WHERE claim_id = $6
|
|
RETURNING claim_id, claim_code, claim_name, claim_color, start_range, end_range, parameter_id
|
|
`, [claim_code, claim_name, claim_color, start_range, end_range, req.params.id]);
|
|
|
|
if (!result) {
|
|
return res.status(404).json({ success: false, error: 'Claim status nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data: result, message: 'Claim status actualizat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.delete('/status/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const result = await query('DELETE FROM claim WHERE claim_id = $1 RETURNING *', [req.params.id]);
|
|
if (result.length === 0) {
|
|
return res.status(404).json({ success: false, error: 'Claim status nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, message: 'Claim status șters cu succes', deleted: true });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// CLAIM TYPE (Type of Claim with Base Weight)
|
|
// Leaf node - no children
|
|
// ============================================================================
|
|
|
|
interface ClaimType {
|
|
claim_type_id: number;
|
|
claim_type_code: string;
|
|
claim_type_name: string;
|
|
base_weight: number;
|
|
description: string;
|
|
verification_method: string;
|
|
parameter_id: number;
|
|
}
|
|
|
|
router.get('/types', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await query<ClaimType>(
|
|
'SELECT claim_type_id, claim_type_code, claim_type_name, base_weight, description, verification_method, parameter_id FROM claim_type ORDER BY base_weight DESC'
|
|
);
|
|
res.json({ success: true, data, count: data.length });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.get('/types/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await queryOne<ClaimType>(
|
|
'SELECT * FROM claim_type WHERE claim_type_id = $1',
|
|
[req.params.id]
|
|
);
|
|
if (!data) {
|
|
return res.status(404).json({ success: false, error: 'Claim type nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.post('/types', async (req: Request, res: Response) => {
|
|
try {
|
|
const { claim_type_code, claim_type_name, base_weight, description, verification_method } = req.body;
|
|
if (!claim_type_code || !claim_type_name) {
|
|
return res.status(400).json({ success: false, error: 'Câmpuri obligatorii: claim_type_code, claim_type_name' });
|
|
}
|
|
|
|
const result = await transaction(async (client) => {
|
|
const parameterId = await createParameter(client, 11); // Parameter type for claim types
|
|
const id = await getNextId(client, 'claim_type', 'claim_type_id');
|
|
const insertResult = await client.query(`
|
|
INSERT INTO claim_type (claim_type_id, claim_type_code, claim_type_name, base_weight, description, verification_method, parameter_id)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING *
|
|
`, [id, claim_type_code, claim_type_name, base_weight || 1, description || '', verification_method || '', parameterId]);
|
|
return insertResult.rows[0];
|
|
});
|
|
|
|
res.status(201).json({ success: true, data: result, message: 'Claim type creat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.put('/types/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const { claim_type_code, claim_type_name, base_weight, description, verification_method } = req.body;
|
|
const result = await queryOne<ClaimType>(`
|
|
UPDATE claim_type
|
|
SET claim_type_code = COALESCE($1, claim_type_code),
|
|
claim_type_name = COALESCE($2, claim_type_name),
|
|
base_weight = COALESCE($3, base_weight),
|
|
description = COALESCE($4, description),
|
|
verification_method = COALESCE($5, verification_method)
|
|
WHERE claim_type_id = $6
|
|
RETURNING *
|
|
`, [claim_type_code, claim_type_name, base_weight, description, verification_method, req.params.id]);
|
|
|
|
if (!result) {
|
|
return res.status(404).json({ success: false, error: 'Claim type nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data: result, message: 'Claim type actualizat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.delete('/types/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const result = await query('DELETE FROM claim_type WHERE claim_type_id = $1 RETURNING *', [req.params.id]);
|
|
if (result.length === 0) {
|
|
return res.status(404).json({ success: false, error: 'Claim type nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, message: 'Claim type șters cu succes', deleted: true });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// CONFIDENCE LEVELS
|
|
// Leaf node - no children
|
|
// ============================================================================
|
|
|
|
interface ConfidenceLevel {
|
|
confidence_id: number;
|
|
confidence_name: string;
|
|
confidence_level: number;
|
|
confidence_color: string;
|
|
action: string;
|
|
start_range: number;
|
|
end_range: number;
|
|
parameter_id: number;
|
|
}
|
|
|
|
router.get('/confidence', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await query<ConfidenceLevel>(
|
|
'SELECT * FROM confidence ORDER BY confidence_level'
|
|
);
|
|
res.json({ success: true, data, count: data.length });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.get('/confidence/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await queryOne<ConfidenceLevel>(
|
|
'SELECT * FROM confidence WHERE confidence_id = $1',
|
|
[req.params.id]
|
|
);
|
|
if (!data) {
|
|
return res.status(404).json({ success: false, error: 'Confidence level nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.post('/confidence', async (req: Request, res: Response) => {
|
|
try {
|
|
const { confidence_name, confidence_level, confidence_color, action, start_range, end_range } = req.body;
|
|
if (!confidence_name || confidence_level === undefined) {
|
|
return res.status(400).json({ success: false, error: 'Câmpuri obligatorii: confidence_name, confidence_level' });
|
|
}
|
|
|
|
const result = await transaction(async (client) => {
|
|
const parameterId = await createParameter(client, 12); // Parameter type for confidence
|
|
const id = await getNextId(client, 'confidence', 'confidence_id');
|
|
const insertResult = await client.query(`
|
|
INSERT INTO confidence (confidence_id, confidence_name, confidence_level, confidence_color, action, start_range, end_range, parameter_id)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
RETURNING *
|
|
`, [id, confidence_name, confidence_level, confidence_color || '#808080', action || '', start_range || 0, end_range || 100, parameterId]);
|
|
return insertResult.rows[0];
|
|
});
|
|
|
|
res.status(201).json({ success: true, data: result, message: 'Confidence level creat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.put('/confidence/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const { confidence_name, confidence_level, confidence_color, action, start_range, end_range } = req.body;
|
|
const result = await queryOne<ConfidenceLevel>(`
|
|
UPDATE confidence
|
|
SET confidence_name = COALESCE($1, confidence_name),
|
|
confidence_level = COALESCE($2, confidence_level),
|
|
confidence_color = COALESCE($3, confidence_color),
|
|
action = COALESCE($4, action),
|
|
start_range = COALESCE($5, start_range),
|
|
end_range = COALESCE($6, end_range)
|
|
WHERE confidence_id = $7
|
|
RETURNING *
|
|
`, [confidence_name, confidence_level, confidence_color, action, start_range, end_range, req.params.id]);
|
|
|
|
if (!result) {
|
|
return res.status(404).json({ success: false, error: 'Confidence level nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data: result, message: 'Confidence level actualizat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.delete('/confidence/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const result = await query('DELETE FROM confidence WHERE confidence_id = $1 RETURNING *', [req.params.id]);
|
|
if (result.length === 0) {
|
|
return res.status(404).json({ success: false, error: 'Confidence level nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, message: 'Confidence level șters cu succes', deleted: true });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// INTERPRETATION (Source Concordance)
|
|
// Leaf node - no children
|
|
// ============================================================================
|
|
|
|
interface Interpretation {
|
|
interpretation_id: number;
|
|
interpretation: string;
|
|
start_range: number;
|
|
end_range: number;
|
|
parameter_id: number;
|
|
}
|
|
|
|
router.get('/interpretation', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await query<Interpretation>(
|
|
'SELECT interpretation_id, interpretation, start_range, end_range, parameter_id FROM interpretation ORDER BY interpretation_id'
|
|
);
|
|
res.json({ success: true, data, count: data.length });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.get('/interpretation/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const data = await queryOne<Interpretation>(
|
|
'SELECT * FROM interpretation WHERE interpretation_id = $1',
|
|
[req.params.id]
|
|
);
|
|
if (!data) {
|
|
return res.status(404).json({ success: false, error: 'Interpretation nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.post('/interpretation', async (req: Request, res: Response) => {
|
|
try {
|
|
const { interpretation, start_range, end_range } = req.body;
|
|
if (!interpretation) {
|
|
return res.status(400).json({ success: false, error: 'Câmp obligatoriu: interpretation' });
|
|
}
|
|
|
|
const result = await transaction(async (client) => {
|
|
const parameterId = await createParameter(client, 13); // Parameter type for interpretation
|
|
const id = await getNextId(client, 'interpretation', 'interpretation_id');
|
|
const insertResult = await client.query(`
|
|
INSERT INTO interpretation (interpretation_id, interpretation, start_range, end_range, parameter_id)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING *
|
|
`, [id, interpretation, start_range || 0, end_range || 100, parameterId]);
|
|
return insertResult.rows[0];
|
|
});
|
|
|
|
res.status(201).json({ success: true, data: result, message: 'Interpretation creat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.put('/interpretation/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const { interpretation, start_range, end_range } = req.body;
|
|
const result = await queryOne<Interpretation>(`
|
|
UPDATE interpretation
|
|
SET interpretation = COALESCE($1, interpretation),
|
|
start_range = COALESCE($2, start_range),
|
|
end_range = COALESCE($3, end_range)
|
|
WHERE interpretation_id = $4
|
|
RETURNING *
|
|
`, [interpretation, start_range, end_range, req.params.id]);
|
|
|
|
if (!result) {
|
|
return res.status(404).json({ success: false, error: 'Interpretation nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, data: result, message: 'Interpretation actualizat cu succes' });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
router.delete('/interpretation/:id', async (req: Request, res: Response) => {
|
|
try {
|
|
const result = await query('DELETE FROM interpretation WHERE interpretation_id = $1 RETURNING *', [req.params.id]);
|
|
if (result.length === 0) {
|
|
return res.status(404).json({ success: false, error: 'Interpretation nu a fost găsit' });
|
|
}
|
|
res.json({ success: true, message: 'Interpretation șters cu succes', deleted: true });
|
|
} catch (error) {
|
|
internalError(res, error);
|
|
}
|
|
});
|
|
|
|
// ============================================================================
|
|
// COMBINED: GET ALL CLAIMS DATA
|
|
// ============================================================================
|
|
|
|
router.get('/all', async (req: Request, res: Response) => {
|
|
try {
|
|
const [
|
|
claimStatus,
|
|
claimTypes,
|
|
confidence,
|
|
interpretation
|
|
] = await Promise.all([
|
|
query<ClaimStatus>('SELECT claim_id, claim_code, claim_name, claim_color, start_range, end_range FROM claim ORDER BY claim_id'),
|
|
query<ClaimType>('SELECT claim_type_id, claim_type_code, claim_type_name, base_weight, description, verification_method FROM claim_type ORDER BY base_weight DESC'),
|
|
query<ConfidenceLevel>('SELECT * FROM confidence ORDER BY confidence_level'),
|
|
query<Interpretation>('SELECT interpretation_id, interpretation, start_range, end_range FROM interpretation ORDER BY interpretation_id')
|
|
]);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: {
|
|
claimStatus,
|
|
claimTypes,
|
|
confidence,
|
|
interpretation
|
|
},
|
|
counts: {
|
|
claimStatus: claimStatus.length,
|
|
claimTypes: claimTypes.length,
|
|
confidence: confidence.length,
|
|
interpretation: interpretation.length,
|
|
total: claimStatus.length + claimTypes.length + confidence.length + interpretation.length
|
|
}
|
|
});
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error'
|
|
} as ApiResponse<never>);
|
|
}
|
|
});
|
|
|
|
export default router;
|