Merge pull request 'feat: open guard cancel recap recon and add new logic for recap recon cancel handler and change logic recap handler' (#79) from feat/reconciliation into production
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build encountered an error Details

Reviewed-on: #79
pull/81/head 1.0.6-production.1
firmanr 2024-09-06 11:42:28 +00:00
commit 4c25b2cbec
6 changed files with 98 additions and 31 deletions

View File

@ -43,11 +43,20 @@ export class BatchCancelReconciliationManager extends BaseBatchUpdateStatusManag
payment_date: this.data.payment_date, payment_date: this.data.payment_date,
}); });
// FIXME => VALIDATION GUARD CANCEL FOR RECONCILIATION FROM CASHIER
if (data.is_recap_transaction) { if (data.is_recap_transaction) {
throw new UnprocessableEntityException({ // throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY, // statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Gagagl! tidak dapat batalkan data rekap`, // message: `Gagagl! tidak dapat batalkan data rekap`,
error: 'Unprocessable Entity', // error: 'Unprocessable Entity',
// });
Object.assign(this.data, {
reconciliation_confirm_by: null,
reconciliation_confirm_date: null,
reconciliation_status: STATUS.PENDING,
payment_code: null,
settlement_date: null,
}); });
} }

View File

@ -37,26 +37,39 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager<Transac
error: 'Unprocessable Entity', error: 'Unprocessable Entity',
}); });
} else if (this.data.is_recap_transaction) { } else if (this.data.is_recap_transaction) {
throw new UnprocessableEntityException({ // FIXME => VALIDATION GUARD CANCEL FOR RECONCILIATION FROM CASHIER
statusCode: HttpStatus.UNPROCESSABLE_ENTITY, // throw new UnprocessableEntityException({
message: `Gagagl! tidak dapat batalkan data rekap`, // statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
error: 'Unprocessable Entity', // message: `Gagagl! tidak dapat batalkan data rekap`,
}); // error: 'Unprocessable Entity',
// });
} }
return; return;
} }
async beforeProcess(): Promise<void> { async beforeProcess(): Promise<void> {
Object.assign(this.data, { if (this.data.is_recap_transaction) {
reconciliation_mdr: this.data.reconciliation_mdr ?? null, Object.assign(this.data, {
reconciliation_confirm_by: this.user.name, reconciliation_confirm_by: null,
reconciliation_confirm_date: new Date().getTime(), reconciliation_confirm_date: null,
status: this.dataStatus, reconciliation_status: STATUS.PENDING,
reconciliation_status: this.dataStatus, payment_code: null,
payment_date: this.data.payment_date, settlement_date: null,
settlement_date: });
this.dataStatus === STATUS.REJECTED ? null : this.data.settlement_date, } else {
}); Object.assign(this.data, {
reconciliation_mdr: this.data.reconciliation_mdr ?? null,
reconciliation_confirm_by: this.user.name,
reconciliation_confirm_date: new Date().getTime(),
status: this.dataStatus,
reconciliation_status: this.dataStatus,
payment_date: this.data.payment_date,
settlement_date:
this.dataStatus === STATUS.REJECTED
? null
: this.data.settlement_date,
});
}
return; return;
} }

View File

