gemcap-be-common 1.3.97 → 1.3.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,6 @@ import Decimal from 'decimal.js';
5
5
 
6
6
  import { IEmail } from '../interfaces/email.interface';
7
7
  import { calculateTimeZone } from '../helpers/date.helper';
8
- import { roundToXDigits } from '../helpers/numbers.helper';
9
8
  import { createFilteredObject } from '../helpers/common.helper';
10
9
  import { IPaginatorOptions } from '../interfaces/collaterals.interface';
11
10
  import { BBCDateModel } from '../models/BBCDate.model';
@@ -400,7 +399,7 @@ export class LoanTransactionsService {
400
399
  }
401
400
  }
402
401
 
403
- async recalculateBalance(transactionId: string) {
402
+ async recalculateBalanceLegacy(transactionId: string) {
404
403
  const changedTransaction = await LoanTransaction.findById<ILoanTransactionDoc & {
405
404
  createdAt: Date
406
405
  }>(transactionId);
@@ -408,127 +407,240 @@ export class LoanTransactionsService {
408
407
  console.error(`no transactions with id ${transactionId}`);
409
408
  return;
410
409
  }
411
- setTimeout(async () => {
412
- await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: false });
413
- const previousTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
414
- {
415
- $match: {
416
- 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
417
- 'date': { $lt: new Date(changedTransaction.date) },
418
- },
419
- }, {
420
- $sort: {
421
- 'date': -1,
422
- 'order': -1,
423
- 'createdAt': -1,
424
- },
425
- }, {
426
- $limit: 1,
427
- },
428
- ]);
429
- let currentBalance = previousTransaction.length
430
- ? previousTransaction[0].balance ?? 0
431
- : 0;
432
- const recalculatedTransactions = await LoanTransaction.aggregate<ILoanTransactionDoc>([
433
- {
434
- $match: {
435
- 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
436
- 'date': { $gte: new Date(changedTransaction.date) },
437
- },
438
- }, {
439
- $sort: {
440
- 'date': 1,
441
- 'order': 1,
442
- 'createdAt': 1,
443
- },
444
- },
445
- ]);
446
- for (const transaction of recalculatedTransactions) {
447
- currentBalance = new Decimal(currentBalance).add(transaction.amount).toNumber();
448
- await LoanTransaction.findByIdAndUpdate(transaction._id, { balance: currentBalance });
410
+
411
+ const recalcBalance = async () => {
412
+ const session = await mongoose.startSession();
413
+ try {
414
+ await session.withTransaction(async () => {
415
+ await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: false });
416
+ const previousTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
417
+ {
418
+ $match: {
419
+ 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
420
+ 'date': { $lt: new Date(changedTransaction.date) },
421
+ },
422
+ }, {
423
+ $sort: {
424
+ 'date': -1,
425
+ 'order': -1,
426
+ 'createdAt': -1,
427
+ },
428
+ }, {
429
+ $limit: 1,
430
+ },
431
+ ]);
432
+ let currentBalance = previousTransaction.length
433
+ ? previousTransaction[0].balance ?? 0
434
+ : 0;
435
+ const recalculatedTransactions = await LoanTransaction.aggregate<ILoanTransactionDoc>([
436
+ {
437
+ $match: {
438
+ 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
439
+ 'date': { $gte: new Date(changedTransaction.date) },
440
+ },
441
+ }, {
442
+ $sort: {
443
+ 'date': 1,
444
+ 'order': 1,
445
+ 'createdAt': 1,
446
+ },
447
+ },
448
+ ]);
449
+ for (const transaction of recalculatedTransactions) {
450
+ currentBalance = new Decimal(currentBalance).add(transaction.amount).toNumber();
451
+ await LoanTransaction.findByIdAndUpdate(transaction._id, { balance: currentBalance });
452
+ }
453
+ await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: true });
454
+ });
455
+ } finally {
456
+ await session.endSession();
449
457
  }
