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); 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 { public generateSecureOTP(): string {
let otp: string; let otp: string;
@ -46,11 +54,12 @@ export class OtpService {
Math.floor(Math.random() * 10).toString(), Math.floor(Math.random() * 10).toString(),
).join(''); ).join('');
} while ( } while (
this.hasNoMatchLength(otp) ||
this.hasSequentialDigits(otp) || this.hasSequentialDigits(otp) ||
this.hasRepeatedDigits(otp) || this.hasRepeatedDigits(otp) ||
this.isPalindrome(otp) || this.isPalindrome(otp) ||
this.hasPartiallyRepeatedDigits(otp) || this.hasPartiallyRepeatedDigits(otp) ||
otp?.length < this.otpLength this.hasStartWithZero(otp)
); );
return otp; return otp;
} }

View File

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

View File

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

View File

@ -58,9 +58,10 @@ export class ReconciliationDataOrchestrator {
return this.batchConfirmManager.getResult(); return this.batchConfirmManager.getResult();
} }
async cancel(dataId): Promise<string> { async cancel(dataId, body): Promise<string> {
this.cancelManager.setData(dataId, STATUS.REJECTED); this.cancelManager.setData(dataId, STATUS.REJECTED);
this.cancelManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); this.cancelManager.setService(this.serviceData, TABLE_NAME.TRANSACTION);
this.cancelManager.setCustomBodyRequest(body);
await this.cancelManager.execute(); await this.cancelManager.execute();
return this.cancelManager.getResult(); 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 { TransactionEntity } from '../../transaction/domain/entities/transaction.entity';
import { UpdateReconciliationDto } from './dto/reconciliation.dto'; import { UpdateReconciliationDto } from './dto/reconciliation.dto';
import { RecapReconciliationDto } from './dto/recap.dto'; import { RecapReconciliationDto } from './dto/recap.dto';
import { OtpVerifyDto } from './dto/cancel-top-dto';
@ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`) @ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.RECONCILIATION}`) @Controller(`v1/${MODULE_NAME.RECONCILIATION}`)
@ -40,8 +41,11 @@ export class ReconciliationDataController {
} }
@Patch(':id/cancel') @Patch(':id/cancel')
async cancel(@Param('id') dataId: string): Promise<string> { async cancel(
return await this.orchestrator.cancel(dataId); @Param('id') dataId: string,
@Body() body: OtpVerifyDto,
): Promise<string> {
return await this.orchestrator.cancel(dataId, body);
} }
@Put('/batch-cancel') @Put('/batch-cancel')