Skip to content

WEB-172 Merging all changes from main branch into dev branch #2450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/app/directives/directives.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CommonModule } from '@angular/common';
/** Custom Directives */
import { HasPermissionDirective } from './has-permission/has-permission.directive';
import { FormatAmountDirective } from './format-amount.directive';
import { ValidateOnFocusDirective } from './validate-on-focus.directive';

/**
* Directives Module
Expand All @@ -17,11 +18,13 @@ import { FormatAmountDirective } from './format-amount.directive';
],
declarations: [
HasPermissionDirective,
FormatAmountDirective
FormatAmountDirective,
ValidateOnFocusDirective
],
exports: [
HasPermissionDirective,
FormatAmountDirective
FormatAmountDirective,
ValidateOnFocusDirective
]
})
export class DirectivesModule {}
18 changes: 18 additions & 0 deletions src/app/directives/validate-on-focus.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Directive, ElementRef, HostListener } from '@angular/core';
import { NgControl } from '@angular/forms';

@Directive({
selector: '[mifosxValidateOnFocus]'
})
export class ValidateOnFocusDirective {
constructor(
private control: NgControl,
private el: ElementRef
) {}

@HostListener('focus')
onFocus() {
this.control.control?.markAsTouched();
this.control.control?.updateValueAndValidity();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class LoansAccountScheduleStepComponent {
dateFormat
);
delete payload['enableInstallmentLevelDelinquency'];
delete payload['externalId'];

this.loansService.calculateLoanSchedule(payload).subscribe((response: any) => {
this.repaymentScheduleDetails = response;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class MakeRepaymentComponent implements OnInit {
* and initialize with the required values
*/
ngOnInit() {
this.command = this.dataObject.type.code.split('.')[1];
this.maxDate = this.settingsService.businessDate;
this.createRepaymentLoanForm();
this.setRepaymentLoanDetails();
Expand All @@ -76,14 +77,27 @@ export class MakeRepaymentComponent implements OnInit {
this.settingsService.businessDate,
Validators.required
],
transactionAmount: [
'',
Validators.required
],
externalId: '',
paymentTypeId: '',
note: ''
});

if (this.isCapitalizedIncome()) {
this.repaymentLoanForm.addControl(
'transactionAmount',
new UntypedFormControl('', [
Validators.required,
Validators.min(0.001),
Validators.max(this.dataObject.amount)])
);
} else {
this.repaymentLoanForm.addControl(
'transactionAmount',
new UntypedFormControl('', [
Validators.required,
Validators.min(0.001)])
);
}
}

