Compare commits

...

2 Commits

Author SHA1 Message Date
Kallesh B S
f2d095098c CARNETCONTROLCENTER_PKG API 2025-07-03 15:30:09 +05:30
Kallesh B S
49fe236b30 updated forgot password 2025-07-03 14:35:45 +05:30
10 changed files with 413 additions and 71 deletions

45
package-lock.json generated
View File

@ -35,7 +35,8 @@
"pg": "^8.13.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20"
"typeorm": "^0.3.20",
"uuid": "^11.1.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
@ -53,6 +54,7 @@
"@types/node": "^22.10.7",
"@types/oracledb": "^6.5.4",
"@types/supertest": "^6.0.2",
"@types/uuid": "^10.0.0",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
@ -541,6 +543,15 @@
"node": ">=16"
}
},
"node_modules/@azure/msal-node/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@ -4155,6 +4166,13 @@
"@types/superagent": "^8.1.0"
}
},
"node_modules/@types/uuid": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/validator": {
"version": "13.12.2",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz",
@ -16041,19 +16059,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/typeorm/node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
@ -16253,12 +16258,16 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {

View File

@ -46,7 +46,8 @@
"pg": "^8.13.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20"
"typeorm": "^0.3.20",
"uuid": "^11.1.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
@ -64,6 +65,7 @@
"@types/node": "^22.10.7",
"@types/oracledb": "^6.5.4",
"@types/supertest": "^6.0.2",
"@types/uuid": "^10.0.0",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",

View File

@ -8,6 +8,7 @@ import { RegisterGuard } from 'src/guards/register.guard';
import { UserMaintenanceService } from 'src/oracle/user-maintenance/user-maintenance.service';
import { RolesGuard } from 'src/guards/roles.guard';
import { Roles } from 'src/decorators/roles.decorator';
import { JwtAuthGuard } from 'src/guards/jwt-auth.guard';
@ApiTags('Auth')
@Controller()
@ -95,7 +96,7 @@ export class AuthController {
return { statusCode: 200, message: "Tokens refreshed successfully" }
}
@UseGuards(RolesGuard)
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('sa')
@Post('sendmail')
@HttpCode(200)

View File

@ -13,6 +13,8 @@ import { BadRequestException } from 'src/exceptions/badRequest.exception';
import { MailerService } from '@nestjs-modules/mailer';
import { JwtService } from '@nestjs/jwt';
import { UserMaintenanceService } from 'src/oracle/user-maintenance/user-maintenance.service';
import { v4 as uuidv4 } from 'uuid';
interface DecodedToken {
email: string;
@ -36,8 +38,7 @@ export class AuthService {
private readonly CLIENT_SECRET: string;
private readonly TOKEN_URL: string;
private readonly USERS_URL: string;
private readonly JWT_REGISTER_PRIVATE_KEY: string;
private readonly JWT_REGISTER_PUBLIC_KEY: string;
constructor(
private readonly oracleDBService: OracleDBService,
@ -47,13 +48,6 @@ export class AuthService {
@Inject("REGISTER_SIGN_JWT") readonly REGISTER_SIGN_JWT: JwtService,
@Inject("REGISTER_VERIFY_JWT") readonly REGISTER_VERIFY_JWT: JwtService
) {
const JWT_REGISTER_PRIVATE_KEY = this.configService.get<string>('JWT_REGISTER_PRIVATE_KEY');
if (!JWT_REGISTER_PRIVATE_KEY) throw new Error('Environment variable JWT_REGISTER_PRIVATE_KEY is not set');
this.JWT_REGISTER_PRIVATE_KEY = Buffer.from(JWT_REGISTER_PRIVATE_KEY, 'base64').toString('utf-8');
const JWT_REGISTER_PUBLIC_KEY = this.configService.get<string>('JWT_REGISTER_PUBLIC_KEY');
if (!JWT_REGISTER_PUBLIC_KEY) throw new Error('Environment variable JWT_REGISTER_PRIVATE_KEY is not set');
this.JWT_REGISTER_PUBLIC_KEY = Buffer.from(JWT_REGISTER_PUBLIC_KEY, 'base64').toString('utf-8');
const KEYCLOAK_URL = this.configService.get<string>('KEYCLOAK_URL');
if (!KEYCLOAK_URL) throw new Error('Environment variable KEYCLOAK_URL is not set');
@ -80,6 +74,7 @@ export class AuthService {
this.TOKEN_URL = `${this.KEYCLOAK_BASE_URL}/token`
this.USERS_URL = `${this.KEYCLOAK_URL}/admin/realms/${this.KEYCLOAK_REALM}/users`;
}
async login(body: AuthLoginDTO) {
@ -330,6 +325,60 @@ export class AuthService {
}
}
async getUserDetailsByEmail(email: string): Promise<any> {
try {
const adminAccessToken = await this.getAdminAccessToken();
const userResponse = await axios.get(`${this.USERS_URL}?email=${encodeURIComponent(email)}`, {
headers: {
Authorization: `Bearer ${adminAccessToken}`
},
});
if (userResponse.status !== 200 || !Array.isArray(userResponse.data)) {
console.log("userResponse : ", userResponse);
console.log("userResponse status : ", userResponse.status);
throw new UnauthorizedException("Authentication failed");
}
const user = userResponse.data[0];
if (userResponse.data.length === 0 || !user?.id) {
throw new BadRequestException("Invalid User");
}
const roleUrl = `${this.USERS_URL}/${user.id}/role-mappings`;
const roleResponse = await axios.get(roleUrl, {
headers: {
Authorization: `Bearer ${adminAccessToken}`
},
});
const clientRoles = roleResponse.data;
return {
id: user.id,
email: user.email,
forgotPassword: user.attributes?.["forgot-password"][0] || null,
role: clientRoles.clientMappings?.[this.CLIENT_ID]?.mappings?.map((x) => x.name) || []
};
} catch (error) {
console.log(error.message);
console.log("Error in getUserDetailsByEmail ...........");
if (error instanceof UnauthorizedException) {
throw error;
}
else if (error instanceof BadRequestException) {
throw error;
}
throw new InternalServerErrorException();
}
}
async forgotPassword(body: AuthLoginDTO, req: Request) {
let det = await req['user'];
@ -337,25 +386,42 @@ export class AuthService {
throw new BadRequestException();
}
const validatePasswordURLSearchParams = new URLSearchParams({
grant_type: 'password',
client_id: `${this.CLIENT_ID}`,
client_secret: `${this.CLIENT_SECRET}`,
username: `${body.P_EMAILADDR}`,
password: `${body.P_PASSWORD}`,
});
const userDetails = await this.getUserDetailsByEmail(body.P_EMAILADDR);
if (userDetails.forgotPassword !== det.rstID) {
throw new UnauthorizedException("Token verification failed");
}
let adminAccessToken = await this.getAdminAccessToken();
console.log(`${this.USERS_URL}/${userDetails.id}/reset-password`);
const options2: AxiosRequestConfig = {
method: 'POST',
url: this.TOKEN_URL,
method: 'PUT',
url: `${this.USERS_URL}/${userDetails.id}/reset-password`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Bearer ${adminAccessToken}`,
'Content-Type': 'application/json',
},
data: validatePasswordURLSearchParams
data: {
type: 'password',
value: body.P_PASSWORD,
temporary: false,
}
};
try {
const { data } = await axios.request(options2);
console.log("forgot password status API request", data.status);
const forgotPasswordStatus = await this.updateForgotPassword(userDetails)
if (!forgotPasswordStatus) {
throw new InternalServerErrorException("Update forgot-password failed")
}
return { statusCode: 200, message: 'password reset successfull' }
} catch (error) {
// console.error(error);
@ -405,6 +471,9 @@ export class AuthService {
email: body.P_EMAILADDR,
emailVerified: true,
enabled: true,
attributes: {
"forgot-password": ["none"]
},
credentials: [
{
type: 'password',
@ -670,31 +739,103 @@ export class AuthService {
}
}
async generateUUID() {
try {
const uuid = uuidv4();
if (!uuid || typeof uuid !== 'string') {
throw new InternalServerErrorException('Invalid UUID generated');
}
return uuid;
} catch (error) {
console.error('UUID generation failed:', error.message);
throw new InternalServerErrorException('Failed to generate unique ID');
}
}
async generateToken(payload: { P_TO: string, uuid?: string }) {
let token: string;
try {
if (payload.uuid) {
token = this.REGISTER_SIGN_JWT.sign({ email: payload.P_TO, rstID: payload.uuid });
}
else {
token = this.REGISTER_SIGN_JWT.sign({ email: payload.P_TO });
}
console.log("Register token : ", token);
if (!token) {
console.log("Register Token Generation failed");
throw new InternalServerErrorException();
}
return token;
}
catch (error) {
console.log(error.message);
throw new InternalServerErrorException("Register Token Generation failed")
}
}
async updateForgotPassword(user: any, rstID?: string) {
try {
const adminAccessToken = await this.getAdminAccessToken();
const options2: AxiosRequestConfig = {
method: 'PUT',
url: `${this.USERS_URL}/${user.id}`,
headers: {
Authorization: `Bearer ${adminAccessToken}`,
'Content-Type': 'application/json',
},
data: {
"username": `${user.email}`,
// firstName:"A",
// lastName:"B",
"email": `${user.email}`,
"emailVerified": true,
"enabled": true,
"attributes": {
"forgot-password": [`${rstID ? rstID : "none"}`]
}
}
};
const userResponse = await axios.request(options2);
if (userResponse.status !== 204) {
console.log("Error updating forgot-password : ");
throw new InternalServerErrorException();
}
return true;
} catch (error) {
console.log("Error updating forgot password");
console.log("Error : ", error.message);
return false;
}
}
async sendMail(body: SendMailDTO) {
console.log("mail body : ", body);
let token: string;
let ROLE: string = "";
let uuid: string | undefined = undefined;
let userDetails: any | null = null;
if (body.P_MAIL_TYPE === MailTypeDTO.FORGOT_PASSWORD) {
try {
const response: any = await this.userMaintenanceService.ValidateEmail({ P_EMAILADDR: body.P_TO })
uuid = await this.generateUUID();
userDetails = await this.getUserDetailsByEmail(body.P_TO);
if (Array.isArray(response) && !response[0].ERRORMESG) {
switch (response[0].SOURCE) {
case "USCIB": ROLE = 'ua'; break;
case "SP": ROLE = 'sa'; break;
case "CLIENT": ROLE = 'ca'; break;
}
}
else if (Array.isArray(response) && response[0].ERRORMESG) {
throw new BadRequestException();
}
ROLE = userDetails.role[0];
if (!ROLE) {
console.log("ERROR while giving access : ");
throw new InternalServerErrorException();
}
} catch (error) {
if (error instanceof BadRequestException) {
throw error
@ -704,21 +845,9 @@ export class AuthService {
}
}
token = await this.generateToken({ P_TO: body.P_TO, uuid: uuid })
try {
token = this.REGISTER_SIGN_JWT.sign({ email: body.P_TO });
console.log("Register token : ", token);
if (!token) {
console.log("Register Token Generation failed");
throw new InternalServerErrorException();
}
}
catch (error) {
console.log(error.message);
throw new InternalServerErrorException("Register Token Generation failed")
}
const mailTemplate: MailTemplateDTO | null = await this.getMailTemplate(body, token, ROLE);
@ -729,7 +858,20 @@ export class AuthService {
try {
await this.mailService.sendMail({ ...mailTemplate });
console.log('Email sent successfully');
if (body.P_MAIL_TYPE === MailTypeDTO.FORGOT_PASSWORD) {
let forgotPasswordStatus = await this.updateForgotPassword(userDetails, uuid);
if (forgotPasswordStatus) {
return { statusCode: 200, message: 'Email sent successfully' };
}
throw new InternalServerErrorException();
}
return { statusCode: 200, message: 'Email sent successfully' };
} catch (error) {
console.error('Failed to send email:', error.message, error);
throw new InternalServerErrorException();

View File

@ -30,6 +30,7 @@ export class HEADERID_DTO {
@Min(0, { message: 'Property P_HEADERID must be at least 0 or more' })
@IsInt({ message: 'Property P_HEADERID allows only whole numbers' })
@IsNumber({}, { message: 'Property P_HEADERID must be a number' })
@Transform(({ value }) => Number(value))
@IsDefined({ message: 'Property P_HEADERID is required' })
P_HEADERID: number;
}

View File

@ -82,3 +82,7 @@ export class UpdateShippingDetailsDTO extends IntersectionType(
PAYMENTMETHOD_DTO, CUSTCOURIERNO_DTO, REFNO_DTO, NOTES_DTO, USERID_DTO
) { }
export class GetCarnetControlCenterDTO extends IntersectionType(
SPID_DTO, USERID_DTO, HEADERID_DTO
) { }

View File

@ -9,6 +9,7 @@ import { AuthService } from 'src/auth/auth.service';
import * as jwt from 'jsonwebtoken';
import { InternalServerException } from 'src/exceptions/internalServerError.exception';
import { UnauthorizedException as UE } from 'src/exceptions/unauthorized.exception';
import { MailTypeDTO } from 'src/auth/auth.dto';
@Global()
@Injectable()
@ -19,6 +20,15 @@ export class JwtAuthGuard implements CanActivate {
const request = context.switchToHttp().getRequest();
const token = request.cookies?.access_token;
const body = request.body;
console.log("from JWT guard body : ", body);
if (body.P_MAIL_TYPE && body.P_MAIL_TYPE === MailTypeDTO.FORGOT_PASSWORD) {
return true;
}
if (!token) {
console.log("No token found from Request");
throw new UnauthorizedException('Authentication failed');

View File

@ -9,6 +9,7 @@ import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorator';
import * as jwt from 'jsonwebtoken';
import { UnauthorizedException } from 'src/exceptions/unauthorized.exception';
import { MailTypeDTO } from 'src/auth/auth.dto';
@Injectable()
export class RolesGuard implements CanActivate {
@ -24,6 +25,14 @@ export class RolesGuard implements CanActivate {
const request = context.switchToHttp().getRequest();
const token = request.cookies?.access_token; // Token is stored as HttpOnly cookie
const body = request.body;
console.log("from JWT guard body : ", body);
if (body.P_MAIL_TYPE && body.P_MAIL_TYPE === MailTypeDTO.FORGOT_PASSWORD) {
return true;
}
if (!token) {
throw new UnauthorizedException('Authentication Failed');
}

View File

@ -1,4 +1,4 @@
import { Body, Controller, Patch, Post, Put, UseGuards } from '@nestjs/common';
import { Body, Controller, Get, Param, Patch, Post, Put, UseGuards } from '@nestjs/common';
import { CarnetApplicationService } from './carnet-application.service';
import { ApiTags } from '@nestjs/swagger';
@ -7,7 +7,7 @@ import {
AddCountriesDTO,
AddGenerallistItemsDTO,
CA_UpdateHolderDTO,
CarnetProcessingCenterDTO, CreateApplicationDTO, SaveCarnetApplicationDTO, TransmitApplicationtoProcessDTO,
CarnetProcessingCenterDTO, CreateApplicationDTO, GetCarnetControlCenterDTO, SaveCarnetApplicationDTO, TransmitApplicationtoProcessDTO,
UpdateExpGoodsAuthRepDTO,
UpdateShippingDetailsDTO
} from 'src/dto/property.dto';
@ -97,4 +97,27 @@ export class CarnetApplicationController {
CloseCarnet(@Body() body: CarnetProcessingCenterDTO) {
return this.carnetApplicationService.CloseCarnet(body);
}
// [ CARNETCONTROLCENTER_PKG ]
@Get('GetHolderstoEdit/:P_SPID/:P_USERID/:P_HEADERID')
GetHolderstoEdit(@Param() body: GetCarnetControlCenterDTO) {
return this.carnetApplicationService.GetHolderstoEdit(body);
}
@Get('GetGoodsDetailstoEdit/:P_SPID/:P_USERID/:P_HEADERID')
GetGoodsDetailstoEdit(@Param() body: GetCarnetControlCenterDTO) {
return this.carnetApplicationService.GetHolderstoEdit(body);
}
@Get('GetCountryDetailstoEdit/:P_SPID/:P_USERID/:P_HEADERID')
GetCountryDetailstoEdit(@Param() body: GetCarnetControlCenterDTO) {
return this.carnetApplicationService.GetHolderstoEdit(body);
}
@Get('GetShipPaymentDetailstoEdit/:P_SPID/:P_USERID/:P_HEADERID')
GetShipPaymentDetailstoEdit(@Param() body: GetCarnetControlCenterDTO) {
return this.carnetApplicationService.GetHolderstoEdit(body);
}
}

View File

@ -11,7 +11,8 @@ import {
AddGenerallistItemsDTO,
AddCountriesDTO,
UpdateShippingDetailsDTO,
CA_UpdateHolderDTO
CA_UpdateHolderDTO,
GetCarnetControlCenterDTO
} from 'src/dto/property.dto';
import { OracleService } from '../oracle.service';
@ -691,4 +692,144 @@ export class CarnetApplicationService {
}
}
// [ CARNETCONTROLCENTER_PKG ]
async GetHolderstoEdit(body: GetCarnetControlCenterDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
CARNETCONTROLCENTER_PKG.GetHolderstoEdit(
:P_SPID, :P_USERID , :P_HEADERID, :P_CURSOR
);
END;`,
{
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_HEADERID: { val: body.P_HEADERID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
return await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
async GetGoodsDetailstoEdit(body: GetCarnetControlCenterDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
CARNETCONTROLCENTER_PKG.GetGoodsDetailstoEdit(
:P_SPID, :P_USERID , :P_HEADERID, :P_CURSOR
);
END;`,
{
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_HEADERID: { val: body.P_HEADERID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
return await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
async GetCountryDetailstoEdit(body: GetCarnetControlCenterDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
CARNETCONTROLCENTER_PKG.GetCountryDetailstoEdit(
:P_SPID, :P_USERID , :P_HEADERID, :P_CURSOR
);
END;`,
{
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_HEADERID: { val: body.P_HEADERID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
return await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
async GetShipPaymentDetailstoEdit(body: GetCarnetControlCenterDTO) {
let connection;
try {
connection = await this.oracleDBService.getConnection();
const result = await connection.execute(
`BEGIN
CARNETCONTROLCENTER_PKG.GetShipPaymentDetailstoEdit(
:P_SPID, :P_USERID , :P_HEADERID, :P_CURSOR
);
END;`,
{
P_SPID: { val: body.P_SPID, type: oracledb.DB_TYPE_NUMBER },
P_USERID: { val: body.P_USERID, type: oracledb.DB_TYPE_NVARCHAR },
P_HEADERID: { val: body.P_HEADERID, type: oracledb.DB_TYPE_NUMBER },
P_CURSOR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }
},
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
);
const outBinds = result.outBinds;
if (!outBinds?.P_CURSOR) {
this.logger.error('One or more expected cursors are missing from stored procedure output.');
throw new InternalServerException("Incomplete data received from the database.");
}
return await fetchCursor(outBinds.P_CURSOR, CarnetApplicationService.name);
} catch (error) {
handleError(error, CarnetApplicationService.name)
} finally {
await closeOracleDbConnection(connection, CarnetApplicationService.name)
}
}
}