From 843e4ed981078f5b4b60316809ca4c7d7434eaa9 Mon Sep 17 00:00:00 2001 From: Muskan Khedia <36980003+muskankhedia@users.noreply.github.com> Date: Fri, 10 Jul 2020 15:50:02 +0530 Subject: [PATCH] feat: add create loan account (#1050) Fixes: #220 Co-authored-by: Abhijeet Singh --- .../clients-view/clients-view.component.html | 2 +- ...eposits-account-currency-step.component.ts | 4 - ...posits-account-preview-step.component.html | 2 +- ...eposits-account-settings-step.component.ts | 2 +- ...rring-deposits-account-view.component.html | 2 +- .../recurring-deposits-routing.module.ts | 2 +- .../recurring-deposits.module.ts | 5 +- .../loans-account-template.resolver.ts | 29 ++ .../create-loans-account.component.html | 66 ++++ .../create-loans-account.component.scss | 0 .../create-loans-account.component.spec.ts | 25 ++ .../create-loans-account.component.ts | 148 ++++++++ ...count-add-collateral-dialog.component.html | 39 ++ ...count-add-collateral-dialog.component.scss | 0 ...nt-add-collateral-dialog.component.spec.ts | 25 ++ ...account-add-collateral-dialog.component.ts | 42 +++ .../loans-account-charges-step.component.html | 176 +++++++++ .../loans-account-charges-step.component.scss | 36 ++ ...ans-account-charges-step.component.spec.ts | 25 ++ .../loans-account-charges-step.component.ts | 249 +++++++++++++ .../loans-account-details-step.component.html | 101 +++++ .../loans-account-details-step.component.scss | 24 ++ ...ans-account-details-step.component.spec.ts | 25 ++ .../loans-account-details-step.component.ts | 111 ++++++ .../loans-account-preview-step.component.html | 273 ++++++++++++++ .../loans-account-preview-step.component.scss | 24 ++ ...ans-account-preview-step.component.spec.ts | 25 ++ .../loans-account-preview-step.component.ts | 32 ++ .../loans-account-terms-step.component.html | 350 ++++++++++++++++++ .../loans-account-terms-step.component.scss | 24 ++ ...loans-account-terms-step.component.spec.ts | 25 ++ .../loans-account-terms-step.component.ts | 142 +++++++ src/app/loans/loans-routing.module.ts | 22 +- src/app/loans/loans.module.ts | 15 + src/app/loans/loans.service.ts | 53 ++- 35 files changed, 2105 insertions(+), 20 deletions(-) create mode 100644 src/app/loans/common-resolvers/loans-account-template.resolver.ts create mode 100644 src/app/loans/create-loans-account/create-loans-account.component.html create mode 100644 src/app/loans/create-loans-account/create-loans-account.component.scss create mode 100644 src/app/loans/create-loans-account/create-loans-account.component.spec.ts create mode 100644 src/app/loans/create-loans-account/create-loans-account.component.ts create mode 100644 src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.html create mode 100644 src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.scss create mode 100644 src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.spec.ts create mode 100644 src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html create mode 100644 src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.scss create mode 100644 src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.spec.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html create mode 100644 src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.scss create mode 100644 src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.spec.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html create mode 100644 src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.scss create mode 100644 src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.spec.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html create mode 100644 src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.scss create mode 100644 src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.spec.ts create mode 100644 src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts diff --git a/src/app/clients/clients-view/clients-view.component.html b/src/app/clients/clients-view/clients-view.component.html index f7bf3582d..6c980574e 100644 --- a/src/app/clients/clients-view/clients-view.component.html +++ b/src/app/clients/clients-view/clients-view.component.html @@ -32,7 +32,7 @@ - + diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component.ts b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component.ts index f72b5e37e..e2fcd1e93 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component.ts @@ -29,11 +29,7 @@ export class RecurringDepositsAccountCurrencyStepComponent implements OnInit, On } ngOnChanges() { - console.log('out here'); - if (this.recurringDepositsAccountProductTemplate) { - console.log('here'); - this.recurringDepositAccountCurrencyForm.patchValue({ 'currencyCode': this.recurringDepositsAccountProductTemplate.currency.code, 'decimalPlaces': this.recurringDepositsAccountProductTemplate.currency.decimalPlaces diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html index c404f9fd4..805c9caac 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-preview-step/recurring-deposits-account-preview-step.component.html @@ -79,7 +79,7 @@
Recurring Deposit Amount: - {{ recurringDepositAccountData.mandatoryRecommendedDepositAmount}} + {{ recurringDepositAccountData?.mandatoryRecommendedDepositAmount}}
diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-settings-step/recurring-deposits-account-settings-step.component.ts b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-settings-step/recurring-deposits-account-settings-step.component.ts index cdb3214f0..cabdd3ff4 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-settings-step/recurring-deposits-account-settings-step.component.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-stepper/recurring-deposits-account-settings-step/recurring-deposits-account-settings-step.component.ts @@ -85,7 +85,7 @@ export class RecurringDepositsAccountSettingsStepComponent implements OnInit, On this.recurringDepositAccountSettingsForm.patchValue({ 'lockinPeriodFrequency': this.recurringDepositsAccountTemplate.lockinPeriodFrequency, 'lockinPeriodFrequencyType': this.recurringDepositsAccountTemplate.lockinPeriodFrequencyType && this.recurringDepositsAccountTemplate.lockinPeriodFrequencyType.id, - 'mandatoryRecommendedDepositAmount': this.recurringDepositsAccountProductTemplate.mandatoryRecommendedDepositAmount, + 'mandatoryRecommendedDepositAmount': this.recurringDepositsAccountTemplate.mandatoryRecommendedDepositAmount, }); } } diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-account-view/recurring-deposits-account-view.component.html b/src/app/deposits/recurring-deposits/recurring-deposits-account-view/recurring-deposits-account-view.component.html index b758742d8..861428542 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-account-view/recurring-deposits-account-view.component.html +++ b/src/app/deposits/recurring-deposits/recurring-deposits-account-view/recurring-deposits-account-view.component.html @@ -128,7 +128,7 @@ Recurring Deposit Amount - {{recurringDepositsAccountData.currency.displaySymbol}} {{recurringDepositsAccountData.mandatoryRecommendedDepositAmount | number}} + {{recurringDepositsAccountData.currency.displaySymbol}} {{recurringDepositsAccountData?.mandatoryRecommendedDepositAmount | number}} diff --git a/src/app/deposits/recurring-deposits/recurring-deposits-routing.module.ts b/src/app/deposits/recurring-deposits/recurring-deposits-routing.module.ts index e7d06aefb..65f1ce16e 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits-routing.module.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits-routing.module.ts @@ -32,7 +32,7 @@ import { RecurringDepositsAccountTransactionTemplateResolver } from './common-re const routes: Routes = [ { path: '', - data: { title: extract('All RecurringDeposits'), breadcrumb: 'RecurringDeposits', routeParamBreadcrumb: false }, + data: { title: extract('All Recurring Deposits'), breadcrumb: 'Recurring Deposits', routeParamBreadcrumb: false }, children: [ { path: 'create-recurring-deposits-account', diff --git a/src/app/deposits/recurring-deposits/recurring-deposits.module.ts b/src/app/deposits/recurring-deposits/recurring-deposits.module.ts index 7ecd5ec54..6a36cd313 100644 --- a/src/app/deposits/recurring-deposits/recurring-deposits.module.ts +++ b/src/app/deposits/recurring-deposits/recurring-deposits.module.ts @@ -32,9 +32,6 @@ import { ApproveRecurringDepositsAccountComponent } from './recurring-deposits-a import { RejectRecurringDepositsAccountComponent } from './recurring-deposits-account-actions/reject-recurring-deposits-account/reject-recurring-deposits-account.component'; import { WithdrawByClientRecurringDepositsAccountComponent } from './recurring-deposits-account-actions/withdraw-by-client-recurring-deposits-account/withdraw-by-client-recurring-deposits-account.component'; import { AddChargeRecurringDepositsAccountComponent } from './recurring-deposits-account-actions/add-charge-recurring-deposits-account/add-charge-recurring-deposits-account.component'; - -/* Dialog Components */ -import { RecurringDepositConfirmationDialogComponent } from './recurring-deposits-account-view/custom-dialogs/recurring-deposit-confirmation-dialog/recurring-deposit-confirmation-dialog.component'; import { EditRecurringDepositAccountComponent } from './edit-recurring-deposit-account/edit-recurring-deposit-account.component'; import { PrematureCloseRecurringDepositAccountComponent } from './recurring-deposits-account-actions/premature-close-recurring-deposit-account/premature-close-recurring-deposit-account.component'; import { CloseRecurringDepositsAccountComponent } from './recurring-deposits-account-actions/close-recurring-deposits-account/close-recurring-deposits-account.component'; @@ -42,6 +39,8 @@ import { DepositRecurringDepositsAccountComponent } from './recurring-deposits-a import { ViewTransactionComponent } from './recurring-deposits-account-view/transactions-tab/view-transaction/view-transaction.component'; import { EditTransactionComponent } from './recurring-deposits-account-view/transactions-tab/edit-transaction/edit-transaction.component'; +/* Dialog Components */ +import { RecurringDepositConfirmationDialogComponent } from './recurring-deposits-account-view/custom-dialogs/recurring-deposit-confirmation-dialog/recurring-deposit-confirmation-dialog.component'; /** * RecurringDeposits Module diff --git a/src/app/loans/common-resolvers/loans-account-template.resolver.ts b/src/app/loans/common-resolvers/loans-account-template.resolver.ts new file mode 100644 index 000000000..76bd4b1d9 --- /dev/null +++ b/src/app/loans/common-resolvers/loans-account-template.resolver.ts @@ -0,0 +1,29 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { LoansService } from '../loans.service'; + +/** + * Loan accounts template data resolver. + */ +@Injectable() +export class LoansAccountTemplateResolver implements Resolve { + /** + * @param {ProductsService} productsService Products service. + */ + constructor(private loansService: LoansService) { } + + /** + * Returns the loan account template data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const clientId = route.parent.parent.paramMap.get('clientId'); + return this.loansService.getLoansAccountTemplateResource(clientId); + } +} diff --git a/src/app/loans/create-loans-account/create-loans-account.component.html b/src/app/loans/create-loans-account/create-loans-account.component.html new file mode 100644 index 000000000..c13b1caf0 --- /dev/null +++ b/src/app/loans/create-loans-account/create-loans-account.component.html @@ -0,0 +1,66 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + DETAILS + + + + + + + + + TERMS + + + + + + + + + CHARGES + + + + + + + + + PREVIEW + + + + + + + + +
diff --git a/src/app/loans/create-loans-account/create-loans-account.component.scss b/src/app/loans/create-loans-account/create-loans-account.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/loans/create-loans-account/create-loans-account.component.spec.ts b/src/app/loans/create-loans-account/create-loans-account.component.spec.ts new file mode 100644 index 000000000..1ae7571ee --- /dev/null +++ b/src/app/loans/create-loans-account/create-loans-account.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateLoansAccountComponent } from './create-loans-account.component'; + +describe('CreateLoansAccountComponent', () => { + let component: CreateLoansAccountComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CreateLoansAccountComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateLoansAccountComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/create-loans-account/create-loans-account.component.ts b/src/app/loans/create-loans-account/create-loans-account.component.ts new file mode 100644 index 000000000..0cced6711 --- /dev/null +++ b/src/app/loans/create-loans-account/create-loans-account.component.ts @@ -0,0 +1,148 @@ +/** Angular Imports */ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { DatePipe } from '@angular/common'; + +/** Custom Services */ +import { LoansService } from '../loans.service'; + +/** Step Components */ +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'; + +/** + * Create loans account + */ +@Component({ + selector: 'mifosx-create-loans-account', + templateUrl: './create-loans-account.component.html', + styleUrls: ['./create-loans-account.component.scss'] +}) +export class CreateLoansAccountComponent implements OnInit { + + /** Imports all the step component */ + @ViewChild(LoansAccountDetailsStepComponent) loansAccountDetailsStep: LoansAccountDetailsStepComponent; + @ViewChild(LoansAccountTermsStepComponent) loansAccountTermsStep: LoansAccountTermsStepComponent; + @ViewChild(LoansAccountChargesStepComponent) loansAccountChargesStep: LoansAccountChargesStepComponent; + + /** Loans Account Template */ + loansAccountTemplate: any; + /** Loans Account Product Template */ + loansAccountProductTemplate: any; + /** Collateral Options */ + collateralOptions: any; + + /** + * Sets loans account create form. + * @param {route} ActivatedRoute Activated Route. + * @param {router} Router Router. + * @param {datePipe} DatePipe Date Pipe + * @param {loansService} LoansService Loans Service + */ + constructor(private route: ActivatedRoute, + private router: Router, + private datePipe: DatePipe, + private loansService: LoansService + ) { + this.route.data.subscribe((data: { loansAccountTemplate: any }) => { + this.loansAccountTemplate = data.loansAccountTemplate; + }); + } + + ngOnInit() { + } + + /** + * Sets loans account product template and collateral template + * @param {any} $event API response + */ + setTemplate($event: any) { + this.loansAccountProductTemplate = $event; + this.loansService.getLoansCollateralTemplateResource(this.loansAccountProductTemplate.loanProductId).subscribe((response: any) => { + this.collateralOptions = response.loanCollateralOptions; + }); + } + + /** Get Loans Account Details Form Data */ + get loansAccountDetailsForm() { + return this.loansAccountDetailsStep.loansAccountDetailsForm; + } + + /** Get Loans Account Terms Form Data */ + get loansAccountTermsForm() { + return this.loansAccountTermsStep.loansAccountTermsForm; + } + + /** Checks wheter all the forms in different steps are valid or not */ + get loansAccountFormValid() { + return ( + this.loansAccountDetailsForm.valid && + this.loansAccountTermsForm.valid + ); + } + + /** Retrieves Data of all forms except Currency to submit the data */ + get loansAccount() { + return { + ...this.loansAccountDetailsStep.loansAccountDetails, + ...this.loansAccountTermsStep.loansAccountTerms, + ...this.loansAccountChargesStep.loansAccountCharges, + }; + } + + /** + * Submits Data to create loan account + */ + submit() { + const locale = 'en'; + const dateFormat = 'dd MMMM yyyy'; + const loanType = 'individual'; + let loansAccountData = { + ...this.loansAccount, + clientId: this.loansAccountTemplate.clientId, + charges: this.loansAccount.charges.map((charge: any) => ({ + chargeId: charge.id, + amount: charge.amount, + dueDate: charge.dueDate && this.datePipe.transform(charge.dueDate, dateFormat), + })), + collateral: this.loansAccount.collateral.map((collateralEle: any) => ({ + type: collateralEle.type, + value: collateralEle.value, + description: collateralEle.description + })), + interestChargedFromDate: this.datePipe.transform(this.loansAccount.interestChargedFromDate, dateFormat), + repaymentsStartingFromDate: this.datePipe.transform(this.loansAccount.repaymentsStartingFromDate, dateFormat), + submittedOnDate: this.datePipe.transform(this.loansAccount.submittedOnDate, dateFormat), + expectedDisbursementDate: this.datePipe.transform(this.loansAccount.expectedDisbursementDate, dateFormat), + dateFormat, + locale, + loanType + }; + + if (loansAccountData.syncRepaymentsWithMeeting) { + loansAccountData.calendarId = this.loansAccountProductTemplate.calendarOptions[0].id; + delete loansAccountData.syncRepaymentsWithMeeting; + } + + if (loansAccountData.recalculationRestFrequencyDate) { + loansAccountData.recalculationRestFrequencyDate = this.datePipe.transform(this.loansAccount.recalculationRestFrequencyDate, dateFormat); + } + + if (loansAccountData.recalculationCompoundingFrequencyDate) { + loansAccountData.recalculationCompoundingFrequencyDate = this.datePipe.transform(this.loansAccount.recalculationCompoundingFrequencyDate, dateFormat); + } + + if (loansAccountData.interestCalculationPeriodType === 0) { + loansAccountData.allowPartialPeriodInterestCalcualtion = false; + } + if (!(loansAccountData.isFloatingInterestRate === false)){ + delete loansAccountData.isFloatingInterestRate; + } + + this.loansService.createLoansAccount(loansAccountData).subscribe((response: any) => { + this.router.navigate(['../', response.resourceId], { relativeTo: this.route }); + }); + } + +} diff --git a/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.html b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.html new file mode 100644 index 000000000..dfe35591e --- /dev/null +++ b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.html @@ -0,0 +1,39 @@ +

Add Loan Collateral

+ +
+ + + Collateral Type + + + {{ collateralType.name }} + + + + Collateral Type is required + + + + + Value + + + Value is required + + + + + Description + + + Description is required + + + +
+ + + + + diff --git a/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.scss b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.spec.ts b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.spec.ts new file mode 100644 index 000000000..a8f964a62 --- /dev/null +++ b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoansAccountAddCollateralDialogComponent } from './loans-account-add-collateral-dialog.component'; + +describe('LoansAccountAddCollateralDialogComponent', () => { + let component: LoansAccountAddCollateralDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoansAccountAddCollateralDialogComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoansAccountAddCollateralDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.ts b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.ts new file mode 100644 index 000000000..a6c43c497 --- /dev/null +++ b/src/app/loans/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; + +@Component({ + selector: 'mifosx-loans-account-add-collateral-dialog', + templateUrl: './loans-account-add-collateral-dialog.component.html', + styleUrls: ['./loans-account-add-collateral-dialog.component.scss'] +}) +export class LoansAccountAddCollateralDialogComponent implements OnInit { + + layout: { + addButtonText?: string + } = { + addButtonText: 'Add' + }; + + addCollateralForm: FormGroup; + collateralTypeData: any; + + constructor(public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any, + private formBuilder: FormBuilder) { + this.createAddCollateralForm(); + } + + ngOnInit() { + this.dialogRef.updateSize('400px'); + console.log('collateral: ', this.data.collateralTypeOptions); + + this.collateralTypeData = this.data.collateralTypeOptions; + } + + createAddCollateralForm() { + this.addCollateralForm = this.formBuilder.group({ + 'type': ['', Validators.required], + 'value': ['', Validators.required], + 'description': ['', Validators.required] + }); + } + +} diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html new file mode 100644 index 000000000..d693daf19 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.html @@ -0,0 +1,176 @@ +
+ + + Charge + + + {{ charge.name }} + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + {{ charge.name + ', ' + charge.currency.displaySymbol }} + Type + {{ charge.chargeCalculationType.value }} + Amount + {{ charge.amount }} + + Collected On + {{ charge.chargeTimeType.value }} + Date + + {{(charge.dueDate | date) || 'Unassigned'}} + + + {{(charge.feeOnMonthDay | date) || 'Unassigned'}} + + + N/A + + + Actions + +
+ + + +

Overdue Charges

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Name {{ charge.name }},{{ charge.currency.displaySymbol }} Type {{ charge.chargeCalculationType.value }} Amount {{ charge.amount| number }} Collected On {{ charge.chargeTimeType.value }}
+ + + +
+
+

Collaterals Data

+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Type {{ ele.type.name }} Value {{ ele.value }} Description {{ ele.description }} Actions + +
+ +
+ +
+ + +
diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.scss b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.scss new file mode 100644 index 000000000..b75026c68 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.scss @@ -0,0 +1,36 @@ +table { + width: 100%; +} + +.mat-elevation-z1 { + margin: 1em 0 1.5em 0; +} + +.margin-t { + margin-top: 1em; +} + +h4 { + font-weight: 500; + margin: 1em 0; +} + +h3 { + font-weight: 500; +} + +mat-divider { + margin: 1em 0 1em 0; +} + +.margin-v { + margin: 1em 0; +} + +.margin-b { + margin: 0 0 1em 0; +} + +.tableName { + padding-left: 2%; +} diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.spec.ts b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.spec.ts new file mode 100644 index 000000000..774bbabc8 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoansAccountChargesStepComponent } from './loans-account-charges-step.component'; + +describe('LoansAccountChargesStepComponent', () => { + let component: LoansAccountChargesStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoansAccountChargesStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoansAccountChargesStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts new file mode 100644 index 000000000..931e25574 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-charges-step/loans-account-charges-step.component.ts @@ -0,0 +1,249 @@ +/** Angular Imports */ +import { Component, OnInit, Input, OnChanges } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import { MatDialog } from '@angular/material'; + +/** 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/loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component'; + +/** Custom Services */ +import { DatePipe } from '@angular/common'; +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'; + +/** + * Recurring Deposit Account Charges Step + */ +@Component({ + selector: 'mifosx-loans-account-charges-step', + templateUrl: './loans-account-charges-step.component.html', + styleUrls: ['./loans-account-charges-step.component.scss'] +}) +export class LoansAccountChargesStepComponent implements OnInit, OnChanges { + + // @Input loansAccountProductTemplate: LoansAccountProductTemplate + @Input() loansAccountProductTemplate: any; + // @Imput loansAccountTemplate: LoansAccountTemplate + @Input() loansAccountTemplate: any; + // @Input() loansAccountFormValid: LoansAccountFormValid + @Input() loansAccountFormValid: boolean; + // @Input collateralOptionsL Collateral Options + @Input() collateralOptions: any; + + /** Charges Data */ + chargeData: any; + /** Charges Data Source */ + chargesDataSource: {}[] = []; + /** Overdue Charges Data Source */ + overDueChargesDataSource: {}[] = []; + /** Collateral Data Source */ + collateralDataSource: {}[] = []; + /** Charges table columns */ + chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'date', 'action']; + /** Columns to be displayed in overdue charges table. */ + overdueChargesDisplayedColumns: string[] = ['name', 'type', 'amount', 'collectedon']; + /** Columns to be displayed in collateral table. */ + loanCollateralDisplayedColumns: string[] = ['type', 'value', 'description', 'action']; + /** Component is pristine if there has been no changes by user interaction */ + pristine = true; + + /** + * Loans Account Charges Form Step + * @param {dialog} MatDialog Mat Dialog + * @param {datePipe} DatePipe DatePipe + */ + constructor(public dialog: MatDialog, + private datePipe: DatePipe) { + } + + ngOnInit() { + if ( this.loansAccountTemplate.charges) { + this.chargesDataSource = this.loansAccountTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || []; + } + } + + /** + * Executes on change of input values + */ + ngOnChanges() { + if (this.loansAccountProductTemplate) { + this.chargeData = this.loansAccountProductTemplate.chargeOptions; + if (this.loansAccountProductTemplate.overdueCharges) { + this.overDueChargesDataSource = this.loansAccountProductTemplate.overdueCharges; + } + } + } + + /** + * Add a 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 = 'dd MMMM yyyy'; + const date = this.datePipe.transform(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; + } + + /** + * Delete a particular charge + * @param 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; + } + }); + } + + // TODO: Needs to be completed + addCollateral() { + const addCollateralDialogRef = this.dialog.open(LoansAccountAddCollateralDialogComponent, { + data: { collateralOptions: this.collateralOptions } + }); + addCollateralDialogRef.afterClosed().subscribe((response: any) => { + if (response.addCollateralForm) { + console.log('formdata: ', response.addCollateralForm); + const collateralData = { + type: response.addCollateralForm.value.type, + value: response.addCollateralForm.value.value, + description: response.addCollateralForm.value.description + }; + this.collateralDataSource = this.collateralDataSource.concat(collateralData); + + } + }); + } + + deleteCollateral(id: any) { + const deleteCollateralDialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `collateral` } + }); + deleteCollateralDialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.collateralDataSource.splice(this.collateralDataSource.indexOf(id), 1); + this.collateralDataSource = this.collateralDataSource.concat([]); + this.pristine = false; + } + }); + } + + /** + * Returns Loans Account Charges and Collateral Form + */ + get loansAccountCharges() { + return { + charges: this.chargesDataSource, + collateral: this.collateralDataSource + }; + } + +} diff --git a/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html new file mode 100644 index 000000000..1ffb94645 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.html @@ -0,0 +1,101 @@ +
+ +
+ + + Product Name + + + {{ product.name }} + + + + Product Name is required + + + + + Loan officer + + + {{ loanOfficer.displayName }} + + + + + + Loan purpose + + + {{ loanPurpose.name }} + + + + + + Fund + + + {{ fund.name }} + + + + + + Submitted on + + + + + Submitted on is required + + + + + Disbursement on + + + + + Disbursement on is required + + + + + External id + + + + + +

Savings Linkage

+ + + Link savings + + + {{ savingaccount.productName }} + + + + + +

Create standing instructions at disbursement

+
+ +
+ +
+ + +
+ +
diff --git a/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.scss b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.scss new file mode 100644 index 000000000..17e77f81d --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.scss @@ -0,0 +1,24 @@ +h4 { + font-weight: 500; + margin: 1em 0; +} + +h3 { + font-weight: 500; +} + +mat-divider { + margin: 1em 0 2em 0; +} + +.margin-v { + margin: 1em 0; +} + +.margin-b { + margin: 0 0 1em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.spec.ts b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.spec.ts new file mode 100644 index 000000000..3f978a3c6 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoansAccountDetailsStepComponent } from './loans-account-details-step.component'; + +describe('LoansAccountDetailsStepComponent', () => { + let component: LoansAccountDetailsStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoansAccountDetailsStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoansAccountDetailsStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts new file mode 100644 index 000000000..db5fdd5d8 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-details-step/loans-account-details-step.component.ts @@ -0,0 +1,111 @@ +/** Angular Imports */ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; + +/** Custom Services */ +import { LoansService } from '../../loans.service'; +import { DatePipe } from '@angular/common'; +import { ReportsRoutingModule } from 'app/reports/reports-routing.module'; + +/** + * Loans Account Details Step + */ +@Component({ + selector: 'mifosx-loans-account-details-step', + templateUrl: './loans-account-details-step.component.html', + styleUrls: ['./loans-account-details-step.component.scss'] +}) +export class LoansAccountDetailsStepComponent implements OnInit { + + /** Loans Account Template */ + @Input() loansAccountTemplate: any; + + /** Minimum date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum date allowed. */ + maxDate = new Date(); + /** Product Data */ + productData: 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: FormGroup; + + /** 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. + */ + constructor(private formBuilder: FormBuilder, + private loansService: LoansService, + private datePipe: DatePipe) { + this.createLoansAccountDetailsForm(); + } + + ngOnInit() { + this.buildDependencies(); + if (this.loansAccountTemplate) { + this.productData = this.loansAccountTemplate.productOptions; + if (this.loansAccountTemplate.loanProductId) { + this.loansAccountDetailsForm.patchValue({ + 'productId': this.loansAccountTemplate.loanProductId, + 'submittedOnDate': this.loansAccountTemplate.timeline.submittedOnDate && new Date(this.loansAccountTemplate.timeline.submittedOnDate), + }); + } + } + } + + /** + * Creates loans account details form. + */ + createLoansAccountDetailsForm() { + this.loansAccountDetailsForm = this.formBuilder.group({ + 'productId': ['', Validators.required], + 'loanOfficerId': [''], + 'loanPurposeId': [''], + 'fundId': [''], + 'submittedOnDate': ['', Validators.required], + 'expectedDisbursementDate': ['', Validators.required], + 'externalId': [''], + 'linkAccountId': [''], + 'createStandingInstructionAtDisbursement': [''] + }); + } + + /** + * Fetches loans account product template on productId value changes + */ + buildDependencies() { + const clientId = this.loansAccountTemplate.clientId; + this.loansAccountDetailsForm.get('productId').valueChanges.subscribe((productId: string) => { + this.loansService.getLoansAccountTemplateResource(clientId, productId).subscribe((response: any) => { + this.loansAccountProductTemplate.emit(response); + this.loanOfficerOptions = response.loanOfficerOptions; + this.loanPurposeOptions = response.loanPurposeOptions; + this.fundOptions = response.fundOptions; + this.accountLinkingOptions = response.accountLinkingOptions; + if (response.createStandingInstructionAtDisbursement) { + this.loansAccountDetailsForm.get('createStandingInstructionAtDisbursement').patchValue(response.createStandingInstructionAtDisbursement); + } + }); + }); + } + + /** + * Returns loans account details form value. + */ + get loansAccountDetails() { + return this.loansAccountDetailsForm.value; + } + +} diff --git a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html new file mode 100644 index 000000000..7118bfb85 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.html @@ -0,0 +1,273 @@ +
+ +

Details

+ + + +
+ Product: + {{ loansAccount.productId | find:loansAccountTemplate.productOptions:'id':'name' }} +
+ +
+ Loan officer: : + {{ loansAccount.loanOfficerId | find:loansAccountProductTemplate.loanOfficerOptions:'id':'name'}} +
+ +
+ Loan purpose: + {{ loansAccount.loanPurposeId | find:loansAccountProductTemplate.loanPurposeOptions:'id':'name'}} +
+ +
+ Fund: + {{ loansAccount.fundId | find:loansAccountProductTemplate.fundOptions:'id':'name'}} +
+ +
+ Submitted on: + {{ loansAccount.submittedOnDate | date }} +
+ +
+ Disbursement on: + {{ loansAccount.expectedDisbursementDate | date }} +
+ +
+ External id + {{ loansAccount.externalId }} +
+ +

Terms

+ + + +
+ Principal: + {{ loansAccount.principal }} {{loansAccountProductTemplate.currency.DisplaySymbol}} +
+ +
+ Loan term: + {{ loansAccount.loanTermFrequency }} + {{loansAccount.loanTermFrequencyType | find: loansAccountProductTemplate.termFrequencyTypeOptions:'id':'name'}} +
+ +
+ Number of repayments + {{ loansAccount.numberOfRepayments }} +
+ +
+ Repaid every + {{loansAccount.repaymentEvery}} + {{loansAccount.repaymentFrequencyType | find: loansAccountProductTemplate.termFrequencyTypeOptions:'id':'name'}} + {{loansAccount.repaymentFrequencyNthDayType | find: loansAccountProductTemplate.repaymentFrequencyNthDayTypeOptions:'id': 'name'}} + {{loansAccount.repaymentFrequencyDayOfWeekType | find: loansAccountProductTemplate.repaymentFrequencyDaysOfWeekTypeOptions:'id': 'name'}} +
+ +
+ First repayment on + {{ loansAccount.repaymentsStartingFromDate | date }} +
+ +
+ Interest charged from + {{ loansAccount.interestChargedFromDate | date }} +
+ +
+ Nominal interest rate + {{loansAccount.interestRatePerPeriod}}  {{loansAccountProductTemplate.interestRateFrequencyType.value}} +
+ +
+ Interest method + {{loansAccount.interestType}} +
+ +
+ Is Equal Amortization + {{loansAccount.isEqualAmortization}} +
+ +
+ Amortization + {{ loansAccount.amortizationType | find:loansAccountTemplate.amortizationTypeOptions:'id':'name' }} +
+ +
+ Interest calculation period + {{ loansAccount.interestCalculationPeriodType | find:loansAccountTemplate.interestCalculationPeriodTypeOptions:'id':'name' }} +
+ +
+ Calculate interest for exact days in partial period + {{ loansAccount.allowPartialPeriodInterestCalcualtion }} +
+ +
+ Arrears tolerance + {{loansAccount.inArrearsTolerance}} {{loansAccountProductTemplate.currency.displaySymbol}} +
+ +
+ Interest free period + {{ loansAccount.graceOnInterestCharged }} +
+ +
+ Repayment strategy + {{ loansAccount.transactionProcessingStrategyId | find:loansAccountTemplate.transactionProcessingStrategyOptions:'id':'name' }} +
+ +

Moratorium

+ + + +
+ On principal payment + {{ loansAccount.graceOnPrincipalPayment }} +
+ +
+ On interest payment + {{ loansAccount.graceOnInterestPayment }} +
+ +
+ On Arrears Aging + {{ loansAccount.graceOnArrearsAgeing }} +
+ +
+ Is Topup Loan? + {{ loansAccount.isTopup }} +
+ +
+ Recalculate Interest + {{ loansAccountProductTemplate?.isInterestRecalculationEnabled? 'Yes' : 'No' }} +
+ +
+ Days in month + {{ loansAccountProductTemplate?.daysInMonthType.value }} +
+ +
+ +

Charges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + {{ charge.name + ', ' + charge.currency.displaySymbol }} + Type + {{ charge.chargeCalculationType.value }} + Amount + {{ charge.amount }} + Collected On + {{ charge.chargeTimeType.value }} + Date + + {{(charge.dueDate | date) || 'Unassigned'}} + + + {{(charge.feeOnMonthDay | date) || 'Unassigned'}} + + + N/A + +
+ +
+ +
+ +

Overdue Charges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name {{ charge.name }},{{ charge.currency.displaySymbol }} Type {{ charge.chargeCalculationType.value }} Amount {{ charge.amount| number }} Collected On {{ charge.chargeTimeType.value }}
+
+ +
+ +
+ + + +
diff --git a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.scss b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.scss new file mode 100644 index 000000000..87258dacb --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.scss @@ -0,0 +1,24 @@ +table { + width: 100%; +} + +.mat-elevation-z1 { + margin: 1em 0 1.5em 0; +} + +h2, h3, h4 { + margin: 0; + font-weight: 500; +} + +span { + margin: 0.5em 0; +} + +mat-divider { + margin: 0 0 0.5em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.spec.ts b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.spec.ts new file mode 100644 index 000000000..be0f2bc28 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoansAccountPreviewStepComponent } from './loans-account-preview-step.component'; + +describe('LoansAccountPreviewStepComponent', () => { + let component: LoansAccountPreviewStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoansAccountPreviewStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoansAccountPreviewStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.ts new file mode 100644 index 000000000..a2a23a639 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component.ts @@ -0,0 +1,32 @@ +/** Angular Imports */ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; + +/** + * Create Loans Account Preview Step + */ +@Component({ + selector: 'mifosx-loans-account-preview-step', + templateUrl: './loans-account-preview-step.component.html', + styleUrls: ['./loans-account-preview-step.component.scss'] +}) +export class LoansAccountPreviewStepComponent implements OnInit { + + /** Loans Account Template */ + @Input() loansAccountTemplate: any; + /** Loans Account Product Template */ + @Input() loansAccountProductTemplate: any; + /** Loans Account Data */ + @Input() loansAccount: any; + /** Submit Loans Account */ + @Output() submit = new EventEmitter(); + + /** Charges Displayed Columns */ + chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'date']; + /** Overdue Charges Displayed Columns */ + overdueChargesDisplayedColumns: string[] = ['name', 'type', 'amount', 'collectedon']; + + constructor() { } + + ngOnInit() { } + +} diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html new file mode 100644 index 000000000..eb7533088 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.html @@ -0,0 +1,350 @@ +
+ +
+ + + Principal {{loansAccountProductTemplate?.currency.displaySymbol}} + + + Principal is required + + + +

Term Options

+ + + Loan Term + + + Loan Term is required + + + + + Frequency + + + {{ type.value }} + + + + Frequency is required + + + + + Number of repayments + + + Number of repayments is required + + + + + First repayment on + + + + + + + Repaid every + + + Repaid every is required + + + + + Frequency + + + {{ repaymentFrequencyType.value }} + + + + + + Select On + + + {{ repaymentFrequencyNthDayType.value }} + + + + + + Select Day + + + {{ repaymentFrequencyDayOfWeekType.value }} + + + + + + Interest charged from + + + + + + + + + Nominal interest rate + + + + + Interest method + + + {{ interestType.value }} + + + + + +

Is Equal Amortization

+
+ +
+ + + + + +
+ + + Interest Method + + + {{ interestType.value }} + + + + + +

Is Floating Rate?

+
+ +
+ +
+ + + Amortization + + + {{ amortizationType.value }} + + + + +

Interest Calculations

+ + + Interest calculation period + + + {{ interestCalculationPeriodType.value }} + + + + + + +

Calculate interest for exact days in partial period

+
+ + + Arrears tolerance {{loansAccountProductTemplate?.currency.displaySymbol}} + + + + + Interest free period + + + + + Repayment strategy + + + {{ transactionProcessingStrategy.value }} + + + + +

Moratorium

+ + + On Principal Payment + + + + On Interest Payment + + + + On Arreas Aging + + + + Installment Amount + + + + + + +

Is Topup Loan?

+
+ + + Loan closed with Topup + + + {{ clientActiveLoan.accountNo }} + + + + +
+ + + +
+ Recalculate Interest + {{ loansAccountProductTemplate?.isInterestRecalculationEnabled? 'Yes' : 'No' }} +
+ +
+ Days in year + {{ loansAccountProductTemplate?.daysInYearType.value}} +
+ + + +
+ Advance payments adjustment type + {{ loansAccountProductTemplate?.interestRecalculationData.rescheduleStrategyType.value }} +
+ +
+ Days in month + {{ loansAccountProductTemplate?.daysInMonthType.value }} +
+ +
+ + + +
+ Interest recalculation compounding on + {{ loansAccountProductTemplate?.interestRecalculationData.interestRecalculationCompoundingType.value }} +
+ +
+ Frequency Interval for recalculation + + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyType.value}} + + on {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyWeekday.value}} + on day + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyOnDay}} + on + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyNthDay.value}} + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyWeekday.value}} + +
+ +
+ +
+ Frequency Interval for recalculation + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationRestFrequencyInterval}} +
+ + + +
+ Frequency for compounding + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyType.value}} + + on {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyWeekday.value}} + + on day + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyOnDay}} + + on + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyNthDay.value}} + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyWeekday.value}} + + +
+ +
+ +
+ Frequency Interval for compounding + {{ loansAccountProductTemplate?.interestRecalculationData.recalculationCompoundingFrequencyInterval}} +
+ +
+
+ + +
+ +
diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.scss b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.scss new file mode 100644 index 000000000..17e77f81d --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.scss @@ -0,0 +1,24 @@ +h4 { + font-weight: 500; + margin: 1em 0; +} + +h3 { + font-weight: 500; +} + +mat-divider { + margin: 1em 0 2em 0; +} + +.margin-v { + margin: 1em 0; +} + +.margin-b { + margin: 0 0 1em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.spec.ts b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.spec.ts new file mode 100644 index 000000000..8bbbb2bb6 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoansAccountTermsStepComponent } from './loans-account-terms-step.component'; + +describe('LoansAccountTermsStepComponent', () => { + let component: LoansAccountTermsStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoansAccountTermsStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoansAccountTermsStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts new file mode 100644 index 000000000..75337e5d4 --- /dev/null +++ b/src/app/loans/loans-account-stepper/loans-account-terms-step/loans-account-terms-step.component.ts @@ -0,0 +1,142 @@ +/** Angular Imports */ +import { Component, OnInit, Input, OnChanges } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; + +/** + * Create Loans Account Terms Step + */ +@Component({ + selector: 'mifosx-loans-account-terms-step', + templateUrl: './loans-account-terms-step.component.html', + styleUrls: ['./loans-account-terms-step.component.scss'] +}) +export class LoansAccountTermsStepComponent implements OnInit, OnChanges { + + /** Loans Account Product Template */ + @Input() loansAccountProductTemplate: any; + + /** Minimum date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum date allowed. */ + maxDate = new Date(); + /** Loans Account Terms Form */ + loansAccountTermsForm: FormGroup; + /** 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; + /** Transaction Processing Strategy Data */ + transactionProcessingStrategyData: any; + /** Client Active Loan Data */ + clientActiveLoanData: any; + + /** + * Create Loans Account Terms Form + * @param formBuilder FormBuilder + * @param route Route + * @param router Router + */ + constructor(private formBuilder: FormBuilder, + private route: ActivatedRoute, + private router: Router) { + this.createloansAccountTermsForm(); + } + /** + * Executes on change of input values + */ + ngOnChanges() { + if (this.loansAccountProductTemplate) { + this.loansAccountTermsForm.patchValue({ + 'principal': this.loansAccountProductTemplate.principal, + 'loanTermFrequency': this.loansAccountProductTemplate.termFrequency, + 'loanTermFrequencyType': this.loansAccountProductTemplate.termPeriodFrequencyType.id, + 'numberOfRepayments': this.loansAccountProductTemplate.numberOfRepayments, + 'repaymentEvery': this.loansAccountProductTemplate.repaymentEvery, + 'repaymentFrequencyType': this.loansAccountProductTemplate.repaymentFrequencyType.id, + 'interestRatePerPeriod': this.loansAccountProductTemplate.interestRatePerPeriod, + 'amortizationType': this.loansAccountProductTemplate.amortizationType.id, + 'isEqualAmortization': this.loansAccountProductTemplate.isEqualAmortization, + 'interestType': this.loansAccountProductTemplate.interestType.id, + 'isFloatingInterestRate': this.loansAccountProductTemplate.isLoanProductLinkedToFloatingRate ? false : '', + 'interestCalculationPeriodType': this.loansAccountProductTemplate.interestCalculationPeriodType.id, + 'allowPartialPeriodInterestCalcualtion': this.loansAccountProductTemplate.allowPartialPeriodInterestCalcualtion, + 'inArrearsTolerance': this.loansAccountProductTemplate.inArrearsTolerance, + 'graceOnPrincipalPayment': this.loansAccountProductTemplate.graceOnPrincipalPayment, + 'graceOnInterestPayment': this.loansAccountProductTemplate.graceOnInterestPayment, + 'graceOnArrearsAgeing': this.loansAccountProductTemplate.graceOnArrearsAgeing, + 'transactionProcessingStrategyId': this.loansAccountProductTemplate.transactionProcessingStrategyId, + 'graceOnInterestCharged': this.loansAccountProductTemplate.graceOnInterestCharged, + 'fixedEmiAmount': this.loansAccountProductTemplate.fixedEmiAmount, + 'maxOutstandingLoanBalance': this.loansAccountProductTemplate.maxOutstandingLoanBalance + }); + this.setOptions(); + } + } + + ngOnInit() { } + + /** Create Loans Account Terms Form */ + createloansAccountTermsForm() { + this.loansAccountTermsForm = this.formBuilder.group({ + 'principal': ['', Validators.required], + 'loanTermFrequency': ['', Validators.required], + 'loanTermFrequencyType': ['', Validators.required], + 'numberOfRepayments': ['', Validators.required], + 'repaymentEvery': ['', Validators.required], + 'repaymentFrequencyType': ['', Validators.required], + 'repaymentFrequencyNthDayType': ['', Validators.required], + 'repaymentFrequencyDayOfWeekType': ['', Validators.required], + 'repaymentsStartingFromDate': [''], + 'interestChargedFromDate': [''], + 'interestRatePerPeriod': [''], + 'interestType': [''], + // 'interestRateDifferential': [''], + 'isFloatingInterestRate': [''], + 'isEqualAmortization': [''], + 'amortizationType': [''], + 'interestCalculationPeriodType': [''], + 'allowPartialPeriodInterestCalcualtion': [''], + 'inArrearsTolerance': [''], + 'graceOnInterestCharged': [''], + 'transactionProcessingStrategyId': [''], + 'graceOnPrincipalPayment': [''], + 'graceOnInterestPayment': [''], + 'graceOnArrearsAgeing': [''], + 'loanIdToClose': [''], + 'fixedEmiAmount': [''], + 'isTopup': [''], + 'maxOutstandingLoanBalance': [''] + }); + } + + /** + * 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.transactionProcessingStrategyData = this.loansAccountProductTemplate.transactionProcessingStrategyOptions; + this.clientActiveLoanData = this.loansAccountProductTemplate.clientActiveLoanOptions; + } + + /** + * Returns loans account terms form value. + */ + get loansAccountTerms() { + return this.loansAccountTermsForm.value; + } + +} diff --git a/src/app/loans/loans-routing.module.ts b/src/app/loans/loans-routing.module.ts index 9bb3150a7..3e3cdea36 100644 --- a/src/app/loans/loans-routing.module.ts +++ b/src/app/loans/loans-routing.module.ts @@ -18,6 +18,10 @@ import { OverdueChargesTabComponent } from './loans-view/overdue-charges-tab/ove import { ChargesTabComponent } from './loans-view/charges-tab/charges-tab.component'; import { DatatableTabComponent } from './loans-view/datatable-tab/datatable-tab.component'; import { LoanAccountActionsComponent } from './loans-view/loan-account-actions/loan-account-actions.component'; +import { FloatingInterestRatesComponent } from './loans-view/floating-interest-rates/floating-interest-rates.component'; +import { LoanTrancheDetailsComponent } from './loans-view/loan-tranche-details/loan-tranche-details.component'; +import { LoanCollateralTabComponent } from './loans-view/loan-collateral-tab/loan-collateral-tab.component'; +import { CreateLoansAccountComponent } from './create-loans-account/create-loans-account.component'; /** Custom Resolvers */ import { LoanChargeTemplateResolver } from './common-resolvers/loan-charge-template.resolver'; @@ -28,15 +32,22 @@ import { LoanDetailsChargesResolver } from './common-resolvers/loan-details-char import { LoanDatatablesResolver } from './common-resolvers/loan-datatables.resolver'; import { LoanDatatableResolver } from './common-resolvers/loan-datatable.resolver'; import { LoanActionButtonResolver } from './common-resolvers/loan-action-button.resolver'; -import { FloatingInterestRatesComponent } from './loans-view/floating-interest-rates/floating-interest-rates.component'; -import { LoanTrancheDetailsComponent } from './loans-view/loan-tranche-details/loan-tranche-details.component'; -import { LoanCollateralTabComponent } from './loans-view/loan-collateral-tab/loan-collateral-tab.component'; +import { LoansAccountTemplateResolver } from './common-resolvers/loans-account-template.resolver'; const routes: Routes = [ { path: '', // Component to View All existing Loans Comes Here - children: [{ + children: [ + { + path: 'create-loans-account', + data: { title: extract('Create Loans Account'), breadcrumb: 'Create Loans Account' }, + component: CreateLoansAccountComponent, + resolve: { + loansAccountTemplate: LoansAccountTemplateResolver + } + }, + { path: ':loanId', data: { title: extract('Loan View'), routeParamBreadcrumb: 'loanId' }, component: LoansViewComponent, @@ -177,7 +188,8 @@ const routes: Routes = [ LoanDetailsChargesResolver, LoanDatatablesResolver, LoanDatatableResolver, - LoanActionButtonResolver + LoanActionButtonResolver, + LoansAccountTemplateResolver ] }) diff --git a/src/app/loans/loans.module.ts b/src/app/loans/loans.module.ts index 45691eacf..05517868b 100644 --- a/src/app/loans/loans.module.ts +++ b/src/app/loans/loans.module.ts @@ -36,6 +36,12 @@ import { LoanTrancheDetailsComponent } from './loans-view/loan-tranche-details/l import { CloseAsRescheduledComponent } from './loans-view/loan-account-actions/close-as-rescheduled/close-as-rescheduled.component'; import { LoanRescheduleComponent } from './loans-view/loan-account-actions/loan-reschedule/loan-reschedule.component'; import { LoanCollateralTabComponent } from './loans-view/loan-collateral-tab/loan-collateral-tab.component'; +import { CreateLoansAccountComponent } from './create-loans-account/create-loans-account.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 { LoansAccountPreviewStepComponent } from './loans-account-stepper/loans-account-preview-step/loans-account-preview-step.component'; +import { LoansAccountAddCollateralDialogComponent } from './loans-account-add-collateral-dialog/loans-account-add-collateral-dialog.component'; /** * Loans Module @@ -72,6 +78,15 @@ import { LoanCollateralTabComponent } from './loans-view/loan-collateral-tab/loa CloseAsRescheduledComponent, LoanRescheduleComponent, LoanCollateralTabComponent, + CreateLoansAccountComponent, + LoansAccountDetailsStepComponent, + LoansAccountTermsStepComponent, + LoansAccountChargesStepComponent, + LoansAccountPreviewStepComponent, + LoansAccountAddCollateralDialogComponent, + ], + entryComponents: [ + LoansAccountAddCollateralDialogComponent ], providers: [DatePipe], }) diff --git a/src/app/loans/loans.service.ts b/src/app/loans/loans.service.ts index 8f4d2983d..ea8e94268 100644 --- a/src/app/loans/loans.service.ts +++ b/src/app/loans/loans.service.ts @@ -164,7 +164,7 @@ export class LoansService { return this.http.delete(`/datatables/${datatableName}/${loanId}`, { params: httpParams }); } - /* + /** * @param {string} loanId Loan Id. * @param {any} data Data. * @returns {Observable} @@ -174,7 +174,11 @@ export class LoansService { return this.http.post(`/loans/${loanId}`, data, {params: httpParams}); } - getForeclosureData(loanId: any, foreclosuredata: any) { + /** + * @param {string|number} loanId Loan Id. + * @param {any} foreclosuredata ForeClosure Data + */ + getForeclosureData(loanId: string|number, foreclosuredata: any) { const httpParams = new HttpParams().set('command', foreclosuredata.command) .set('dateFormat', foreclosuredata.dateFormat) .set('locale', foreclosuredata.locale) @@ -183,19 +187,62 @@ export class LoansService { } + /** + * @param {string|number} loanId Loan Id + * @param {any} data Data + */ loanForclosureData(loanId: any, data: any) { const httpParams = new HttpParams().set('command', 'foreclosure'); return this.http.post(`/loans/${loanId}/transactions`, data, {params: httpParams}); } + /** + * Returns the Reschedule Loans Template + */ rescheduleLoanTemplate() { return this.http.get('/rescheduleloans/template'); } + /** + * Submits Reschedule Data + * @param {any} data Data + */ submitRescheduleData(data: any) { - // https://staging.mifos.io/fineract-provider/api/v1/rescheduleloans?command=reschedule const httpParams = new HttpParams().set('command', 'reschedule'); return this.http.post('/rescheduleloans', data, {params: httpParams}); } + /** + * Gets Loan Account Template + * @param {any} clientId Client ID + * @param {any} productId Product ID + */ + getLoansAccountTemplateResource(clientId: any, productId?: any): Observable { + let httpParams = new HttpParams().set('activeOnly', 'true') + .set('clientId', clientId) + .set('staffInSelectedOfficeOnly', 'true') + .set('templateType', 'individual'); + httpParams = productId ? httpParams.set('productId', productId) : httpParams; + return this.http.get('/loans/template', { params: httpParams }); + } + + /** + * Get Loans Collateral Template + * @param {any} productId Product ID + */ + getLoansCollateralTemplateResource(productId: any): Observable { + const httpParams = new HttpParams().set('fields', 'id, loanCollateralOptions') + .set('productId', productId) + .set('templateType', 'collateral'); + return this.http.get('/loans/template', { params: httpParams }); + } + + /** + * Creates Loans Account + * @param {any} loanAccount Loan Account + */ + createLoansAccount(loanAccount: any): Observable { + return this.http.post('/loans', loanAccount); + } + }