643 lines
22 KiB
TypeScript
643 lines
22 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { ClientContactControlsDTO, CreateClientContactsDTO, CreateClientDataDTO, CreateClientLocationsDTO, GetClientContactByLacationIdDTO, GetClientDTO, GetPreparersDTO, UpdateClientContactsDTO, UpdateClientDTO, UpdateClientLocationsDTO } from 'src/dto/property.dto';
|
|
import { checkPgUserDefinedErrors, handlePgError, releasePgClient, ResponseStatus, toUpperCaseKeys } from 'src/utils/helper';
|
|
import { PoolClient } from 'pg';
|
|
import { PgDBService } from 'src/db/db.service';
|
|
|
|
@Injectable()
|
|
export class ManageClientsService {
|
|
|
|
constructor(private readonly pgDBService: PgDBService) { }
|
|
|
|
CreateClientData = async (body: CreateClientDataDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_createclientdata($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_CLIENTNAME,
|
|
body.P_LOOKUPCODE,
|
|
body.P_ADDRESS1,
|
|
body.P_ADDRESS2,
|
|
body.P_CITY,
|
|
body.P_STATE,
|
|
body.P_ZIP,
|
|
body.P_COUNTRY,
|
|
body.P_ISSUINGREGION,
|
|
body.P_REVENUELOCATION,
|
|
body.P_USERID,
|
|
body.P_INDUSTRYTYPE,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
UpdateClient = async (body: UpdateClientDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_updateclient($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_CLIENTID,
|
|
body.P_PREPARERNAME,
|
|
body.P_ADDRESS1,
|
|
body.P_ADDRESS2,
|
|
body.P_CITY,
|
|
body.P_STATE,
|
|
body.P_ZIP,
|
|
body.P_COUNTRY,
|
|
body.P_REVENUELOCATION,
|
|
body.P_USERID,
|
|
body.P_INDUSTRYTYPE,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
UpdateClientContacts = async (body: UpdateClientContactsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_updateclientcontacts($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_CLIENTCONTACTID,
|
|
body.P_FIRSTNAME,
|
|
body.P_LASTNAME,
|
|
body.P_MIDDLEINITIAL,
|
|
body.P_TITLE,
|
|
body.P_PHONENO,
|
|
body.P_FAXNO,
|
|
body.P_MOBILENO,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
UpdateClientLocations = async (body: UpdateClientLocationsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_updateclientlocations($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_CLIENTLOCATIONID,
|
|
body.P_LOCATIONNAME,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
async CreateClientContact(body: CreateClientContactsDTO) {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
// Helper to escape special characters for Postgres composite fields
|
|
function escapePostgresCompositeField(str: string): string {
|
|
if (str == null) return '';
|
|
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
}
|
|
|
|
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".managepreparer_pkg_createclientcontact($1, $2, $3::"CARNETSYS".contactstable[], $4, $5, $6)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_LOCATIONID,
|
|
pgArrayLiteral,
|
|
body.P_DEFCONTACTFLAG,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
}
|
|
|
|
SetDefaultClientContacts = async (body: ClientContactControlsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_setdefaultclientcontacts($1, $2, $3, $4)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTCONTACTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
}
|
|
|
|
InactivateClientContacts = async (body: ClientContactControlsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_inactivateclientcontacts($1, $2, $3, $4)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTCONTACTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
}
|
|
|
|
ReactivateClientContacts = async (body: ClientContactControlsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_reactivateclientcontacts($1, $2, $3, $4)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTCONTACTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
}
|
|
|
|
CreateClientLocation = async (body: CreateClientLocationsDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
function escapePostgresCompositeField(str: string): string {
|
|
if (str == null) return '';
|
|
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
}
|
|
|
|
const addressLiterals = body.P_CLIENTLOCADDRESSTABLE.map(c => {
|
|
const fields = [
|
|
c.P_NAMEOF || '',
|
|
c.P_ADDRESS1 || '',
|
|
c.P_ADDRESS2 || '',
|
|
c.P_CITY || '',
|
|
c.P_STATE || '',
|
|
c.P_ZIP || '',
|
|
c.P_COUNTRY || '',
|
|
].map(field => escapePostgresCompositeField(field)).join(',');
|
|
|
|
|
|
return `"(${fields})"`; // Composite literal wrapped in double quotes
|
|
});
|
|
|
|
const pgArrayLiteral = `{${addressLiterals.join(',')}}`;
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_updateclientlocations($1, $2, $3::"CARNETSYS".addresstable[], $4, $5)
|
|
`;
|
|
await client.query(callProcQuery,
|
|
[
|
|
body.P_SPID,
|
|
body.P_CLIENTID,
|
|
pgArrayLiteral,
|
|
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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
GetPreparers = async (body: GetPreparersDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_getpreparers($1, $2, $3, $4, $5, $6, $7)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_NAME, body.P_LOOKUPCODE, body.P_CITY, body.P_STATE, body.P_STATUS, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
GetPreparerByClientid = async (body: GetClientDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_getpreparerlocbyclientid($1, $2, $3)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
GetPreparerContactsByClientid = async (body: GetClientDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_getpreparercontactsbyclientid($1, $2, $3)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
|
|
GetPreparerContactsbyClientLocationID = async (body: GetClientContactByLacationIdDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_getpreparercontactsbyclientlocationid($1, $2, $3)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_LOCATIONID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
}
|
|
|
|
GetPreparerLocByClientid = async (body: GetClientDTO) => {
|
|
let client: PoolClient | null = null;
|
|
|
|
const cursorName = 'p_cursor';
|
|
|
|
try {
|
|
client = await this.pgDBService.getConnection();
|
|
|
|
await client.query('BEGIN');
|
|
|
|
const callProcQuery = `
|
|
CALL "CARNETSYS".managepreparer_pkg_getpreparerlocbyclientid($1, $2, $3)
|
|
`;
|
|
await client.query(callProcQuery, [body.P_SPID, body.P_CLIENTID, 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, ManageClientsService.name)
|
|
} finally {
|
|
releasePgClient(client)
|
|
}
|
|
};
|
|
}
|