import { Injectable } from '@nestjs/common'; import { PgDBService } from 'src/db/db.service'; import { CONTACTSTABLE_ROW_DTO, CreateHolderContactsDTO, CreateHoldersDTO, GetHolderDTO, HolderActivateOrInactivateDTO, HolderContactActivateOrInactivateDTO, UpdateHolderContactDTO, UpdateHolderDTO } from 'src/dto/property.dto'; import { PoolClient } from 'pg'; import { checkPgUserDefinedErrors, handlePgError, releasePgClient, ResponseStatus, toUpperCaseKeys } from 'src/utils/helper'; @Injectable() export class ManageHolderService { constructor(private readonly pgDBService: PgDBService) { } SearchHolder = async (body: { P_SPID: number, P_USERID: string, P_HOLDERID: number | null, P_HOLDERNAME: string | null }) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_searchholder($1, $2, $3, $4, $5) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERID, body.P_HOLDERNAME, body.P_USERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return toUpperCaseKeys(fetchResult.rows); } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } } async CreateHolders(body: CreateHoldersDTO) { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_createholderdata($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) `; await client.query(callProcQuery, [ body.P_SPID, body.P_CLIENTLOCATIONID, body.P_HOLDERNO, body.P_HOLDERTYPE, body.P_USCIBMEMBERFLAG, body.P_GOVAGENCYFLAG, body.P_HOLDERNAME, body.P_NAMEQUALIFIER, body.P_ADDLNAME, body.P_ADDRESS1, body.P_ADDRESS2, body.P_CITY, body.P_STATE, body.P_ZIP, body.P_COUNTRY, body.P_USERID, cursorName ]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 201, message: ResponseStatus.created, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } } async CreateHoldercontact(body: CreateHolderContactsDTO) { let client: PoolClient | null = null; const cursorName = 'p_cursor'; // Helper to escape special characters for Postgres composite fields function escapePostgresCompositeField(str: string): string { if (str == null) return ''; return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const contactLiterals = body.P_CONTACTSTABLE.map(c => { const fields = [ c.P_FIRSTNAME || '', c.P_LASTNAME || '', c.P_MIDDLEINITIAL || '', c.P_TITLE || '', c.P_EMAILADDRESS || '', c.P_PHONENO || '', c.P_MOBILENO || '', c.P_FAXNO || '', ].map(field => escapePostgresCompositeField(field)).join(','); return `"(${fields})"`; // Composite literal wrapped in double quotes }); const pgArrayLiteral = `{${contactLiterals.join(',')}}`; const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_createholdercontact($1, $2, $3::"CARNETSYS".contactstable[], $4, $5) `; await client.query(callProcQuery, [ body.P_SPID, body.P_HOLDERID, pgArrayLiteral, body.P_USERID, cursorName, ]); // Fetch results from cursor (do NOT quote cursor name) const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // Close cursor await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 201, message: ResponseStatus.created, ...toUpperCaseKeys(fetchResult?.rows)[0], }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name); } finally { releasePgClient(client); } } UpdateHolder = async (body: UpdateHolderDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_updateholder($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) `; await client.query(callProcQuery, [ body.P_HOLDERID, body.P_SPID, body.P_LOCATIONID, body.P_HOLDERNO, body.P_HOLDERTYPE, body.P_USCIBMEMBERFLAG, body.P_GOVAGENCYFLAG, body.P_HOLDERNAME, body.P_NAMEQUALIFIER, body.P_ADDLNAME, body.P_ADDRESS1, body.P_ADDRESS2, body.P_CITY, body.P_STATE, body.P_ZIP, body.P_COUNTRY, body.P_USERID, cursorName ]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.updated, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; GetHolderRecord = async (body: GetHolderDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_getholdermaster($1, $2, $3) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return toUpperCaseKeys(fetchResult.rows)[0]??[]; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; UpdateHolderContact = async (body: UpdateHolderContactDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_updateholder($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) `; await client.query(callProcQuery, [ body.P_HOLDERCONTACTID, body.P_SPID, body.P_FIRSTNAME, body.P_LASTNAME, body.P_MIDDLEINITIAL, body.P_TITLE, body.P_PHONENO, body.P_MOBILENO, body.P_FAXNO, body.P_EMAILADDRESS, body.P_USERID, cursorName ]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.updated, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; GetHolderContacts = async (body: GetHolderDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_getholdercontacts($1, $2, $3) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return toUpperCaseKeys(fetchResult.rows); } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; InactivateHolder = async (body: HolderActivateOrInactivateDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_inactivateholder($1, $2, $3, $4) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERID, body.P_USERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.inActivate, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; ReactivateHolder = async (body: HolderActivateOrInactivateDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_reactivateholder($1, $2, $3, $4) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERID, body.P_USERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.reActivate, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; InactivateHolderContact = async (body: HolderContactActivateOrInactivateDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_inactivateholdercontact($1, $2, $3, $4) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERCONTACTID, body.P_USERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.inActivate, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; ReactivateHolderContact = async (body: HolderContactActivateOrInactivateDTO) => { let client: PoolClient | null = null; const cursorName = 'p_cursor'; try { client = await this.pgDBService.getConnection(); await client.query('BEGIN'); const callProcQuery = ` CALL "CARNETSYS".manageholder_pkg_reactivateholdercontact($1, $2, $3, $4) `; await client.query(callProcQuery, [body.P_SPID, body.P_HOLDERCONTACTID, body.P_USERID, cursorName]); // 🚫 DON'T quote the cursor name in FETCH const fetchResult = await client.query(`FETCH ALL FROM ${cursorName}`); // 🚫 DON'T quote the cursor name in CLOSE await client.query(`CLOSE ${cursorName}`); await client.query('COMMIT'); checkPgUserDefinedErrors(fetchResult); return { statusCode: 200, message: ResponseStatus.reActivate, ...toUpperCaseKeys(fetchResult?.rows)[0] }; } catch (error) { if (client) await client.query('ROLLBACK'); handlePgError(error, ManageHolderService.name) } finally { releasePgClient(client) } }; }