fix: bug preventing individual loan application, improved GLIM (#2329)
This fixes a recently introduced bug preventing individual loan applications. It also simplifies dramatically the GLIM process, improving GLIM user experience and functionality. Partially addresses: WEB-9
This commit is contained in:
@@ -23,38 +23,57 @@
|
||||
<mat-step [stepControl]="loansAccountDetailsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-glim-details-step
|
||||
<mifosx-loans-account-details-step
|
||||
[loansAccountTemplate]="loansAccountTemplate"
|
||||
(loansAccountProductTemplate)="setTemplate($event)"
|
||||
[gsimData]="gsimData"
|
||||
#loansAccountDetailsForm
|
||||
>
|
||||
</mifosx-glim-details-step>
|
||||
</mifosx-loans-account-details-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="loansAccountTermsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-glim-terms-step
|
||||
<mifosx-loans-account-terms-step
|
||||
#loanAccountTerms
|
||||
[loansAccountProductTemplate]="loansAccountProductTemplate"
|
||||
[loansAccountTemplate]="loansAccountTemplate"
|
||||
[collateralOptions]="collateralOptions"
|
||||
[loansAccountFormValid]="loansAccountFormValid"
|
||||
[loanPrincipal]="loanPrincipal"
|
||||
>
|
||||
</mifosx-glim-terms-step>
|
||||
</mifosx-loans-account-terms-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||
|
||||
<mifosx-glim-charges-step
|
||||
<mifosx-loans-account-charges-step
|
||||
#loanAccountCharges
|
||||
[loansAccountProductTemplate]="loansAccountProductTemplate"
|
||||
[loansAccountTemplate]="loansAccountTemplate"
|
||||
[loansAccountFormValid]="loansAccountFormValid"
|
||||
[activeClientMembers]="activeClientMembers"
|
||||
>
|
||||
</mifosx-glim-charges-step>
|
||||
</mifosx-loans-account-charges-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step state="repayment" *ngIf="loansAccountFormValid" completed>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.REPAYMENT SCHEDULE' | translate }}</ng-template>
|
||||
|
||||
<mifosx-loans-account-schedule-step
|
||||
#loanAccountSchedule
|
||||
[loansAccountTemplate]="loansAccountTemplate"
|
||||
[loansAccountProductTemplate]="loansAccountProductTemplate"
|
||||
[loansAccount]="loansAccount"
|
||||
[currencyCode]="currencyCode"
|
||||
>
|
||||
</mifosx-loans-account-schedule-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step *ngFor="let datatable of datatables">
|
||||
<ng-template matStepLabel>{{ datatable.registeredTableName }}</ng-template>
|
||||
|
||||
<mifosx-loans-account-datatable-step [datatableData]="datatable" #dtloan></mifosx-loans-account-datatable-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step state="preview" *ngIf="loansAccountFormValid" completed>
|
||||
@@ -64,6 +83,7 @@
|
||||
[loansAccountTemplate]="loansAccountTemplate"
|
||||
[loansAccountProductTemplate]="loansAccountProductTemplate"
|
||||
[loansAccount]="loansAccount"
|
||||
[activeClientMembers]="activeClientMembers"
|
||||
(submitEvent)="submit()"
|
||||
>
|
||||
</mifosx-loans-account-preview-step>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
/** Angular Imports */
|
||||
import { Component, ViewChild, /* Felix: added: */ QueryList, ViewChildren } from '@angular/core';
|
||||
import { Component, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { LoansService } from 'app/loans/loans.service';
|
||||
import { LoansService } from '../../loans.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
|
||||
/** Step Components */
|
||||
import { GlimChargesStepComponent } from './glim-account-stepper/glim-charges-step/glim-charges-step.component';
|
||||
import { GlimDetailsStepComponent } from './glim-account-stepper/glim-details-step/glim-details-step.component';
|
||||
import { GlimTermsStepComponent } from './glim-account-stepper/glim-terms-step/glim-terms-step.component';
|
||||
import { LoansAccountDetailsStepComponent } from '../../loans-account-stepper/loans-account-details-step/loans-account-details-step.component';
|
||||
import { LoansAccountTermsStepComponent } from '../../loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component';
|
||||
import { LoansAccountChargesStepComponent } from '../../loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component';
|
||||
import { LoansAccountDatatableStepComponent } from '../../loans-account-stepper/loans-account-datatable-step/loans-account-datatable-step.component';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-create-glim-account',
|
||||
@@ -17,34 +20,33 @@ import { GlimTermsStepComponent } from './glim-account-stepper/glim-terms-step/g
|
||||
styleUrls: ['./create-glim-account.component.scss']
|
||||
})
|
||||
export class CreateGlimAccountComponent {
|
||||
/** Imports all the step components */
|
||||
@ViewChild(GlimDetailsStepComponent, { static: true }) loansAccountDetailsStep: GlimDetailsStepComponent;
|
||||
@ViewChild(GlimTermsStepComponent, { static: true }) loansAccountTermsStep: GlimTermsStepComponent;
|
||||
@ViewChild(GlimChargesStepComponent, { static: true }) loansAccountChargesStep: GlimChargesStepComponent;
|
||||
/** Imports all the step component */
|
||||
@ViewChild(LoansAccountDetailsStepComponent, { static: true })
|
||||
loansAccountDetailsStep: LoansAccountDetailsStepComponent;
|
||||
@ViewChild(LoansAccountTermsStepComponent, { static: true }) loansAccountTermsStep: LoansAccountTermsStepComponent;
|
||||
@ViewChild(LoansAccountChargesStepComponent, { static: true })
|
||||
loansAccountChargesStep: LoansAccountChargesStepComponent;
|
||||
/** Get handle on dtloan tags in the template */
|
||||
@ViewChildren('dtloan') loanDatatables: QueryList<LoansAccountDatatableStepComponent>;
|
||||
|
||||
/** Loans Account Template */
|
||||
loansAccountTemplate: any;
|
||||
/** Loans Account Product Template */
|
||||
loansAccountProductTemplate: any | null = null;
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
/** Selected Members */
|
||||
selectedMembers: any;
|
||||
/** Collateral Options */
|
||||
collateralOptions: any;
|
||||
/** Multi Disburse Loan */
|
||||
multiDisburseLoan: any;
|
||||
/** Principal Amount */
|
||||
principal: any;
|
||||
datatables: any = [];
|
||||
/** Currency Code */
|
||||
currencyCode: string;
|
||||
|
||||
// accountLinkingOptions: any;
|
||||
|
||||
// totalLoan: any;
|
||||
|
||||
// /** Table Data Source */
|
||||
// dataSource: any;
|
||||
// /** Selected Members */
|
||||
selectedMembers: any;
|
||||
// /** Selected Members */
|
||||
activeClientMembers: any;
|
||||
gsimData: any;
|
||||
|
||||
/**
|
||||
* Sets loans account create form.
|
||||
* @param {route} ActivatedRoute Activated Route.
|
||||
@@ -57,29 +59,53 @@ export class CreateGlimAccountComponent {
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private loansService: LoansService,
|
||||
private settingsService: SettingsService
|
||||
private settingsService: SettingsService,
|
||||
private clientService: ClientsService,
|
||||
private dateUtils: Dates
|
||||
) {
|
||||
this.route.data.subscribe((data: { loansAccountTemplate: any; gsimData: any; groupsData: any }) => {
|
||||
this.route.data.subscribe((data: { loansAccountTemplate: any; groupsData: any }) => {
|
||||
this.loansAccountTemplate = data.loansAccountTemplate;
|
||||
this.activeClientMembers = data.groupsData.activeClientMembers;
|
||||
this.gsimData = data.gsimData;
|
||||
this.dataSource = data.groupsData.activeClientMembers;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets glim account product template and collateral template
|
||||
* Sets loans account product template and collateral template
|
||||
* @param {any} $event API response
|
||||
*/
|
||||
setTemplate($event: any) {
|
||||
this.loansAccountProductTemplate = $event;
|
||||
this.currencyCode = this.loansAccountProductTemplate.currency.code;
|
||||
const clientId = this.loansAccountTemplate.clientId;
|
||||
if (!!clientId) {
|
||||
this.clientService.getCollateralTemplate(clientId).subscribe((response: any) => {
|
||||
this.collateralOptions = response;
|
||||
});
|
||||
} else {
|
||||
// Fineract API doesn't have "Group Collateral Management" endpoint; from the obsolete
|
||||
// community app it appears getCollateralTemplate(clientId) is called as well, but it's not clear how
|
||||
// the clientId is selected from the clientIds that belong to the group.
|
||||
console.error('No collateral data requested from Fineract, collateral might misbehave');
|
||||
}
|
||||
const entityId = this.loansAccountTemplate.clientId
|
||||
? this.loansAccountTemplate.clientId
|
||||
: this.loansAccountTemplate.group.id;
|
||||
const isGroup = this.loansAccountTemplate.clientId ? false : true;
|
||||
const productId = this.loansAccountProductTemplate.loanProductId;
|
||||
this.loansService.getLoansAccountTemplateResource(entityId, true, productId).subscribe((response: any) => {
|
||||
this.loansService.getLoansAccountTemplateResource(entityId, isGroup, productId).subscribe((response: any) => {
|
||||
this.multiDisburseLoan = response.multiDisburseLoan;
|
||||
});
|
||||
this.setDatatables();
|
||||
}
|
||||
|
||||
setDatatables(): void {
|
||||
this.datatables = [];
|
||||
|
||||
if (this.loansAccountProductTemplate.datatables) {
|
||||
this.loansAccountProductTemplate.datatables.forEach((datatable: any) => {
|
||||
this.datatables.push(datatable);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Get Loans Account Details Form Data */
|
||||
@@ -92,6 +118,13 @@ export class CreateGlimAccountComponent {
|
||||
return this.loansAccountTermsStep.loansAccountTermsForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves savings account terms form.
|
||||
*/
|
||||
get activeClientMembers() {
|
||||
return this.dataSource;
|
||||
}
|
||||
|
||||
/** Checks whether all the forms in different steps are valid or not */
|
||||
get loansAccountFormValid() {
|
||||
return (
|
||||
@@ -101,93 +134,105 @@ export class CreateGlimAccountComponent {
|
||||
|
||||
/** Gets principal Amount */
|
||||
get loanPrincipal() {
|
||||
return this.loansAccountChargesStep.selectedClientMembers.selectedMembers.reduce(
|
||||
(acc: number, member: any) => acc + member.principal,
|
||||
0
|
||||
);
|
||||
return this.loansAccountTermsStep.loansAccountTermsForm.value.principal;
|
||||
}
|
||||
|
||||
/** Retrieves Data of all forms except Currency to submit the data */
|
||||
get loansAccount() {
|
||||
this.selectedMembers = this.loansAccountChargesStep.selectedClientMembers;
|
||||
return {
|
||||
...this.loansAccountDetailsStep.loansAccountDetails,
|
||||
...this.loansAccountTermsStep.loansAccountTerms,
|
||||
...this.loansAccountChargesStep.loansAccountCharges,
|
||||
...this.loansAccountTermsStep.loanCollateral,
|
||||
...this.loansAccountTermsStep.disbursementData
|
||||
};
|
||||
}
|
||||
|
||||
// setData(client: any): any {
|
||||
// const locale = this.settingsService.language.code;
|
||||
// const dateFormat = this.settingsService.dateFormat;
|
||||
// // const monthDayFormat = 'dd MMMM';
|
||||
// const data = {
|
||||
// ...this.loansAccount,
|
||||
// charges: this.loansAccount.charges.map((charge: any) => ({
|
||||
// chargeId: charge.id,
|
||||
// amount: charge.amount
|
||||
// })),
|
||||
// clientId: client.id,
|
||||
// totalLoan: this.totalLoan,
|
||||
// loanType: 'glim',
|
||||
// amortizationType: 1,
|
||||
// isParentAccount: true,
|
||||
// principal: client.principal,
|
||||
// syncDisbursementWithMeeting: false,
|
||||
// expectedDisbursementDate: this.dateUtils.formatDate(this.loansAccount.expectedDisbursementDate, dateFormat),
|
||||
// submittedOnDate: this.dateUtils.formatDate(this.loansAccount.submittedOnDate, dateFormat),
|
||||
// dateFormat,
|
||||
// // monthDayFormat,
|
||||
// locale
|
||||
// };
|
||||
// data.groupId = this.loansAccountTemplate.group.id;
|
||||
setData(client: any, totalLoan: number): any {
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
// const monthDayFormat = 'dd MMMM';
|
||||
const data = {
|
||||
...this.loansAccount,
|
||||
charges: this.loansAccount.charges.map((charge: any) => ({
|
||||
chargeId: charge.id,
|
||||
amount: charge.amount
|
||||
})),
|
||||
clientId: client.id,
|
||||
totalLoan: totalLoan,
|
||||
loanType: 'glim',
|
||||
amortizationType: 1,
|
||||
isParentAccount: true,
|
||||
principal: client.principal,
|
||||
syncDisbursementWithMeeting: false,
|
||||
expectedDisbursementDate: this.dateUtils.formatDate(this.loansAccount.expectedDisbursementDate, dateFormat),
|
||||
submittedOnDate: this.dateUtils.formatDate(this.loansAccount.submittedOnDate, dateFormat),
|
||||
dateFormat,
|
||||
// monthDayFormat,
|
||||
locale
|
||||
};
|
||||
data.groupId = this.loansAccountTemplate.group.id;
|
||||
|
||||
// return JSON.stringify(data);
|
||||
// }
|
||||
delete data.principalAmount;
|
||||
// TODO: 2025-03-17: Apparently (?!) unsupported for GLIM
|
||||
delete data.allowPartialPeriodInterestCalculation;
|
||||
delete data.multiDisburseLoan;
|
||||
delete data.isFloatingInterestRate;
|
||||
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
|
||||
/** Request Body Data */
|
||||
buildRequestData(): any[] {
|
||||
const requestData = [];
|
||||
const memberSelected = this.selectedMembers.selectedMembers;
|
||||
const totalLoan = this.totalLoanAmount();
|
||||
for (let index = 0; index < memberSelected.length; index++) {
|
||||
requestData.push({
|
||||
requestId: index.toString(),
|
||||
method: 'POST',
|
||||
relativeUrl: 'loans',
|
||||
body: this.setData(memberSelected[index], totalLoan)
|
||||
});
|
||||
}
|
||||
return requestData;
|
||||
}
|
||||
|
||||
totalLoanAmount(): number {
|
||||
let total = 0;
|
||||
const memberSelected = this.selectedMembers.selectedMembers;
|
||||
for (let index = 0; index < memberSelected.length; index++) {
|
||||
total += memberSelected[index].principal;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GLIM account.
|
||||
*/
|
||||
submit() {
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
|
||||
const totalLoan = this.loanPrincipal;
|
||||
const payloadData: any[] = [];
|
||||
this.loansAccountChargesStep.selectedClientMembers.selectedMembers.forEach((member: any, index: number) => {
|
||||
this.loansAccountTemplate.clientId = member.id;
|
||||
this.loansAccountTemplate.loanPurposeId = member.loanPurposeId;
|
||||
this.loansAccountTemplate.principal = member.principal;
|
||||
this.loansAccountTemplate.totalLoan = totalLoan;
|
||||
|
||||
const payload = this.loansService.buildLoanRequestPayload(
|
||||
this.loansAccount,
|
||||
this.loansAccountTemplate,
|
||||
this.loansAccountProductTemplate.calendarOptions,
|
||||
locale,
|
||||
dateFormat
|
||||
);
|
||||
|
||||
payloadData.push(this.getWrapper(payload, index));
|
||||
});
|
||||
|
||||
this.loansService.createGlimAccount(payloadData).subscribe((response: any) => {
|
||||
this.router.navigate(
|
||||
[
|
||||
'../',
|
||||
JSON.parse(response[0].body).glimId
|
||||
],
|
||||
{ relativeTo: this.route }
|
||||
);
|
||||
const data = this.buildRequestData();
|
||||
this.loansService.createGlimAccount(data).subscribe((response: any) => {
|
||||
const body = JSON.parse(response[0].body);
|
||||
if (body.glimId) {
|
||||
this.router.navigate(
|
||||
[
|
||||
'../',
|
||||
body.glimId
|
||||
],
|
||||
{ relativeTo: this.route }
|
||||
);
|
||||
} else {
|
||||
this.notify(body, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getWrapper(payload: any, requestId: number): any {
|
||||
return {
|
||||
body: JSON.stringify(payload),
|
||||
method: 'POST',
|
||||
relativeUrl: 'loans',
|
||||
requestId: requestId
|
||||
};
|
||||
notify(body: any, data: any) {
|
||||
let message = body.defaultUserMessage + ' ';
|
||||
while (body.errors?.length > 0) message += body.errors.pop().developerMessage + ' ';
|
||||
message += 'Data: ' + JSON.stringify(data);
|
||||
console.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
||||
<mat-select #charge>
|
||||
<mat-option *ngFor="let charge of chargeData" [value]="charge">
|
||||
{{ charge.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div fxFlex="48%" fxFlexAlign="center">
|
||||
<button type="button" mat-raised-button color="primary" (click)="addCharge(charge)" [disabled]="!charge.value">
|
||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Add' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table
|
||||
fxFlex="98%"
|
||||
class="mat-elevation-z1"
|
||||
mat-table
|
||||
[dataSource]="chargesDataSource"
|
||||
[hidden]="chargesDataSource.length === 0"
|
||||
>
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="chargeCalculationType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeCalculationType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.amount }}
|
||||
<button mat-icon-button color="primary" (click)="editChargeAmount(charge)">
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="chargeTimeType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeTimeType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
<span
|
||||
*ngIf="charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee'"
|
||||
>
|
||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
<span *ngIf="charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee'">
|
||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
!(
|
||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date' ||
|
||||
charge.chargeTimeType.value === 'Weekly Fee'
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ 'labels.inputs.N/A' | translate }}
|
||||
</span>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
*ngIf="
|
||||
charge.chargeTimeType.value === 'Weekly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date'
|
||||
"
|
||||
(click)="editChargeDate(charge)"
|
||||
>
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">{{ 'labels.heading.Overdue Charges' | translate }}</h4>
|
||||
|
||||
<table mat-table class="mat-elevation-z1" [dataSource]="overDueChargesDataSource">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.name }},{{ charge.currency.displaySymbol }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeCalculationType.value }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="collectedon">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeTimeType.value }}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="overdueChargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: overdueChargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
|
||||
<div fxFlex="98%" *ngIf="activeClientMembers">
|
||||
<table fxFlex="98%" mat-table [dataSource]="dataSource">
|
||||
<!-- Check Column -->
|
||||
<ng-container matColumnDef="check">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox (change)="toggleSelects()" [(ngModel)]="selectAllItems"></mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-checkbox (change)="toggleSelect()" [(ngModel)]="element.selected"></mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Position Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Weight Column -->
|
||||
<ng-container matColumnDef="purpose">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Loan Purpose' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Select Purpose' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="element.loanPurposeId">
|
||||
<mat-option *ngFor="let purpose of loanPurposeOptions" [value]="purpose.id">
|
||||
{{ purpose.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Symbol Column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Original Loan' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-form-field>
|
||||
<input type="number" matInput [(ngModel)]="element.principal" />
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumn"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumn"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext [disabled]="!loansAccountFormValid">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
<button mat-raised-button *ngIf="loanId" [routerLink]="['../', 'general']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
.margin-t {
|
||||
margin-top: 2em;
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlimChargesStepComponent } from './glim-charges-step.component';
|
||||
|
||||
describe('GlimChargesStepComponent', () => {
|
||||
let component: GlimChargesStepComponent;
|
||||
let fixture: ComponentFixture<GlimChargesStepComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [GlimChargesStepComponent]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GlimChargesStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
-283
@@ -1,283 +0,0 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
/** Dialog Components */
|
||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||
|
||||
/** Custom Services */
|
||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/**
|
||||
* GLIM Account Charges Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-glim-charges-step',
|
||||
templateUrl: './glim-charges-step.component.html',
|
||||
styleUrls: ['./glim-charges-step.component.scss']
|
||||
})
|
||||
export class GlimChargesStepComponent implements OnInit, OnChanges {
|
||||
/** Savings Account Product Template */
|
||||
@Input() loansAccountProductTemplate: any;
|
||||
/** Savings Account Template */
|
||||
@Input() loansAccountTemplate: any;
|
||||
// @Input() loansAccountFormValid: LoansAccountFormValid
|
||||
@Input() loansAccountFormValid: boolean;
|
||||
// active Client Members in case of GLIM Account
|
||||
@Input() activeClientMembers?: any;
|
||||
|
||||
/** Charge Data */
|
||||
chargeData: any = [];
|
||||
/** Charges Data Source */
|
||||
chargesDataSource: {}[] = [];
|
||||
/** Overdue Charges Data Source */
|
||||
overDueChargesDataSource: {}[] = [];
|
||||
/** Display columns for charges table */
|
||||
chargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'chargeCalculationType',
|
||||
'amount',
|
||||
'chargeTimeType',
|
||||
'date',
|
||||
'action'
|
||||
];
|
||||
/** Columns to be displayed in overdue charges table. */
|
||||
overdueChargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'type',
|
||||
'amount',
|
||||
'collectedon'
|
||||
];
|
||||
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
/** Check for select all the Clients List */
|
||||
selectAllItems = false;
|
||||
/** Loan Purpose Options */
|
||||
loanPurposeOptions: string[] = [];
|
||||
/** Table Displayed Columns */
|
||||
displayedColumn: string[] = [
|
||||
'check',
|
||||
'id',
|
||||
'name',
|
||||
'purpose',
|
||||
'amount'
|
||||
];
|
||||
/** Component is pristine if there has been no changes by user interaction */
|
||||
pristine = true;
|
||||
loanId: any = null;
|
||||
|
||||
/**
|
||||
* GLIM Account Charges Form Step
|
||||
* @param {dialog} MatDialog Mat Dialog
|
||||
* @param {Dates} dateUtils Date Utils
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {SettingsService} settingsService Settings Service
|
||||
*/
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
private dateUtils: Dates,
|
||||
private route: ActivatedRoute,
|
||||
private settingsService: SettingsService
|
||||
) {
|
||||
this.loanId = this.route.snapshot.params['loanId'];
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// TODO: This might still need adjustments due to GLIM
|
||||
this.dataSource = new MatTableDataSource<any>(this.activeClientMembers);
|
||||
|
||||
// Loan Account code:
|
||||
if (this.loansAccountTemplate.charges) {
|
||||
this.chargesDataSource =
|
||||
this.loansAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.loansAccountProductTemplate) {
|
||||
this.loanPurposeOptions = this.loansAccountProductTemplate.loanPurposeOptions;
|
||||
|
||||
this.chargeData = this.loansAccountProductTemplate.chargeOptions;
|
||||
if (this.loansAccountProductTemplate.overdueCharges) {
|
||||
this.overDueChargesDataSource = this.loansAccountProductTemplate.overdueCharges;
|
||||
}
|
||||
if (
|
||||
this.loansAccountProductTemplate.charges &&
|
||||
this.loansAccountProductTemplate.charges.length > 0 &&
|
||||
this.chargesDataSource.length === 0
|
||||
) {
|
||||
this.chargesDataSource =
|
||||
this.loansAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the charge to charges table
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
addCharge(charge: any) {
|
||||
this.chargesDataSource = this.chargesDataSource.concat([charge.value]);
|
||||
charge.value = '';
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Amount
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeAmount(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new InputBase({
|
||||
controlName: 'amount',
|
||||
label: 'Amount',
|
||||
value: charge.amount,
|
||||
type: 'number',
|
||||
required: false
|
||||
})
|
||||
|
||||
];
|
||||
const data = {
|
||||
title: 'Edit Charge Amount',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
const newCharge = { ...charge, amount: response.data.value.amount };
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Date
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeDate(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new DatepickerBase({
|
||||
controlName: 'date',
|
||||
label: 'Date',
|
||||
value: charge.dueDate || charge.feeOnMonthDay || '',
|
||||
type: 'datetime-local',
|
||||
required: false
|
||||
})
|
||||
|
||||
];
|
||||
const data = {
|
||||
title: 'Edit Charge Date',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
let newCharge: any;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const date = this.dateUtils.formatDate(response.data.value.date, dateFormat);
|
||||
switch (charge.chargeTimeType.value) {
|
||||
case 'Specified due date':
|
||||
case 'Weekly Fee':
|
||||
newCharge = { ...charge, dueDate: date };
|
||||
break;
|
||||
case 'Annual Fee':
|
||||
newCharge = { ...charge, feeOnMonthDay: date };
|
||||
break;
|
||||
}
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Fee Interval
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeFeeInterval(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new InputBase({
|
||||
controlName: 'feeInterval',
|
||||
label: 'Fee Interval',
|
||||
value: charge.feeInterval,
|
||||
type: 'text',
|
||||
required: false
|
||||
})
|
||||
|
||||
];
|
||||
const data = {
|
||||
title: 'Edit Charge Fee Interval',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
const newCharge = { ...charge, feeInterval: response.data.value.feeInterval };
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the charge from charges table
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
deleteCharge(charge: any) {
|
||||
const deleteChargeDialogRef = this.dialog.open(DeleteDialogComponent, {
|
||||
data: { deleteContext: `charge ${charge.name}` }
|
||||
});
|
||||
deleteChargeDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.delete) {
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
this.pristine = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns loan account charges
|
||||
*/
|
||||
get loansAccountCharges() {
|
||||
return { charges: this.chargesDataSource };
|
||||
}
|
||||
|
||||
get selectedClientMembers() {
|
||||
return { selectedMembers: this.activeClientMembers.filter((item: any) => item.selected) };
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
const sum = this.activeClientMembers
|
||||
?.filter((item: any) => item.selected)
|
||||
.reduce((acc: number, member: any) => acc + member.principal, 0);
|
||||
return sum > 0;
|
||||
}
|
||||
|
||||
toggleSelects() {
|
||||
for (const member of this.activeClientMembers) {
|
||||
member.selected = this.selectAllItems;
|
||||
}
|
||||
}
|
||||
|
||||
toggleSelect() {
|
||||
const len = this.activeClientMembers.length;
|
||||
this.selectAllItems =
|
||||
len === 0 ? false : this.activeClientMembers.filter((item: any) => item.selected).length === len;
|
||||
}
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
<form [formGroup]="loansAccountDetailsForm">
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<mat-form-field fxFlex="72%">
|
||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||
<mat-select
|
||||
required
|
||||
matTooltip="{{ 'tooltips.Name of the loan product' | translate }}"
|
||||
formControlName="productId"
|
||||
>
|
||||
<mat-option>
|
||||
<ngx-mat-select-search [formControl]="filterFormCtrl"></ngx-mat-select-search>
|
||||
</mat-option>
|
||||
<mat-option *ngFor="let product of productData | async" [value]="product.id">
|
||||
{{ product.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column" *ngIf="loanProductSelected">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Loan officer' | translate }}</mat-label>
|
||||
<mat-select
|
||||
formControlName="loanOfficerId"
|
||||
matTooltip="{{ 'tooltips.Financial institution representative' | translate }}"
|
||||
>
|
||||
<mat-option *ngFor="let loanOfficer of loanOfficerOptions" [value]="loanOfficer.id">
|
||||
{{ loanOfficer.displayName }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Fund' | translate }}</mat-label>
|
||||
<mat-select formControlName="fundId">
|
||||
<mat-option *ngFor="let fund of fundOptions" [value]="fund.id">
|
||||
{{ fund.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" (click)="submitPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Submitted on' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="submitPicker"
|
||||
matTooltip="{{ 'tooltips.Date the loan account application' | translate }}"
|
||||
required
|
||||
formControlName="submittedOnDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="submitPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #submitPicker></mat-datepicker>
|
||||
<mat-error *ngIf="loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')">
|
||||
{{ 'labels.inputs.Submitted on' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" (click)="disbursementPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Disbursement on' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="loansAccountDetailsForm.value.submittedOnDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="disbursementPicker"
|
||||
matTooltip="{{ 'tooltips.Date that the loan account disbursed' | translate }}"
|
||||
required
|
||||
formControlName="expectedDisbursementDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="disbursementPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #disbursementPicker></mat-datepicker>
|
||||
<mat-error *ngIf="loansAccountDetailsForm.controls.submittedOnDate?.hasError('required')">
|
||||
{{ 'labels.inputs.Disbursement on' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Link savings' | translate }}</mat-label>
|
||||
<mat-select formControlName="linkAccountId">
|
||||
<mat-option *ngFor="let savingaccount of gsimData" [value]="savingaccount.id">
|
||||
{{ savingaccount.accountNumber }} {{ savingaccount.productName }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious disabled>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
<button mat-raised-button *ngIf="loanId" [routerLink]="['../', 'general']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlimDetailsStepComponent } from './glim-details-step.component';
|
||||
|
||||
describe('GlimDetailsStepComponent', () => {
|
||||
let component: GlimDetailsStepComponent;
|
||||
let fixture: ComponentFixture<GlimDetailsStepComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [GlimDetailsStepComponent]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GlimDetailsStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
|
||||
/** Custom Services */
|
||||
import { LoansService } from '../../../../loans.service';
|
||||
import { Commons } from 'app/core/utils/commons';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { ReplaySubject, Subject } from 'rxjs';
|
||||
|
||||
// Felix: TODO's:
|
||||
// - gsimData was already declared, when I found this file. Apparently,
|
||||
// glim accounts can only be linked to gsim accounts. Needs to be tested
|
||||
// as soon as gsim accounts are implemented.
|
||||
/**
|
||||
* GLIM Account Details Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-glim-details-step',
|
||||
templateUrl: './glim-details-step.component.html',
|
||||
styleUrls: ['./glim-details-step.component.scss']
|
||||
})
|
||||
export class GlimDetailsStepComponent implements OnInit, OnDestroy {
|
||||
/** Loans Account Template */
|
||||
@Input() loansAccountTemplate: any;
|
||||
@Input() gsimData: any;
|
||||
/** Minimum date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
/** Maximum date allowed. */
|
||||
maxDate = new Date(2100, 0, 1);
|
||||
/** Product Data */
|
||||
productList: any;
|
||||
/** Loan Officer Data */
|
||||
loanOfficerOptions: any;
|
||||
/** Loan Purpose Options */
|
||||
loanPurposeOptions: any;
|
||||
/** Fund Options */
|
||||
fundOptions: any;
|
||||
/** Account Linking Options */
|
||||
accountLinkingOptions: any;
|
||||
/** For edit loan accounts form */
|
||||
isFieldOfficerPatched = false;
|
||||
/** Loans Account Details Form */
|
||||
loansAccountDetailsForm: UntypedFormGroup;
|
||||
|
||||
loanId: any = null;
|
||||
|
||||
loanProductSelected = false;
|
||||
/** Currency data. */
|
||||
protected productData: ReplaySubject<string[]> = new ReplaySubject<string[]>(1);
|
||||
/** control for the filter select */
|
||||
protected filterFormCtrl: UntypedFormControl = new UntypedFormControl('');
|
||||
/** Subject that emits when the component has been destroyed. */
|
||||
protected _onDestroy = new Subject<void>();
|
||||
|
||||
/** Loans Account Template with product data */
|
||||
@Output() loansAccountProductTemplate = new EventEmitter();
|
||||
/**
|
||||
* Sets loans account details form.
|
||||
* @param {FormBuilder} formBuilder Form Builder.
|
||||
* @param {LoansService} loansService Loans Service.
|
||||
* @param {SettingsService} settingsService SettingsService
|
||||
*/
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private loansService: LoansService,
|
||||
private route: ActivatedRoute,
|
||||
private settingsService: SettingsService,
|
||||
private commons: Commons
|
||||
) {
|
||||
this.loanId = this.route.snapshot.params['loanId'];
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.createLoansAccountDetailsForm();
|
||||
this.maxDate = this.settingsService.maxFutureDate; // Felix: originally: businessDate;
|
||||
this.buildDependencies();
|
||||
if (this.loansAccountTemplate) {
|
||||
this.productList = this.loansAccountTemplate.productOptions.sort(this.commons.dynamicSort('name'));
|
||||
if (this.loansAccountTemplate.loanProductId) {
|
||||
console.log('this.loansAccountTemplate.loanOfficerId: ', this.loansAccountTemplate.loanOfficerId);
|
||||
this.loansAccountDetailsForm.patchValue({
|
||||
productId: this.loansAccountTemplate.loanProductId,
|
||||
submittedOnDate:
|
||||
this.loansAccountTemplate.timeline.submittedOnDate &&
|
||||
new Date(this.loansAccountTemplate.timeline.submittedOnDate),
|
||||
loanOfficerId: this.loansAccountTemplate.loanOfficerId,
|
||||
loanPurposeId: this.loansAccountTemplate.loanPurposeId,
|
||||
fundId: this.loansAccountTemplate.fundId,
|
||||
expectedDisbursementDate:
|
||||
this.loansAccountTemplate.timeline.expectedDisbursementDate &&
|
||||
new Date(this.loansAccountTemplate.timeline.expectedDisbursementDate),
|
||||
externalId: this.loansAccountTemplate.externalId
|
||||
});
|
||||
}
|
||||
}
|
||||
this.filterFormCtrl.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => {
|
||||
this.searchItem();
|
||||
});
|
||||
this.productData.next(this.productList.slice());
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._onDestroy.next();
|
||||
this._onDestroy.complete();
|
||||
}
|
||||
|
||||
searchItem(): void {
|
||||
if (this.productList) {
|
||||
const search: string = this.filterFormCtrl.value.toLowerCase();
|
||||
|
||||
if (!search) {
|
||||
this.productData.next(this.productList.slice());
|
||||
} else {
|
||||
this.productData.next(
|
||||
this.productList.filter((option: any) => {
|
||||
return option['name'].toLowerCase().indexOf(search) >= 0;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates glim account details form.
|
||||
*/
|
||||
createLoansAccountDetailsForm() {
|
||||
this.loansAccountDetailsForm = this.formBuilder.group({
|
||||
productId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
loanOfficerId: [''],
|
||||
// Felix: found: loanPurposeId: [''], - not sure, where we add this list...
|
||||
fundId: [''],
|
||||
submittedOnDate: [
|
||||
new Date(),
|
||||
Validators.required
|
||||
],
|
||||
expectedDisbursementDate: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
externalId: [''],
|
||||
linkAccountId: [''],
|
||||
createStandingInstructionAtDisbursement: ['']
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches loans account product template on productId value changes
|
||||
*/
|
||||
buildDependencies() {
|
||||
const groupId = this.loansAccountTemplate.group.id;
|
||||
this.loansAccountDetailsForm.get('productId').valueChanges.subscribe((productId: string) => {
|
||||
this.loansService.getLoansAccountTemplateResource(groupId, true, productId).subscribe((response: any) => {
|
||||
this.loansAccountProductTemplate.emit(response);
|
||||
this.loanOfficerOptions = response.loanOfficerOptions;
|
||||
this.loanPurposeOptions = response.loanPurposeOptions;
|
||||
this.fundOptions = response.fundOptions;
|
||||
this.accountLinkingOptions = response.accountLinkingOptions;
|
||||
this.loanProductSelected = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns loans account details form value.
|
||||
*/
|
||||
get loansAccountDetails() {
|
||||
return this.loansAccountDetailsForm.getRawValue(); // Felix: was: value;
|
||||
}
|
||||
}
|
||||
-321
@@ -1,321 +0,0 @@
|
||||
<div fxLayout="row wrap" fxLayout.lt-md="column">
|
||||
<h3 class="mat-h3" fxFlexFill>{{ 'labels.heading.Details' | translate }}</h3>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Product' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.productId | find: loansAccountTemplate.productOptions : 'id' : 'name' }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.loanOfficerId">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Loan officer' | translate }}:</span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccount.loanOfficerId | find: loansAccountProductTemplate.loanOfficerOptions : 'id' : 'displayName'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.loanPurposeId">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Loan purpose' | translate }}:</span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccount.loanPurposeId | find: loansAccountProductTemplate.loanPurposeOptions : 'id' : 'name'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.fundId">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Fund' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.fundId | find: loansAccountProductTemplate.fundOptions : 'id' : 'name' }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Submitted on' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.submittedOnDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Disbursement on' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.expectedDisbursementDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.externalId">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.External id' | translate }}:</span>
|
||||
<span fxFlex="60%">
|
||||
<mifosx-external-identifier externalId="{{ loansAccount.externalId }}"></mifosx-external-identifier>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 class="mat-h3" fxFlexFill>{{ 'labels.heading.Terms' | translate }}</h3>
|
||||
|
||||
<mat-divider fxFlexFill></mat-divider>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Principal' | translate }}:</span>
|
||||
<span fxFlex="60%">
|
||||
{{
|
||||
loansAccount.principalAmount | currency: loansAccountProductTemplate.currency.code : 'symbol-narrow' : '1.2-2'
|
||||
}}
|
||||
<span class="m-l-5">{{ loansAccountProductTemplate.currency.code }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Loan Term' | translate }}:</span>
|
||||
<span fxFlex="60%"
|
||||
>{{ loansAccount.loanTermFrequency }}
|
||||
{{
|
||||
loansAccount.loanTermFrequencyType | find: loansAccountProductTemplate.termFrequencyTypeOptions : 'id' : 'name'
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Number of repayments' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.numberOfRepayments }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Repaid every' | translate }}:</span>
|
||||
<span fxFlex="60%"
|
||||
>{{ loansAccount.repaymentEvery }}
|
||||
{{
|
||||
loansAccount.repaymentFrequencyType | find: loansAccountProductTemplate.termFrequencyTypeOptions : 'id' : 'name'
|
||||
}}
|
||||
{{
|
||||
loansAccount.repaymentFrequencyNthDayType
|
||||
| find: loansAccountProductTemplate.repaymentFrequencyNthDayTypeOptions : 'id' : 'name'
|
||||
}}
|
||||
{{
|
||||
loansAccount.repaymentFrequencyDayOfWeekType
|
||||
| find: loansAccountProductTemplate.repaymentFrequencyDaysOfWeekTypeOptions : 'id' : 'name'
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="productEnableDownPayment">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Enable Down Payments' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.enableDownPayment | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.repaymentsStartingFromDate">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.First repayment on' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.repaymentsStartingFromDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.interestChargedFromDate">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Interest charged from' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.interestChargedFromDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.interestRatePerPeriod">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Nominal interest rate' | translate }}:</span>
|
||||
<span fxFlex="60%"
|
||||
>{{ loansAccount.interestRatePerPeriod }} {{
|
||||
loansAccountProductTemplate.interestRateFrequencyType.value
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.interestType">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Interest method' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.interestType }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.isEqualAmortization">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Is Equal Amortization' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.isEqualAmortization }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Amortization' | translate }}</span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccount.amortizationType | find: loansAccountProductTemplate.amortizationTypeOptions : 'id' : 'value'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Interest calculation period' | translate }}: </span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccount.interestCalculationPeriodType
|
||||
| find: loansAccountProductTemplate.interestCalculationPeriodTypeOptions : 'id' : 'value'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.allowPartialPeriodInterestCalculation">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Calculate interest for exact days in partial period' | translate }}:</span>
|
||||
<span fxFlex="60%">{{ loansAccount.allowPartialPeriodInterestCalculation }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.inArrearsTolerance">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Arrears tolerance' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.inArrearsTolerance }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.graceOnInterestCharged">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Interest free period' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.graceOnInterestCharged }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Repayment strategy' | translate }}: </span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccount.transactionProcessingStrategyCode
|
||||
| find: loansAccountProductTemplate.transactionProcessingStrategyOptions : 'code' : 'name'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%"> {{ 'labels.inputs.Installment Amount' | translate }} </span>
|
||||
<span fxFlex="60%"> {{ loansAccount.fixedEmiAmount | formatNumber }} </span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%"> {{ 'labels.inputs.Balloon Repayment Amount' | translate }} </span>
|
||||
<span fxFlex="60%"> {{ loansAccount.balloonRepaymentAmount | formatNumber }} </span>
|
||||
</div>
|
||||
|
||||
<mat-divider fxFlexFill></mat-divider>
|
||||
|
||||
<h3 class="mat-h3" fxFlexFill>{{ 'labels.heading.Moratorium' | translate }}</h3>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.graceOnPrincipalPayment">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.On principal payment' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.graceOnPrincipalPayment }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.graceOnInterestPayment">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.On interest payment' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.graceOnInterestPayment }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.graceOnArrearsAgeing">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.On Arrears Aging' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccount.graceOnArrearsAgeing }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%"> {{ 'labels.inputs.Enable installment level Delinquency' | translate }} </span>
|
||||
<span fxFlex="60%"> {{ loansAccount.enableInstallmentLevelDelinquency | yesNo }} </span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.isTopup">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Is Topup Loan' | translate }}? </span>
|
||||
<span fxFlex="60%">{{ loansAccount.isTopup }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Recalculate Interest' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccountProductTemplate?.isInterestRecalculationEnabled | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccountProductTemplate?.daysInMonthType">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Days in month' | translate }}: </span>
|
||||
<span fxFlex="60%">{{ loansAccountProductTemplate?.daysInMonthType.value }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccount.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||
<h3 class="mat-h3" fxFlexFill>{{ 'labels.heading.Charges' | translate }}</h3>
|
||||
|
||||
<mat-divider fxFlexFill></mat-divider>
|
||||
|
||||
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="loansAccount.charges">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="chargeCalculationType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeCalculationType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.amount }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="chargeTimeType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeTimeType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
<span
|
||||
*ngIf="charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee'"
|
||||
>
|
||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
<span *ngIf="charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee'">
|
||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
!(
|
||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date' ||
|
||||
charge.chargeTimeType.value === 'Weekly Fee'
|
||||
)
|
||||
"
|
||||
>
|
||||
{{ 'labels.inputs.N/A' | translate }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccountProductTemplate.overdueCharges.length" fxLayout="row wrap" fxLayout.lt-md="column">
|
||||
<h3 fxFlex="98%" class="mat-h3">{{ 'labels.heading.Overdue Charges' | translate }}</h3>
|
||||
|
||||
<mat-divider fxFlex="98%"></mat-divider>
|
||||
|
||||
<table mat-table class="mat-elevation-z1" [dataSource]="loansAccountProductTemplate.overdueCharges">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.name }},{{ charge.currency.displaySymbol }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeCalculationType.value }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.amount | formatNumber }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="collectedon">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">{{ charge.chargeTimeType.value }}</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="overdueChargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: overdueChargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button [routerLink]="['../..']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="submitEvent.emit()">
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlimPreviewStepComponent } from './glim-preview-step.component';
|
||||
|
||||
describe('GlimPreviewStepComponent', () => {
|
||||
let component: GlimPreviewStepComponent;
|
||||
let fixture: ComponentFixture<GlimPreviewStepComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [GlimPreviewStepComponent]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GlimPreviewStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
import { Component, EventEmitter, Input, Output, OnChanges, SimpleChanges } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Create GLIM Account Preview Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-glim-preview-step',
|
||||
templateUrl: './glim-preview-step.component.html',
|
||||
styleUrls: ['./glim-preview-step.component.scss']
|
||||
})
|
||||
export class GlimPreviewStepComponent implements OnChanges {
|
||||
/** Loans Account Template */
|
||||
@Input() loansAccountTemplate: any = [];
|
||||
/** Loans Account Product Template */
|
||||
@Input() loansAccountProductTemplate: any;
|
||||
/** Loans Account Data */
|
||||
@Input() loansAccount: any;
|
||||
/** Submit Loans Account */
|
||||
@Output() submitEvent = new EventEmitter();
|
||||
|
||||
/** Charges Displayed Columns */
|
||||
chargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'chargeCalculationType',
|
||||
'amount',
|
||||
'chargeTimeType',
|
||||
'date'
|
||||
];
|
||||
/** Overdue Charges Displayed Columns */
|
||||
overdueChargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'type',
|
||||
'amount',
|
||||
'collectedon'
|
||||
];
|
||||
|
||||
productEnableDownPayment = false;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.productEnableDownPayment = this.loansAccountProductTemplate.product.enableDownPayment;
|
||||
}
|
||||
}
|
||||
-614
@@ -1,614 +0,0 @@
|
||||
<form [formGroup]="loansAccountTermsForm">
|
||||
<div fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<!-- <mifosx-input-amount
|
||||
fxFlex="48%"
|
||||
*ngIf="currency"
|
||||
[currency]="currency"
|
||||
[isRequired]="true"
|
||||
[inputFormControl]="loansAccountTermsForm.controls.principalAmount"
|
||||
[inputLabel]="'Principal'"
|
||||
>
|
||||
</mifosx-input-amount> -->
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">
|
||||
{{ 'labels.heading.Term Options' | translate }}
|
||||
<i
|
||||
class="fas fa-question"
|
||||
matTooltip="The loan term parameter in loan accounts
|
||||
is the default-calculated field based on the number of repayments and repaid every value entered by the user"
|
||||
></i>
|
||||
</h4>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Loan Term' | translate }}</mat-label>
|
||||
<input type="number" matInput required disabled formControlName="loanTermFrequency" />
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.loanTermFrequency.hasError('required')">
|
||||
{{ 'labels.inputs.Loan Term' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<mat-select required formControlName="loanTermFrequencyType">
|
||||
<mat-option *ngFor="let type of termFrequencyTypeData" [value]="type.id">
|
||||
{{ type.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.loanTermFrequencyType.hasError('required')">
|
||||
{{ 'labels.inputs.Frequency' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="20%" *ngIf="hasFixedLength()">
|
||||
<mat-label>{{ 'labels.inputs.Fixed Length' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="fixedLength" />
|
||||
</mat-form-field>
|
||||
<span fxFlex="20%" *ngIf="hasFixedLength()" class="label-field">{{
|
||||
loansAccountTermsForm.value.loanTermFrequencyType | find: termFrequencyTypeData : 'id' : 'value'
|
||||
}}</span>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">Repayments</h4>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Number of repayments' | translate }}</mat-label>
|
||||
<input
|
||||
type="number"
|
||||
matInput
|
||||
formControlName="numberOfRepayments"
|
||||
matTooltip="Enter the total count of repayments that loan schedule has excluding the down-payment. Example:
|
||||
If the loan account has 4 Installments including down-payment then number of repayments is equal to 3"
|
||||
/>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.numberOfRepayments.hasError('required')">
|
||||
{{ 'labels.inputs.Number of repayments' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%" (click)="repaymentsPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.First repayment on' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
matTooltip="{{ 'tooltips.May be entered to override' | translate }}"
|
||||
[matDatepicker]="repaymentsPicker"
|
||||
formControlName="repaymentsStartingFromDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="repaymentsPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #repaymentsPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%" (click)="interestPicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Interest charged from' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="interestPicker"
|
||||
matTooltip="{{ 'tooltips.May be entered to override the date' | translate }}"
|
||||
formControlName="interestChargedFromDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="interestPicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #interestPicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">
|
||||
{{ 'labels.heading.Repaid Every' | translate }}
|
||||
<i
|
||||
class="fas fa-question"
|
||||
matTooltip="{{ 'tooltips.Fields are input to calculating the repayment schedule' | translate }}"
|
||||
></i>
|
||||
</h4>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Repaid every' | translate }}</mat-label>
|
||||
<input
|
||||
type="number"
|
||||
matInput
|
||||
required
|
||||
formControlName="repaymentEvery"
|
||||
matTooltip="{{ 'tooltips.Fields are input to calculating the repayment schedule' | translate }}"
|
||||
/>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.repaymentEvery.hasError('required')">
|
||||
{{ 'labels.inputs.Repaid every' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<mat-select formControlName="repaymentFrequencyType" disabled required>
|
||||
<mat-option *ngFor="let repaymentFrequencyType of termFrequencyTypeData" [value]="repaymentFrequencyType.id">
|
||||
{{ repaymentFrequencyType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="14%" *ngIf="loansAccountTermsForm.controls.repaymentFrequencyType.value === 2">
|
||||
<mat-label>{{ 'labels.inputs.Select On' | translate }}</mat-label>
|
||||
<mat-select formControlName="repaymentFrequencyNthDayType">
|
||||
<mat-option
|
||||
*ngFor="let repaymentFrequencyNthDayType of repaymentFrequencyNthDayTypeData"
|
||||
[value]="repaymentFrequencyNthDayType.id"
|
||||
>
|
||||
{{ repaymentFrequencyNthDayType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="14%" *ngIf="loansAccountTermsForm.controls.repaymentFrequencyType.value === 2">
|
||||
<mat-label>{{ 'labels.inputs.Select Day' | translate }}</mat-label>
|
||||
<mat-select formControlName="repaymentFrequencyDayOfWeekType">
|
||||
<mat-option
|
||||
*ngFor="let repaymentFrequencyDayOfWeekType of repaymentFrequencyDaysOfWeekTypeData"
|
||||
[value]="repaymentFrequencyDayOfWeekType.id"
|
||||
>
|
||||
{{ repaymentFrequencyDayOfWeekType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox
|
||||
fxFlex="73%"
|
||||
labelPosition="before"
|
||||
formControlName="enableDownPayment"
|
||||
matTooltip="Leave this checkbox checked if the loan has Down Payment, A Down Payment is
|
||||
a sum a buyer pays upfront when purchasing a good. It represents a percentage of the total purchase price, and the balance is usually financed"
|
||||
class="margin-b"
|
||||
*ngIf="productEnableDownPayment"
|
||||
>
|
||||
{{ 'labels.inputs.Enable Down Payment' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">{{ 'labels.inputs.Nominal interest rate' | translate }}</h4>
|
||||
|
||||
<ng-container *ngIf="!loansAccountTermsData?.isLoanProductLinkedToFloatingRate">
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Nominal interest rate' | translate }} %</mat-label>
|
||||
<input type="number" matInput formControlName="interestRatePerPeriod" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestRateFrequencyType">
|
||||
<mat-option
|
||||
*ngFor="let interestRateFrequencyType of interestRateFrequencyTypeData"
|
||||
[value]="interestRateFrequencyType.id"
|
||||
>
|
||||
{{ interestRateFrequencyType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Nominal interest rate frequency' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Interest method' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestType" matTooltip="{{ 'tooltips.The Interest method value' | translate }}">
|
||||
<mat-option *ngFor="let interestType of interestTypeData" [value]="interestType.id">
|
||||
{{ interestType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Amortization' | translate }}</mat-label>
|
||||
<mat-select
|
||||
required
|
||||
matTooltip="{{ 'tooltips.The Amortization value' | translate }}"
|
||||
formControlName="amortizationType"
|
||||
>
|
||||
<mat-option *ngFor="let amortizationType of amortizationTypeData" [value]="amortizationType.id">
|
||||
{{ amortizationType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.amortizationType.hasError('required')">
|
||||
{{ 'labels.inputs.Amortization Type' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%" *ngIf="isEqualPrincipalPayments()">
|
||||
<mat-label>{{ 'labels.inputs.Principal Percentage Per Installment' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="fixedPrincipalPercentagePerInstallment" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox
|
||||
fxFlex="23%"
|
||||
formControlName="isEqualAmortization"
|
||||
[checked]="loansAccountTermsData?.isEqualAmortization"
|
||||
>
|
||||
<p>{{ 'labels.inputs.Is Equal Amortization' | translate }}</p>
|
||||
</mat-checkbox>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="loansAccountTermsData?.isLoanProductLinkedToFloatingRate">
|
||||
<div fxFlex="48%" fxLayout="row wrap" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Interest Method' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestType">
|
||||
<mat-option *ngFor="let interestType of interestTypeData" [value]="interestType.id">
|
||||
{{ interestType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Amortization' | translate }}</mat-label>
|
||||
<mat-select
|
||||
required
|
||||
matTooltip="{{ 'tooltips.The Amortization value' | translate }}"
|
||||
formControlName="amortizationType"
|
||||
>
|
||||
<mat-option *ngFor="let amortizationType of amortizationTypeData" [value]="amortizationType.id">
|
||||
{{ amortizationType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.amortizationType.hasError('required')">
|
||||
{{ 'labels.inputs.Amortization Type' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox fxFlex="48%" formControlName="isFloatingInterestRate">
|
||||
<p>{{ 'labels.inputs.Is Floating Rate' | translate }}?</p>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">{{ 'labels.inputs.Loan Schedule' | translate }}</h4>
|
||||
|
||||
<div fxFlex="48%" *ngIf="loanScheduleType">
|
||||
<span fxFlex="40%"
|
||||
><p>{{ 'labels.inputs.Loan Schedule Type' | translate }}</p></span
|
||||
>
|
||||
<span fxFlex="60%"
|
||||
><p>{{ loanScheduleType.value | translateKey: 'catalogs' }}</p></span
|
||||
>
|
||||
</div>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Repayment Strategy' | translate }}</mat-label>
|
||||
<mat-select
|
||||
formControlName="transactionProcessingStrategyCode"
|
||||
matTooltip="{{ 'tooltips.The repayment strategy' | translate }}"
|
||||
[disabled]="repaymentStrategyDisabled"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let transactionProcessingStrategy of transactionProcessingStrategyOptions"
|
||||
[value]="transactionProcessingStrategy.code"
|
||||
>
|
||||
{{ transactionProcessingStrategy.name | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
<mat-error *ngIf="loansAccountTermsForm.controls.transactionProcessingStrategyCode.hasError('required')">
|
||||
{{ 'labels.inputs.Repayment Strategy' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%" *ngIf="loansAccountTermsData?.canDefineInstallmentAmount">
|
||||
<mat-label>{{ 'labels.inputs.Installment Amount' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="fixedEmiAmount" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Balloon Repayment Amount' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="balloonRepaymentAmount" />
|
||||
</mat-form-field>
|
||||
|
||||
<h4 fxFlex="98%" class="mat-h4">{{ 'labels.heading.Interest Calculations' | translate }}</h4>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Interest calculation period' | translate }}</mat-label>
|
||||
<mat-select
|
||||
formControlName="interestCalculationPeriodType"
|
||||
matTooltip="{{ 'tooltips.Daily - Will Calculate the interest' | translate }}"
|
||||
>
|
||||
<mat-option
|
||||
*ngFor="let interestCalculationPeriodType of interestCalculationPeriodTypeData"
|
||||
[value]="interestCalculationPeriodType.id"
|
||||
>
|
||||
{{ interestCalculationPeriodType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox
|
||||
fxFlex="48%"
|
||||
formControlName="allowPartialPeriodInterestCalculation"
|
||||
matTooltip="{{ 'tooltips.To be used with SAME AS REPAYMENT PERIOD' | translate }}"
|
||||
>
|
||||
<p>{{ 'labels.inputs.Calculate interest for exact days in partial period' | translate }}</p>
|
||||
</mat-checkbox>
|
||||
|
||||
<ng-container *ngIf="isProgressive">
|
||||
<mat-checkbox formControlName="interestRecognitionOnDisbursementDate" fxFlex="48%">
|
||||
<p>{{ 'labels.inputs.Is interest recognition on disbursement date?' | translate }}</p>
|
||||
</mat-checkbox>
|
||||
</ng-container>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Arrears tolerance' | translate }} </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
formControlName="inArrearsTolerance"
|
||||
matTooltip="{{ 'tooltips.With Arrears tolerance' | translate }}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Interest free period' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
formControlName="graceOnInterestCharged"
|
||||
matTooltip="{{ 'tooltips.If the Interest Free Period' | translate }}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- <h4 fxFlex="98%" class="mat-h4">
|
||||
{{ 'labels.heading.Moratorium' | translate }}
|
||||
<i class="fas fa-question" matTooltip="{{ 'tooltips.The moratorium information' | translate }}"></i>
|
||||
</h4>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Grace on principal payment' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="graceOnPrincipalPayment" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="23%">
|
||||
<mat-label>{{ 'labels.inputs.Grace on interest payment' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="graceOnInterestPayment" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.On arrears ageing' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="graceOnArrearsAgeing" />
|
||||
</mat-form-field> -->
|
||||
|
||||
<div fxFlexFill fxFlex="48%" *ngIf="isDelinquencyEnabled()">
|
||||
<p>
|
||||
<span fxFlex="53%"
|
||||
><b>{{ 'labels.inputs.Delinquency Bucket' | translate }}</b></span
|
||||
>
|
||||
<span fxFlex="48%">{{ loanProduct?.delinquencyBucket.name }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div fxFlex="48%" *ngIf="isDelinquencyEnabled()">
|
||||
<mat-checkbox formControlName="enableInstallmentLevelDelinquency">
|
||||
<p>{{ 'labels.inputs.Enable installment level Delinquency' | translate }}</p>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<ng-container fxFlex="48%" *ngIf="loansAccountTermsData?.isTopup">
|
||||
<mat-checkbox fxFlex="20%" formControlName="isTopup">
|
||||
<p>{{ 'labels.inputs.Is Topup Loan' | translate }}?</p>
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-form-field fxFlex="24%" *ngIf="loansAccountTermsForm.controls.isTopup.value">
|
||||
<mat-label>{{ 'labels.inputs.Loan closed with Topup' | translate }}</mat-label>
|
||||
<mat-select formControlName="loanIdToClose">
|
||||
<mat-option *ngFor="let clientActiveLoan of clientActiveLoanData" [value]="clientActiveLoan.id">
|
||||
{{ clientActiveLoan.accountNo }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<mat-divider fxFlex="98%" class="margin-t"></mat-divider>
|
||||
|
||||
<div fxFlexFill class="margin-t">
|
||||
<span fxFlex="40%"
|
||||
><b>{{ 'labels.inputs.Recalculate Interest' | translate }}</b></span
|
||||
>
|
||||
<span fxFlex="60%">{{ loansAccountTermsData?.isInterestRecalculationEnabled | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccountTermsData?.isInterestRecalculationEnabled">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Days in year' | translate }}</span>
|
||||
<span fxFlex="60%">{{ loansAccountTermsData.daysInYearType.value | translateKey: 'catalogs' }}</span>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="loansAccountTermsData?.isInterestRecalculationEnabled">
|
||||
<div fxFlexFill *ngIf="loansAccountTermsData?.isInterestRecalculationEnabled">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Advance payments adjustment type' | translate }}</span>
|
||||
<span fxFlex="60%">{{ loansAccountTermsData.interestRecalculationData.rescheduleStrategyType.value }}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill *ngIf="loansAccountTermsData?.isInterestRecalculationEnabled">
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Days in month' | translate }}</span>
|
||||
<span fxFlex="60%">{{ loansAccountTermsData.daysInMonthType.value | translateKey: 'catalogs' }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="loansAccountTermsData?.isInterestRecalculationEnabled">
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Interest recalculation compounding on' | translate }}</span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccountTermsData.interestRecalculationData.interestRecalculationCompoundingType.value
|
||||
| translateKey: 'catalogs'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Frequency Interval for recalculation' | translate }}</span>
|
||||
<span fxFlex="60%">
|
||||
<span>{{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyType.value }}</span>
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyType.id === 3 &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyWeekday
|
||||
"
|
||||
>
|
||||
on {{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyWeekday.value }}</span
|
||||
>
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyType.id === 4 &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyOnDay
|
||||
"
|
||||
>on day {{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyOnDay }}</span
|
||||
>
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyType.id === 4 &&
|
||||
!loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyOnDay &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyNthDay
|
||||
"
|
||||
>on
|
||||
{{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyNthDay.value }}
|
||||
{{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyWeekday.value }}</span
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div
|
||||
fxFlexFill
|
||||
*ngIf="
|
||||
loansAccountTermsData?.isInterestRecalculationEnabled &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyType.id !== 1
|
||||
"
|
||||
>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Frequency Interval for recalculation' | translate }}</span>
|
||||
<span fxFlex="60%">{{ loansAccountTermsData.interestRecalculationData.recalculationRestFrequencyInterval }}</span>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="multiDisburseLoan">
|
||||
<mat-divider fxFlex="98%"></mat-divider>
|
||||
<div fxFlexFill *ngIf="allowAddDisbursementDetails()">
|
||||
<h4 fxFlex="98%" class="mat-h4">{{ 'labels.heading.Loan Tranche Details' | translate }}</h4>
|
||||
<mat-form-field fxFlex="48%">
|
||||
<mat-label>{{ 'labels.inputs.Maximum allowed outstanding balance' | translate }}</mat-label>
|
||||
<input matInput required formControlName="maxOutstandingLoanBalance" />
|
||||
</mat-form-field>
|
||||
<span fxFlex>
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
color="primary"
|
||||
required
|
||||
(click)="addDisbursementDataEntry(disbursementData)"
|
||||
[disabled]="isMultiDisbursedCompleted"
|
||||
>
|
||||
<fa-icon icon="plus-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div fxFlexFill *ngIf="!allowAddDisbursementDetails()">
|
||||
<h4 fxFlex="98%" class="mat-h4">
|
||||
{{ 'labels.heading.Loan Tranche Details are not allowed for this Loan Product' | translate }}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="disbursementDataSource" [hidden]="disbursementDataSource.length === 0">
|
||||
<ng-container matColumnDef="expectedDisbursementDate">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Eexpected Disbursement Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
{{ row.expectedDisbursementDate | dateFormat }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="principal">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Principal' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
{{ row.principal }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let row; let rowIndex = index">
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
(click)="removeDisbursementDataEntry(rowIndex)"
|
||||
matTooltip="{{ 'tooltips.Delete' | translate }}"
|
||||
matTooltipPosition="left"
|
||||
>
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="disbursementDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: disbursementDisplayedColumns"></tr>
|
||||
</table>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="
|
||||
loansAccountTermsData?.isInterestRecalculationEnabled &&
|
||||
loansAccountTermsData.interestRecalculationData.interestRecalculationCompoundingType.id !== 0
|
||||
"
|
||||
>
|
||||
<div fxFlexFill>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Frequency for compounding' | translate }}</span>
|
||||
<span fxFlex="60%"
|
||||
>{{ loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyType.value }}
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyType.id === 3 &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyWeekday
|
||||
"
|
||||
>
|
||||
on {{ loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyWeekday.value }}
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyOnDay
|
||||
"
|
||||
>on day
|
||||
{{ loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyOnDay }}
|
||||
</span>
|
||||
<span
|
||||
*ngIf="
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyType.id === 4 &&
|
||||
!loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyOnDay &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyNthDay
|
||||
"
|
||||
>on
|
||||
{{ loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyNthDay.value }}
|
||||
{{ loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyWeekday.value }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div
|
||||
fxFlexFill
|
||||
*ngIf="
|
||||
loansAccountTermsData?.isInterestRecalculationEnabled &&
|
||||
loansAccountTermsData.interestRecalculationData.interestRecalculationCompoundingType.id !== 0 &&
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyType.id !== 1
|
||||
"
|
||||
>
|
||||
<span fxFlex="40%">{{ 'labels.inputs.Frequency Interval for compounding' | translate }}</span>
|
||||
<span fxFlex="60%">{{
|
||||
loansAccountTermsData.interestRecalculationData.recalculationCompoundingFrequencyInterval
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
<button mat-raised-button *ngIf="loanId" [routerLink]="['../', 'general']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
.margin-t {
|
||||
margin-top: 2em;
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlimTermsStepComponent } from './glim-terms-step.component';
|
||||
|
||||
describe('GlimTermsStepComponent', () => {
|
||||
let component: GlimTermsStepComponent;
|
||||
let fixture: ComponentFixture<GlimTermsStepComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [GlimTermsStepComponent]
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GlimTermsStepComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
-520
@@ -1,520 +0,0 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
// import { LoansAccountAddCollateralDialogComponent } from 'app/loans/custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component';
|
||||
import { LoanProducts } from 'app/products/loan-products/loan-products';
|
||||
import { LoanProduct } from 'app/products/loan-products/models/loan-product.model';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||
// import { Currency } from 'app/shared/models/general.model';
|
||||
import { CodeName, OptionData } from 'app/shared/models/option-data.model';
|
||||
|
||||
/**
|
||||
* Create GLIM Terms Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-glim-terms-step',
|
||||
templateUrl: './glim-terms-step.component.html',
|
||||
styleUrls: ['./glim-terms-step.component.scss']
|
||||
})
|
||||
export class GlimTermsStepComponent implements OnInit, OnChanges {
|
||||
/** Loans Product Options */
|
||||
@Input() loansProductOptions: any;
|
||||
/** Loans Account Product Template */
|
||||
@Input() loansAccountProductTemplate: any;
|
||||
/** Loans Account Template */
|
||||
@Input() loansAccountTemplate: any;
|
||||
loansAccountTermsData: any;
|
||||
|
||||
/** Is Multi Disburse Loan */
|
||||
multiDisburseLoan: any;
|
||||
// @Input() loansAccountFormValid: LoansAccountFormValid
|
||||
@Input() loansAccountFormValid: boolean;
|
||||
// @Input loanPrincipal: Loan Principle
|
||||
@Input() loanPrincipal: any;
|
||||
|
||||
/** Minimum date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
/** Maximum date allowed. */
|
||||
maxDate = new Date(2100, 0, 1);
|
||||
/** Loans Account Terms Form */
|
||||
loansAccountTermsForm: UntypedFormGroup;
|
||||
/** Term Frequency Type Data */
|
||||
termFrequencyTypeData: any;
|
||||
/** Repayment Frequency Nth Day Type Data */
|
||||
repaymentFrequencyNthDayTypeData: any;
|
||||
/** Repayment Frequency Days of Week Type Data */
|
||||
repaymentFrequencyDaysOfWeekTypeData: any;
|
||||
/** Interest Type Data */
|
||||
interestTypeData: any;
|
||||
/** Amortization Type Data */
|
||||
amortizationTypeData: any;
|
||||
/** Interest Calculation Period Type Data */
|
||||
interestCalculationPeriodTypeData: any;
|
||||
/** Client Active Loan Data */
|
||||
clientActiveLoanData: any;
|
||||
/** Multi Disbursement Data */
|
||||
disbursementDataSource: {}[] = [];
|
||||
/** Loan repayment strategies */
|
||||
transactionProcessingStrategyOptions: any = [];
|
||||
repaymentStrategyDisabled = false;
|
||||
/** Disbursement Data Displayed Columns */
|
||||
disbursementDisplayedColumns: string[] = [
|
||||
'expectedDisbursementDate',
|
||||
'principal',
|
||||
'actions'
|
||||
];
|
||||
/** Multi Disbursement Control */
|
||||
totalMultiDisbursed: any = 0;
|
||||
isMultiDisbursedCompleted = false;
|
||||
|
||||
/** Component is pristine if there has been no changes by user interaction */
|
||||
pristine = true;
|
||||
|
||||
loanId: any = null;
|
||||
loanScheduleType: OptionData | null = null;
|
||||
loanProduct: LoanProduct | null = null;
|
||||
interestRateFrequencyTypeData: any[] = [];
|
||||
// currency: Currency;
|
||||
|
||||
productEnableDownPayment = false;
|
||||
isProgressive = false;
|
||||
|
||||
/**
|
||||
* Create Loans Account Terms Form
|
||||
* @param formBuilder FormBuilder
|
||||
* @param {SettingsService} settingsService SettingsService
|
||||
* @param {ActivatedRoute} route ActivatedRoute
|
||||
* @param {MatDialog} dialog MatDialog
|
||||
*/
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private settingsService: SettingsService,
|
||||
private route: ActivatedRoute,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
this.loanId = this.route.snapshot.params['loanId'];
|
||||
this.createGLIMTermsForm();
|
||||
}
|
||||
/**
|
||||
* Executes on change of input values
|
||||
*/
|
||||
ngOnChanges() {
|
||||
if (this.loansAccountProductTemplate) {
|
||||
// this.currency = this.loansAccountProductTemplate.currency;
|
||||
|
||||
this.loansAccountTermsData = this.loansAccountProductTemplate;
|
||||
if (this.loanId != null && this.loansAccountTemplate.accountNo) {
|
||||
this.loansAccountTermsData = this.loansAccountTemplate;
|
||||
}
|
||||
this.productEnableDownPayment = this.loansAccountTermsData.product.enableDownPayment;
|
||||
this.isProgressive =
|
||||
this.loansAccountTermsData.loanScheduleType.code == LoanProducts.LOAN_SCHEDULE_TYPE_PROGRESSIVE;
|
||||
if (this.loansAccountTermsData.product) {
|
||||
this.loanProduct = this.loansAccountTermsData.product;
|
||||
}
|
||||
|
||||
this.interestRateFrequencyTypeData = this.loansAccountTermsData.interestRateFrequencyTypeOptions;
|
||||
|
||||
this.loansAccountTermsForm.patchValue({
|
||||
principalAmount: this.loansAccountTermsData.principal,
|
||||
loanTermFrequency: this.loansAccountTermsData.termFrequency,
|
||||
loanTermFrequencyType: this.loansAccountTermsData.termPeriodFrequencyType.id,
|
||||
numberOfRepayments: this.loansAccountTermsData.numberOfRepayments,
|
||||
repaymentEvery: this.loansAccountTermsData.repaymentEvery,
|
||||
repaymentFrequencyType: this.loansAccountTermsData.repaymentFrequencyType.id,
|
||||
amortizationType: this.loansAccountTermsData.amortizationType.id,
|
||||
isEqualAmortization: this.loansAccountTermsData.isEqualAmortization,
|
||||
interestType: this.loansAccountTermsData.interestType.id,
|
||||
isFloatingInterestRate: this.loansAccountTermsData.isLoanProductLinkedToFloatingRate ? false : '',
|
||||
interestCalculationPeriodType: this.loansAccountTermsData.interestCalculationPeriodType.id,
|
||||
allowPartialPeriodInterestCalculation: this.loansAccountTermsData.allowPartialPeriodInterestCalculation,
|
||||
inArrearsTolerance: this.loansAccountTermsData.inArrearsTolerance,
|
||||
graceOnInterestCharged: this.loansAccountTermsData.graceOnInterestCharged,
|
||||
fixedEmiAmount: this.loansAccountTermsData.fixedEmiAmount,
|
||||
maxOutstandingLoanBalance: this.loansAccountTermsData.maxOutstandingLoanBalance,
|
||||
transactionProcessingStrategyCode: this.loansAccountTermsData.transactionProcessingStrategyCode,
|
||||
interestRateDifferential: this.loansAccountTermsData.interestRateDifferential,
|
||||
multiDisburseLoan: this.loansAccountTermsData.multiDisburseLoan,
|
||||
interestRateFrequencyType: this.loansAccountTermsData.interestRateFrequencyType.id,
|
||||
balloonRepaymentAmount: this.loansAccountTermsData.balloonRepaymentAmount,
|
||||
interestRecognitionOnDisbursementDate: this.loansAccountTermsData.interestRecognitionOnDisbursementDate || false
|
||||
});
|
||||
|
||||
this.setAdvancedPaymentStrategyControls();
|
||||
|
||||
if (this.loansAccountTermsData.loanScheduleType.code == LoanProducts.LOAN_SCHEDULE_TYPE_CUMULATIVE) {
|
||||
this.loansAccountTermsForm.removeControl('interestRecognitionOnDisbursementDate');
|
||||
}
|
||||
|
||||
if (this.loansAccountTermsData.isLoanProductLinkedToFloatingRate) {
|
||||
this.loansAccountTermsForm.removeControl('interestRatePerPeriod');
|
||||
}
|
||||
|
||||
this.multiDisburseLoan = this.loansAccountTermsData.multiDisburseLoan;
|
||||
if (this.loansAccountTermsData.disbursementDetails) {
|
||||
this.disbursementDataSource = this.loansAccountTermsData.disbursementDetails;
|
||||
this.totalMultiDisbursed = 0;
|
||||
this.disbursementDataSource.forEach((item: any) => {
|
||||
this.totalMultiDisbursed += item.principal;
|
||||
});
|
||||
}
|
||||
if (this.isDelinquencyEnabled()) {
|
||||
this.loansAccountTermsForm.addControl(
|
||||
'enableInstallmentLevelDelinquency',
|
||||
new UntypedFormControl(
|
||||
this.loansAccountTermsData.enableInstallmentLevelDelinquency ||
|
||||
this.loanProduct.enableInstallmentLevelDelinquency
|
||||
)
|
||||
);
|
||||
}
|
||||
if (this.productEnableDownPayment) {
|
||||
const enableDownPayment = this.loansAccountTermsData['enableDownPayment'] === false ? false : true;
|
||||
this.loansAccountTermsForm.addControl('enableDownPayment', new UntypedFormControl(enableDownPayment));
|
||||
}
|
||||
|
||||
const allowAttributeOverrides = this.loansAccountTermsData.product.allowAttributeOverrides;
|
||||
if (!allowAttributeOverrides.repaymentEvery) {
|
||||
this.loansAccountTermsForm.controls.repaymentEvery.disable();
|
||||
this.loansAccountTermsForm.controls.repaymentFrequencyType.disable();
|
||||
}
|
||||
if (!allowAttributeOverrides.interestType) {
|
||||
this.loansAccountTermsForm.controls.interestType.disable();
|
||||
}
|
||||
if (!allowAttributeOverrides.amortizationType) {
|
||||
this.loansAccountTermsForm.controls.amortizationType.disable();
|
||||
}
|
||||
if (!allowAttributeOverrides.interestCalculationPeriodType) {
|
||||
this.loansAccountTermsForm.controls.interestCalculationPeriodType.disable();
|
||||
this.loansAccountTermsForm.controls.allowPartialPeriodInterestCalculation.disable();
|
||||
}
|
||||
if (!allowAttributeOverrides.inArrearsTolerance) {
|
||||
this.loansAccountTermsForm.controls.inArrearsTolerance.disable();
|
||||
}
|
||||
if (!allowAttributeOverrides.transactionProcessingStrategyCode) {
|
||||
this.loansAccountTermsForm.controls.transactionProcessingStrategyCode.disable();
|
||||
}
|
||||
this.setOptions();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.maxFutureDate;
|
||||
this.loansAccountTermsData = this.loansAccountProductTemplate;
|
||||
if (this.loanId != null && this.loansAccountTemplate.accountNo) {
|
||||
this.loansAccountTermsData = this.loansAccountTemplate;
|
||||
}
|
||||
|
||||
if (this.loansAccountTermsData) {
|
||||
if (this.loansAccountTermsData.loanProductId) {
|
||||
this.loansAccountTermsForm.patchValue({
|
||||
repaymentsStartingFromDate:
|
||||
this.loansAccountTermsData.expectedFirstRepaymentOnDate &&
|
||||
new Date(this.loansAccountTermsData.expectedFirstRepaymentOnDate)
|
||||
});
|
||||
}
|
||||
this.loansAccountTermsForm.patchValue({
|
||||
principalAmount: this.loansAccountTermsData.principal,
|
||||
loanTermFrequency: this.loansAccountTermsData.termFrequency,
|
||||
loanTermFrequencyType: this.loansAccountTermsData.termPeriodFrequencyType.id,
|
||||
numberOfRepayments: this.loansAccountTermsData.numberOfRepayments,
|
||||
repaymentEvery: this.loansAccountTermsData.repaymentEvery,
|
||||
repaymentFrequencyType: this.loansAccountTermsData.repaymentFrequencyType.id,
|
||||
amortizationType: this.loansAccountTermsData.amortizationType.id,
|
||||
isEqualAmortization: this.loansAccountTermsData.isEqualAmortization,
|
||||
interestType: this.loansAccountTermsData.interestType.id,
|
||||
isFloatingInterestRate: this.loansAccountTermsData.isLoanProductLinkedToFloatingRate ? false : '',
|
||||
interestCalculationPeriodType: this.loansAccountTermsData.interestCalculationPeriodType.id,
|
||||
allowPartialPeriodInterestCalculation: this.loansAccountTermsData.allowPartialPeriodInterestCalculation,
|
||||
inArrearsTolerance: this.loansAccountTermsData.inArrearsTolerance,
|
||||
graceOnInterestCharged: this.loansAccountTermsData.graceOnInterestCharged,
|
||||
fixedEmiAmount: this.loansAccountTermsData.fixedEmiAmount,
|
||||
maxOutstandingLoanBalance: this.loansAccountTermsData.maxOutstandingLoanBalance,
|
||||
transactionProcessingStrategyCode: this.loansAccountTermsData.transactionProcessingStrategyCode,
|
||||
interestRateDifferential: this.loansAccountTermsData.interestRateDifferential,
|
||||
multiDisburseLoan: this.loansAccountTermsData.multiDisburseLoan,
|
||||
interestRateFrequencyType: this.loansAccountTermsData.interestRateFrequencyType.id,
|
||||
balloonRepaymentAmount: this.loansAccountTermsData.balloonRepaymentAmount,
|
||||
interestRecognitionOnDisbursementDate: this.loansAccountTermsData.interestRecognitionOnDisbursementDate || false
|
||||
});
|
||||
}
|
||||
this.createGLIMTermsForm();
|
||||
this.setAdvancedPaymentStrategyControls();
|
||||
// this.setCustomValidators();
|
||||
this.setLoanTermListener();
|
||||
}
|
||||
|
||||
allowAddDisbursementDetails() {
|
||||
return this.multiDisburseLoan && !this.loansAccountTermsData.disallowExpectedDisbursements;
|
||||
}
|
||||
|
||||
/** Custom Validators for the form */
|
||||
// setCustomValidators() {
|
||||
// const repaymentFrequencyNthDayType = this.loansAccountTermsForm.get('repaymentFrequencyNthDayType');
|
||||
// const repaymentFrequencyDayOfWeekType = this.loansAccountTermsForm.get('repaymentFrequencyDayOfWeekType');
|
||||
|
||||
// this.loansAccountTermsForm.get('repaymentFrequencyType').valueChanges.subscribe((repaymentFrequencyType) => {
|
||||
// if (repaymentFrequencyType === 2) {
|
||||
// repaymentFrequencyNthDayType.setValidators([Validators.required]);
|
||||
// repaymentFrequencyDayOfWeekType.setValidators([Validators.required]);
|
||||
// } else {
|
||||
// repaymentFrequencyNthDayType.setValidators(null);
|
||||
// repaymentFrequencyDayOfWeekType.setValidators(null);
|
||||
// }
|
||||
|
||||
// repaymentFrequencyNthDayType.updateValueAndValidity();
|
||||
// repaymentFrequencyDayOfWeekType.updateValueAndValidity();
|
||||
// });
|
||||
// }
|
||||
|
||||
/** Custom Listeners for the form to calculate Loan Term */
|
||||
setLoanTermListener() {
|
||||
this.loansAccountTermsForm.get('numberOfRepayments').valueChanges.subscribe((numberOfRepayments) => {
|
||||
const repaymentEvery: number = this.loansAccountTermsForm.value.repaymentEvery;
|
||||
this.calculateLoanTerm(numberOfRepayments, repaymentEvery);
|
||||
});
|
||||
|
||||
this.loansAccountTermsForm.get('repaymentEvery').valueChanges.subscribe((repaymentEvery) => {
|
||||
const numberOfRepayments: number = this.loansAccountTermsForm.value.numberOfRepayments;
|
||||
this.calculateLoanTerm(numberOfRepayments, repaymentEvery);
|
||||
});
|
||||
|
||||
this.loansAccountTermsForm.get('loanTermFrequencyType').valueChanges.subscribe((loanTermFrequencyType) => {
|
||||
this.loansAccountTermsForm.patchValue({ repaymentFrequencyType: loanTermFrequencyType });
|
||||
});
|
||||
|
||||
this.loansAccountTermsForm.get('amortizationType').valueChanges.subscribe((amortizationType) => {
|
||||
if (amortizationType === 0) {
|
||||
// Equal Principal Payments
|
||||
this.loansAccountTermsForm.addControl('fixedPrincipalPercentagePerInstallment', new UntypedFormControl(''));
|
||||
} else {
|
||||
// Equal Installments
|
||||
this.loansAccountTermsForm.removeControl('fixedPrincipalPercentagePerInstallment');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setAdvancedPaymentStrategyControls(): void {
|
||||
// Fixed Length validation
|
||||
if (this.loansAccountTermsData) {
|
||||
this.loansAccountTermsForm.removeControl('interestRatePerPeriod');
|
||||
this.loansAccountTermsForm.removeControl('fixedLength');
|
||||
if (this.loansAccountTermsData.product.fixedLength) {
|
||||
this.loansAccountTermsForm.addControl(
|
||||
'interestRatePerPeriod',
|
||||
new UntypedFormControl({ value: 0, disabled: true }, Validators.required)
|
||||
);
|
||||
this.loansAccountTermsForm.addControl(
|
||||
'fixedLength',
|
||||
new UntypedFormControl(this.loansAccountTermsData.product.fixedLength)
|
||||
);
|
||||
} else {
|
||||
this.loansAccountTermsForm.addControl(
|
||||
'interestRatePerPeriod',
|
||||
new UntypedFormControl(this.loansAccountTermsData.interestRatePerPeriod, Validators.required)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasFixedLength(): boolean {
|
||||
if (this.loansAccountTermsData) {
|
||||
return this.loansAccountTermsData.product?.fixedLength ? true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isEqualPrincipalPayments(): boolean {
|
||||
return this.loansAccountTermsForm.value.amortizationType === 0;
|
||||
}
|
||||
|
||||
/** Create Loans Account Terms Form */
|
||||
createGLIMTermsForm() {
|
||||
this.loansAccountTermsForm = this.formBuilder.group({
|
||||
principalAmount: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
loanTermFrequency: [
|
||||
{ value: '', disabled: true },
|
||||
Validators.required
|
||||
],
|
||||
loanTermFrequencyType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
numberOfRepayments: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
repaymentEvery: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
repaymentFrequencyType: [
|
||||
{ value: '', disabled: true },
|
||||
Validators.required
|
||||
],
|
||||
repaymentFrequencyNthDayType: [''],
|
||||
repaymentFrequencyDayOfWeekType: [''],
|
||||
repaymentsStartingFromDate: [''],
|
||||
interestChargedFromDate: [''],
|
||||
interestRatePerPeriod: [''],
|
||||
interestType: [''],
|
||||
isFloatingInterestRate: [''],
|
||||
isEqualAmortization: [''],
|
||||
amortizationType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCalculationPeriodType: [''],
|
||||
allowPartialPeriodInterestCalculation: [''],
|
||||
inArrearsTolerance: [''],
|
||||
graceOnInterestCharged: [''],
|
||||
loanIdToClose: [''],
|
||||
fixedEmiAmount: [''],
|
||||
isTopup: [''],
|
||||
maxOutstandingLoanBalance: [''],
|
||||
interestRateDifferential: [''],
|
||||
transactionProcessingStrategyCode: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
multiDisburseLoan: [false],
|
||||
interestRateFrequencyType: [''],
|
||||
balloonRepaymentAmount: [''],
|
||||
interestRecognitionOnDisbursementDate: [false]
|
||||
});
|
||||
}
|
||||
|
||||
calculateLoanTerm(numberOfRepayments: number, repaymentEvery: number): void {
|
||||
const loanTerm = numberOfRepayments * repaymentEvery;
|
||||
this.loansAccountTermsForm.patchValue({ loanTermFrequency: loanTerm });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Disbursement Data array.
|
||||
* @returns {Array} Disbursement Data array.
|
||||
*/
|
||||
get disbursementData() {
|
||||
return {
|
||||
disbursementData: this.disbursementDataSource
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Disbursement Data entry form to given Disbursement Data entry.
|
||||
*/
|
||||
addDisbursementDataEntry() {
|
||||
const currentPrincipalAmount = this.loansAccountTermsForm.get('principalAmount').value;
|
||||
const formfields: FormfieldBase[] = [
|
||||
new DatepickerBase({
|
||||
controlName: 'expectedDisbursementDate',
|
||||
label: 'Expected Disbursement Date',
|
||||
value: new Date() || '',
|
||||
type: 'datetime-local',
|
||||
minDate: this.minDate,
|
||||
maxDate: this.maxDate,
|
||||
required: true,
|
||||
order: 1
|
||||
}),
|
||||
new InputBase({
|
||||
controlName: 'principal',
|
||||
label: `Principal(It should be less than equal to the ${currentPrincipalAmount})`,
|
||||
value: '',
|
||||
type: 'number',
|
||||
required: true,
|
||||
order: 2
|
||||
})
|
||||
|
||||
];
|
||||
const data = {
|
||||
title: 'Add Disbursement Details',
|
||||
layout: { addButtonText: 'Add' },
|
||||
formfields: formfields
|
||||
};
|
||||
const disbursementDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
disbursementDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
const principal = response.data.value.principal * 1;
|
||||
if (this.totalMultiDisbursed + principal <= currentPrincipalAmount) {
|
||||
this.disbursementDataSource = this.disbursementDataSource.concat(response.data.value);
|
||||
this.totalMultiDisbursed += principal;
|
||||
this.isMultiDisbursedCompleted = this.totalMultiDisbursed === currentPrincipalAmount;
|
||||
this.pristine = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Disbursement Data entry form from given Disbursement Data entry form array at given index.
|
||||
* @param {number} index Array index from where Disbursement Data entry form needs to be removed.
|
||||
*/
|
||||
removeDisbursementDataEntry(index: number) {
|
||||
const currentPrincipalAmount = this.loansAccountTermsForm.get('principalAmount').value;
|
||||
const dialogRef = this.dialog.open(DeleteDialogComponent, {
|
||||
data: { deleteContext: `this` }
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.delete) {
|
||||
const principal = this.disbursementDataSource[index]['principal'] * 1;
|
||||
this.disbursementDataSource.splice(index, 1);
|
||||
this.disbursementDataSource = this.disbursementDataSource.concat([]);
|
||||
this.totalMultiDisbursed -= principal;
|
||||
this.isMultiDisbursedCompleted = this.totalMultiDisbursed === currentPrincipalAmount;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all select dropdown options.
|
||||
*/
|
||||
setOptions() {
|
||||
this.termFrequencyTypeData = this.loansAccountProductTemplate.termFrequencyTypeOptions;
|
||||
this.repaymentFrequencyNthDayTypeData = this.loansAccountProductTemplate.repaymentFrequencyNthDayTypeOptions;
|
||||
this.repaymentFrequencyDaysOfWeekTypeData =
|
||||
this.loansAccountProductTemplate.repaymentFrequencyDaysOfWeekTypeOptions;
|
||||
this.interestTypeData = this.loansAccountProductTemplate.interestTypeOptions;
|
||||
this.amortizationTypeData = this.loansAccountProductTemplate.amortizationTypeOptions;
|
||||
this.interestCalculationPeriodTypeData = this.loansAccountProductTemplate.interestCalculationPeriodTypeOptions;
|
||||
this.clientActiveLoanData = this.loansAccountProductTemplate.clientActiveLoanOptions;
|
||||
this.loanScheduleType = this.loansAccountProductTemplate.loanScheduleType;
|
||||
this.transactionProcessingStrategyOptions = [];
|
||||
if (this.loanScheduleType.code === LoanProducts.LOAN_SCHEDULE_TYPE_CUMULATIVE) {
|
||||
// Filter Advanced Payment Allocation Strategy
|
||||
this.transactionProcessingStrategyOptions =
|
||||
this.loansAccountProductTemplate.transactionProcessingStrategyOptions.filter(
|
||||
(cn: CodeName) => !LoanProducts.isAdvancedPaymentAllocationStrategy(cn.code)
|
||||
);
|
||||
this.repaymentStrategyDisabled = false;
|
||||
} else {
|
||||
// Only Advanced Payment Allocation Strategy
|
||||
this.loansAccountProductTemplate.transactionProcessingStrategyOptions.some((cn: CodeName) => {
|
||||
if (LoanProducts.isAdvancedPaymentAllocationStrategy(cn.code)) {
|
||||
this.transactionProcessingStrategyOptions.push(cn);
|
||||
}
|
||||
});
|
||||
this.repaymentStrategyDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
isDelinquencyEnabled(): boolean {
|
||||
return !!this.loanProduct?.delinquencyBucket?.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns loans account terms form value.
|
||||
*/
|
||||
get loansAccountTerms() {
|
||||
return this.loansAccountTermsForm.getRawValue();
|
||||
}
|
||||
}
|
||||
+54
@@ -132,6 +132,60 @@
|
||||
<tr mat-header-row *matHeaderRowDef="overdueChargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: overdueChargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
|
||||
<div fxFlex="98%" *ngIf="activeClientMembers">
|
||||
<table fxFlex="98%" mat-table [dataSource]="dataSource">
|
||||
<!-- Check Column -->
|
||||
<ng-container matColumnDef="check">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox (change)="toggleSelects()" [(ngModel)]="selectAllItems"></mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-checkbox (change)="toggleSelect()" [(ngModel)]="element.selected"></mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Position Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Weight Column -->
|
||||
<ng-container matColumnDef="purpose">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Loan Purpose' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'labels.inputs.Select Purpose' | translate }}</mat-label>
|
||||
<mat-select [(ngModel)]="element.loanPurposeId">
|
||||
<mat-option *ngFor="let purpose of loanPurposeOptions" [value]="purpose.id">
|
||||
{{ purpose.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Symbol Column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Original Loan' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-form-field>
|
||||
<input type="number" matInput [(ngModel)]="element.principal" />
|
||||
</mat-form-field>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumn"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumn"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div fxLayout="row" class="margin-t" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="2%">
|
||||
|
||||
+46
-2
@@ -1,12 +1,13 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, OnChanges } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
// import { FormControl } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
/** Dialog Components */
|
||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||
import { LoansAccountAddCollateralDialogComponent } from 'app/loans/custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component';
|
||||
// import { LoansAccountAddCollateralDialogComponent } from 'app/loans/custom-dialog/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component';
|
||||
|
||||
/** Custom Services */
|
||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||
@@ -31,6 +32,8 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
@Input() loansAccountTemplate: any;
|
||||
// @Input() loansAccountFormValid: LoansAccountFormValid
|
||||
@Input() loansAccountFormValid: boolean;
|
||||
/** active Client Members in case of GSIM Account */
|
||||
@Input() activeClientMembers?: any;
|
||||
|
||||
/** Charges Data */
|
||||
chargeData: any;
|
||||
@@ -56,6 +59,20 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
'amount',
|
||||
'collectedon'
|
||||
];
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
/** Check for select all the Clients List */
|
||||
selectAllItems = false;
|
||||
/** Loan Purpose Options */
|
||||
loanPurposeOptions: string[] = [];
|
||||
/** Table Displayed Columns */
|
||||
displayedColumn: string[] = [
|
||||
'check',
|
||||
'id',
|
||||
'name',
|
||||
'purpose',
|
||||
'amount'
|
||||
];
|
||||
/** Component is pristine if there has been no changes by user interaction */
|
||||
pristine = true;
|
||||
/** Check if value of collateral added is more than principal amount */
|
||||
@@ -84,6 +101,7 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
this.chargesDataSource =
|
||||
this.loansAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
}
|
||||
this.dataSource = new MatTableDataSource<any>(this.activeClientMembers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,6 +109,7 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
*/
|
||||
ngOnChanges() {
|
||||
if (this.loansAccountProductTemplate) {
|
||||
this.loanPurposeOptions = this.loansAccountProductTemplate.loanPurposeOptions;
|
||||
this.chargeData = this.loansAccountProductTemplate.chargeOptions;
|
||||
if (this.loansAccountProductTemplate.overdueCharges) {
|
||||
this.overDueChargesDataSource = this.loansAccountProductTemplate.overdueCharges;
|
||||
@@ -236,6 +255,13 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
});
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
return (
|
||||
!this.activeClientMembers ||
|
||||
this.selectedClientMembers?.selectedMembers?.reduce((acc: any, cur: any) => acc + (cur.principal ?? 0), 0) > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Loans Account Charges and Collateral Form
|
||||
*/
|
||||
@@ -244,4 +270,22 @@ export class LoansAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
charges: this.chargesDataSource
|
||||
};
|
||||
}
|
||||
|
||||
get selectedClientMembers() {
|
||||
return { selectedMembers: this.activeClientMembers.filter((item: any) => item.selected) };
|
||||
}
|
||||
|
||||
/** Toggle all checks */
|
||||
toggleSelects() {
|
||||
for (const member of this.activeClientMembers) {
|
||||
member.selected = this.selectAllItems;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if all the checks are selected */
|
||||
toggleSelect() {
|
||||
const len = this.activeClientMembers.length;
|
||||
this.selectAllItems =
|
||||
len === 0 ? false : this.activeClientMembers.filter((item: any) => item.selected).length === len;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -42,6 +42,10 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div fxFlex="98%" *ngIf="activeClientMembers">
|
||||
<! -- TODO: Display activeClientMembers -->
|
||||
</div>
|
||||
|
||||
<h3 class="mat-h3" fxFlexFill>{{ 'labels.heading.Terms' | translate }}</h3>
|
||||
|
||||
<mat-divider fxFlexFill></mat-divider>
|
||||
|
||||
+13
@@ -1,5 +1,6 @@
|
||||
/** Angular Imports */
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
/**
|
||||
* Create Loans Account Preview Step
|
||||
@@ -16,6 +17,9 @@ export class LoansAccountPreviewStepComponent implements OnChanges {
|
||||
@Input() loansAccountProductTemplate: any;
|
||||
/** Loans Account Data */
|
||||
@Input() loansAccount: any;
|
||||
/** active Client Members in case of GSIM Account */
|
||||
@Input() activeClientMembers?: any;
|
||||
|
||||
/** Submit Loans Account */
|
||||
@Output() submitEvent = new EventEmitter();
|
||||
|
||||
@@ -35,11 +39,20 @@ export class LoansAccountPreviewStepComponent implements OnChanges {
|
||||
'collectedon'
|
||||
];
|
||||
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
productEnableDownPayment = false;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.productEnableDownPayment = this.loansAccountProductTemplate.product.enableDownPayment;
|
||||
if (this.activeClientMembers) {
|
||||
this.dataSource = new MatTableDataSource<any>(this.activeClientMembers);
|
||||
this.loansAccount.principalAmount = this.activeClientMembers.reduce(
|
||||
(acc: number, member: any) => acc + (member.principal ?? 0),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -146,6 +146,7 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges {
|
||||
amortizationType: this.loansAccountTermsData.amortizationType.id,
|
||||
isEqualAmortization: this.loansAccountTermsData.isEqualAmortization,
|
||||
interestType: this.loansAccountTermsData.interestType.id,
|
||||
// TODO: 2025-03-17: Is this correct?
|
||||
isFloatingInterestRate: this.loansAccountTermsData.isLoanProductLinkedToFloatingRate ? false : '',
|
||||
interestCalculationPeriodType: this.loansAccountTermsData.interestCalculationPeriodType.id,
|
||||
allowPartialPeriodInterestCalculation: this.loansAccountTermsData.allowPartialPeriodInterestCalculation,
|
||||
|
||||
@@ -351,7 +351,6 @@ const routes: Routes = [
|
||||
component: CreateGlimAccountComponent,
|
||||
resolve: {
|
||||
loansAccountTemplate: GLIMLoanTemplateResolver,
|
||||
gsimData: GSIMAccountsResolver,
|
||||
groupsData: GroupViewResolver
|
||||
}
|
||||
},
|
||||
|
||||
@@ -58,10 +58,6 @@ import { ViewTransactionComponent } from './loans-view/transactions/view-transac
|
||||
import { EditTransactionComponent } from './loans-view/transactions/edit-transaction/edit-transaction.component';
|
||||
import { GlimAccountComponent } from './glim-account/glim-account.component';
|
||||
import { CreateGlimAccountComponent } from './glim-account/create-glim-account/create-glim-account.component';
|
||||
import { GlimDetailsStepComponent } from './glim-account/create-glim-account/glim-account-stepper/glim-details-step/glim-details-step.component';
|
||||
import { GlimChargesStepComponent } from './glim-account/create-glim-account/glim-account-stepper/glim-charges-step/glim-charges-step.component';
|
||||
import { GlimTermsStepComponent } from './glim-account/create-glim-account/glim-account-stepper/glim-terms-step/glim-terms-step.component';
|
||||
import { GlimPreviewStepComponent } from './glim-account/create-glim-account/glim-account-stepper/glim-preview-step/glim-preview-step.component';
|
||||
|
||||
/** Dialog Components */
|
||||
import { LoansAccountViewGuarantorDetailsDialogComponent } from './custom-dialog/loans-account-view-guarantor-details-dialog/loans-account-view-guarantor-details-dialog.component';
|
||||
@@ -152,10 +148,6 @@ import { AddInterestPauseComponent } from './loans-view/loan-account-actions/add
|
||||
LoanCreditBalanceRefundComponent,
|
||||
GlimAccountComponent,
|
||||
CreateGlimAccountComponent,
|
||||
GlimDetailsStepComponent,
|
||||
GlimChargesStepComponent,
|
||||
GlimTermsStepComponent,
|
||||
GlimPreviewStepComponent,
|
||||
LoanDelinquencyTagsTabComponent,
|
||||
LoansAccountScheduleStepComponent,
|
||||
EditRepaymentScheduleComponent,
|
||||
|
||||
@@ -619,7 +619,7 @@ export class LoansService {
|
||||
}));
|
||||
}
|
||||
|
||||
if (loansAccountTemplate.clientId && loansAccountTemplate.group.id) {
|
||||
if (loansAccountTemplate.clientId && loansAccountTemplate.group?.id) {
|
||||
loansAccountData.clientId = loansAccountTemplate.clientId;
|
||||
loansAccountData.groupId = loansAccountTemplate.group.id;
|
||||
loansAccountData.loanType = 'glim';
|
||||
|
||||
Reference in New Issue
Block a user