Skip to content

Commit b5d209a

Browse files
WEB-813: Working Capital loan delinquency actions
1 parent 8ecadcc commit b5d209a

12 files changed

Lines changed: 422 additions & 135 deletions

src/app/core/utils/dates.ts

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

55+
public isBefore(date1: Date, date2: Date): boolean {
56+
return (
57+
Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate()) <
58+
Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate())
59+
);
60+
}
61+
62+
public isAfter(date1: Date, date2: Date): boolean {
63+
return (
64+
Date.UTC(date1.getFullYear(), date1.getMonth(), date1.getDate()) >
65+
Date.UTC(date2.getFullYear(), date2.getMonth(), date2.getDate())
66+
);
67+
}
68+
5569
public parseDatetime(value: any): Date {
5670
return moment(value).toDate();
5771
}

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,69 @@
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.Minimum Payment Type' | translate }}</mat-label>
20+
<mat-select formControlName="minimumPaymentType" required>
21+
@for (minimumPaymentType of minimumPaymentTypeOptions; track minimumPaymentType) {
22+
<mat-option [value]="minimumPaymentType.id">
23+
{{ minimumPaymentType.value | translateKey: 'catalogs' }}
24+
</mat-option>
25+
}
26+
</mat-select>
27+
</mat-form-field>
28+
29+
<mat-form-field class="flex-98">
30+
<mat-label>{{ 'labels.inputs.Period Payment Frequency' | translate }}</mat-label>
31+
<input
32+
type="number"
33+
matInput
34+
required
35+
formControlName="frequency"
36+
matTooltip="{{ 'tooltips.Fields are input to calculating the repayment schedule' | translate }}"
37+
/>
38+
@if (delinquencyActionForm.controls.frequency.hasError('required')) {
39+
<mat-error>
40+
{{ 'labels.inputs.Period Payment Frequency' | translate }} {{ 'labels.commons.is' | translate }}
41+
<strong>{{ 'labels.commons.required' | translate }}</strong>
42+
</mat-error>
43+
}
44+
</mat-form-field>
45+
46+
<mat-form-field class="flex-98">
47+
<mat-label>{{ 'labels.inputs.Period Payment Frequency Type' | translate }}</mat-label>
48+
<mat-select formControlName="frequencyType" required>
49+
@for (frequencyType of frequencyTypeOptions; track frequencyType) {
50+
<mat-option [value]="frequencyType.id">
51+
{{ frequencyType.value | translateKey: 'catalogs' }}
52+
</mat-option>
53+
}
54+
</mat-select>
55+
</mat-form-field>
56+
</div>
57+
</div>
58+
59+
<mat-dialog-actions class="layout-row layout-xs-column layout-align-center gap-2percent">
60+
<button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
61+
<button
62+
mat-raised-button
63+
color="primary"
64+
[mat-dialog-close]="{ data: delinquencyActionForm }"
65+
[disabled]="!delinquencyActionForm.valid || delinquencyActionForm.pristine"
66+
>
67+
{{ 'labels.buttons.Reschedule' | translate }}
68+
</button>
69+
</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,74 @@
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+
minimumPaymentTypeOptions: StringEnumOptionData[] = [];
47+
48+
constructor() {
49+
this.createDelinquencyActionForm();
50+
this.frequencyTypeOptions = this.data.frequencyTypeOptions;
51+
this.minimumPaymentTypeOptions = this.data.minimumPaymentTypeOptions;
52+
}
53+
54+
createDelinquencyActionForm() {
55+
this.delinquencyActionForm = this.formBuilder.group({
56+
minimumPayment: [
57+
'',
58+
Validators.required
59+
],
60+
minimumPaymentType: [
61+
'',
62+
Validators.required
63+
],
64+
frequency: [
65+
'',
66+
Validators.required
67+
],
68+
frequencyType: [
69+
'',
70+
Validators.required
71+
]
72+
});
73+
}
74+
}

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)