From 9e47ba20eeb86d66a028da27bcd5e73295141ce6 Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Thu, 18 Sep 2025 02:07:55 -0500 Subject: [PATCH] WEB-316: Advance Accounting rule can be set for each classification type --- .../loan-product-summary.component.html | 70 ++++- .../loan-product-summary.component.ts | 53 +++- ...ced-accounting-mapping-rule.component.html | 40 +++ ...ced-accounting-mapping-rule.component.scss | 12 + ...anced-accounting-mapping-rule.component.ts | 297 ++++++++++++++++++ ...oan-product-accounting-step.component.html | 22 ++ .../loan-product-accounting-step.component.ts | 94 +++++- .../models/loan-product.model.ts | 15 + src/app/products/products.module.ts | 4 +- src/app/shared/models/general.model.ts | 18 +- .../custom-parameters-popover.component.ts | 2 +- src/assets/translations/cs-CS.json | 2 + src/assets/translations/de-DE.json | 2 + src/assets/translations/en-US.json | 2 + src/assets/translations/es-CL.json | 2 + src/assets/translations/es-MX.json | 2 + src/assets/translations/fr-FR.json | 2 + src/assets/translations/it-IT.json | 2 + src/assets/translations/ko-KO.json | 2 + src/assets/translations/lt-LT.json | 2 + src/assets/translations/lv-LV.json | 2 + src/assets/translations/ne-NE.json | 2 + src/assets/translations/pt-PT.json | 2 + src/assets/translations/sw-SW.json | 2 + 24 files changed, 639 insertions(+), 14 deletions(-) create mode 100644 src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.html create mode 100644 src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.scss create mode 100644 src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.ts 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 7d294db5b..c0ae61e4d 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 @@ -722,7 +722,7 @@ {{ loanProduct.multiDisburseLoan | yesNo }} -
+
{{ 'labels.inputs.Maximum Tranche count' | translate }}: {{ loanProduct.maxTrancheCount }}
@@ -737,7 +737,7 @@

{{ 'labels.heading.Event Settings' | translate }}

