Loan account Contract Termination transaction

This commit is contained in:
Jose Alberto Hernandez
2025-05-25 12:28:11 -05:00
parent 8fe04b08a3
commit 28dfe561b8
30 changed files with 4977 additions and 3071 deletions
@@ -75,6 +75,8 @@ export class LoanActionButtonResolver {
return this.loansService.getLoanActionTemplate(loanId, 'charge-off');
} else if (loanActionButton === 'Capitalized Income') {
return this.loansService.getLoanActionTemplate(loanId, 'capitalizedIncome');
} else if (loanActionButton === 'Contract Termination') {
return this.loansService.getLoanActionTemplate(loanId, 'contractTermination');
} else {
return undefined;
}
@@ -5,7 +5,10 @@
[dataObject]="actionButtonData"
></mifosx-assign-loan-officer>
<mifosx-foreclosure [dataObject]="actionButtonData" *ngIf="actions['Foreclosure']"></mifosx-foreclosure>
<mifosx-prepay-loan [dataObject]="actionButtonData" *ngIf="actions['Prepay Loan']"></mifosx-prepay-loan>
<mifosx-prepay-loan
[dataObject]="actionButtonData"
*ngIf="actions['Prepay Loan'] || actions['Contract Termination']"
></mifosx-prepay-loan>
<mifosx-make-repayment [dataObject]="actionButtonData" *ngIf="actions['Make Repayment']"></mifosx-make-repayment>
<mifosx-make-repayment [dataObject]="actionButtonData" *ngIf="actions['Capitalized Income']"></mifosx-make-repayment>
<mifosx-make-repayment [dataObject]="actionButtonData" *ngIf="actions['Goodwill Credit']"></mifosx-make-repayment>
@@ -52,6 +52,7 @@ export class LoanAccountActionsComponent {
'Interest Payment Waiver': boolean;
'Add Interest Pause': boolean;
'Capitalized Income': boolean;
'Contract Termination': boolean;
} = {
Close: false,
'Undo Approval': false,
@@ -88,7 +89,8 @@ export class LoanAccountActionsComponent {
'Re-Amortize': false,
'Interest Payment Waiver': false,
'Add Interest Pause': false,
'Capitalized Income': false
'Capitalized Income': false,
'Contract Termination': false
};
actionButtonData: any;
@@ -119,5 +121,7 @@ export class LoanAccountActionsComponent {
}
this.actions[this.actionName as keyof typeof this.actions] = true;
});
this.actionButtonData['actionName'] = this.actionName;
}
}
@@ -3,7 +3,7 @@
<form [formGroup]="prepayLoanForm" (ngSubmit)="submit()">
<mat-card-content>
<div class="layout-column">
<mat-form-field (click)="transactionDatePicker.open()">
<mat-form-field (click)="transactionDatePicker.open()" *ngIf="!contractTermination">
<mat-label>{{ 'labels.inputs.Transaction Date' | translate }}</mat-label>
<input
matInput
@@ -56,6 +56,7 @@
</div>
<mifosx-input-amount
*ngIf="!contractTermination"
[currency]="currency"
[isRequired]="true"
[inputFormControl]="prepayLoanForm.controls.transactionAmount"
@@ -68,7 +69,7 @@
<input matInput formControlName="externalId" />
</mat-form-field>
<mat-form-field>
<mat-form-field *ngIf="!contractTermination">
<mat-label>{{ 'labels.inputs.Payment Type' | translate }}</mat-label>
<mat-select formControlName="paymentTypeId">
<mat-option *ngFor="let paymentType of paymentTypes" [value]="paymentType.id">
@@ -77,7 +78,7 @@
</mat-select>
</mat-form-field>
<div class="flex-fill">
<div class="flex-fill" *ngIf="!contractTermination">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75" (click)="addPaymentDetails()">
<mat-slide-toggle>
<div [className]="showPaymentDetails ? 'enabled' : 'disabled'">
@@ -38,6 +38,7 @@ export class PrepayLoanComponent implements OnInit {
prepayData: any;
currency: Currency | null = null;
contractTermination: boolean;
/**
* @param {FormBuilder} formBuilder Form Builder.
@@ -62,10 +63,13 @@ export class PrepayLoanComponent implements OnInit {
* and initialize with the required values
*/
ngOnInit() {
this.prepayData = this.dataObject;
this.contractTermination = this.dataObject['actionName'] == 'Contract Termination';
this.maxDate = this.settingsService.businessDate;
this.createprepayLoanForm();
this.setPrepayLoanDetails();
this.prepayData = this.dataObject;
if (!this.contractTermination) {
this.setPrepayLoanDetails();
}
if (this.dataObject.currency) {
this.currency = this.dataObject.currency;
}
@@ -75,19 +79,26 @@ export class PrepayLoanComponent implements OnInit {
* Creates the prepay loan form.
*/
createprepayLoanForm() {
this.prepayLoanForm = this.formBuilder.group({
transactionDate: [
new Date(),
Validators.required
],
transactionAmount: [
'',
Validators.required
],
externalId: [''],
paymentTypeId: [''],
note: ['']
});
if (this.contractTermination) {
this.prepayLoanForm = this.formBuilder.group({
externalId: [''],
note: ['']
});
} else {
this.prepayLoanForm = this.formBuilder.group({
transactionDate: [
new Date(),
Validators.required
],
transactionAmount: [
'',
Validators.required
],
externalId: [''],
paymentTypeId: [''],
note: ['']
});
}
}
/**
@@ -133,7 +144,7 @@ export class PrepayLoanComponent implements OnInit {
/**
* Submits the prepay loan form
*/
submit() {
submitRepayment() {
const prepayLoanFormData = this.prepayLoanForm.value;
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
@@ -151,4 +162,21 @@ export class PrepayLoanComponent implements OnInit {
this.router.navigate(['../../general'], { relativeTo: this.route });
});
}
submitContractTermination() {
const data = {
...this.prepayLoanForm.value
};
this.loanService.loanActionButtons(this.loanId, 'contractTermination', data).subscribe((response: any) => {
this.router.navigate(['../../general'], { relativeTo: this.route });
});
}
submit() {
if (this.contractTermination) {
this.submitContractTermination();
} else {
this.submitRepayment();
}
}
}
@@ -1,3 +1,5 @@
import { OptionData } from 'app/shared/models/option-data.model';
/** Recurring Deposits Account Buttons Configuration */
export class LoansAccountButtonConfiguration {
optionArray: {
@@ -16,8 +18,8 @@ export class LoansAccountButtonConfiguration {
taskPermissionName?: string;
}[];
constructor(status: string) {
this.setOptions(status);
constructor(status: string, substatus: OptionData) {
this.setOptions(status, substatus);
this.setButtons(status);
}
@@ -154,7 +156,7 @@ export class LoansAccountButtonConfiguration {
}
}
setOptions(status: string) {
setOptions(status: string, substatus: OptionData) {
switch (status) {
case 'Active':
this.optionArray = [
@@ -199,6 +201,12 @@ export class LoansAccountButtonConfiguration {
taskPermissionName: 'SALE_LOAN'
}
];
if (!this.isContractTermination(substatus)) {
this.optionArray.push({
name: 'Contract Termination',
taskPermissionName: 'CONTRACT_TERMINATION_LOAN'
});
}
this.optionPaymentArray = [
{
name: 'Goodwill Credit',
@@ -281,4 +289,11 @@ export class LoansAccountButtonConfiguration {
addButton(option: { name: string; icon: string; taskPermissionName?: string }) {
this.buttonsArray.push(option);
}
private isContractTermination(substatus: OptionData): boolean {
if (substatus == null) {
return false;
}
return substatus.code === 'loanSubStatus.loanSubStatusType.contractTermination';
}
}
@@ -22,9 +22,7 @@
<i
class="fa fa-stop"
matTooltip="{{ 'labels.status.' + loanDetailsData.status.value | translate }}"
[ngClass]="
loanDetailsData.inArrears ? 'status-active-overdue' : (iconLoanStatusColor() | statusLookup)
"
[ngClass]="iconLoanStatusColor() | statusLookup"
></i>
<span class="m-r-5">{{ 'labels.heading.Loan Product' | translate }} :</span>
<span class="m-r-5"
@@ -17,6 +17,7 @@ import { Currency } from 'app/shared/models/general.model';
import { DelinquencyPausePeriod } from '../models/loan-account.model';
import { TranslateService } from '@ngx-translate/core';
import { LoanTransaction } from 'app/products/loan-products/models/loan-account.model';
import { OptionData } from 'app/shared/models/option-data.model';
@Component({
selector: 'mifosx-loans-view',
@@ -46,6 +47,7 @@ export class LoansViewComponent implements OnInit {
loanDelinquencyClassificationStyle = '';
loanStatus: LoanStatus;
loanSubStatus: OptionData | null = null;
currency: Currency;
loanReAged = false;
loanReAmortized = false;
@@ -63,6 +65,7 @@ export class LoansViewComponent implements OnInit {
this.loanDatatables = data.loanDatatables;
this.loanDisplayArrearsDelinquency = data.loanArrearsDelinquencyConfig.value || 0;
this.loanStatus = this.loanDetailsData.status;
this.loanSubStatus = this.loanDetailsData.subStatus === undefined ? null : this.loanDetailsData.subStatus;
this.currency = this.loanDetailsData.currency;
if (this.loanStatus.active) {
this.loanDetailsData.transactions.forEach((lt: LoanTransaction) => {
@@ -75,6 +78,7 @@ export class LoansViewComponent implements OnInit {
}
});
}
this.setConditionalButtons();
}
);
this.loanId = this.route.snapshot.params['loanId'];
@@ -90,6 +94,8 @@ export class LoansViewComponent implements OnInit {
});
this.recalculateInterest = this.loanDetailsData.recalculateInterest || true;
this.status = this.loanDetailsData.status.value;
this.loanStatus = this.loanDetailsData.status;
this.loanSubStatus = this.loanDetailsData.subStatus === undefined ? null : this.loanDetailsData.subStatus;
if (this.loanStatus.active && this.loanDetailsData.multiDisburseLoan) {
if (this.loanDetailsData && this.loanDetailsData.transactions) {
this.loanDetailsData.transactions.forEach((transaction: any) => {
@@ -112,7 +118,7 @@ export class LoansViewComponent implements OnInit {
// Defines the buttons based on the status of the loan account
setConditionalButtons() {
this.buttonConfig = new LoansAccountButtonConfiguration(this.status);
this.buttonConfig = new LoansAccountButtonConfiguration(this.status, this.loanSubStatus);
if (this.status === 'Submitted and pending approval') {
this.buttonConfig.addOption({
@@ -345,9 +351,32 @@ export class LoansViewComponent implements OnInit {
if (this.loanDetailsData.chargedOff) {
return 'loanStatusType.chargeoff';
}
if (this.isContractTermination(this.loanSubStatus)) {
return 'loanSubStatusType.contractTermination';
}
if (this.loanDetailsData.inArrears) {
return 'loanStatusType.activeOverdue';
}
return this.loanDetailsData.status.code;
}
loanStatusTooltip() {
if (this.loanDetailsData.chargedOff) {
return 'Chargeoff';
}
if (this.loanDetailsData.inArrears) {
return 'activeOverdue';
}
return this.loanDetailsData.status.code;
}
loanSubStatusTooltip() {
if (this.isContractTermination(this.loanSubStatus)) {
return 'contractTermination';
}
return '';
}
/**
* Delete loan Account
*/
@@ -375,4 +404,11 @@ export class LoansViewComponent implements OnInit {
.navigateByUrl(`/clients/${clientId}/loans-accounts`, { skipLocationChange: true })
.then(() => this.router.navigate([url]));
}
private isContractTermination(substatus: OptionData): boolean {
if (substatus == null) {
return false;
}
return substatus.code === 'loanSubStatus.loanSubStatusType.contractTermination';
}
}
@@ -179,6 +179,7 @@ export class TransactionsTabComponent implements OnInit {
* REAGE:29
* REAMORTIZE:30
* INTEREST REFUND:33
* CONTRACT_TERMINATION:90
*/
showTransactions(transactionsData: LoanTransaction) {
if ([
@@ -195,7 +196,8 @@ export class TransactionsTabComponent implements OnInit {
29,
30,
31,
33
33,
90
].includes(transactionsData.type.id)) {
this.router.navigate([transactionsData.id], { relativeTo: this.route });
}
@@ -22,7 +22,7 @@
<div class="flex-100" [ngClass]="loanTransactionColor()"></div>
<div class="flex-50 mat-body-strong">
{{ 'Transaction Id' | translate }}
{{ 'Transaction Id' | translateKey: 'catalogs' }}
</div>
<div class="flex-50">
@@ -30,7 +30,7 @@
</div>
<div class="flex-50 mat-body-strong" *ngIf="transactionData.toLoanTransactionRelation">
{{ 'Repayment Transaction' | translate }}
{{ 'Repayment Transaction' | translateKey: 'catalogs' }}
</div>
<div class="flex-50" *ngIf="transactionData.toLoanTransactionRelation">
@@ -40,15 +40,15 @@
</div>
<div class="flex-50 mat-body-strong">
{{ 'Type' | translate }}
{{ 'Type' | translateKey: 'catalogs' }}
</div>
<div class="flex-50">
{{ transactionData.type.value }}
{{ transactionData.type.value | translateKey: 'catalogs' }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'Transaction Date' | translate }}
{{ 'Transaction Date' | translateKey: 'catalogs' }}
</div>
<div class="flex-50">
@@ -56,7 +56,7 @@
</div>
<div class="flex-50 mat-body-strong">
{{ 'Currency' | translate }}
{{ 'Currency' | translateKey: 'catalogs' }}
</div>
<div class="flex-50">
@@ -64,7 +64,7 @@
</div>
<div class="flex-50 mat-body-strong">
{{ 'Amount' | translate }}
{{ 'Amount' | translateKey: 'catalogs' }}
</div>
<div class="flex-50">
@@ -108,7 +108,7 @@
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
<td mat-cell *matCellDef="let transactionRel">
{{ transactionRel.amount | currency }} {{ transactionData.currency.code }}
{{ transactionRel.amount | currency: transactionData.currency.code : 'symbol-narrow' : '1.2-2' }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+2
View File
@@ -16,6 +16,8 @@ export class StatusLookupPipe implements PipeTransform {
'loanStatusType.active': 'status-active',
'loanStatusType.overpaid': 'status-overpaid',
'loanStatusType.chargeoff': 'status-chargeoff',
'loanStatusType.activeOverdue': 'status-active-overdue',
'loanSubStatusType.contractTermination': 'subStatus-contract-termination',
'savingsAccountStatusType.submitted.and.pending.approval': 'status-pending',
'savingsAccountStatusType.approved': 'status-approved',
'savingsAccountStatusType.active': 'status-active',
@@ -199,8 +199,8 @@
<mat-label>{{ 'labels.inputs.Minimum Charge Cap' | translate }}</mat-label>
<input matInput autofocus formControlName="minCap" mifosxValidateOnFocus />
<mat-error *ngIf="chargeForm.controls.minCap.hasError('maxValue')">
{{ 'errors.validation.msg.loanproduct.maximumGap.not.greater.than.specified.number' | translate }} ({{
chargeForm.controls.minCap.value
{{ 'errors.validation.msg.loanproduct.minimumGap.not.greater.than.specified.number' | translate }} ({{
chargeForm.controls.maxCap.value
}})
</mat-error>
</mat-form-field>
@@ -114,8 +114,8 @@
<mat-label>{{ 'labels.inputs.Minimum Charge Cap' | translate }}</mat-label>
<input matInput autofocus formControlName="minCap" mifosxValidateOnFocus />
<mat-error *ngIf="chargeForm.controls.minCap.hasError('maxValue')">
{{ 'errors.validation.msg.loanproduct.maximumGap.not.greater.than.specified.number' | translate }} ({{
chargeForm.controls.minCap.value
{{ 'errors.validation.msg.loanproduct.minimumGap.not.greater.than.specified.number' | translate }} ({{
chargeForm.controls.maxCap.value
}})
</mat-error>
</mat-form-field>