Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into development

pull/157/head 1.6.14-alpha.1
shancheas 2025-06-10 16:34:17 +07:00
commit 033ed0046e
6 changed files with 56 additions and 11 deletions

View File

@ -38,6 +38,14 @@ export class OtpService {
return Object.values(counts).some((count) => count > 2);
}
private hasNoMatchLength(str: string) {
return str.length !== this.otpLength;
}
private hasStartWithZero(str: string) {
return str.split('')[0] === '0';
}
public generateSecureOTP(): string {
let otp: string;
@ -46,11 +54,12 @@ export class OtpService {
Math.floor(Math.random() * 10).toString(),
).join('');
} while (
this.hasNoMatchLength(otp) ||
this.hasSequentialDigits(otp) ||
this.hasRepeatedDigits(otp) ||
this.isPalindrome(otp) ||
this.hasPartiallyRepeatedDigits(otp) ||
otp?.length < this.otpLength
this.hasStartWithZero(otp)
);
return otp;
}

View File

@ -74,7 +74,9 @@ export class OtpVerificationService {
const createdAtMoment = moment(Number(activeOTP.created_at));
const nowMoment = moment(Number(dateNow));
const diffSeconds = nowMoment.diff(createdAtMoment, 'seconds');
if (diffSeconds < 60) {
const isProduction = process.env.NODE_ENV === 'true';
if (diffSeconds < 60 && isProduction) {
throw new BadRequestException(
'An active OTP request was made recently. Please try again later.',
);
@ -116,10 +118,13 @@ export class OtpVerificationService {
);
}
let otp: any;
// Build a where condition with OR between target_id and reference
const otp = await this.otpVerificationRepo.findOne({
where: [
{
if (target_id) {
otp = await this.otpVerificationRepo.findOne({
where: {
otp_code,
action_type,
target_id,
@ -127,7 +132,10 @@ export class OtpVerificationService {
is_used: false,
is_replaced: false,
},
{
});
} else if (reference) {
otp = await this.otpVerificationRepo.findOne({
where: {
otp_code,
action_type,
reference,
@ -135,8 +143,8 @@ export class OtpVerificationService {
is_used: false,
is_replaced: false,
},
],
});
}
if (!otp) {
throw new BadRequestException('Invalid or expired OTP.');

View File

@ -15,6 +15,12 @@ import { TransactionEntity } from 'src/modules/transaction/transaction/domain/en
@Injectable()
export class CancelReconciliationManager extends BaseUpdateStatusManager<TransactionEntity> {
protected payloadBody: any;
setCustomBodyRequest(body) {
this.payloadBody = body;
}
getResult(): string {
return `Success active data ${this.result.id}`;
}
@ -50,6 +56,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager<Transac
async beforeProcess(): Promise<void> {
if (this.data.is_recap_transaction) {
Object.assign(this.data, {
otp_code: this.payloadBody?.otp_code,
reconciliation_confirm_by: null,
reconciliation_confirm_date: null,
reconciliation_status: STATUS.PENDING,
@ -58,6 +65,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager<Transac
});
} else {
Object.assign(this.data, {
otp_code: this.payloadBody?.otp_code,
reconciliation_mdr: this.data.reconciliation_mdr ?? null,
reconciliation_confirm_by: this.user.name,
reconciliation_confirm_date: new Date().getTime(),
@ -70,6 +78,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager<Transac
: this.data.settlement_date,
});
}
return;
}

View File

@ -58,9 +58,10 @@ export class ReconciliationDataOrchestrator {
return this.batchConfirmManager.getResult();
}
async cancel(dataId): Promise<string> {
async cancel(dataId, body): Promise<string> {
this.cancelManager.setData(dataId, STATUS.REJECTED);
this.cancelManager.setService(this.serviceData, TABLE_NAME.TRANSACTION);
this.cancelManager.setCustomBodyRequest(body);
await this.cancelManager.execute();
return this.cancelManager.getResult();
}

View File

@ -0,0 +1,14 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class OtpVerifyDto {
@ApiProperty({
name: 'otp_code',
type: String,
required: true,
example: '2345',
})
@IsString()
@IsNotEmpty()
otp_code: string;
}

View File

@ -16,6 +16,7 @@ import { Public } from 'src/core/guards';
import { TransactionEntity } from '../../transaction/domain/entities/transaction.entity';
import { UpdateReconciliationDto } from './dto/reconciliation.dto';
import { RecapReconciliationDto } from './dto/recap.dto';
import { OtpVerifyDto } from './dto/cancel-top-dto';
@ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.RECONCILIATION}`)
@ -40,8 +41,11 @@ export class ReconciliationDataController {
}
@Patch(':id/cancel')
async cancel(@Param('id') dataId: string): Promise<string> {
return await this.orchestrator.cancel(dataId);
async cancel(
@Param('id') dataId: string,
@Body() body: OtpVerifyDto,
): Promise<string> {
return await this.orchestrator.cancel(dataId, body);
}
@Put('/batch-cancel')