-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add LoA validation on pay now #1718
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { ReadLoaDto } from 'src/modules/special-auth/dto/response/read-loa.dto'; | ||
import { PermitType } from '../enum/permit-type.enum'; | ||
import { Loas } from '../interface/permit.template.interface'; | ||
import { Permit } from 'src/modules/permit-application-payment/permit/entities/permit.entity'; | ||
import * as dayjs from 'dayjs'; | ||
|
||
export const isVehicleTypeValid = ( | ||
permitVehicleType: string, | ||
permitVehicleId: string, | ||
powerUnits?: string[], | ||
trailers?: string[], | ||
): boolean => { | ||
const isPowerUnitAllowed = | ||
permitVehicleType === 'powerUnit' | ||
? powerUnits.includes(permitVehicleId) | ||
: true; | ||
|
||
const isTrailerAllowed = | ||
permitVehicleType === 'trailer' ? trailers.includes(permitVehicleId) : true; | ||
|
||
return isPowerUnitAllowed && isTrailerAllowed; | ||
}; | ||
|
||
export const isPermitTypeValid = ( | ||
permitTypePermit: PermitType, | ||
permitType: PermitType[], | ||
): boolean => { | ||
return permitType.includes(permitTypePermit); | ||
}; | ||
|
||
export const isValidDateForLoa = ( | ||
loaDetail: Loas | ReadLoaDto, | ||
permit: Permit, | ||
): boolean => { | ||
const { startDate, expiryDate } = loaDetail; | ||
const { startDate: permitStartDate, expiryDate: permitExpiryDate } = | ||
permit.permitData; | ||
return ( | ||
dayjs(startDate).isBefore(permitStartDate, 'day') && | ||
(expiryDate ? dayjs(expiryDate).isAfter(permitExpiryDate, 'day') : true) | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,6 +73,14 @@ import { | |
} from '../../../common/helper/common.helper'; | ||
import { SpecialAuth } from 'src/modules/special-auth/entities/special-auth.entity'; | ||
import { TIMEZONE_PACIFIC } from 'src/common/constants/api.constant'; | ||
import { PermitData } from 'src/common/interface/permit.template.interface'; | ||
import { LoaDetail } from 'src/modules/special-auth/entities/loa-detail.entity'; | ||
import { | ||
isPermitTypeValid, | ||
isValidDateForLoa, | ||
isVehicleTypeValid, | ||
} from 'src/common/helper/validate-loa.helper'; | ||
import { ReadLoaDto } from 'src/modules/special-auth/dto/response/read-loa.dto'; | ||
|
||
@Injectable() | ||
export class PaymentService { | ||
|
@@ -87,6 +95,8 @@ export class PaymentService { | |
private paymentMethodTypeRepository: Repository<PaymentMethodType>, | ||
@InjectRepository(PaymentCardType) | ||
private paymentCardTypeRepository: Repository<PaymentCardType>, | ||
@InjectRepository(LoaDetail) | ||
private loaDetailRepository: Repository<LoaDetail>, | ||
@InjectMapper() private readonly classMapper: Mapper, | ||
@Inject(CACHE_MANAGER) | ||
private readonly cacheManager: Cache, | ||
|
@@ -331,6 +341,13 @@ export class PaymentService { | |
throw new BadRequestException( | ||
'Application in its current status cannot be processed for payment.', | ||
); | ||
const permitData = JSON.parse( | ||
application.permitData.permitData, | ||
) as PermitData; | ||
// If application includes LoAs then validate Loa data. | ||
if (permitData.loas) { | ||
await this.isValidLoa(application); | ||
} | ||
} | ||
const totalTransactionAmount = await this.validateApplicationAndPayment( | ||
createTransactionDto, | ||
|
@@ -887,4 +904,137 @@ export class PaymentService { | |
})), | ||
) as PermitHistoryDto[]; | ||
} | ||
|
||
async isValidLoa(permit: Permit): Promise<void> { | ||
const { companyId } = permit.company; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add optional chaining |
||
const permitData = JSON.parse(permit.permitData.permitData) as PermitData; | ||
const { vehicleId: permitVehicleId, vehicleType: permitVehicleType } = | ||
permitData.vehicleDetails; | ||
const loaNumbers = permitData.loas.map((loa) => loa.loaNumber); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add optional chaining |
||
const readLoaDto = await this.findLoas(companyId, loaNumbers); | ||
|
||
// Validate LOA details and permit data against database entries | ||
this.validateLoaDetails( | ||
readLoaDto, | ||
permit, | ||
permitVehicleId, | ||
permitVehicleType, | ||
); | ||
|
||
// validate LoA snapshot in permit Data | ||
this.validatePermitDataAgainstLoas( | ||
permitData, | ||
permit, | ||
permitVehicleId, | ||
permitVehicleType, | ||
); | ||
} | ||
private validateLoaDetails( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to Loa helper or util or the loa module |
||
readLoaDtos: ReadLoaDto[], | ||
permit: Permit, | ||
permitVehicleId: string, | ||
permitVehicleType: string, | ||
) { | ||
for (const readLoaDto of readLoaDtos) { | ||
const loaPowerUnits = readLoaDto.powerUnits; | ||
const loaTrailers = readLoaDto.trailers; | ||
const loaPermitTypes = readLoaDto.loaPermitType; | ||
if (!isValidDateForLoa(readLoaDto, permit)) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} has LoA with invalid date(s).`, | ||
); | ||
} | ||
if ( | ||
!isVehicleTypeValid( | ||
permitVehicleType, | ||
permitVehicleId, | ||
loaPowerUnits, | ||
loaTrailers, | ||
) | ||
) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} has LoA with invalid vehicle(s).`, | ||
); | ||
} | ||
if (!isPermitTypeValid(permit.permitType, loaPermitTypes)) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} ha LoA with invalid permitType.`, | ||
); | ||
} | ||
} | ||
} | ||
|
||
private validatePermitDataAgainstLoas( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactor to Loa helper or util or the loa module |
||
permitData: PermitData, | ||
permit: Permit, | ||
permitVehicleId: string, | ||
permitVehicleType: string, | ||
) { | ||
for (const loa of permitData.loas) { | ||
const permitLoaPowerUnits = loa.powerUnits; | ||
const permitLoaTrailers = loa.trailers; | ||
const permitTypesLoa = loa.loaPermitType; | ||
if (!isValidDateForLoa(loa, permit)) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} has LoA snapshot with invalid date(s).`, | ||
); | ||
} | ||
|
||
if ( | ||
!isVehicleTypeValid( | ||
permitVehicleType, | ||
permitVehicleId, | ||
permitLoaPowerUnits, | ||
permitLoaTrailers, | ||
) | ||
) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} has LoA snapshot with invalid vehicle(s).`, | ||
); | ||
} | ||
if (!isPermitTypeValid(permit.permitType, permitTypesLoa)) { | ||
throw new UnprocessableEntityException( | ||
`${permit.applicationNumber} has LoA snapshot with invalid permitType.`, | ||
); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves a single LOA (Letter of Authorization) detail for a specified company. | ||
* | ||
* Steps: | ||
* 1. Fetches the LOA detail from the repository based on company ID and LOA Number. | ||
* 2. Ensures the fetched LOA detail is active. | ||
* 3. Includes relations (company, loaVehicles, loaPermitTypes) in the query. | ||
* | ||
* @param {number} companyId - ID of the company for which to fetch the LOA detail. | ||
* @param {number} loaId - ID of the LOA to be fetched. | ||
* @returns {Promise<LoaDetail>} - Returns a Promise that resolves to the LOA detail. | ||
*/ | ||
@LogAsyncMethodExecution() | ||
async findLoas( | ||
companyId: number, | ||
loaNumbers: number[], | ||
): Promise<ReadLoaDto[]> { | ||
// Fetch initial active LOA details | ||
const loaDetails = await this.loaDetailRepository.find({ | ||
where: { | ||
loaNumber: In(loaNumbers), | ||
isActive: true, | ||
company: { companyId }, | ||
}, | ||
relations: ['company', 'loaVehicles', 'loaPermitTypes'], | ||
}); | ||
|
||
const readLoaDto = await this.classMapper.mapArrayAsync( | ||
loaDetails, | ||
LoaDetail, | ||
ReadLoaDto, | ||
{ | ||
extraArgs: () => ({ companyId: companyId }), | ||
}, | ||
); | ||
return readLoaDto; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be part of the validate endpoint?