Skip to content

Commit fd079b0

Browse files
WEB-813: Working Capital loan delinquency actions
1 parent bc47971 commit fd079b0

12 files changed

Lines changed: 335 additions & 122 deletions

src/app/core/utils/dates.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ export class Dates {
5252
}
5353
}
5454

55+
public isBefore(date1: Date, date2: Date): boolean {
56+
return date1 < date2;
57+
}
58+
59+
public isAfter(date1: Date, date2: Date): boolean {
60+
return date1 > date2;
61+
}
62+
5563
public parseDatetime(value: any): Date {
5664
return moment(value).toDate();
5765
}

src/app/loans/common-resolvers/loan-delinquency-actions.resolver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export class LoanDelinquencyActionsResolver extends LoanBaseResolver {
2929
resolve(route: ActivatedRouteSnapshot): Observable<any> {
3030
this.initialize(route);
3131
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
32-
return this.loansService.getDelinquencyActions(this.loanAccountPath, loanId);
32+
if (!isNaN(+loanId)) {
33+
return this.loansService.getDelinquencyActions(this.loanAccountPath, loanId);
34+
}
3335
}
3436
}

src/app/loans/common-resolvers/loan-delinquency-data.resolver.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export class LoanDelinquencyDataResolver extends LoanBaseResolver {
2727
constructor() {
2828
super();
2929
}
30-
3130
/**
3231
* Returns the Loans with Association data.
3332
* @returns {Observable<any>}
@@ -36,9 +35,9 @@ export class LoanDelinquencyDataResolver extends LoanBaseResolver {
3635
this.initialize(route);
3736
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
3837
if (!isNaN(+loanId)) {
39-
if (this.isLoanProduct) {
40-
return this.loansService.getDelinquencyData(loanId);
41-
}
38+
return this.isLoanProduct
39+
? this.loansService.getDelinquencyData(loanId)
40+
: this.loansService.getWorkingCapitalLoanDetails(loanId);
4241
}
4342
}
4443
}

src/app/loans/common-resolvers/loan-delinquency-tags.resolver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ export class LoanDelinquencyTagsResolver extends LoanBaseResolver {
3636
this.initialize(route);
3737
const loanId = route.paramMap.get('loanId') || route.parent.paramMap.get('loanId');
3838
if (!isNaN(+loanId)) {
39-
return this.loansService.getDelinquencyTags(this.loanAccountPath, loanId);
39+
if (this.isLoanProduct) {
40+
return this.loansService.getDelinquencyTags(loanId);
41+
}
4042
}
4143
}
4244
}

src/app/loans/custom-dialog/loan-delinquency-action-dialog/loan-delinquency-action-dialog.component.html

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,48 @@
88

99
<h2 mat-dialog-title>{{ 'labels.heading.Loan Delinquency Actions' | translate }}</h2>
1010

11-
<div mat-dialog-content [formGroup]="delinquencyActionForm" class="layout-column">
12-
<mat-form-field class="flex-48" (click)="validFromDatePicker.open()">
13-
<mat-label>{{ 'labels.inputs.Start Date' | translate }}</mat-label>
14-
<input
15-
matInput
16-
[min]="minDate"
17-
[max]="maxDate"
18-
[matDatepicker]="validFromDatePicker"
19-
required
20-
formControlName="startDate"
21-
/>
22-
<mat-datepicker-toggle matSuffix [for]="validFromDatePicker"></mat-datepicker-toggle>
23-
<mat-datepicker #validFromDatePicker></mat-datepicker>
24-
@if (delinquencyActionForm.controls.startDate.hasError('required')) {
25-
<mat-error>
26-
{{ 'labels.inputs.Start Date' | translate }} {{ 'labels.commons.is' | translate }}
27-
<strong>{{ 'labels.commons.required' | translate }}</strong>
28-
</mat-error>
29-
}
30-
</mat-form-field>
11+
<div mat-dialog-content [formGroup]="delinquencyActionForm">
12+
<div class="layout-column">
13+
<mat-form-field class="flex-48" (click)="validFromDatePicker.open()">
14+
<mat-label>{{ 'labels.inputs.Start Date' | translate }}</mat-label>
15+
<input
16+
matInput
17+
[min]="minDate"
18+
[max]="maxDate"
19+
[matDatepicker]="validFromDatePicker"
20+
required
21+
formControlName="startDate"
22+
/>
23+
<mat-datepicker-toggle matSuffix [for]="validFromDatePicker"></mat-datepicker-toggle>
24+
<mat-datepicker #validFromDatePicker></mat-datepicker>
25+
@if (delinquencyActionForm.controls.startDate.hasError('required')) {
26+
<mat-error>
27+
{{ 'labels.inputs.Start Date' | translate }} {{ 'labels.commons.is' | translate }}
28+
<strong>{{ 'labels.commons.required' | translate }}</strong>
29+
</mat-error>
30+
}
31+
</mat-form-field>
3132

32-
<mat-form-field class="flex-48" (click)="validTillDatePicker.open()">
33-
<mat-label>{{ 'labels.inputs.End Date' | translate }}</mat-label>
34-
<input
35-
matInput
36-
[min]="delinquencyActionForm.value.startDate"
37-
[max]="maxDate"
38-
[matDatepicker]="validTillDatePicker"
39-
required
40-
formControlName="endDate"
41-
/>
42-
<mat-datepicker-toggle matSuffix [for]="validTillDatePicker"></mat-datepicker-toggle>
43-
<mat-datepicker #validTillDatePicker></mat-datepicker>
44-
@if (delinquencyActionForm.controls.endDate.hasError('required')) {
45-
<mat-error>
46-
{{ 'labels.inputs.End Date' | translate }} {{ 'labels.commons.is' | translate }}
47-
<strong>{{ 'labels.commons.required' | translate }}</strong>
48-
</mat-error>
49-
}
50-
</mat-form-field>
33+
<mat-form-field class="flex-48" (click)="validTillDatePicker.open()">
34+
<mat-label>{{ 'labels.inputs.End Date' | translate }}</mat-label>
35+
<input
36+
matInput
37+
[min]="delinquencyActionForm.value.startDate"
38+
[max]="maxDate"
39+
[matDatepicker]="validTillDatePicker"
40+
required
41+
formControlName="endDate"
42+
/>
43+
<mat-datepicker-toggle matSuffix [for]="validTillDatePicker"></mat-datepicker-toggle>
44+
<mat-datepicker #validTillDatePicker></mat-datepicker>
45+
@if (delinquencyActionForm.controls.endDate.hasError('required')) {
46+
<mat-error>
47+
{{ 'labels.inputs.End Date' | translate }} {{ 'labels.commons.is' | translate }}
48+
<strong>{{ 'labels.commons.required' | translate }}</strong>
49+
</mat-error>
50+
}
51+
</mat-form-field>
52+
</div>
5153
</div>
5254

5355
<mat-dialog-actions class="layout-row layout-xs-column layout-align-center gap-2percent">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<!--
2+
Copyright since 2025 Mifos Initiative
3+
4+
This Source Code Form is subject to the terms of the Mozilla Public
5+
License, v. 2.0. If a copy of the MPL was not distributed with this
6+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
-->
8+
9+
<h2 mat-dialog-title>{{ 'labels.heading.Loan Delinquency Actions' | translate }}</h2>
10+
11+
<div mat-dialog-content [formGroup]="delinquencyActionForm">
12+
<div class="layout-column">
13+
<mat-form-field class="flex-98">
14+
<mat-label>{{ 'labels.inputs.Minimum Payment' | translate }}</mat-label>
15+
<input type="number" matInput formControlName="minimumPayment" />
16+
</mat-form-field>
17+
18+
<mat-form-field class="flex-98">
19+
<mat-label>{{ 'labels.inputs.Period Payment Frequency' | translate }}</mat-label>
20+
<input
21+
type="number"
22+
matInput
23+
required
24+
formControlName="frequency"
25+
matTooltip="{{ 'tooltips.Fields are input to calculating the repayment schedule' | translate }}"
26+
/>
27+
@if (delinquencyActionForm.controls.frequency.hasError('required')) {
28+
<mat-error>
29+
{{ 'labels.inputs.Period Payment Frequency' | translate }} {{ 'labels.commons.is' | translate }}
30+
<strong>{{ 'labels.commons.required' | translate }}</strong>
31+
</mat-error>
32+
}
33+
</mat-form-field>
34+
35+
<mat-form-field class="flex-98">
36+
<mat-label>{{ 'labels.inputs.Period Payment Frequency Type' | translate }}</mat-label>
37+
<mat-select formControlName="frequencyType" required>
38+
@for (frequencyType of frequencyTypeOptions; track frequencyType) {
39+
<mat-option [value]="frequencyType.id">
40+
{{ frequencyType.value | translateKey: 'catalogs' }}
41+
</mat-option>
42+
}
43+
</mat-select>
44+
</mat-form-field>
45+
</div>
46+
</div>
47+
48+
<mat-dialog-actions class="layout-row layout-xs-column layout-align-center gap-2percent">
49+
<button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
50+
<button
51+
mat-raised-button
52+
color="primary"
53+
[mat-dialog-close]="{ data: delinquencyActionForm }"
54+
[disabled]="!delinquencyActionForm.valid || delinquencyActionForm.pristine"
55+
>
56+
{{ 'labels.buttons.Reschedule' | translate }}
57+
</button>
58+
</mat-dialog-actions>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
import { Component, inject } from '@angular/core';
10+
import { UntypedFormBuilder, UntypedFormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
11+
import {
12+
MAT_DIALOG_DATA,
13+
MatDialogRef,
14+
MatDialogTitle,
15+
MatDialogContent,
16+
MatDialogActions,
17+
MatDialogClose
18+
} from '@angular/material/dialog';
19+
import { CdkScrollable } from '@angular/cdk/scrolling';
20+
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
21+
import { MatTooltip } from '@angular/material/tooltip';
22+
import { StringEnumOptionData } from 'app/shared/models/option-data.model';
23+
24+
@Component({
25+
selector: 'mifosx-loan-delinquency-action-reschedule-dialog',
26+
templateUrl: './loan-delinquency-action-reschedule-dialog.component.html',
27+
styleUrl: './loan-delinquency-action-reschedule-dialog.component.scss',
28+
imports: [
29+
...STANDALONE_SHARED_IMPORTS,
30+
MatDialogTitle,
31+
CdkScrollable,
32+
MatDialogContent,
33+
MatDialogActions,
34+
MatDialogClose,
35+
MatTooltip
36+
]
37+
})
38+
export class LoanDelinquencyActionRescheduleDialogComponent {
39+
dialogRef = inject<MatDialogRef<LoanDelinquencyActionRescheduleDialogComponent>>(MatDialogRef);
40+
data = inject(MAT_DIALOG_DATA);
41+
private formBuilder = inject(UntypedFormBuilder);
42+
43+
delinquencyActionForm: UntypedFormGroup;
44+
45+
frequencyTypeOptions: StringEnumOptionData[] = [];
46+
47+
constructor() {
48+
this.createDelinquencyActionForm();
49+
this.frequencyTypeOptions = this.data.frequencyTypeOptions;
50+
}
51+
52+
createDelinquencyActionForm() {
53+
this.delinquencyActionForm = this.formBuilder.group({
54+
minimumPayment: [
55+
'',
56+
Validators.required
57+
],
58+
frequency: [
59+
'',
60+
Validators.required
61+
],
62+
frequencyType: [
63+
'',
64+
Validators.required
65+
]
66+
});
67+
}
68+
}

src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ <h4 class="mat-h4 flex-98">
5959
/>
6060
@if (loansAccountTermsForm.controls.repaymentEvery.hasError('required')) {
6161
<mat-error>
62-
{{ 'labels.inputs.Repaid every' | translate }} {{ 'labels.commons.is' | translate }}
62+
{{ 'labels.inputs.Period Payment Frequency' | translate }} {{ 'labels.commons.is' | translate }}
6363
<strong>{{ 'labels.commons.required' | translate }}</strong>
6464
</mat-error>
6565
}

0 commit comments

Comments
 (0)