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 4cbc584c0..a6d8fe235 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 @@ -53,17 +53,19 @@ - + - PAYMENT STRATEGY + PAYMENT ALLOCATION diff --git a/src/app/products/loan-products/create-loan-product/create-loan-product.component.ts b/src/app/products/loan-products/create-loan-product/create-loan-product.component.ts index d86b1e8f7..3cb67d8e6 100644 --- a/src/app/products/loan-products/create-loan-product/create-loan-product.component.ts +++ b/src/app/products/loan-products/create-loan-product/create-loan-product.component.ts @@ -8,12 +8,12 @@ import { LoanProductCurrencyStepComponent } from '../loan-product-stepper/loan-p import { LoanProductTermsStepComponent } from '../loan-product-stepper/loan-product-terms-step/loan-product-terms-step.component'; import { LoanProductSettingsStepComponent } from '../loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component'; import { LoanProductChargesStepComponent } from '../loan-product-stepper/loan-product-charges-step/loan-product-charges-step.component'; -import { LoanProductPaymentStrategyStepComponent } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component'; import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component'; /** Custom Services */ import { ProductsService } from 'app/products/products.service'; import { LoanProducts } from '../loan-products'; +import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component'; @Component({ selector: 'mifosx-create-loan-product', @@ -26,7 +26,6 @@ export class CreateLoanProductComponent implements OnInit { @ViewChild(LoanProductCurrencyStepComponent, { static: true }) loanProductCurrencyStep: LoanProductCurrencyStepComponent; @ViewChild(LoanProductTermsStepComponent, { static: true }) loanProductTermsStep: LoanProductTermsStepComponent; @ViewChild(LoanProductSettingsStepComponent, { static: true }) loanProductSettingsStep: LoanProductSettingsStepComponent; - @ViewChild(LoanProductPaymentStrategyStepComponent, { static: true }) loanProductPaymentStrategyStep: LoanProductPaymentStrategyStepComponent; @ViewChild(LoanProductChargesStepComponent, { static: true }) loanProductChargesStep: LoanProductChargesStepComponent; @ViewChild(LoanProductAccountingStepComponent, { static: true }) loanProductAccountingStep: LoanProductAccountingStepComponent; @@ -34,7 +33,8 @@ export class CreateLoanProductComponent implements OnInit { accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)']; itemsByDefault: any[] = []; - isAdvancePaymentStrategy = false; + isAdvancedPaymentStrategy = false; + paymentAllocation: PaymentAllocation[] = []; /** * @param {ActivatedRoute} route Activated Route. @@ -75,6 +75,14 @@ export class CreateLoanProductComponent implements OnInit { return this.loanProductTermsStep.loanProductTermsForm; } + advancePaymentStrategy(value: string) { + this.isAdvancedPaymentStrategy = (value === 'advanced-payment-allocation-strategy'); + } + + setPaymentAllocation(paymentAllocation: PaymentAllocation[]) { + this.paymentAllocation = paymentAllocation; + } + get loanProductSettingsForm() { return this.loanProductSettingsStep.loanProductSettingsForm; } @@ -83,12 +91,7 @@ export class CreateLoanProductComponent implements OnInit { return this.loanProductAccountingStep.loanProductAccountingForm; } - get loanProductPaymentStrategyForm() { - return this.loanProductPaymentStrategyStep.loanProductPaymentStrategyForm; - } - get loanProductFormValid() { - this.isAdvancePaymentStrategy = (this.loanProductSettingsStep.loanProductSettings.transactionProcessingStrategyCode === 'advanced-payment-allocation-strategy'); return ( this.loanProductDetailsForm.valid && this.loanProductCurrencyForm.valid && @@ -99,7 +102,7 @@ export class CreateLoanProductComponent implements OnInit { } get loanProduct() { - return { + const loanProduct = { ...this.loanProductDetailsStep.loanProductDetails, ...this.loanProductCurrencyStep.loanProductCurrency, ...this.loanProductTermsStep.loanProductTerms, @@ -107,6 +110,10 @@ export class CreateLoanProductComponent implements OnInit { ...this.loanProductChargesStep.loanProductCharges, ...this.loanProductAccountingStep.loanProductAccounting }; + if (this.isAdvancedPaymentStrategy) { + loanProduct['paymentAllocation'] = this.paymentAllocation; + } + return loanProduct; } submit() { 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 9c1040a03..9951034bc 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 @@ -46,18 +46,32 @@ - + SETTINGS + + + PAYMENT ALLOCATION + + + + + + CHARGES diff --git a/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.ts b/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.ts index 1ec078717..d34b3eed4 100644 --- a/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.ts +++ b/src/app/products/loan-products/edit-loan-product/edit-loan-product.component.ts @@ -14,6 +14,7 @@ import { LoanProductAccountingStepComponent } from '../loan-product-stepper/loan import { ProductsService } from 'app/products/products.service'; import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model'; import { LoanProducts } from '../loan-products'; +import { PaymentAllocation } from '../loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component'; @Component({ selector: 'mifosx-edit-loan-product', @@ -33,6 +34,10 @@ export class EditLoanProductComponent implements OnInit { accountingRuleData = ['None', 'Cash', 'Accrual (periodic)', 'Accrual (upfront)']; itemsByDefault: GlobalConfiguration[] = []; + isAdvancedPaymentStrategy = false; + wasPaymentAllocationChanged = false; + paymentAllocation: PaymentAllocation[] = []; + /** * @param {ActivatedRoute} route Activated Route. * @param {ProductsService} productsService Product Service. @@ -75,6 +80,18 @@ export class EditLoanProductComponent implements OnInit { return this.loanProductSettingsStep.loanProductSettingsForm; } + advancePaymentStrategy(value: string): void { + this.isAdvancedPaymentStrategy = (value === 'advanced-payment-allocation-strategy'); + } + + setPaymentAllocation(paymentAllocation: PaymentAllocation[]): void { + this.paymentAllocation = paymentAllocation; + } + + paymentAllocationChanged(value: boolean): void { + this.wasPaymentAllocationChanged = value; + } + get loanProductAccountingForm() { return this.loanProductAccountingStep.loanProductAccountingForm; } @@ -92,13 +109,14 @@ export class EditLoanProductComponent implements OnInit { !this.loanProductTermsForm.pristine || !this.loanProductSettingsForm.pristine || !this.loanProductChargesStep.pristine || - !this.loanProductAccountingForm.pristine + !this.loanProductAccountingForm.pristine || + this.wasPaymentAllocationChanged ) ); } get loanProduct() { - return { + const loanProduct = { ...this.loanProductDetailsStep.loanProductDetails, ...this.loanProductCurrencyStep.loanProductCurrency, ...this.loanProductTermsStep.loanProductTerms, @@ -106,6 +124,10 @@ export class EditLoanProductComponent implements OnInit { ...this.loanProductChargesStep.loanProductCharges, ...this.loanProductAccountingStep.loanProductAccounting }; + if (this.isAdvancedPaymentStrategy) { + loanProduct['paymentAllocation'] = this.paymentAllocation; + } + return loanProduct; } submit() { diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.html index 18341eca4..dfc2c98ec 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.html @@ -1,43 +1,27 @@
- - Transaction Type - - - {{ transactionType }} + + Future Installment Allocation Rule + + + {{ installmentAllocation.name | translate }} -
- -
-
+ + + + + - - - - - - - - - - - + diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.ts index b28feea81..3ff8816c2 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-payment-strategy-step/loan-product-payment-strategy-step.component.ts @@ -1,10 +1,26 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { MatTable } from '@angular/material/table'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; +interface PaymentCode { + name: string; + code: string; +} + +interface PaymentAllocationOrder { + paymentAllocationRule: string; + order: number; +} + +export interface PaymentAllocation { + transactionType: string; + paymentAllocationOrder: PaymentAllocationOrder[]; + futureInstallmentAllocationRule: string; +} + @Component({ selector: 'mifosx-loan-product-payment-strategy-step', templateUrl: './loan-product-payment-strategy-step.component.html', @@ -13,61 +29,116 @@ import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.co export class LoanProductPaymentStrategyStepComponent implements OnInit { @Input() loanProductsTemplate: any; - loanProductPaymentStrategyForm: FormGroup; + @Output() paymentAllocation = new EventEmitter(); + @Output() paymentAllocationChange = new EventEmitter(); - transactionTypesData: any = []; - transactionTypeOptions: any = []; + transactionTypesData: PaymentCode[] = []; + transactionTypeOptions: PaymentCode[] = []; + installmentAllocationOptions: PaymentCode[] = []; - transactionType = new FormControl('', Validators.required); + transactionType = new FormControl(''); + futureInstallmentAllocationRule = new FormControl('', Validators.required); - stepOrderHasChanged = false; @ViewChild('table') table: MatTable; /** Columns to be displayed in the table. */ - displayedColumns: string[] = ['transactionName', 'transactionOrder', 'actions']; + displayedColumns: string[] = ['order', 'transactionName']; - constructor(private formBuilder: FormBuilder, - private dialog: MatDialog) { - this.createForm(); - } + constructor(private dialog: MatDialog) { } ngOnInit(): void { this.transactionTypesData = []; + this.setOptions(); + this.sendPaymentAllocation(); } - createForm(): void { - this.loanProductPaymentStrategyForm = this.formBuilder.group({ - 'transactionType': ['', Validators.required], - }); + setOptions(): void { + this.transactionTypeOptions = [ + { name: 'Past due penalty', code: 'PAST_DUE_PENALTY' }, + { name: 'Past due fee', code: 'PAST_DUE_FEE' }, + { name: 'Past due principal', code: 'PAST_DUE_PRINCIPAL' }, + { name: 'Past due interest', code: 'PAST_DUE_INTEREST' }, + { name: 'Due penalty', code: 'DUE_PENALTY' }, + { name: 'Due fee', code: 'DUE_FEE' }, + { name: 'Due principal', code: 'DUE_PRINCIPAL' }, + { name: 'Due interest', code: 'DUE_INTEREST' }, + { name: 'In advance penalty', code: 'IN_ADVANCE_PENALTY' }, + { name: 'In advance fee', code: 'IN_ADVANCE_FEE' }, + { name: 'In advance principal', code: 'IN_ADVANCE_PRINCIPAL' }, + { name: 'In advance interest', code: 'IN_ADVANCE_INTEREST' } + ]; + this.transactionTypesData = this.transactionTypeOptions; + + this.installmentAllocationOptions = [ + { name: 'Next Installment', code: 'NEXT_INSTALLMENT' }, + { name: 'Last Installment', code: 'LAST_INSTALLMENT' }, + { name: 'Reamortization', code: 'REAMORTIZATION' } + ]; + + this.futureInstallmentAllocationRule.patchValue('NEXT_INSTALLMENT'); } addTransactionType(): void { - console.log(this.transactionType.value); - this.transactionTypesData.append(this.transactionType.value); + this.transactionTypeOptions.forEach((txType: PaymentCode) => { + if (txType.code === this.transactionType.value) { + this.transactionTypesData = [ + ...this.transactionTypesData, + txType + ]; + this.sendPaymentAllocation(); + this.removePaymentCode(txType); + this.resetTransactionType(); + } + }); + } + + removePaymentCode(txType: PaymentCode): void { + this.transactionTypeOptions = this.transactionTypeOptions.filter(({ code }) => code !== txType.code); + } + + resetTransactionType(): void { + this.transactionType.patchValue(''); + this.transactionType.markAsUntouched(); } removeTransactionType(index: number) { + const transactionTypeName = this.transactionTypesData[index].name; const dialogRef = this.dialog.open(DeleteDialogComponent, { - data: { deleteContext: `this` } + data: { deleteContext: transactionTypeName } }); dialogRef.afterClosed().subscribe((response: any) => { if (response.delete) { this.transactionTypesData.splice(index, 1); this.transactionTypesData = this.transactionTypesData.concat([]); this.transactionTypesData = [...this.transactionTypesData]; - this.stepOrderHasChanged = true; + this.sendPaymentAllocation(); } }); } - dropTable(event: CdkDragDrop) { const prevIndex = this.transactionTypesData.findIndex((d: any) => d === event.item.data); moveItemInArray(this.transactionTypesData, prevIndex, event.currentIndex); this.transactionTypesData = [...this.transactionTypesData]; this.table.renderRows(); - this.stepOrderHasChanged = true; + this.sendPaymentAllocation(); + this.paymentAllocationChange.emit(true); } + sendPaymentAllocation(): void { + const paymentAllocationOrder: PaymentAllocationOrder[] = []; + this.transactionTypesData.forEach((txType: PaymentCode, index: number) => { + paymentAllocationOrder.push({ 'paymentAllocationRule': txType.code, order: (index + 1) }); + }); + + const paymentAllocation: PaymentAllocation[] = []; + paymentAllocation.push({ + 'futureInstallmentAllocationRule': this.futureInstallmentAllocationRule.value, + 'transactionType': 'DEFAULT', + 'paymentAllocationOrder': paymentAllocationOrder + }); + + this.paymentAllocation.emit(paymentAllocation); + } } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html index 7b480e6f1..3c8386283 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-preview-step/loan-product-preview-step.component.html @@ -328,6 +328,40 @@ find:loanProductsTemplate.transactionProcessingStrategyOptions:'code':'name' }} +
+
+
+ Transaction Type: + {{ paymentAllocation.transactionType }} +
+ +
+ Future Installment Allocation: + {{ paymentAllocation.futureInstallmentAllocationRule }} +
+ +
+ Payment Allocation Order + +
Order {{ (rowIndex + 1) }} Transaction Type {{ jobStep.stepName }} Order {{ jobStep.order }} Actions - - {{ transactionType.name }}
+ + + + + + + + + + + + +
OrderPayment Allocation Rule
{{ (idx + 1) }}{{ paymentAllocation.paymentAllocationRule }}
+ +
+
+ +
Grace on principal payment: {{ loanProduct.graceOnPrincipalPayment }} 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 83d9dab7e..d83cbaff3 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 @@ -1,4 +1,4 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; import { LoanProducts } from '../../loan-products'; import { rangeValidator } from 'app/shared/validators/percentage.validator'; @@ -12,6 +12,7 @@ export class LoanProductSettingsStepComponent implements OnInit { @Input() loanProductsTemplate: any; @Input() isLinkedToFloatingInterestRates: FormControl; + @Output() advancePaymentStrategy = new EventEmitter(); loanProductSettingsForm: FormGroup; @@ -353,6 +354,11 @@ export class LoanProductSettingsStepComponent implements OnInit { } }); + this.loanProductSettingsForm.get('transactionProcessingStrategyCode').valueChanges + .subscribe((transactionProcessingStrategyCode: string) => { + this.advancePaymentStrategy.emit(transactionProcessingStrategyCode); + }); + this.loanProductSettingsForm.get('allowAttributeConfiguration').valueChanges .subscribe((allowAttributeConfiguration: any) => { if (allowAttributeConfiguration) { diff --git a/src/app/products/loan-products/view-loan-product/general-tab/general-tab.component.html b/src/app/products/loan-products/view-loan-product/general-tab/general-tab.component.html index 69c1a94ce..12da93a2f 100644 --- a/src/app/products/loan-products/view-loan-product/general-tab/general-tab.component.html +++ b/src/app/products/loan-products/view-loan-product/general-tab/general-tab.component.html @@ -88,9 +88,11 @@
Over Applied Amount: - {{ loanProduct.overAppliedNumber | formatNumber + {{ loanProduct.overAppliedNumber + | formatNumber }} % - {{ loanProduct.overAppliedNumber | formatNumber }} {{ + {{ loanProduct.overAppliedNumber | + formatNumber }} {{ loanProduct.currencyCode }}
@@ -329,6 +331,40 @@ {{ loanProduct.transactionProcessingStrategyName }}
+
+
+
+ Transaction Type: + {{ paymentAllocation.transactionType }} +
+ +
+ Future Installment Allocation: + {{ paymentAllocation.futureInstallmentAllocationRule }} +
+ +
+ Payment Allocation Order + + + + + + + + + + + + + + +
OrderPayment Allocation Rule
{{ (idx + 1) }}{{ paymentAllocation.paymentAllocationRule }}
+
+
+
+
+
Grace on principal payment: {{ loanProduct.graceOnPrincipalPayment }} @@ -709,15 +745,18 @@ Income from Recovery Repayments: ({{loanProduct.accountingMappings.incomeFromRecoveryAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromRecoveryAccount.name }} - Income from ChargeOff Interest: + Income from ChargeOff + Interest: ({{loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromChargeOffInterestAccount.name }} - Income from ChargeOff Fees: + Income from ChargeOff + Fees: ({{loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromChargeOffFeesAccount.name }} - Income from ChargeOff Penalty: + Income from ChargeOff + Penalty: ({{loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.glCode}}) {{ loanProduct.accountingMappings.incomeFromChargeOffPenaltyAccount.name }} @@ -736,7 +775,8 @@ ({{loanProduct.accountingMappings.chargeOffExpenseAccount.glCode}}) {{ loanProduct.accountingMappings.chargeOffExpenseAccount.name }} - ChargeOff Fraud Expense: + ChargeOff Fraud + Expense: ({{loanProduct.accountingMappings.chargeOffFraudExpenseAccount.glCode}}) {{ loanProduct.accountingMappings.chargeOffFraudExpenseAccount.name }}