From 636ccb74b051b6acdf397a0b4f17acae69b0ea0b Mon Sep 17 00:00:00 2001 From: Abhay Chawla Date: Sun, 14 Jul 2019 17:52:24 +0530 Subject: [PATCH] feat: view loan product component (#442) Fixes: #438 --- .../loan-products/loan-product.resolver.ts | 31 + .../loan-products.component.html | 2 +- .../loan-products.component.scss | 4 + .../view-loan-product.component.html | 736 ++++++++++++++++++ .../view-loan-product.component.scss | 24 + .../view-loan-product.component.spec.ts | 25 + .../view-loan-product.component.ts | 28 + src/app/products/products-routing.module.ts | 11 + src/app/products/products.module.ts | 4 +- src/app/products/products.service.ts | 4 + 10 files changed, 867 insertions(+), 2 deletions(-) create mode 100644 src/app/products/loan-products/loan-product.resolver.ts create mode 100644 src/app/products/loan-products/view-loan-product/view-loan-product.component.html create mode 100644 src/app/products/loan-products/view-loan-product/view-loan-product.component.scss create mode 100644 src/app/products/loan-products/view-loan-product/view-loan-product.component.spec.ts create mode 100644 src/app/products/loan-products/view-loan-product/view-loan-product.component.ts diff --git a/src/app/products/loan-products/loan-product.resolver.ts b/src/app/products/loan-products/loan-product.resolver.ts new file mode 100644 index 000000000..7c4a5746c --- /dev/null +++ b/src/app/products/loan-products/loan-product.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { ProductsService } from '../products.service'; + +/** + * Loan Product data resolver. + */ +@Injectable() +export class LoanProductResolver implements Resolve { + + /** + * @param {ProductsService} productsService Products service. + */ + constructor(private productsService: ProductsService) {} + + /** + * Returns the loan product data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.paramMap.get('id'); + return this.productsService.getLoanProduct(id); + } + +} diff --git a/src/app/products/loan-products/loan-products.component.html b/src/app/products/loan-products/loan-products.component.html index f247c36c7..e5b98c4de 100644 --- a/src/app/products/loan-products/loan-products.component.html +++ b/src/app/products/loan-products/loan-products.component.html @@ -43,7 +43,7 @@ - + diff --git a/src/app/products/loan-products/loan-products.component.scss b/src/app/products/loan-products/loan-products.component.scss index 1922e7ffa..24edb63bb 100644 --- a/src/app/products/loan-products/loan-products.component.scss +++ b/src/app/products/loan-products/loan-products.component.scss @@ -1,3 +1,7 @@ table { width: 100%; + + .select-row:hover { + cursor: pointer; + } } diff --git a/src/app/products/loan-products/view-loan-product/view-loan-product.component.html b/src/app/products/loan-products/view-loan-product/view-loan-product.component.html new file mode 100644 index 000000000..48e4e2fcb --- /dev/null +++ b/src/app/products/loan-products/view-loan-product/view-loan-product.component.html @@ -0,0 +1,736 @@ +
+ +
+ +
+ + + + + +
+ +

{{ loanProduct.name }}

+ + + +

Details

+ + + +
+ Short Name: + {{ loanProduct.shortName }} +
+ +
+ Fund Name: + {{ loanProduct.fundName }} +
+ +
+ Include in Customer Loan Counter: + {{ loanProduct.includeInBorrowerCycle ? 'Yes' : 'No' }} +
+ +
+ Start Date: + {{ loanProduct.startDate | date }} +
+ +
+ Close Date: + {{ loanProduct.closeDate | date }} +
+ +
+ Description: + {{ loanProduct.description }} +
+ +

Currency

+ + + +
+ Currency: + {{ loanProduct.currency.name }} +
+ +
+ Decimal Places: + {{ loanProduct.currency.decimalPlaces }} +
+ +
+ Currency in multiples of: + {{ loanProduct.currency.inMultiplesOf }} +
+ +
+ Installment in multiples of: + {{ loanProduct.installmentAmountInMultiplesOf }} +
+ +

Terms

+ + + +
+ Principal: + {{ loanProduct.principal + ' (Min: ' + (loanProduct.minPrincipal ? loanProduct.minPrincipal : loanProduct.principal) + ', Max: ' + (loanProduct.maxPrincipal ? loanProduct.maxPrincipal : loanProduct.principal) + ')' }} +
+ +
+ Number of Repayments: + {{ loanProduct.numberOfRepayments + ' (Min: ' + (loanProduct.minNumberOfRepayments ? loanProduct.minNumberOfRepayments : loanProduct.numberOfRepayments) + ', Max: ' + (loanProduct.maxNumberOfRepayments ? loanProduct.maxNumberOfRepayments : loanProduct.numberOfRepayments) + ')' }} +
+ +
+ Linked to floating interest rates: + {{ loanProduct.isLinkedToFloatingInterestRates ? 'Yes' : 'No' }} +
+ +
+ Nominal interest rate: + {{ loanProduct.interestRatePerPeriod + ' (Min: ' + (loanProduct.minInterestRatePerPeriod ? loanProduct.minInterestRatePerPeriod : loanProduct.interestRatePerPeriod) + ', Max: ' + (loanProduct.maxInterestRatePerPeriod ? loanProduct.maxInterestRatePerPeriod : loanProduct.interestRatePerPeriod) + ')' + ' ' + loanProduct.interestRateFrequencyType.value }} +
+ +
+ Floating Lending Interest Rate: + {{ loanProduct.floatingRateName }} + Interest Rate Differential: + {{ loanProduct.interestRateDifferential }} + Floating Interest Rate Calculation Allowed: + {{ loanProduct.isFloatingInterestRateCalculationAllowed ? 'Yes' : 'No' }} + Floating Interest Rate: + {{ loanProduct.defaultDifferentialLendingRate + ' (Min: ' + (loanProduct.minDifferentialLendingRate ? loanProduct.minDifferentialLendingRate : loanProduct.defaultDifferentialLendingRate) + ', Max: ' + (loanProduct.maxDifferentialLendingRate ? loanProduct.maxDifferentialLendingRate : loanProduct.defaultDifferentialLendingRate) + ')' }} +
+ +
+ Terms vary based on loan cycle: + {{ loanProduct.useBorrowerCycle ? 'Yes' : 'No' }} +
+ +
+ +

Principal by loan cycle

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Condition + {{ variation.valueConditionType.value }} + Loan Cycle + {{ variation.borrowerCycleNumber }} + Minimum + {{ variation.minValue }} + Default + {{ variation.defaultValue }} + Maximum + {{ variation.maxValue }} +
+ +
+ +
+ +

Number of Repayments by loan cycle

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Condition + {{ variation.valueConditionType.value }} + Loan Cycle + {{ variation.borrowerCycleNumber }} + Minimum + {{ variation.minValue }} + Default + {{ variation.defaultValue }} + Maximum + {{ variation.maxValue }} +
+ +
+ +
+ +

Nominal Interest Rate by loan cycle

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Condition + {{ variation.valueConditionType.value }} + Loan Cycle + {{ variation.borrowerCycleNumber }} + Minimum + {{ variation.minValue }} + Default + {{ variation.defaultValue }} + Maximum + {{ variation.maxValue }} +
+ +
+ +
+ Repay Every: + {{ loanProduct.repaymentEvery + ' ' + loanProduct.repaymentFrequencyType.value }} +
+ +
+ Minimum days between disbursal and first repayment date: + {{ loanProduct.minimumDaysBetweenDisbursalAndFirstRepayment }} +
+ +

Settings

+ + + +
+ Amortization: + {{ loanProduct.amortizationType.value }} +
+ +
+ Is Equal Amortization: + {{ loanProduct.isEqualAmortization ? 'Yes' : 'No' }} +
+ +
+ Interest Method: + {{ loanProduct.interestType.value }} +
+ +
+ Interest Calculation Period: + {{ loanProduct.interestCalculationPeriodType.value }} +
+ +
+ Allow Partial Interest Calculation with same as repayment: + {{ loanProduct.allowPartialPeriodInterestCalcualtion ? 'Yes' : 'No' }} +
+ +
+ Repayment Strategy: + {{ loanProduct.transactionProcessingStrategyName }} +
+ +
+ Grace on principal payment: + {{ loanProduct.graceOnPrincipalPayment }} +
+ +
+ Grace on interest payment: + {{ loanProduct.graceOnInterestPayment }} +
+ +
+ Interest free period: + {{ loanProduct.graceOnInterestCharged }} +
+ +
+ Arrears tolerance: + {{ loanProduct.inArrearsTolerance }} +
+ +
+ Days in year: + {{ loanProduct.daysInYearType.value }} +
+ +
+ Days in month: + {{ loanProduct.daysInMonthType.value }} +
+ +
+ Allow fixing of the installment amount: + {{ loanProduct.canDefineInstallmentAmount ? 'Yes' : 'No' }} +
+ +
+ Number of days a loan may be overdue before moving into arrears: + {{ loanProduct.graceOnArrearsAgeing }} +
+ +
+ Maximum number of days a loan may be overdue before becoming a NPA (non performing asset): + {{ loanProduct.overdueDaysForNPA }} +
+ +
+ Account moves out of NPA only after all arrears have been cleared: + {{ loanProduct.accountMovesOutOfNPAOnlyOnArrearsCompletion ? 'Yes' : 'No' }} +
+ +
+ Principal Threshold (%) for Last Instalment: + {{ loanProduct.principalThresholdForLastInstallment }} +
+ +
+ Variable Installments allowed: + {{ loanProduct.allowVariableInstallments ? 'Yes' : 'No' }} +
+ +
+ Variable Installments: + {{ '(Min: ' + loanProduct.minimumGap + ', Max: ' + (loanProduct.maximumGap ? loanProduct.maximumGap : loanProduct.minimumGap) + ')' }} +
+ +
+ Allowed to be used for providing Topup Loans: + {{ loanProduct.canUseForTopup ? 'Yes' : 'No' }} +
+ +

Interest Recalculation

+ + + +
+ Recalculate Interest: + {{ loanProduct.isInterestRecalculationEnabled ? 'Yes' : 'No' }} +
+ +
+ Pre-closure interest calculation rule: + {{ loanProduct.interestRecalculationData.preClosureInterestCalculationStrategy.value }} + Advance payments adjustment type: + {{ loanProduct.interestRecalculationData.rescheduleStrategyType.value }} + Interest recalculation compounding on: + {{ loanProduct.interestRecalculationData.interestRecalculationCompoundingType.value }} +
+ Frequency for compounding: + + {{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyType.value }} +   {{ 'on ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyNthDay.value }} +   {{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyWeekday.value }} +   {{ 'on day ' + loanProduct.interestRecalculationData.recalculationCompoundingFrequencyOnDay }} + +
+ Frequency Interval for compounding: + {{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyInterval }} +
+ Compounding Frequency Date: + {{ loanProduct.interestRecalculationData.recalculationCompoundingFrequencyDate }} +
+
+
+
+ Frequency for recalculate Outstanding Principal: + + {{ loanProduct.interestRecalculationData.recalculationRestFrequencyType.value }} +   {{ 'on ' + loanProduct.interestRecalculationData.recalculationRestFrequencyNthDay.value }} +   {{ loanProduct.interestRecalculationData.recalculationRestFrequencyWeekday.value }} +   {{ 'on day ' + loanProduct.interestRecalculationData.recalculationRestFrequencyOnDay }} + +
+
+ Frequency Interval for recalculation: + {{ loanProduct.interestRecalculationData.recalculationRestFrequencyInterval }} +
+ Rest Frequency Date: + {{ loanProduct.interestRecalculationData.recalculationRestFrequencyDate }} +
+
+
+ Is Arrears recognization based on original schedule: + {{ loanProduct.isArrearsBasedOnOriginalSchedule ? 'Yes' : 'No' }} +
+
+ +

Guarantee Requirements

+ + + +
+ Place Guarantee Funds On-Hold: + {{ loanProduct.holdGuaranteeFunds ? 'Yes' : 'No' }} +
+ +
+ Mandatory Guarantee(%): + {{ loanProduct.productGuaranteeData.mandatoryGuarantee }} +
+ Minimum Guarantee from Own Funds(%): + {{ loanProduct.productGuaranteeData.minimumGuaranteeFromOwnFunds }} +
+
+ Minimum Guarantee from Guarantor Funds(%): + {{ loanProduct.productGuaranteeData.minimumGuaranteeFromGuarantor }} +
+
+ +

Loan Tranche Details

+ + + +
+ Enable Multiple Disbursals: + {{ loanProduct.multiDisburseLoan ? 'Yes' : 'No' }} +
+ +
+ Maximum Tranche count: + {{ loanProduct.maxTrancheCount }} +
+ Maximum allowed outstanding balance: + {{ loanProduct.outstandingLoanBalance }} +
+
+ +

Configurable Terms and Settings

+ + + +
+ Allow overriding select terms and settings in loan accounts: + {{ loanProduct.allowAttributeConfiguration ? 'Yes' : 'No' }} +
+ +
+ Amortization: + {{ loanProduct.allowAttributeOverrides.amortizationType ? 'Yes' : 'No' }} + Interest method: + {{ loanProduct.allowAttributeOverrides.interestType ? 'Yes' : 'No' }} + Repayment strategy: + {{ loanProduct.allowAttributeOverrides.transactionProcessingStrategyId ? 'Yes' : 'No' }} + Interest calculation period: + {{ loanProduct.allowAttributeOverrides.interestCalculationPeriodType ? 'Yes' : 'No' }} + Arrears tolerance: + {{ loanProduct.allowAttributeOverrides.inArrearsTolerance ? 'Yes' : 'No' }} + Repaid every: + {{ loanProduct.allowAttributeOverrides.repaymentEvery ? 'Yes' : 'No' }} + Moratorium: + {{ loanProduct.allowAttributeOverrides.graceOnPrincipalAndInterestPayment ? 'Yes' : 'No' }} + Number of days a loan may be overdue before moving into arrears: + {{ loanProduct.allowAttributeOverrides.graceOnArrearsAgeing ? 'Yes' : 'No' }} +
+ +
+ +

Charges

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

Overdue Charges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + {{ overdueCharge.name + ', ' + overdueCharge.currency.displaySymbol }} + Type + {{ overdueCharge.chargeCalculationType.value }} + Amount + {{ overdueCharge.amount }} + Collected On + {{ overdueCharge.chargeTimeType.value }} +
+ +
+ +

Accounting

+ + + +
+ Type: + {{ loanProduct.accountingRule.value }} +
+ +
+ +

Assets

+ +
+ Fund source: + {{ loanProduct.accountingMappings.fundSourceAccount.name }} + Loan portfolio: + {{ loanProduct.accountingMappings.loanPortfolioAccount.name }} +
+ Interest Receivable: + {{ loanProduct.accountingMappings.receivableInterestAccount.name }} + Fees Receivable: + {{ loanProduct.accountingMappings.receivableInterestAccount.name }} + Penalties Receivable: + {{ loanProduct.accountingMappings.receivablePenaltyAccount.name }} +
+ Transfer in suspense: + {{ loanProduct.accountingMappings.transfersInSuspenseAccount.name }} +
+ +

Income

+ +
+ Income from Interest: + {{ loanProduct.accountingMappings.interestOnLoanAccount.name }} + Income from fees: + {{ loanProduct.accountingMappings.incomeFromFeeAccount.name }} + Income from penalties: + {{ loanProduct.accountingMappings.incomeFromPenaltyAccount.name }} + Income from Recovery Repayments: + {{ loanProduct.accountingMappings.incomeFromRecoveryAccount.name }} +
+ +

Expenses

+ +
+ Losses written off: + {{ loanProduct.accountingMappings.writeOffAccount.name }} +
+ +

Liabilities

+ +
+ Over payment liability: + {{ loanProduct.accountingMappings.overpaymentLiabilityAccount.name }} +
+ +
+ +

Advanced Accounting Rules

+ + + +
+ +

Fund Sources for Payment Channels

+ + + + + + + + + + + + + + + + +
Payment Type + {{ paymentFundSource.paymentType.name }} + Fund Source + {{ paymentFundSource.fundSourceAccount.name }} +
+ +
+ +
+ +

Fees to Specific Income Accounts

+ + + + + + + + + + + + + + + + +
Fees + {{ feesIncome.charge.name }} + Income Account + {{ feesIncome.incomeAccount.name }} +
+ +
+ +
+ +

Penalties to Specific Income Accounts

+ + + + + + + + + + + + + + + + +
Penalty + {{ penaltyIncome.charge.name }} + Income Account + {{ penaltyIncome.incomeAccount.name }} +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/src/app/products/loan-products/view-loan-product/view-loan-product.component.scss b/src/app/products/loan-products/view-loan-product/view-loan-product.component.scss new file mode 100644 index 000000000..a9f4fce2a --- /dev/null +++ b/src/app/products/loan-products/view-loan-product/view-loan-product.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-card .mat-divider-horizontal.mat-divider-inset { + margin: 0 0 0.5em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/products/loan-products/view-loan-product/view-loan-product.component.spec.ts b/src/app/products/loan-products/view-loan-product/view-loan-product.component.spec.ts new file mode 100644 index 000000000..4337f3613 --- /dev/null +++ b/src/app/products/loan-products/view-loan-product/view-loan-product.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewLoanProductComponent } from './view-loan-product.component'; + +describe('ViewLoanProductComponent', () => { + let component: ViewLoanProductComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ViewLoanProductComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewLoanProductComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/products/loan-products/view-loan-product/view-loan-product.component.ts b/src/app/products/loan-products/view-loan-product/view-loan-product.component.ts new file mode 100644 index 000000000..63257744f --- /dev/null +++ b/src/app/products/loan-products/view-loan-product/view-loan-product.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'mifosx-view-loan-product', + templateUrl: './view-loan-product.component.html', + styleUrls: ['./view-loan-product.component.scss'] +}) +export class ViewLoanProductComponent implements OnInit { + + loanProduct: any; + + variationsDisplayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue']; + chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType']; + paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId']; + feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId']; + + constructor(private route: ActivatedRoute) { + this.route.data.subscribe((data: { loanProduct: any }) => { + this.loanProduct = data.loanProduct; + }); + } + + ngOnInit() { + this.loanProduct.allowAttributeConfiguration = Object.values(this.loanProduct.allowAttributeOverrides).some((attribute: boolean) => attribute); + } + +} diff --git a/src/app/products/products-routing.module.ts b/src/app/products/products-routing.module.ts index d89cfc913..33db8714e 100644 --- a/src/app/products/products-routing.module.ts +++ b/src/app/products/products-routing.module.ts @@ -12,6 +12,7 @@ import { extract } from '../core/i18n/i18n.service'; import { ProductsComponent } from './products.component'; import { LoanProductsComponent } from './loan-products/loan-products.component'; import { CreateLoanProductComponent } from './loan-products/create-loan-product/create-loan-product.component'; +import { ViewLoanProductComponent } from './loan-products/view-loan-product/view-loan-product.component'; import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component'; import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component'; import { ChargesComponent } from './charges/charges.component'; @@ -22,6 +23,7 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax /** Custom Resolvers */ import { LoanProductsResolver } from './loan-products/loan-products.resolver'; import { LoanProductsTemplateResolver } from './loan-products/loan-products-template.resolver'; +import { LoanProductResolver } from './loan-products/loan-product.resolver'; import { RecurringDepositProductsResolver } from './recurring-deposit-products/recurring-deposit-products.resolver'; import { ChargesResolver } from './charges/charges.resolver'; import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver'; @@ -57,6 +59,14 @@ const routes: Routes = [ resolve: { loanProductsTemplate: LoanProductsTemplateResolver } + }, + { + path: ':id', + component: ViewLoanProductComponent, + data: { title: extract('View Loan Product'), routeParamBreadcrumb: 'id' }, + resolve: { + loanProduct: LoanProductResolver + } } ] }, @@ -126,6 +136,7 @@ const routes: Routes = [ providers: [ LoanProductsResolver, LoanProductsTemplateResolver, + LoanProductResolver, RecurringDepositProductsResolver, ChargesResolver, FixedDepositProductsResolver, diff --git a/src/app/products/products.module.ts b/src/app/products/products.module.ts index 3038abffd..2136b7105 100644 --- a/src/app/products/products.module.ts +++ b/src/app/products/products.module.ts @@ -24,6 +24,7 @@ import { ChargesComponent } from './charges/charges.component'; import { FixedDepositProductsComponent } from './fixed-deposit-products/fixed-deposit-products.component'; import { ProductsMixComponent } from './products-mix/products-mix.component'; import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax-components.component'; +import { ViewLoanProductComponent } from './loan-products/view-loan-product/view-loan-product.component'; /** * Products Module @@ -52,7 +53,8 @@ import { ManageTaxComponentsComponent } from './manage-tax-components/manage-tax ChargesComponent, FixedDepositProductsComponent, ProductsMixComponent, - ManageTaxComponentsComponent + ManageTaxComponentsComponent, + ViewLoanProductComponent ], entryComponents: [ ], diff --git a/src/app/products/products.service.ts b/src/app/products/products.service.ts index 48f38a515..c2c5acbb2 100644 --- a/src/app/products/products.service.ts +++ b/src/app/products/products.service.ts @@ -33,6 +33,10 @@ export class ProductsService { return this.http.get('/loanproducts/template'); } + getLoanProduct(loanProductId: string): Observable { + return this.http.get(`/loanproducts/${loanProductId}`); + } + /** * @returns {Observable} Recurring deposit products data */