diff --git a/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.html b/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.html
index c590803e1..cbb2f9216 100644
--- a/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.html
+++ b/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.html
@@ -290,6 +290,11 @@
Repay Every:
{{ loanProduct.repaymentEvery + ' ' + loanProduct.repaymentFrequencyType.value }}
+
+
+ Fixed Length:
+ {{ loanProduct.fixedLength + ' ' + loanProduct.repaymentFrequencyType.value }}
+
Minimum days between disbursal and first repayment date:
diff --git a/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.ts b/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.ts
index e2c97beed..8dbaf0472 100644
--- a/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.ts
+++ b/src/app/products/loan-products/common/loan-product-summary/loan-product-summary.component.ts
@@ -154,6 +154,11 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges {
optionValue = this.optionDataLookUp(this.loanProduct.interestCalculationPeriodType, this.loanProductsTemplate.interestCalculationPeriodTypeOptions);
this.loanProduct.interestCalculationPeriodType = optionValue;
+ if (!this.loanProduct.repaymentFrequencyType || !this.loanProduct.repaymentFrequencyType.value) {
+ optionValue = this.optionDataLookUp(this.loanProduct.repaymentFrequencyType, this.loanProductsTemplate.repaymentFrequencyTypeOptions);
+ this.loanProduct.repaymentFrequencyType = optionValue;
+ }
+
optionValue = this.optionDataLookUp(this.loanProduct.daysInMonthType, this.loanProductsTemplate.daysInMonthTypeOptions);
this.loanProduct.daysInMonthType = optionValue;
optionValue = this.optionDataLookUp(this.loanProduct.daysInYearType, this.loanProductsTemplate.daysInYearTypeOptions);
diff --git a/src/app/products/loan-products/create-loan-product/create-loan-product.component.html b/src/app/products/loan-products/create-loan-product/create-loan-product.component.html
index b278329bc..f1d0cc6b0 100644
--- a/src/app/products/loan-products/create-loan-product/create-loan-product.component.html
+++ b/src/app/products/loan-products/create-loan-product/create-loan-product.component.html
@@ -38,14 +38,6 @@
-
-
- TERMS
-
-
-
-
-
SETTINGS
@@ -79,6 +71,14 @@
+
+
+ TERMS
+
+
+
+
+
CHARGES
diff --git a/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.html b/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.html
index c4f18073b..6073b91fd 100644
--- a/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.html
+++ b/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.html
@@ -38,14 +38,6 @@
-
-
- TERMS
-
-
-
-
-
SETTINGS
@@ -79,6 +71,14 @@
+
+
+ TERMS
+
+
+
+
+
CHARGES
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts
index 22b74676d..21086bb5d 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.ts
@@ -4,6 +4,7 @@ import { LoanProducts } from '../../loan-products';
import { rangeValidator } from 'app/shared/validators/percentage.validator';
import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model';
import { CodeName, OptionData } from 'app/shared/models/option-data.model';
+import { ProcessingStrategyService } from '../../services/processing-strategy.service';
@Component({
@@ -46,7 +47,8 @@ export class LoanProductSettingsStepComponent implements OnInit {
/** Values to Days for Repayments */
defaultConfigValues: GlobalConfiguration[] = [];
- constructor(private formBuilder: UntypedFormBuilder) {
+ constructor(private formBuilder: UntypedFormBuilder,
+ private processingStrategyService: ProcessingStrategyService) {
this.createLoanProductSettingsForm();
this.setConditionalControls();
}
@@ -117,11 +119,8 @@ export class LoanProductSettingsStepComponent implements OnInit {
});
this.isAdvancedTransactionProcessingStrategy = LoanProducts.isAdvancedPaymentAllocationStrategy(transactionProcessingStrategyCode);
- if (this.isAdvancedTransactionProcessingStrategy) {
- this.loanProductSettingsForm.addControl('loanScheduleProcessingType', new UntypedFormControl(
- this.loanProductsTemplate.loanScheduleProcessingType.code || LoanProducts.LOAN_SCHEDULE_PROCESSING_TYPE_HORIZONTAL,
- [Validators.required ]));
- }
+ this.processingStrategyService.initialize(this.isAdvancedTransactionProcessingStrategy);
+ this.validateAdvancedPaymentStrategyControls();
if (this.loanProductsTemplate.dueDaysForRepaymentEvent != null &&
this.loanProductsTemplate.overDueDaysForRepaymentEvent != null) {
@@ -405,12 +404,8 @@ export class LoanProductSettingsStepComponent implements OnInit {
.subscribe((transactionProcessingStrategyCode: string) => {
this.advancePaymentStrategy.emit(transactionProcessingStrategyCode);
this.isAdvancedTransactionProcessingStrategy = LoanProducts.isAdvancedPaymentAllocationStrategy(transactionProcessingStrategyCode);
- if (this.isAdvancedTransactionProcessingStrategy) {
- this.loanProductSettingsForm.addControl('loanScheduleProcessingType', new UntypedFormControl(
- this.loanProductsTemplate.loanScheduleProcessingType.code || LoanProducts.LOAN_SCHEDULE_PROCESSING_TYPE_HORIZONTAL, [Validators.required]));
- } else {
- this.loanProductSettingsForm.removeControl('loanScheduleProcessingType');
- }
+ this.processingStrategyService.initialize(this.isAdvancedTransactionProcessingStrategy);
+ this.validateAdvancedPaymentStrategyControls();
});
this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges
@@ -484,6 +479,7 @@ export class LoanProductSettingsStepComponent implements OnInit {
});
this.isAdvancedTransactionProcessingStrategy = true;
}
+ this.processingStrategyService.initialize(this.isAdvancedTransactionProcessingStrategy);
});
}
@@ -519,4 +515,12 @@ export class LoanProductSettingsStepComponent implements OnInit {
return productSettings;
}
+ private validateAdvancedPaymentStrategyControls(): void {
+ if (this.isAdvancedTransactionProcessingStrategy) {
+ this.loanProductSettingsForm.addControl('loanScheduleProcessingType', new UntypedFormControl(
+ this.loanProductsTemplate.loanScheduleProcessingType.code || LoanProducts.LOAN_SCHEDULE_PROCESSING_TYPE_HORIZONTAL, [Validators.required]));
+ } else {
+ this.loanProductSettingsForm.removeControl('loanScheduleProcessingType');
+ }
+ }
}
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html
index b078429c9..32732a771 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.html
@@ -87,7 +87,11 @@
Interest Rates
-
+
+ Is Zero Interest?
+
+
+
Is Linked to floating interest rates?
@@ -97,7 +101,7 @@
Minimum
-
+
Minimum Value must be greater than 0
@@ -105,7 +109,7 @@
Default
-
+
Default nominal interest rate is required
@@ -113,7 +117,7 @@
Maximum
-
+
Maximum Value must be greater equal to than 0 and must be greater than Minimum Principal
@@ -121,7 +125,7 @@
Frequency
-
+
{{ interestRateFrequencyType.value }}
@@ -133,7 +137,7 @@
-
+
Floating interest rate
@@ -390,9 +394,9 @@
- Repaid every
+ Repaid every
-
+
Frequency
@@ -400,7 +404,7 @@
-
+
Type
@@ -412,6 +416,15 @@
+ Fixed Length
+
+
+ Fixed Length
+
+
+ {{ loanProductTermsForm.value.repaymentFrequencyType | find:repaymentFrequencyTypeData:'id':'value' }}
+
Minimum days between disbursal and first repayment date
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.scss
index 8381fd426..a7c4a3409 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.scss
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.scss
@@ -21,3 +21,7 @@ mat-divider {
.margin-t {
margin-top: 1em;
}
+
+.label-field {
+ margin-top: 32px;
+}
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.ts
index 91778c3ae..4a31e2d7b 100644
--- a/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.ts
+++ b/src/app/products/loan-products/loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormArray, UntypedFormControl } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { TooltipPosition } from '@angular/material/tooltip';
@@ -9,18 +9,23 @@ import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.co
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
+import { LoanProducts } from '../../loan-products';
+import { ProcessingStrategyService } from '../../services/processing-strategy.service';
@Component({
selector: 'mifosx-loan-product-terms-step',
templateUrl: './loan-product-terms-step.component.html',
styleUrls: ['./loan-product-terms-step.component.scss']
})
-export class LoanProductTermsStepComponent implements OnInit {
+export class LoanProductTermsStepComponent implements OnInit, OnChanges {
@Input() loanProductsTemplate: any;
loanProductTermsForm: UntypedFormGroup;
+ /** Zero Interest control. */
+ zeroInterest = new UntypedFormControl(false);
+
valueConditionTypeData: any;
floatingRateData: any;
interestRateFrequencyTypeData: any;
@@ -29,9 +34,11 @@ export class LoanProductTermsStepComponent implements OnInit {
repaymentStartDateTypeOptions: any;
displayedColumns: string[] = ['valueConditionType', 'borrowerCycleNumber', 'minValue', 'defaultValue', 'maxValue', 'actions'];
+ isAdvancedTransactionProcessingStrategy = false;
constructor(private formBuilder: UntypedFormBuilder,
- public dialog: MatDialog) {
+ private processingStrategyService: ProcessingStrategyService,
+ private dialog: MatDialog) {
this.createLoanProductTermsForm();
this.setConditionalControls();
}
@@ -83,6 +90,13 @@ export class LoanProductTermsStepComponent implements OnInit {
this.formBuilder.array(this.loanProductsTemplate.numberOfRepaymentVariationsForBorrowerCycle.map((variation: any) => ({ ...variation, valueConditionType: variation.valueConditionType.id }))));
this.loanProductTermsForm.setControl('interestRateVariationsForBorrowerCycle',
this.formBuilder.array(this.loanProductsTemplate.interestRateVariationsForBorrowerCycle.map((variation: any) => ({ ...variation, valueConditionType: variation.valueConditionType.id }))));
+
+ this.zeroInterest.patchValue((this.loanProductsTemplate.minInterestRatePerPeriod === 0 && this.loanProductsTemplate.interestRatePerPeriod === 0 && this.loanProductsTemplate.maxInterestRatePerPeriod === 0));
+
+ this.processingStrategyService.advancedTransactionProcessingStrategy.subscribe((value: boolean) => {
+ this.isAdvancedTransactionProcessingStrategy = value;
+ });
+ this.validateAdvancedPaymentStrategyControls();
}
createLoanProductTermsForm() {
@@ -103,10 +117,15 @@ export class LoanProductTermsStepComponent implements OnInit {
'repaymentEvery': ['', Validators.required],
'repaymentFrequencyType': ['', Validators.required],
'minimumDaysBetweenDisbursalAndFirstRepayment': [''],
- 'repaymentStartDateType': [1]
+ 'repaymentStartDateType': [1],
+ 'fixedLength': [null]
});
}
+ ngOnChanges(changes: SimpleChanges): void {
+ this.validateAdvancedPaymentStrategyControls();
+ }
+
setConditionalControls() {
this.loanProductTermsForm.get('allowApprovedDisbursedAmountsOverApplied').valueChanges
.subscribe(allowApprovedDisbursedAmountsOverApplied => {
@@ -149,18 +168,37 @@ export class LoanProductTermsStepComponent implements OnInit {
}
});
- this.loanProductTermsForm.get('useBorrowerCycle').valueChanges
- .subscribe(useBorrowerCycle => {
- if (useBorrowerCycle) {
- this.loanProductTermsForm.addControl('principalVariationsForBorrowerCycle', this.formBuilder.array([]));
- this.loanProductTermsForm.addControl('numberOfRepaymentVariationsForBorrowerCycle', this.formBuilder.array([]));
- this.loanProductTermsForm.addControl('interestRateVariationsForBorrowerCycle', this.formBuilder.array([]));
- } else {
- this.loanProductTermsForm.removeControl('principalVariationsForBorrowerCycle');
- this.loanProductTermsForm.removeControl('numberOfRepaymentVariationsForBorrowerCycle');
- this.loanProductTermsForm.removeControl('interestRateVariationsForBorrowerCycle');
- }
- });
+ this.loanProductTermsForm.get('useBorrowerCycle').valueChanges
+ .subscribe(useBorrowerCycle => {
+ if (useBorrowerCycle) {
+ this.loanProductTermsForm.addControl('principalVariationsForBorrowerCycle', this.formBuilder.array([]));
+ this.loanProductTermsForm.addControl('numberOfRepaymentVariationsForBorrowerCycle', this.formBuilder.array([]));
+ this.loanProductTermsForm.addControl('interestRateVariationsForBorrowerCycle', this.formBuilder.array([]));
+ } else {
+ this.loanProductTermsForm.removeControl('principalVariationsForBorrowerCycle');
+ this.loanProductTermsForm.removeControl('numberOfRepaymentVariationsForBorrowerCycle');
+ this.loanProductTermsForm.removeControl('interestRateVariationsForBorrowerCycle');
+ }
+ });
+
+ this.zeroInterest.valueChanges.subscribe(zeroInterest => {
+ if (zeroInterest) {
+ this.loanProductTermsForm.get('minInterestRatePerPeriod').patchValue(0);
+ this.loanProductTermsForm.get('minInterestRatePerPeriod').disable();
+ this.loanProductTermsForm.get('interestRatePerPeriod').patchValue(0);
+ this.loanProductTermsForm.get('interestRatePerPeriod').disable();
+ this.loanProductTermsForm.get('maxInterestRatePerPeriod').patchValue(0);
+ this.loanProductTermsForm.get('maxInterestRatePerPeriod').disable();
+ } else {
+ this.loanProductTermsForm.get('minInterestRatePerPeriod').patchValue(this.loanProductsTemplate.minInterestRatePerPeriod);
+ this.loanProductTermsForm.get('minInterestRatePerPeriod').enable();
+ this.loanProductTermsForm.get('interestRatePerPeriod').patchValue(this.loanProductsTemplate.interestRatePerPeriod);
+ this.loanProductTermsForm.get('interestRatePerPeriod').enable();
+ this.loanProductTermsForm.get('maxInterestRatePerPeriod').patchValue(this.loanProductsTemplate.maxInterestRatePerPeriod);
+ this.loanProductTermsForm.get('maxInterestRatePerPeriod').enable();
+ }
+ this.validateAdvancedPaymentStrategyControls();
+ });
}
get principalVariationsForBorrowerCycle(): UntypedFormArray {
@@ -271,4 +309,20 @@ export class LoanProductTermsStepComponent implements OnInit {
return this.loanProductTermsForm.value;
}
+ isZeroInterest(): boolean {
+ return this.zeroInterest.value;
+ }
+
+ allowFixedLength(): boolean {
+ return this.isAdvancedTransactionProcessingStrategy && this.isZeroInterest();
+ }
+
+ private validateAdvancedPaymentStrategyControls(): void {
+ if (this.allowFixedLength()) {
+ this.loanProductTermsForm.get('fixedLength').patchValue(this.loanProductsTemplate.fixedLength || null);
+ } else {
+ this.loanProductTermsForm.get('fixedLength').patchValue(null);
+ }
+ }
+
}
diff --git a/src/app/products/loan-products/services/processing-strategy.service.ts b/src/app/products/loan-products/services/processing-strategy.service.ts
new file mode 100644
index 000000000..197545022
--- /dev/null
+++ b/src/app/products/loan-products/services/processing-strategy.service.ts
@@ -0,0 +1,19 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ProcessingStrategyService {
+
+ advancedTransactionProcessingStrategy = new BehaviorSubject(false);
+
+ constructor() { }
+
+ initialize(value: boolean) {
+ this.advancedTransactionProcessingStrategy.next(value);
+ }
+
+ get isAdvancedTransactionProcessingStrategy(): boolean { return this.advancedTransactionProcessingStrategy.value; }
+
+}