/** * 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 => { 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 => { 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( '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( '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(` 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( '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( '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(` 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( '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( '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(` 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( '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( '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(` 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('SELECT claim_id, claim_code, claim_name, claim_color, start_range, end_range FROM claim ORDER BY claim_id'), query('SELECT claim_type_id, claim_type_code, claim_type_name, base_weight, description, verification_method FROM claim_type ORDER BY base_weight DESC'), query('SELECT * FROM confidence ORDER BY confidence_level'), query('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); } }); export default router;