450
- await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isBalanceActual: true });
451
- });
452
- setTimeout(async () => {
453
- await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: false });
454
- const previousEqualBalancesTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
455
- {
456
- $match: {
457
- 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
458
- 'date': { $lt: new Date(changedTransaction.date) },
459
- $expr: { $eq: ['$balance', '$floatedBalance'] },
458
+ };
459
+
460
+ const recalcFloatedBalance = async () => {
461
+ const session = await mongoose.startSession();
462
+ try {
463
+ await session.withTransaction(async () => {
464
+ await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: false });
465
+ const previousEqualBalancesTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
466
+ {
467
+ $match: {
468
+ 'productId': new mongoose.Types.ObjectId(changedTransaction.productId.toString()),
469
+ 'date': { $lt: new Date(changedTransaction.date) },
470
+ $expr: { $eq: ['$balance', '$floatedBalance'] },
471
+ },
472
+ }, {
473
+ $sort: {
474
+ 'date': -1,
475
+ 'order': -1,
476
+ 'createdAt': -1,
477
+ },
478
+ }, {
479
+ $limit: 1,
480
+ },
481
+ ]);
482
+ let currentFloatedBalance = 0;
483
+ if (previousEqualBalancesTransaction.length) {
484
+ const previousTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
485
+ {
486
+ $match: {
487
+ 'productId': new mongoose.Types.ObjectId(previousEqualBalancesTransaction[0].productId.toString()),
488
+ 'date': { $lt: new Date(previousEqualBalancesTransaction[0].date) },
489
+ },
490
+ }, {
491
+ $sort: {
492
+ 'date': -1,
493
+ 'order': -1,
494
+ 'createdAt': -1,
495
+ },
496
+ }, {
497
+ $limit: 1,
498
+ },
499
+ ]);
500
+ if (previousTransaction.length) {
501
+ currentFloatedBalance = previousTransaction[0].floatedBalance;
502
+ }
503
+ }
504
+ const initTransaction = previousEqualBalancesTransaction.length ? previousEqualBalancesTransaction[0] : changedTransaction;
505
+ const recalculatedTransactions = await LoanTransaction.aggregate<ILoanTransactionDoc>([
506
+ {
507
+ $match: {
508
+ 'productId': new mongoose.Types.ObjectId(initTransaction.productId.toString()),
509
+ 'date': { $gte: new Date(initTransaction.date) },
510
+ },
511
+ }, {
512
+ $sort: {
513
+ 'date': 1,
514
+ 'order': 1,
515
+ 'createdAt': 1,
516
+ },
517
+ },
518
+ ]);
519
+ for (const transaction of recalculatedTransactions) {
520
+ const totalPostponed = await getPostponedTransactions(transaction.date, transaction.productId.toString(), true);
521
+ switch (transaction.transactionType) {
522
+ case ELoanTransactionTypes.COLLECTION:
523
+ await this.cleanRecalculated(transaction._id.toString());
524
+ await this.createPostponedTransaction(transaction);
525
+ currentFloatedBalance = new Decimal(currentFloatedBalance).add(totalPostponed).toNumber();
526
+ break;
527
+ case ELoanTransactionTypes.DISBURSEMENT:
528
+ currentFloatedBalance = new Decimal(currentFloatedBalance).add(transaction.amount).add(totalPostponed).toNumber();
529
+ break;
530
+ case ELoanTransactionTypes.ADJUSTMENT:
531
+ currentFloatedBalance = new Decimal(currentFloatedBalance).add(transaction.amount).add(totalPostponed).toNumber();
532
+ break;
533
+ }
534
+ await LoanTransaction.findByIdAndUpdate(transaction._id, { floatedBalance: currentFloatedBalance });
535
+ }
536
+ await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: true });
537
+ });
538
+ } finally {
539
+ await session.endSession();
540
+ }
541
+ };
542
+
543
+ await recalcBalance();
544
+ await recalcFloatedBalance();
545
+ }
546
+
547
+ async recalculateBalance(transactionId: string) {
548
+ const startTransaction = await LoanTransaction.findById(transactionId).lean();
549
+ if (!startTransaction) {
550
+ console.error(`Transaction not found: ${transactionId}`);
551
+ return;
552
+ }
553
+
554
+ const session = await mongoose.startSession();
555
+ try {
556
+ await session.withTransaction(async () => {
557
+ // Получаем все транзакции, начиная с самой ранней, которую нужно пересчитать
558
+ const earliestTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
559
+ {
560
+ $match: {
561
+ productId: startTransaction.productId,
562
+ date: { $lte: new Date(startTransaction.date) }
563
+ }
460
564
  },
461
- }, {
462
- $sort: {
463
- 'date': 1,
464
- 'order': 1,
465
- 'createdAt': 1,
565
+ { $sort: { date: 1, order: 1, createdAt: 1 } },
566
+ { $limit: 1 }
567
+ ]);
568
+
569
+ const fromDate = earliestTransaction.length
570
+ ? earliestTransaction[0].date
571
+ : startTransaction.date;
572
+
573
+ const allTransactions = await LoanTransaction.aggregate<ILoanTransactionDoc>([
574
+ {
575
+ $match: {
576
+ productId: startTransaction.productId,
577
+ date: { $gte: fromDate }
578
+ }
466
579
  },
467
- }, {
468
- $limit: 1,
469
- },
470
- ]);
471
- let currentFloatedBalance = 0;
472
- if (previousEqualBalancesTransaction.length) {
473
- const previousTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([
580
+ { $sort: { date: 1, order: 1, createdAt: 1 } }
581
+ ]);
582
+
583
+ let balance = 0;
584
+ let floatedBalance = 0;
585
+
586
+ // Найдём стартовые значения
587
+ const prevBeforeStart = await LoanTransaction.aggregate<ILoanTransactionDoc>([
474
588
  {
475
589
  $match: {
476
- 'productId': new mongoose.Types.ObjectId(previousEqualBalancesTransaction[0].productId.toString()),
477
- 'date': { $lt: new Date(previousEqualBalancesTransaction[0].date) },
478
- },
479
- }, {
480
- $sort: {
481
- 'date': -1,
482
- 'order': -1,
483
- 'createdAt': -1,
484
- },
485
- }, {
486
- $limit: 1,
590
+ productId: startTransaction.productId,
591
+ date: { $lt: fromDate }
592
+ }
487
593
  },
594
+ { $sort: { date: -1, order: -1, createdAt: -1 } },
595
+ { $limit: 1 }
488
596
  ]);
489
- if (previousTransaction.length) {
490
- currentFloatedBalance = previousTransaction[0].floatedBalance;
597
+
598
+ if (prevBeforeStart.length) {
599
+ balance = prevBeforeStart[0].balance || 0;
600
+ floatedBalance = prevBeforeStart[0].floatedBalance || 0;
491
601
  }
492
- }
493
- const initTransaction = previousEqualBalancesTransaction.length ? previousEqualBalancesTransaction[0] : changedTransaction;
494
- const recalculatedTransactions = await LoanTransaction.aggregate<ILoanTransactionDoc>([
495
- {
496
- $match: {
497
- 'productId': new mongoose.Types.ObjectId(initTransaction.productId.toString()),
498
- 'date': { $gte: new Date(initTransaction.date) },
499
- },
500
- }, {
501
- $sort: {
502
- 'date': 1,
503
- 'order': 1,
504
- 'createdAt': 1,
505
- },
506
- },
507
- ]);
508
- for (const transaction of recalculatedTransactions) {
509
- const totalPostponed = await getPostponedTransactions(transaction.date, transaction.productId.toString(), true);
510
- switch (transaction.transactionType) {
511
- case ELoanTransactionTypes.COLLECTION:
512
- await this.cleanRecalculated(transaction._id.toString());
513
- await this.createPostponedTransaction(transaction);
514
- currentFloatedBalance = roundToXDigits(currentFloatedBalance + totalPostponed);
515
- break;
516
- case ELoanTransactionTypes.DISBURSEMENT:
517
- currentFloatedBalance = roundToXDigits(currentFloatedBalance + transaction.amount + totalPostponed);
518
- break;
519
- case ELoanTransactionTypes.ADJUSTMENT:
520
- currentFloatedBalance = roundToXDigits(currentFloatedBalance + transaction.amount + totalPostponed);
521
- break;
602
+
603
+ // Один проход для расчёта balance и floatedBalance
604
+ for (const tr of allTransactions) {
605
+ // Пересчёт обычного баланса
606
+ balance = new Decimal(balance).add(tr.amount).toNumber();
607
+
608
+ // Пересчёт floatedBalance
609
+ const totalPostponed = await getPostponedTransactions(tr.date, tr.productId.toString(), true);
610
+ switch (tr.transactionType) {
611
+ case ELoanTransactionTypes.COLLECTION:
612
+ await this.cleanRecalculated(tr._id.toString());
613
+ await this.createPostponedTransaction(tr);
614
+ floatedBalance = new Decimal(floatedBalance).add(totalPostponed).toNumber();
615
+ break;
616
+ case ELoanTransactionTypes.DISBURSEMENT:
617
+ case ELoanTransactionTypes.ADJUSTMENT:
618
+ floatedBalance = new Decimal(floatedBalance).add(tr.amount).add(totalPostponed).toNumber();
619
+ break;
620
+ }
621
+
622
+ await LoanTransaction.updateOne(
623
+ { _id: tr._id },
624
+ { balance, floatedBalance },
625
+ { session }
626
+ );
522
627
  }
523
- await LoanTransaction.findByIdAndUpdate(transaction._id, { floatedBalance: currentFloatedBalance });
524
- }
525
- await LoanProduct.findByIdAndUpdate(changedTransaction.productId, { isFloatedBalanceActual: true });
526
- }, 1_000);
628
+
629
+ await LoanProduct.updateOne(
630
+ { _id: startTransaction.productId },
631
+ { isBalanceActual: true, isFloatedBalanceActual: true },
632
+ { session }
633
+ );
634
+ });
635
+ } finally {
636
+ await session.endSession();
637
+ }
527
638
  }
528
639
 
529
640
  async deleteLoanTransaction(transactionId: string, updateLoanPayment: boolean, userId = null) {
530
641
  const currentTransaction = await LoanTransaction.findById(transactionId).lean();
531
642
  if (!currentTransaction) {
643
+ console.error(`no transactions with id ${transactionId}`);
532
644
  return;
533
645
  }
534
646
  const nextTransaction = await LoanTransaction.aggregate<ILoanTransactionDoc>([