setRepaymentLoanDetails() {
Expand Down Expand Up @@ -114,7 +128,14 @@ export class MakeRepaymentComponent implements OnInit {
}

showDetails(): boolean {
return !['capitalizedIncome'].includes(this.command);
return !this.isCapitalizedIncome();
}

isCapitalizedIncome(): boolean {
return [
'capitalizedIncome',
'capitalizedIncomeAdjustment'
].includes(this.command);
}

/** Submits the repayment form */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ export class LoanTrancheDetailsComponent implements OnInit {
item.expectedDisbursementDate,
this.settingsService.dateFormat
),
principal: item.principal
principal: item.principal,
id: item.id
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@
<span>{{ 'labels.menus.Undo Re-Amortize' | translate }}</span>
</button>
</span>
<span *ngIf="isCapitalizedIncome(transaction.type) && !transaction.manuallyReversed">
<button
mat-menu-item
*mifosxHasPermission="'CAPITALIZED_INCOME_LOAN'"
(click)="capitalizedIncomeAdjustmentTransaction(transaction, $event)"
>
<mat-icon><fa-icon icon="coins" size="sm"></fa-icon></mat-icon>
<span>{{ 'labels.menus.Capitalized Income Adjustment' | translate }}</span>
</button>
</span>
<span *ngIf="viewJournalEntry(transaction.type)">
<button
mat-menu-item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/conf
import { TranslateService } from '@ngx-translate/core';
import { LoanTransaction } from 'app/products/loan-products/models/loan-account.model';
import { LoanTransactionType } from 'app/loans/models/loan-transaction-type.model';
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
import { AlertService } from 'app/core/alert/alert.service';
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';

@Component({
selector: 'mifosx-transactions-tab',
Expand Down Expand Up @@ -80,7 +85,8 @@ export class TransactionsTabComponent implements OnInit {
private dialog: MatDialog,
private loansService: LoansService,
private translateService: TranslateService,
private settingsService: SettingsService
private settingsService: SettingsService,
private alertService: AlertService
) {
this.route.parent.parent.data.subscribe((data: { loanDetailsData: any }) => {
this.transactionsData = data.loanDetailsData.transactions;
Expand Down Expand Up @@ -332,6 +338,10 @@ export class TransactionsTabComponent implements OnInit {
return transactionType.reAmortize || transactionType.code === 'loanTransactionType.reAmortize';
}

private isCapitalizedIncome(transactionType: LoanTransactionType): boolean {
return transactionType.capitalizedIncome || transactionType.code === 'loanTransactionType.capitalizedIncome';
}

private isReAgoeOrReAmortize(transactionType: LoanTransactionType): boolean {
return this.isReAmortize(transactionType) || this.isReAge(transactionType);
}
Expand All @@ -354,4 +364,72 @@ export class TransactionsTabComponent implements OnInit {
}
return true;
}

capitalizedIncomeAdjustmentTransaction(transaction: LoanTransaction) {
const accountId = `${this.loanId}`;
this.loansService
.getLoanTransactionActionTemplate(accountId, 'capitalizedIncomeAdjustment', `${transaction.id}`)
.subscribe((response: any) => {
const transactionDate = response.date || transaction.date;
const transactionAmount = response.amount || transaction.amount;
const formfields: FormfieldBase[] = [
new DatepickerBase({
controlName: 'transactionDate',
label: 'Date',
value: this.dateUtils.parseDate(transactionDate),
type: 'datetime-local',
required: true,
minDate: transaction.date,
order: 1
}),
new InputBase({
controlName: 'amount',
label: 'Amount',
value: transactionAmount,
type: 'number',
required: true,
max: transactionAmount,
min: 0.001,
order: 2
})

];
const data = {
title: `Adjustment ${transaction.type.value} Transaction`,
layout: { addButtonText: 'Adjustment' },
formfields: formfields
};
const chargebackDialogRef = this.dialog.open(FormDialogComponent, { data });
chargebackDialogRef.afterClosed().subscribe((response: { data: any }) => {
if (response.data) {
const dateFormat = this.settingsService.dateFormat;

if (response.data.value.amount <= transactionAmount) {
const locale = this.settingsService.language.code;
const payload = {
transactionDate: this.dateUtils.formatDate(response.data.value.transactionDate, dateFormat),
transactionAmount: response.data.value.amount,
locale,
dateFormat
};
this.loansService
.executeLoansAccountTransactionsCommand(
accountId,
'capitalizedIncomeAdjustment',
payload,
transaction.id
)
.subscribe(() => {
this.router.navigate(['../'], { relativeTo: this.route });
});
} else {
this.alertService.alert({
type: 'BusinessRule',
message: 'Chargeback amount must be lower or equal to: ' + transaction.amount
});
}
}
});
});
}
}
5 changes: 5 additions & 0 deletions src/app/loans/loans.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export class LoansService {
return this.http.get(`/loans/${loanId}/transactions/template`, { params: httpParams });
}

getLoanTransactionActionTemplate(loanId: string, command: string, transactionId: string): Observable<any> {
const httpParams = new HttpParams().set('command', command).set('transactionId', transactionId);
return this.http.get(`/loans/${loanId}/transactions/template`, { params: httpParams });
}

getLoanPrepayLoanActionTemplate(loanId: string, transactionDate: string): Observable<any> {
if (!transactionDate) {
transactionDate = this.dateUtils.formatDate(this.settingsService.businessDate, this.settingsService.dateFormat);
Expand Down
2 changes: 2 additions & 0 deletions src/app/loans/models/loan-transaction-type.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ export interface LoanTransactionType {
downPayment: boolean;
reAge: boolean;
reAmortize: boolean;
capitalizedIncome: boolean;
capitalizedIncomeAdjustment: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,15 +200,22 @@
-->
<mat-form-field fxFlex="48%" *ngIf="showMinMaxCap()">
<mat-label>{{ 'labels.inputs.Minimum Charge Cap' | translate }}</mat-label>
<input matInput formControlName="minCap" />
<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.maxCap.value
}})
</mat-error>
</mat-form-field>

<mat-form-field fxFlex="48%" *ngIf="showMinMaxCap()">
<mat-label>{{ 'labels.inputs.Maximum Charge Cap' | translate }}</mat-label>
<input matInput [min]="chargeForm.value.minCap" formControlName="maxCap" />
<mat-error *ngIf="chargeForm.errors?.minGreaterThanMax">
{{ 'labels.inputs.Maximum Charge Cap' | translate }} ({{ repeatEveryLabel }})
<strong>{{ 'labels.commons.must higher than Minimum Charge Cap' | translate }}</strong>
<input matInput autofocus formControlName="maxCap" mifosxValidateOnFocus />

<mat-error *ngIf="chargeForm.controls.maxCap.hasError('minValue')">
{{ 'errors.validation.msg.loanproduct.maximumGap.not.greater.than.specified.number' | translate }} ({{
chargeForm.controls.minCap.value
}})
</mat-error>
</mat-form-field>

Expand Down
76 changes: 41 additions & 35 deletions src/app/products/charges/create-charge/create-charge.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { Router, ActivatedRoute } from '@angular/router';
import { ProductsService } from '../../products.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { minLessThanOrEqualMaxValidator } from 'app/shared/validators/min-max-values.validator';
import { minNumberValueValidator } from 'app/shared/validators/min-number-value.validator';
import { maxNumberValueValidator } from 'app/shared/validators/max-number-value.validator';

/**
* Create charge component.
Expand Down Expand Up @@ -80,40 +81,45 @@ export class CreateChargeComponent implements OnInit {
* Creates the charge form.
*/
createChargeForm() {
this.chargeForm = this.formBuilder.group(
{
chargeAppliesTo: [
'',
Validators.required
],
name: [
'',
Validators.required
],
currencyCode: [
'',
Validators.required
],
chargeTimeType: [
'',
Validators.required
],
chargeCalculationType: [
'',
Validators.required
],
amount: [
'',
Validators.required
],
active: [false],
penalty: [false],
taxGroupId: [''],
minCap: [''],
maxCap: ['']
},
{ validators: minLessThanOrEqualMaxValidator('minCap', 'maxCap') }
);
this.chargeForm = this.formBuilder.group({
chargeAppliesTo: [
'',
Validators.required
],
name: [
'',
Validators.required
],
currencyCode: [
'',
Validators.required
],
chargeTimeType: [
'',
Validators.required
],
chargeCalculationType: [
'',
Validators.required
],
amount: [
'',
[
Validators.required,
Validators.pattern('^\\s*(?=.*[1-9])\\d*(?:\\.\\d+)?\\s*$')]
],
active: [false],
penalty: [false],
taxGroupId: [null],
minCap: [
null,
[maxNumberValueValidator('maxCap')]
],
maxCap: [
null,
[minNumberValueValidator('minCap')]
]
});
}

/**
Expand Down
Loading