@ -8,12 +8,18 @@ import { Between, IsNull, MoreThan, Not } from 'typeorm';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as moment from 'moment'; import * as moment from 'moment';
import { EMPTY_UUID, STATUS } from 'src/core/strings/constants/base.constants'; import { EMPTY_UUID, STATUS } from 'src/core/strings/constants/base.constants';
import { RecapReconciliationDto } from '../../../infrastructure/dto/recap.dto';
@Injectable() @Injectable()
export class RecapReconciliationManager extends BaseCustomManager<TransactionEntity> { export class RecapReconciliationManager extends BaseCustomManager<TransactionEntity> {
private recapTransactions = {}; private recapTransactions = {};
private startOfDay = moment().startOf('day').valueOf(); private bodyFilter: any;
private endOfDay = moment().endOf('day').valueOf(); private paymentDate: string;
setBodyFilter(body: RecapReconciliationDto) {
this.bodyFilter = body;
this.paymentDate = body.payment_date;
}
get entityTarget(): any { get entityTarget(): any {
return TransactionModel; return TransactionModel;
@ -37,7 +43,7 @@ export class RecapReconciliationManager extends BaseCustomManager<TransactionEnt
is_recap_transaction: false, is_recap_transaction: false,
payment_type: TransactionType.COUNTER, payment_type: TransactionType.COUNTER,
status: STATUS.SETTLED, status: STATUS.SETTLED,
created_at: Between(this.startOfDay, this.endOfDay), payment_date: this.paymentDate,
payment_type_counter: Not(IsNull()), payment_type_counter: Not(IsNull()),
payment_total: MoreThan(0), payment_total: MoreThan(0),
}, },
@ -48,13 +54,17 @@ export class RecapReconciliationManager extends BaseCustomManager<TransactionEnt
creator_counter_no, creator_counter_no,
payment_type_counter, payment_type_counter,
payment_type_method_id, payment_type_method_id,
payment_date,
} = transaction; } = transaction;
const group_by = const group_by =
creator_counter_no + creator_counter_no +
'-' + '-' +
payment_type_counter + payment_type_counter +
'-' + '-' +
payment_type_method_id; payment_type_method_id +
'_' +
payment_date;
if (!this.recapTransactions[group_by]) { if (!this.recapTransactions[group_by]) {
this.recapTransactions[group_by] = []; this.recapTransactions[group_by] = [];
} }
@ -66,17 +76,20 @@ export class RecapReconciliationManager extends BaseCustomManager<TransactionEnt
async process(): Promise<void> { async process(): Promise<void> {
const total_recap = Object.keys(this.recapTransactions); const total_recap = Object.keys(this.recapTransactions);
for (const recap of total_recap) { for (const recap of total_recap) {
const first_transaction = this.recapTransactions[recap][0]; const first_transaction = this.recapTransactions[recap][0];
const { const {
creator_counter_no, creator_counter_no,
payment_type_counter, payment_type_counter,
payment_type_method_id, payment_type_method_id,
payment_date,
} = first_transaction; } = first_transaction;
const query = { const query = {
is_recap_transaction: true, is_recap_transaction: true,
created_at: Between(this.startOfDay, this.endOfDay), payment_date: payment_date,
creator_counter_no: creator_counter_no, creator_counter_no: creator_counter_no,
payment_type: payment_type_counter, payment_type: payment_type_counter,
}; };
@ -94,9 +107,30 @@ export class RecapReconciliationManager extends BaseCustomManager<TransactionEnt
); );
if (exist) { if (exist) {
if (parseFloat(exist.payment_total) !== total) {
const newTotal = total;
const newTotalNet =
total - parseFloat(exist.reconciliation_mdr ?? '0');
Object.assign(exist, {
payment_total: newTotal,
payment_total_net_profit: newTotalNet,
//RESET STATUS TO PENDING
reconciliation_confirm_by: null,
reconciliation_confirm_date: null,
reconciliation_status: STATUS.PENDING,
payment_code: null,
settlement_date: null,
});
} else {
Object.assign(exist, {
payment_total: total,
payment_total_net_profit: total,
});
}
Object.assign(exist, { Object.assign(exist, {
payment_total: total,
payment_total_net_profit: total,
editor_id: this.user.id, editor_id: this.user.id,
editor_name: this.user.name, editor_name: this.user.name,
updated_at: new Date().getTime(), updated_at: new Date().getTime(),
@ -109,8 +143,8 @@ export class RecapReconciliationManager extends BaseCustomManager<TransactionEnt
reconciliation_status: STATUS.PENDING, reconciliation_status: STATUS.PENDING,
status: STATUS.SETTLED, status: STATUS.SETTLED,
type: TransactionType.COUNTER, type: TransactionType.COUNTER,
booking_date: new Date(), booking_date: payment_date,
payment_date: new Date(), payment_date: payment_date,
creator_counter_no: first_transaction.creator_counter_no, creator_counter_no: first_transaction.creator_counter_no,
payment_type: first_transaction.payment_type_counter, payment_type: first_transaction.payment_type_counter,
payment_type_counter: first_transaction.payment_type_counter, payment_type_counter: first_transaction.payment_type_counter,

View File

@ -11,6 +11,7 @@ import { TransactionEntity } from 'src/modules/transaction/transaction/domain/en
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
import { RecapReconciliationManager } from './managers/recap-reconciliation.manager'; import { RecapReconciliationManager } from './managers/recap-reconciliation.manager';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { RecapReconciliationDto } from '../../infrastructure/dto/recap.dto';
@Injectable() @Injectable()
export class ReconciliationDataOrchestrator { export class ReconciliationDataOrchestrator {
@ -31,9 +32,10 @@ export class ReconciliationDataOrchestrator {
return this.updateManager.getResult(); return this.updateManager.getResult();
} }
async recap() { async recap(body: RecapReconciliationDto) {
const data = new TransactionModel(); const data = new TransactionModel();
this.recapManager.setData(data); this.recapManager.setData(data);
this.recapManager.setBodyFilter(body);
this.recapManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); this.recapManager.setService(this.serviceData, TABLE_NAME.TRANSACTION);
await this.recapManager.execute(); await this.recapManager.execute();
return this.recapManager.getResult(); return this.recapManager.getResult();

View File

@ -0,0 +1,8 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';
export class RecapReconciliationDto {
@ApiProperty({ required: true, type: String })
@IsString()
payment_date: string;
}

View File

@ -15,6 +15,7 @@ import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'
import { Public } from 'src/core/guards'; 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';
@ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`) @ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.RECONCILIATION}`) @Controller(`v1/${MODULE_NAME.RECONCILIATION}`)
@ -24,8 +25,8 @@ export class ReconciliationDataController {
constructor(private orchestrator: ReconciliationDataOrchestrator) {} constructor(private orchestrator: ReconciliationDataOrchestrator) {}
@Post('/recap-transaction') @Post('/recap-transaction')
async recap(): Promise<any> { async recap(@Body() body: RecapReconciliationDto): Promise<any> {
return await this.orchestrator.recap(); return await this.orchestrator.recap(body);
} }
@Put('/batch-confirm') @Put('/batch-confirm')