-
+
{{ 'labels.inputs.Use the Global Configurations values to the Repayment Event (notifications)' | translate @@ -1252,6 +1252,72 @@
+ +
+

+ {{ 'labels.heading.Buydown Fee classifications to Income accounts' | translate }} +

+ + + + + + + + + + + + + + +
{{ 'labels.inputs.Classification' | translate }} + {{ classificationToIncomeAccountMapping.classificationCodeValue.name }} + {{ 'labels.inputs.Income Account' | translate }} + ({{ classificationToIncomeAccountMapping.incomeAccount.glCode }}) + {{ classificationToIncomeAccountMapping.incomeAccount.name }} +
+
+ +
+

+ {{ 'labels.heading.Capitalized Income classifications to Income accounts' | translate }} +

+ + + + + + + + + + + + + + +
{{ 'labels.inputs.Classification' | translate }} + {{ capitalizedIncomeClassificationToIncomeAccountMapping.classificationCodeValue.name }} + {{ 'labels.inputs.Income Account' | translate }} + ({{ capitalizedIncomeClassificationToIncomeAccountMapping.incomeAccount.glCode }}) + {{ capitalizedIncomeClassificationToIncomeAccountMapping.incomeAccount.name }} +
+
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 b575ff6c1..14e20d492 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 @@ -1,11 +1,12 @@ import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { DelinquencyBucket, LoanProduct } from '../../models/loan-product.model'; +import { AccountingMappingDTO, DelinquencyBucket, LoanProduct } from '../../models/loan-product.model'; import { AccountingMapping, Charge, - ChargeOffReasonCodeValue, + CodeValue, ChargeOffReasonToExpenseAccountMapping, ChargeToIncomeAccountMapping, + ClassificationToIncomeAccountMapping, GLAccount, PaymentChannelToFundSourceMapping, PaymentType, @@ -114,6 +115,8 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { feeToIncomeAccountMappings: ChargeToIncomeAccountMapping[] = []; penaltyToIncomeAccountMappings: ChargeToIncomeAccountMapping[] = []; chargeOffReasonToExpenseAccountMappings: ChargeOffReasonToExpenseAccountMapping[] = []; + buydownFeeClassificationToIncomeAccountMappings: ClassificationToIncomeAccountMapping[] = []; + capitalizedIncomeClassificationToIncomeAccountMappings: ClassificationToIncomeAccountMapping[] = []; constructor(private accounting: Accounting) {} @@ -145,6 +148,10 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { this.feeToIncomeAccountMappings = this.loanProduct.feeToIncomeAccountMappings || []; this.penaltyToIncomeAccountMappings = this.loanProduct.penaltyToIncomeAccountMappings || []; this.chargeOffReasonToExpenseAccountMappings = this.loanProduct.chargeOffReasonToExpenseAccountMappings || []; + this.buydownFeeClassificationToIncomeAccountMappings = + this.loanProduct.buydownFeeClassificationToIncomeAccountMappings || []; + this.capitalizedIncomeClassificationToIncomeAccountMappings = + this.loanProduct.capitalizedIncomeClassificationToIncomeAccountMappings || []; } else { this.accountingMappings = {}; @@ -159,6 +166,9 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { const assetAndLiabilityAccountData = this.loanProductsTemplate.accountingMappingOptions.assetAndLiabilityAccountOptions || []; const chargeOffReasonOptions: any = this.loanProductsTemplate.chargeOffReasonOptions || []; + const buydownFeeClassificationOptions: any = this.loanProductsTemplate.buydownFeeClassificationOptions || []; + const capitalizedIncomeClassificationOptions: any = + this.loanProductsTemplate.capitalizedIncomeClassificationOptions || []; this.accountingMappings = { fundSourceAccount: this.glAccountLookUp(this.loanProduct.fundSourceAccountId, assetAndLiabilityAccountData), @@ -262,11 +272,42 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { chargeOffReasonCodeValue: { id: optionData.id, name: optionData.value - } as ChargeOffReasonCodeValue + } as CodeValue }); } ); } + + this.buydownFeeClassificationToIncomeAccountMappings = []; + if (this.loanProduct.buydownFeeClassificationToIncomeAccountMappings?.length > 0) { + this.loanProduct.buydownFeeClassificationToIncomeAccountMappings.forEach((m: any) => { + let optionData = this.optionDataLookUp(m.classificationCodeValueId, buydownFeeClassificationOptions); + if (optionData !== null) { + this.buydownFeeClassificationToIncomeAccountMappings.push({ + incomeAccount: this.glAccountLookUp(m.incomeAccountId, incomeAccountData), + classificationCodeValue: { + id: optionData.id, + name: optionData.value + } as CodeValue + }); + } + }); + } + this.capitalizedIncomeClassificationToIncomeAccountMappings = []; + if (this.loanProduct.capitalizedIncomeClassificationToIncomeAccountMappings?.length > 0) { + this.loanProduct.capitalizedIncomeClassificationToIncomeAccountMappings.forEach((m: any) => { + let optionData = this.optionDataLookUp(m.classificationCodeValueId, capitalizedIncomeClassificationOptions); + if (optionData !== null) { + this.capitalizedIncomeClassificationToIncomeAccountMappings.push({ + incomeAccount: this.glAccountLookUp(m.incomeAccountId, incomeAccountData), + classificationCodeValue: { + id: optionData.id, + name: optionData.value + } as CodeValue + }); + } + }); + } } if (this.loanProduct.isInterestRecalculationEnabled) { @@ -453,7 +494,7 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { } optionDataLookUp(itemId: any, optionsData: any[]): OptionData { - let optionData: OptionData | null; + let optionData: OptionData | null = null; optionsData.some((o: any) => { if (o.id === itemId) { optionData = { @@ -576,7 +617,9 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { this.loanProduct.paymentChannelToFundSourceMappings?.length > 0 || this.loanProduct.feeToIncomeAccountMappings?.length > 0 || this.loanProduct.penaltyToIncomeAccountMappings?.length > 0 || - this.loanProduct.chargeOffReasonToExpenseAccountMappings?.length > 0 + this.loanProduct.chargeOffReasonToExpenseAccountMappings?.length > 0 || + this.loanProduct.buydownFeeClassificationToIncomeAccountMappings?.length > 0 || + this.loanProduct.capitalizedIncomeClassificationToIncomeAccountMappings?.length > 0 ); } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.html new file mode 100644 index 000000000..143322298 --- /dev/null +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.html @@ -0,0 +1,40 @@ +
+

+ {{ textHeading | translateKey: 'heading' }} +

+ +
+ +
+ + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.' + textField | translate }} + {{ item.value.name }} + {{ 'labels.inputs.GL Account Name or Code' | translate }}({{ item.glAccount.glCode }}) {{ item.glAccount.name }}{{ 'labels.inputs.Actions' | translate }} + + +
+
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.scss new file mode 100644 index 000000000..d45fe83a7 --- /dev/null +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.scss @@ -0,0 +1,12 @@ +table { + width: 100%; +} + +.mat-elevation-z1 { + margin: 1em 0 1.5em; +} + +h3, +h4 { + font-weight: 500; +} diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.ts new file mode 100644 index 000000000..1b4ed9430 --- /dev/null +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component.ts @@ -0,0 +1,297 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { UntypedFormArray } from '@angular/forms'; +import { MatIconButton } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; +import { + MatCell, + MatCellDef, + MatColumnDef, + MatHeaderCell, + MatHeaderCellDef, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, + MatTable +} from '@angular/material/table'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateService } from '@ngx-translate/core'; +import { AccountingMappingDTO, AdvancedMappingDTO } from 'app/products/loan-products/models/loan-product.model'; +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; +import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; +import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; +import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base'; +import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; + +@Component({ + selector: 'mifosx-advanced-accounting-mapping-rule', + imports: [ + ...STANDALONE_SHARED_IMPORTS, + FaIconComponent, + MatTable, + MatColumnDef, + MatHeaderCellDef, + MatHeaderCell, + MatCellDef, + MatCell, + MatIconButton, + MatHeaderRowDef, + MatHeaderRow, + MatRowDef, + MatRow + ], + templateUrl: './advanced-accounting-mapping-rule.component.html', + styleUrl: './advanced-accounting-mapping-rule.component.scss' +}) +export class AdvancedAccountingMappingRuleComponent implements OnInit { + @Input() formType: string; + @Input() formArray: UntypedFormArray; + @Input() textHeading: string; + @Input() textField: string; + @Input() allowAddAccountingMapping: boolean = true; + @Input() accountingMappingOptions: any[] = []; + + currentFormValues: any[] = []; + @Input() chargeData: any; + @Input() penaltyData: any; + @Input() paymentTypeData: any; + @Input() assetAccountData: any; + @Input() incomeAccountData: any; + @Input() expenseAccountData: any; + @Input() liabilityAccountData: any; + @Input() incomeAndLiabilityAccountData: any; + @Input() assetAndLiabilityAccountData: any; + + @Output() formChangeEvent: EventEmitter = new EventEmitter(); + + tableData: any[] = []; + + tableDisplayedColumns: string[] = [ + 'codeValueId', + 'glAccountId', + 'actions' + ]; + + constructor( + public dialog: MatDialog, + public translateService: TranslateService + ) {} + + ngOnInit(): void { + this.tableData = this.formArray?.value || []; + this.sendParentData(); + } + + add() { + this.currentFormValues = []; + if (this.formType == 'ChargeOffReasonExpense') { + this.allowAddAccountingMapping = true; + this.tableData.forEach((item: any) => this.currentFormValues.push(item.chargeOffReasonCodeValueId)); + if (this.accountingMappingOptions.length == this.currentFormValues.length) { + this.allowAddAccountingMapping = false; + return; + } + } + const data = { ...this.getData(this.formType), pristine: false }; + const dialogRef = this.dialog.open(FormDialogComponent, { data }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.data) { + const addData: AccountingMappingDTO = { + value: this.getValueData(response.data.value.valueId), + glAccount: this.getGlAccountData(response.data.value.glAccountId) + }; + this.tableData.push(addData); + this.sendParentData(); + + if (this.formType == 'ChargeOffReasonExpense') { + this.allowAddAccountingMapping = this.tableData.length < this.accountingMappingOptions.length; + } + } + }); + } + + delete(index: number) { + const dialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: this.translateService.instant('labels.text.this') } + }); + dialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.tableData.splice(index, 1); + this.sendParentData(); + } + }); + } + + sendParentData() { + const advancedMappingDTO: AdvancedMappingDTO = { + formType: this.formType, + values: this.tableData + }; + this.formChangeEvent.emit(advancedMappingDTO); + } + + getData(formType: string, values?: any) { + switch (formType) { + case 'PaymentFundSource': + return { + title: 'Configure Fund Sources for Payment Channels', + formfields: this.getPaymentFundSourceFormfields(values) + }; + case 'FeesIncome': + return { title: 'Map Fees to Income Accounts', formfields: this.getFeesIncomeFormfields(values) }; + case 'PenaltyIncome': + return { + title: 'Map Penalties to Specific Income Accounts', + formfields: this.getPenaltyIncomeFormfields(values) + }; + case 'ChargeOffReasonExpense': + return { + title: 'Map Charge-off reasons to Expense accounts', + formfields: this.getChargeOffReasonExpenseFormfields(values) + }; + case 'BuydownFeeClassificationToIncome': + return { + title: 'Buydown Fee classifications to Income accounts', + formfields: this.getClassificationIncomeFormfields(values) + }; + case 'CapitalizedIncomeClassificationToIncome': + return { + title: 'Capitalized Income classifications to Income accounts', + formfields: this.getClassificationIncomeFormfields(values) + }; + } + } + + getPaymentFundSourceFormfields(values?: any) { + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'paymentTypeId', + label: 'Payment Type', + value: values ? values.paymentTypeId : this.paymentTypeData[0].id, + options: { label: 'name', value: 'id', data: this.paymentTypeData }, + required: true, + order: 1 + }), + new SelectBase({ + controlName: 'fundSourceAccountId', + label: 'Fund Source', + value: values ? values.fundSourceAccountId : this.assetAccountData[0].id, + options: { label: 'name', value: 'id', data: this.assetAccountData }, + required: true, + order: 2 + }) + + ]; + return formfields; + } + + getFeesIncomeFormfields(values?: any) { + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'chargeId', + label: 'Fees', + value: values ? values.chargeId : this.chargeData[0].id, + options: { label: 'name', value: 'id', data: this.chargeData }, + required: true, + order: 1 + }), + new SelectBase({ + controlName: 'incomeAccountId', + label: 'Income Account', + value: values ? values.incomeAccountId : this.incomeAndLiabilityAccountData[0].id, + options: { label: 'name', value: 'id', data: this.incomeAndLiabilityAccountData }, + required: true, + order: 2 + }) + + ]; + return formfields; + } + + getPenaltyIncomeFormfields(values?: any) { + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'chargeId', + label: 'Penalty', + value: values ? values.chargeId : this.penaltyData[0].id, + options: { label: 'name', value: 'id', data: this.penaltyData }, + required: true, + order: 1 + }), + new SelectBase({ + controlName: 'incomeAccountId', + label: 'Income Account', + value: values ? values.incomeAccountId : this.incomeAccountData[0].id, + options: { label: 'name', value: 'id', data: this.incomeAccountData }, + required: true, + order: 2 + }) + + ]; + return formfields; + } + + getChargeOffReasonExpenseFormfields(values?: any) { + const reasonOptions = this.accountingMappingOptions.filter( + (item: any) => !this.currentFormValues.includes(item.id) + ); + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'chargeOffReasonCodeValueId', + label: 'Charge-off reason', + value: values ? values.chargeOffReasonCodeValueId : reasonOptions[0].id, + options: { label: 'name', value: 'id', data: reasonOptions }, + required: true, + order: 1 + }), + new SelectBase({ + controlName: 'expenseAccountId', + label: 'Expense Account', + value: values ? values.expenseAccountId : this.expenseAccountData[0].id, + options: { label: 'name', value: 'id', data: this.expenseAccountData }, + required: true, + order: 2 + }) + + ]; + return formfields; + } + + getClassificationIncomeFormfields(values?: any) { + const classificationOptions = this.accountingMappingOptions.filter( + (item: any) => !this.currentFormValues.includes(item.id) + ); + const formfields: FormfieldBase[] = [ + new SelectBase({ + controlName: 'valueId', + label: 'Classification', + value: values ? values.chargeOffReasonCodeValueId : classificationOptions[0].id, + options: { label: 'name', value: 'id', data: classificationOptions }, + required: true, + order: 1 + }), + new SelectBase({ + controlName: 'glAccountId', + label: 'Income Account', + value: values ? values.incomeAccountId : this.incomeAccountData[0].id, + options: { label: 'name', value: 'id', data: this.incomeAccountData }, + required: true, + order: 2 + }) + + ]; + return formfields; + } + + getValueData(valueId: number): any { + return this.accountingMappingOptions.find((i: any) => { + return i.id === valueId; + }); + } + + getGlAccountData(valueId: number): any { + return this.incomeAccountData.find((i: any) => { + return i.id === valueId; + }); + } +} diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html index 3dcc1f891..94095ebb8 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.html @@ -514,6 +514,28 @@ + + + +
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.ts index 3e973eecd..ce02cd832 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.ts @@ -8,7 +8,10 @@ import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.componen import { TranslateService } from '@ngx-translate/core'; import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base'; -import { ChargeOffReasonToExpenseAccountMapping } from 'app/shared/models/general.model'; +import { + ChargeOffReasonToExpenseAccountMapping, + ClassificationToIncomeAccountMapping +} from 'app/shared/models/general.model'; import { DeferredIncomeRecognition } from '../loan-product-payment-strategy-step/payment-allocation-model'; import { MatRadioGroup, MatRadioButton } from '@angular/material/radio'; import { MatDivider } from '@angular/material/divider'; @@ -31,6 +34,8 @@ import { import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper'; import { FindPipe } from '../../../../pipes/find.pipe'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; +import { AdvancedAccountingMappingRuleComponent } from './advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component'; +import { AccountingMappingDTO, AdvancedMappingDTO } from '../../models/loan-product.model'; @Component({ selector: 'mifosx-loan-product-accounting-step', @@ -57,7 +62,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatRow, MatStepperPrevious, MatStepperNext, - FindPipe + FindPipe, + AdvancedAccountingMappingRuleComponent ] }) export class LoanProductAccountingStepComponent implements OnInit, OnChanges { @@ -78,6 +84,8 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { incomeAndLiabilityAccountData: any; assetAndLiabilityAccountData: any; chargeOffReasonOptions: any; + capitalizedIncomeClassificationOptions: any[] = []; + buydownFeeClassificationOptions: any[] = []; currentFormValues: any[] = []; allowAddChargeOffReasonExpense = true; @@ -123,6 +131,9 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { this.assetAndLiabilityAccountData = this.loanProductsTemplate.accountingMappingOptions.assetAndLiabilityAccountOptions || []; this.chargeOffReasonOptions = this.loanProductsTemplate.chargeOffReasonOptions || []; + this.capitalizedIncomeClassificationOptions = + this.loanProductsTemplate.capitalizedIncomeClassificationOptions || []; + this.buydownFeeClassificationOptions = this.loanProductsTemplate.buydownFeeClassificationOptions || []; this.loanProductAccountingForm.patchValue({ accountingRule: this.loanProductsTemplate.accountingRule.id @@ -201,7 +212,9 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { this.loanProductsTemplate.paymentChannelToFundSourceMappings || this.loanProductsTemplate.feeToIncomeAccountMappings || this.loanProductsTemplate.penaltyToIncomeAccountMappings || - this.loanProductsTemplate.chargeOffReasonToExpenseAccountMappings + this.loanProductsTemplate.chargeOffReasonToExpenseAccountMappings || + this.loanProductsTemplate.buydownFeeClassificationToIncomeAccountMappings || + this.loanProductsTemplate.capitalizedIncomeClassificationToIncomeAccountMappings ? true : false }); @@ -244,6 +257,28 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { ) ) ); + this.loanProductAccountingForm.setControl( + 'buydownfeeClassificationToIncomeAccountMappings', + this.formBuilder.array( + (this.loanProductsTemplate.buydownFeeClassificationToIncomeAccountMappings || []).map( + (m: ClassificationToIncomeAccountMapping) => ({ + value: m.classificationCodeValue, + glAccount: m.incomeAccount + }) + ) + ) + ); + this.loanProductAccountingForm.setControl( + 'capitalizedIncomeClassificationToIncomeAccountMappings', + this.formBuilder.array( + (this.loanProductsTemplate.capitalizedIncomeClassificationToIncomeAccountMappings || []).map( + (m: ClassificationToIncomeAccountMapping) => ({ + value: m.classificationCodeValue, + glAccount: m.incomeAccount + }) + ) + ) + ); } } @@ -341,6 +376,14 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { 'chargeOffReasonToExpenseAccountMappings', this.formBuilder.array([]) ); + this.loanProductAccountingForm.addControl( + 'buydownfeeClassificationToIncomeAccountMappings', + this.formBuilder.array([]) + ); + this.loanProductAccountingForm.addControl( + 'capitalizedIncomeClassificationToIncomeAccountMappings', + this.formBuilder.array([]) + ); } else { this.loanProductAccountingForm.setControl( 'paymentChannelToFundSourceMappings', @@ -352,6 +395,14 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { 'chargeOffReasonToExpenseAccountMappings', this.formBuilder.array([]) ); + this.loanProductAccountingForm.setControl( + 'buydownfeeClassificationToIncomeAccountMappings', + this.formBuilder.array([]) + ); + this.loanProductAccountingForm.setControl( + 'capitalizedIncomeClassificationToIncomeAccountMappings', + this.formBuilder.array([]) + ); } }); } else { @@ -415,6 +466,16 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { return this.loanProductAccountingForm.get('chargeOffReasonToExpenseAccountMappings') as UntypedFormArray; } + get buydownfeeClassificationToIncomeAccountMappings(): UntypedFormArray { + return this.loanProductAccountingForm.get('buydownfeeClassificationToIncomeAccountMappings') as UntypedFormArray; + } + + get capitalizedIncomeClassificationToIncomeAccountMappings(): UntypedFormArray { + return this.loanProductAccountingForm.get( + 'capitalizedIncomeClassificationToIncomeAccountMappings' + ) as UntypedFormArray; + } + setLoanProductAccountingFormDirty() { if (this.loanProductAccountingForm.pristine) { this.loanProductAccountingForm.markAsDirty(); @@ -635,4 +696,31 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { } } } + + formChangeEvent(accountingData: AdvancedMappingDTO) { + if (accountingData.formType === 'BuydownFeeClassificationToIncome') { + this.loanProductAccountingForm.setControl( + 'buydownfeeClassificationToIncomeAccountMappings', + this.formBuilder.array( + (accountingData.values || []).map((m: AccountingMappingDTO) => ({ + classificationCodeValueId: m.value.id, + incomeAccountId: m.glAccount.id + })) + ) + ); + console.log(this.loanProductAccountingForm.value.buydownfeeClassificationToIncomeAccountMappings); + } else if (accountingData.formType === 'CapitalizedIncomeClassificationToIncome') { + this.loanProductAccountingForm.setControl( + 'capitalizedIncomeClassificationToIncomeAccountMappings', + this.formBuilder.array( + (accountingData.values || []).map((m: AccountingMappingDTO) => ({ + classificationCodeValueId: m.value.id, + incomeAccountId: m.glAccount.id + })) + ) + ); + console.log(this.loanProductAccountingForm.value.capitalizedIncomeClassificationToIncomeAccountMappings); + } + this.setLoanProductAccountingFormDirty(); + } } diff --git a/src/app/products/loan-products/models/loan-product.model.ts b/src/app/products/loan-products/models/loan-product.model.ts index d4a6a434f..30f28496f 100644 --- a/src/app/products/loan-products/models/loan-product.model.ts +++ b/src/app/products/loan-products/models/loan-product.model.ts @@ -2,7 +2,10 @@ import { AccountingMapping, ChargeOffReasonToExpenseAccountMapping, ChargeToIncomeAccountMapping, + ClassificationToIncomeAccountMapping, + CodeValue, Currency, + GLAccount, PaymentChannelToFundSourceMapping } from 'app/shared/models/general.model'; import { OptionData, StringEnumOptionData } from 'app/shared/models/option-data.model'; @@ -152,6 +155,8 @@ export interface LoanProduct { enableAccrualActivityPosting?: boolean; supportedInterestRefundTypes?: StringEnumOptionData[]; chargeOffBehaviour?: StringEnumOptionData; + buydownFeeClassificationToIncomeAccountMappings?: ClassificationToIncomeAccountMapping[]; + capitalizedIncomeClassificationToIncomeAccountMappings?: ClassificationToIncomeAccountMapping[]; } export interface AllowAttributeOverrides { @@ -193,3 +198,13 @@ export interface InterestRecalculationData { allowCompoundingOnEod: boolean; disallowInterestCalculationOnPastDue: boolean; } + +export interface AdvancedMappingDTO { + formType: string; + values: AccountingMappingDTO[]; +} + +export interface AccountingMappingDTO { + value: CodeValue; + glAccount: GLAccount; +} diff --git a/src/app/products/products.module.ts b/src/app/products/products.module.ts index b8701b661..8ef155bee 100644 --- a/src/app/products/products.module.ts +++ b/src/app/products/products.module.ts @@ -122,6 +122,7 @@ import { ViewAdvancePaymenyAllocationComponent } from './loan-products/view-loan import { AdvancePaymentAllocationTabComponent } from './loan-products/loan-product-stepper/loan-product-payment-strategy-step/advance-payment-allocation-tab/advance-payment-allocation-tab.component'; import { LoanProductSummaryComponent } from './loan-products/common/loan-product-summary/loan-product-summary.component'; import { LoanProductDeferredIncomeRecognitionStepComponent } from './loan-products/loan-product-stepper/loan-product-capitalized-income-step/loan-product-deferred-income-recognition-step.component'; +import { AdvancedAccountingMappingRuleComponent } from './loan-products/loan-product-stepper/loan-product-accounting-step/advanced-accounting-mapping-rule/advanced-accounting-mapping-rule.component'; /** * Products Module @@ -249,7 +250,8 @@ import { LoanProductDeferredIncomeRecognitionStepComponent } from './loan-produc ViewAdvancePaymenyAllocationComponent, AdvancePaymentAllocationTabComponent, LoanProductSummaryComponent, - LoanProductDeferredIncomeRecognitionStepComponent + LoanProductDeferredIncomeRecognitionStepComponent, + AdvancedAccountingMappingRuleComponent ] }) export class ProductsModule {} diff --git a/src/app/shared/models/general.model.ts b/src/app/shared/models/general.model.ts index 13c04c983..dea5fdecd 100644 --- a/src/app/shared/models/general.model.ts +++ b/src/app/shared/models/general.model.ts @@ -42,11 +42,25 @@ export interface PaymentChannelToFundSourceMapping { export interface ChargeOffReasonToExpenseAccountMapping { chargeOffReasonCodeValueId?: number; expenseAccountId?: number; - chargeOffReasonCodeValue?: ChargeOffReasonCodeValue; + chargeOffReasonCodeValue?: CodeValue; expenseAccount?: AccountingMapping; } -export interface ChargeOffReasonCodeValue { +export interface AccountMapping { + codeValueId?: number; + glAccountId?: number; + codeValue?: CodeValue; + glAccount?: AccountingMapping; +} + +export interface ClassificationToIncomeAccountMapping { + classificationCodeValueId?: number; + incomeAccountId?: number; + classificationCodeValue?: CodeValue; + incomeAccount?: AccountingMapping; +} + +export interface CodeValue { active: boolean; description: string; id: number; diff --git a/src/app/system/manage-jobs/scheduler-jobs/custom-parameters-popover/custom-parameters-popover.component.ts b/src/app/system/manage-jobs/scheduler-jobs/custom-parameters-popover/custom-parameters-popover.component.ts index 01b3e1b16..e0a4e4cc2 100644 --- a/src/app/system/manage-jobs/scheduler-jobs/custom-parameters-popover/custom-parameters-popover.component.ts +++ b/src/app/system/manage-jobs/scheduler-jobs/custom-parameters-popover/custom-parameters-popover.component.ts @@ -10,7 +10,7 @@ import { import { CustomParametersTableComponent } from './custom-parameters-table/custom-parameters-table.component'; import { SystemService } from 'app/system/system.service'; import { CdkScrollable } from '@angular/cdk/scrolling'; -import { NgFor, NgClass } from '@angular/common'; +import { NgClass } from '@angular/common'; import { MatList, MatListItem } from '@angular/material/list'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; diff --git a/src/assets/translations/cs-CS.json b/src/assets/translations/cs-CS.json index 72c0e4290..fc8cb5fc7 100644 --- a/src/assets/translations/cs-CS.json +++ b/src/assets/translations/cs-CS.json @@ -839,6 +839,8 @@ "Bulk Import": "Hromadný import", "Bulk Loan Reassignment": "Hromadné přeřazení půjčky", "Business Rule Parameters": "Parametry obchodních pravidel", + "Buydown Fee classifications to Income accounts": "Klasifikace poplatků za odkup na příjmové účty", + "Capitalized Income classifications to Income accounts": "Klasifikace kapitalizovaných výnosů do výnosových účtů", "COB Catch-Up is": "COB Catch-Up je", "Calculate Interest": "Vypočítejte úrok", "Campaign Message": "Zpráva kampaně", diff --git a/src/assets/translations/de-DE.json b/src/assets/translations/de-DE.json index e3528ac01..d26202f54 100644 --- a/src/assets/translations/de-DE.json +++ b/src/assets/translations/de-DE.json @@ -840,6 +840,8 @@ "Bulk Import": "Massenimport", "Bulk Loan Reassignment": "Neuzuweisung von Massenkrediten", "Business Rule Parameters": "Geschäftsregelparameter", + "Buydown Fee classifications to Income accounts": "Buydown-Gebührenklassifizierungen für Einkommenskonten", + "Capitalized Income classifications to Income accounts": "Klassifizierung kapitalisierter Einkünfte in Einkommenskonten", "COB Catch-Up is": "COB Catch-Up ist", "Calculate Interest": "Zinsen berechnen", "Campaign Message": "Kampagnenbotschaft", diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json index 2d0a67cbf..6ad10cb72 100644 --- a/src/assets/translations/en-US.json +++ b/src/assets/translations/en-US.json @@ -845,6 +845,8 @@ "Bulk Import": "Bulk Import", "Bulk Loan Reassignment": "Bulk Loan Reassignment", "Business Rule Parameters": "Business Rule Parameters", + "Buydown Fee classifications to Income accounts": "Buydown Fee classifications to Income accounts", + "Capitalized Income classifications to Income accounts": "Capitalized Income classifications to Income accounts", "COB Catch-Up is": "COB Catch-Up is", "Calculate Interest": "Calculate Interest", "Campaign Message": "Campaign Message", diff --git a/src/assets/translations/es-CL.json b/src/assets/translations/es-CL.json index 57a7c4400..23e5a45d4 100644 --- a/src/assets/translations/es-CL.json +++ b/src/assets/translations/es-CL.json @@ -839,6 +839,8 @@ "Bulk Import": "Importación masiva", "Bulk Loan Reassignment": "Reasignación de Créditos másiva", "Business Rule Parameters": "Parámetros de reglas de negocio", + "Buydown Fee classifications to Income accounts": "Clasificaciones de comisiones por reducción de precio en cuentas de ingresos", + "Capitalized Income classifications to Income accounts": "Clasificaciones de ingresos capitalizados a cuentas de ingresos", "COB Catch-Up is": "La puesta al día de COB es", "Calculate Interest": "Calcular interés", "Campaign Message": "Mensaje de campaña", diff --git a/src/assets/translations/es-MX.json b/src/assets/translations/es-MX.json index 740290fe6..e16294c18 100644 --- a/src/assets/translations/es-MX.json +++ b/src/assets/translations/es-MX.json @@ -839,6 +839,8 @@ "Bulk Import": "Importación masiva", "Bulk Loan Reassignment": "Reasignación de Créditos másiva", "Business Rule Parameters": "Parámetros de reglas de negocio", + "Buydown Fee classifications to Income accounts": "Clasificaciones de comisiones por reducción de precio en cuentas de ingresos", + "Capitalized Income classifications to Income accounts": "Clasificaciones de ingresos capitalizados a cuentas de ingresos", "COB Catch-Up is": "La puesta al día de COB es", "Calculate Interest": "Calcular interés", "Campaign Message": "Mensaje de campaña", diff --git a/src/assets/translations/fr-FR.json b/src/assets/translations/fr-FR.json index fb6c19b5a..4fa9aea5c 100644 --- a/src/assets/translations/fr-FR.json +++ b/src/assets/translations/fr-FR.json @@ -840,6 +840,8 @@ "Bulk Import": "Importation en masse", "Bulk Loan Reassignment": "Réaffectation groupée de prêts", "Business Rule Parameters": "Paramètres des règles métier", + "Buydown Fee classifications to Income accounts": "Classification des frais de rachat dans les comptes de revenus", + "Capitalized Income classifications to Income accounts": "Classifications des revenus capitalisés vers les comptes de revenus", "COB Catch-Up is": "Le rattrapage COB est", "Calculate Interest": "Calculer les intérêts", "Campaign Message": "Message de campagne", diff --git a/src/assets/translations/it-IT.json b/src/assets/translations/it-IT.json index bacccad23..ec2828e96 100644 --- a/src/assets/translations/it-IT.json +++ b/src/assets/translations/it-IT.json @@ -840,6 +840,8 @@ "Bulk Import": "Importazione in blocco", "Bulk Loan Reassignment": "Riassegnazione del prestito in blocco", "Business Rule Parameters": "Parametri delle regole aziendali", + "Buydown Fee classifications to Income accounts": "Classificazioni delle commissioni di riacquisto nei conti di reddito", + "Capitalized Income classifications to Income accounts": "Classificazioni del reddito capitalizzato nei conti di reddito", "COB Catch-Up is": "Il recupero del COB lo è", "Calculate Interest": "Calcola gli interessi", "Campaign Message": "Messaggio della campagna", diff --git a/src/assets/translations/ko-KO.json b/src/assets/translations/ko-KO.json index 3fd4126be..d1d40d7da 100644 --- a/src/assets/translations/ko-KO.json +++ b/src/assets/translations/ko-KO.json @@ -840,6 +840,8 @@ "Bulk Import": "대량 가져오기", "Bulk Loan Reassignment": "대량대출 재할당", "Business Rule Parameters": "비즈니스 규칙 매개변수", + "Buydown Fee classifications to Income accounts": "소득 계정에 대한 Buydown 수수료 분류", + "Capitalized Income classifications to Income accounts": "자본화된 소득 분류를 소득 계정으로", "COB Catch-Up is": "COB 따라잡기는", "Calculate Interest": "이자 계산", "Campaign Message": "캠페인 메시지", diff --git a/src/assets/translations/lt-LT.json b/src/assets/translations/lt-LT.json index 28b139838..a2a83f51e 100644 --- a/src/assets/translations/lt-LT.json +++ b/src/assets/translations/lt-LT.json @@ -840,6 +840,8 @@ "Bulk Import": "Masinis importas", "Bulk Loan Reassignment": "Masinės paskolos perskirstymas", "Business Rule Parameters": "Verslo taisyklių parametrai", + "Buydown Fee classifications to Income accounts": "Išpirkimo mokesčių klasifikavimas į pajamų sąskaitas", + "Capitalized Income classifications to Income accounts": "Kapitalizuotų pajamų klasifikavimas į pajamų sąskaitas", "COB Catch-Up is": "COB Catch-Up yra", "Calculate Interest": "Apskaičiuokite palūkanas", "Campaign Message": "Kampanijos pranešimas", diff --git a/src/assets/translations/lv-LV.json b/src/assets/translations/lv-LV.json index 1c4c9d794..7f92c8283 100644 --- a/src/assets/translations/lv-LV.json +++ b/src/assets/translations/lv-LV.json @@ -840,6 +840,8 @@ "Bulk Import": "Lielapjoma importēšana", "Bulk Loan Reassignment": "Lielapjoma aizdevuma pārcelšana", "Business Rule Parameters": "Biznesa noteikumu parametri", + "Buydown Fee classifications to Income accounts": "Izpirkšanas maksu klasifikācija ienākumu kontos", + "Capitalized Income classifications to Income accounts": "Kapitalizēto ienākumu klasifikācija ienākumu kontos", "COB Catch-Up is": "COB Catch-Up ir", "Calculate Interest": "Aprēķināt procentus", "Campaign Message": "Kampaņas ziņojums", diff --git a/src/assets/translations/ne-NE.json b/src/assets/translations/ne-NE.json index e844022f6..808c48152 100644 --- a/src/assets/translations/ne-NE.json +++ b/src/assets/translations/ne-NE.json @@ -840,6 +840,8 @@ "Bulk Import": "थोक आयात", "Bulk Loan Reassignment": "बल्क ऋण पुन: असाइनमेन्ट", "Business Rule Parameters": "व्यापार नियम प्यारामिटरहरू", + "Buydown Fee classifications to Income accounts": "आय खाताहरूमा खरिद शुल्क वर्गीकरण", + "Capitalized Income classifications to Income accounts": "आय खाताहरूमा पूंजीकृत आय वर्गीकरण", "COB Catch-Up is": "COB क्याच-अप हो", "Calculate Interest": "ब्याज गणना गर्नुहोस्", "Campaign Message": "अभियान सन्देश", diff --git a/src/assets/translations/pt-PT.json b/src/assets/translations/pt-PT.json index 4a5983dd3..7d4fb4378 100644 --- a/src/assets/translations/pt-PT.json +++ b/src/assets/translations/pt-PT.json @@ -840,6 +840,8 @@ "Bulk Import": "Importação em massa", "Bulk Loan Reassignment": "Reatribuição de empréstimo em massa", "Business Rule Parameters": "Parâmetros de regras de negócios", + "Buydown Fee classifications to Income accounts": "Classificações de taxas de recompra para contas de renda", + "Capitalized Income classifications to Income accounts": "Classificações de renda capitalizada para contas de renda", "COB Catch-Up is": "A recuperação do COB é", "Calculate Interest": "Calcular juros", "Campaign Message": "Mensagem da campanha", diff --git a/src/assets/translations/sw-SW.json b/src/assets/translations/sw-SW.json index 581362f58..7b0d7e32d 100644 --- a/src/assets/translations/sw-SW.json +++ b/src/assets/translations/sw-SW.json @@ -840,6 +840,8 @@ "Bulk Import": "Ingiza Wingi", "Bulk Loan Reassignment": "Ugawaji upya wa mkopo kwa wingi", "Business Rule Parameters": "Vigezo vya Sheria ya Biashara", + "Buydown Fee classifications to Income accounts": "Uainishaji wa Ada ya Kununua kwa akaunti za Mapato", + "Capitalized Income classifications to Income accounts": "Uainishaji wa Mapato ya Mtaji kwa akaunti za Mapato", "COB Catch-Up is": "COB Catch-Up ni", "Calculate Interest": "Hesabu Riba", "Campaign Message": "Ujumbe wa Kampeni",