From 6516d6c95d44581da5106ea65a32f59219d3361a Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Wed, 13 May 2026 10:35:41 +0530 Subject: [PATCH] update loan product code --- .../loan-product-summary.component.html | 95 -- .../loan-product-summary.component.ts | 1 - .../create-loan-product.component.ts | 5 - .../edit-loan-product.component.ts | 5 - ...oan-product-accounting-step.component.html | 927 +++++++---------- ...oan-product-accounting-step.component.scss | 362 +++++-- .../loan-product-accounting-step.component.ts | 967 ++++++++++-------- .../loan-product-currency-step.component.html | 57 -- .../loan-product-currency-step.component.ts | 89 +- .../loan-product-details-step.component.html | 30 - .../loan-product-details-step.component.ts | 20 +- .../loan-product-preview-step.component.html | 1 - .../loan-product-settings-step.component.html | 681 ++++++++---- .../loan-product-settings-step.component.scss | 12 + .../loan-product-settings-step.component.ts | 158 +-- .../loan-product-terms-step.component.html | 332 ------ .../loan-product-terms-step.component.ts | 263 +---- .../products/loan-products/loan-products.ts | 60 +- .../models/loan-product.model.ts | 6 - .../general-tab/general-tab.component.html | 1 - .../general-tab/general-tab.component.ts | 3 - 21 files changed, 1841 insertions(+), 2234 deletions(-) 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 0356064df..d6e4ddc07 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 @@ -15,22 +15,6 @@ {{ 'labels.inputs.Short Name' | translate }}: {{ loanProduct.shortName }} - @if (loanProduct.externalId) { -
- {{ 'labels.inputs.External Id' | translate }}: - {{ loanProduct.externalId }} -
- } - @if (loanProduct.fundId) { -
- {{ 'labels.inputs.Fund' | translate }}: - {{ loanProduct.fundName }} -
- } -
- {{ 'labels.inputs.Include in Customer Loan Counter' | translate }}: - {{ loanProduct.includeInBorrowerCycle | yesNo }} -
@if (loanProduct.startDate) {
{{ 'labels.inputs.Start Date' | translate }}: @@ -63,18 +47,6 @@ {{ 'labels.inputs.Decimal Places' | translate }}: {{ loanProduct.digitsAfterDecimal ?? loanProduct.currency.decimalPlaces }}
- @if (loanProduct.inMultiplesOf || loanProduct.currency?.inMultiplesOf) { -
- {{ 'labels.inputs.Currency in multiples of' | translate }}: - {{ loanProduct.inMultiplesOf ?? loanProduct.currency.inMultiplesOf }} -
- } - @if (loanProduct.installmentAmountInMultiplesOf) { -
- {{ 'labels.inputs.Installment in multiples of' | translate }}: - {{ loanProduct.installmentAmountInMultiplesOf }} -
- }

{{ 'labels.heading.Terms' | translate }}

@@ -414,24 +386,6 @@
} -

{{ 'labels.inputs.Down Payments' | translate }}

- -
- {{ 'labels.inputs.Enable Down Payments' | translate }}: - {{ loanProduct.enableDownPayment | yesNo }} -
- @if (loanProduct.enableDownPayment) { -
- {{ 'labels.inputs.Disbursed Amount Percentage Down Payment' | translate }} (%): - {{ loanProduct.disbursedAmountPercentageForDownPayment }} % -
- } - @if (loanProduct.enableDownPayment) { -
- {{ 'labels.inputs.Enable Auto Repayment for Down Payment' | translate }}: - {{ loanProduct.enableAutoRepaymentForDownPayment | yesNo }} -
- } @if (isAdvancedPaymentAllocation) {

{{ 'labels.inputs.Loan Charge-off behaviour' | translate }}

@@ -737,55 +691,6 @@ }
} -

{{ 'labels.heading.Loan Tranche Details' | translate }}

- -
- {{ 'labels.inputs.Enable Multiple Disbursals' | translate }}: - {{ loanProduct.multiDisburseLoan | yesNo }} -
- @if (loanProduct.multiDisburseLoan) { -
- {{ 'labels.inputs.Maximum Tranche count' | translate }}: - {{ loanProduct.maxTrancheCount }} -
- @if (loanProduct.outstandingLoanBalance) { -
- {{ 'labels.inputs.Maximum allowed outstanding balance' | translate }}: - {{ loanProduct.outstandingLoanBalance }} -
- } -
- {{ 'labels.inputs.Disallow Expected Disbursements' | translate }}: - {{ loanProduct.disallowExpectedDisbursements | yesNo }} -
- @if (loanProduct.loanScheduleType && loanProduct.loanScheduleType.code === 'PROGRESSIVE') { -
- {{ 'labels.inputs.Allow full term for each tranche' | translate }}: - {{ loanProduct.allowFullTermForTranche | yesNo }} -
- } - } -

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

-
- {{ - 'labels.inputs.Use the Global Configurations values to the Repayment Event (notifications)' | translate - }}: - {{ useDueForRepaymentsConfigurations | yesNo }} -
- @if (loanProduct.dueDaysForRepaymentEvent) { -
- {{ 'labels.inputs.Due days for repayment event' | translate }}: - {{ loanProduct.dueDaysForRepaymentEvent | number }} -
- } - @if (loanProduct.overDueDaysForRepaymentEvent) { -
- {{ 'labels.inputs.OverDue days for repayment event' | translate }}: - {{ loanProduct.overDueDaysForRepaymentEvent | number }} -
- }

{{ 'labels.inputs.Configurable Terms and Settings' | translate }}

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 d8dded0ec..04a46f611 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 @@ -84,7 +84,6 @@ export class LoanProductSummaryComponent implements OnInit, OnChanges { @Input() action: string; @Input() loanProduct: LoanProduct; @Input() loanProductsTemplate: any | null; - @Input() useDueForRepaymentsConfigurations: boolean; @Input() paymentAllocations: PaymentAllocation | null; @Input() creditAllocations: CreditAllocation | null; @Input() supportedInterestRefundTypes: StringEnumOptionData[] | null; 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 098401757..70bf6bf1f 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 @@ -304,10 +304,6 @@ export class CreateLoanProductComponent implements OnInit { submit() { const loanProduct = this.loanProducts.buildPayload(this.loanProduct, this.itemsByDefault); - if (loanProduct['useDueForRepaymentsConfigurations'] === true) { - loanProduct['dueDaysForRepaymentEvent'] = null; - loanProduct['overDueDaysForRepaymentEvent'] = null; - } if (this.isAdvancedPaymentStrategy) { loanProduct['supportedInterestRefundTypes'] = this.mapStringEnumOptionToIdList( loanProduct['supportedInterestRefundTypes'] @@ -316,7 +312,6 @@ export class CreateLoanProductComponent implements OnInit { delete loanProduct['supportedInterestRefundTypes']; delete loanProduct['daysInYearCustomStrategy']; } - delete loanProduct['useDueForRepaymentsConfigurations']; this.productsService.createLoanProduct(loanProduct).subscribe((response: any) => { this.router.navigate( 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 9e0b2af1e..b5fb93f9b 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 @@ -353,10 +353,6 @@ export class EditLoanProductComponent implements OnInit { submit() { const loanProduct = this.loanProducts.buildPayload(this.loanProduct, this.itemsByDefault); - if (loanProduct['useDueForRepaymentsConfigurations']) { - loanProduct['dueDaysForRepaymentEvent'] = null; - loanProduct['overDueDaysForRepaymentEvent'] = null; - } if (this.isAdvancedPaymentStrategy) { loanProduct['supportedInterestRefundTypes'] = this.mapStringEnumOptionToIdList( loanProduct['supportedInterestRefundTypes'] @@ -365,7 +361,6 @@ export class EditLoanProductComponent implements OnInit { delete loanProduct['supportedInterestRefundTypes']; delete loanProduct['daysInYearCustomStrategy']; } - delete loanProduct['useDueForRepaymentsConfigurations']; this.productsService.updateLoanProduct(this.loanProductAndTemplate.id, loanProduct).subscribe((response: any) => { this.router.navigate( 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 61943160b..d01180c8a 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 @@ -7,542 +7,407 @@ -->
-
-
- -

{{ 'labels.heading.Accounting Rule' | translate }}

+
+ + + +

Accounting

-
-
- - @for (accountingRule of accountingRuleData; track accountingRule; let i = $index) { - - {{ 'labels.accounting.' + accountingRule | translate }} - - } - + +
+ +
+
+

Accounting Rule

+
+
+ Cash +
+ @if (isAccrualAccounting()) { +
+ {{ + 'labels.inputs.Enable Accrual Activity Posting on Installment Due Date' | translate + }} +
+ }
- @if (isAccountingAccrualBased) { -
- {{ - 'labels.inputs.Enable Accrual Activity Posting on Installment Due Date' | translate - }} + + + @for (section of accountingSectionConfigs; track section.key) { +
+
+

{{ section.title }}

+ +
+
+ @for (row of section.rows; track row.key) { + @if ( + (!row.accrualOnly || isAccrualAccounting()) && + (!row.deferredOnly || isDeferredOnly()) && + (!row.deferredCapitalizationOnly || isDeferredCapitalizationOnly()) && + (!row.deferredBuyDownOnly || isDeferredBuyDownOnly()) && + (!row.deferredBuyDownMerchantOnly || isDeferredBuyDownMerchantOnly()) + ) { +
+
+ + {{ section.pillLabel }} + + {{ row.label }} +
+ +
+ } + } +
+ + +
+ @for ( + otherRow of getOtherGlMappingsArray(section.otherMappingsControlName).controls; + track $index; + let i = $index + ) { +
+
+
+ + {{ section.pillLabel }} + + +
+ +
+ + GL Code + + @for (glAccount of section.otherAccountOptions; track glAccount.id) { + + {{ '(' + glAccount.glCode + ') ' + glAccount.name }} + + } + + +
+
+ +
+ } +
+
+ } + + + @if (loanProductAccountingForm.value.accountingRule >= 2 && loanProductAccountingForm.value.accountingRule <= 4) { +
+
+

Advanced Accounting Rules

+
+
+ {{ + 'labels.heading.Advanced Accounting Rules' | translate + }} +
+ + @if (loanProductAccountingForm.value.advancedAccountingRules) { + +
+
+

{{ 'labels.heading.Configure Fund Sources for Payment Channels' | translate }}

+ +
+ @if (paymentChannelToFundSourceMappings.value.length !== 0) { +
+ + + + + + + + + + + + + + + +
{{ 'labels.inputs.Payment Type' | translate }} + {{ paymentFundSource.paymentTypeId | find: paymentTypeData : 'id' : 'name' }} + {{ 'labels.inputs.Fund Source' | translate }} + {{ paymentFundSource.fundSourceAccountId | find: assetAccountData : 'id' : 'name' }} + {{ 'labels.inputs.Actions' | translate }} + + +
+
+ } +
+ + +
+
+

{{ 'labels.heading.Map Fees to Specific Income Accounts' | translate }}

+ +
+ @if (feeToIncomeAccountMappings.value.length !== 0) { +
+ + + + + + + + + + + + + + + +
{{ 'labels.inputs.Fees' | translate }} + {{ feesIncome.chargeId | find: chargeData : 'id' : 'name' }} + {{ 'labels.inputs.Income Account' | translate }} + {{ feesIncome.incomeAccountId | find: incomeAndLiabilityAccountData : 'id' : 'name' }} + {{ 'labels.inputs.Actions' | translate }} + + +
+
+ } +
+ + +
+
+

{{ 'labels.heading.Map Penalties to Specific Income Accounts' | translate }}

+ +
+ @if (penaltyToIncomeAccountMappings.value.length !== 0) { +
+ + + + + + + + + + + + + + + +
{{ 'labels.inputs.Penalty' | translate }} + {{ penaltyIncome.chargeId | find: penaltyData : 'id' : 'name' }} + {{ 'labels.inputs.Income Account' | translate }} + {{ penaltyIncome.incomeAccountId | find: incomeAccountData : 'id' : 'name' }} + {{ 'labels.inputs.Actions' | translate }} + + +
+
+ } +
+ + +
+
+

{{ 'labels.heading.Map Charge-off reasons to Expense accounts' | translate }}

+ +
+ @if (chargeOffReasonToExpenseAccountMappings.value.length !== 0) { +
+ + + + + + + + + + + + + + + +
{{ 'labels.inputs.Charge-off reason' | translate }} + {{ + chargeOffReasonExpense.chargeOffReasonCodeValueId + | find: chargeOffReasonOptions : 'id' : 'name' + }} + {{ 'labels.inputs.Expense Account' | translate }} + {{ chargeOffReasonExpense.expenseAccountId | find: expenseAccountData : 'id' : 'name' }} + {{ 'labels.inputs.Actions' | translate }} + + +
+
+ } +
+ + + + + + }
}
- - @if (loanProductAccountingForm.value.accountingRule >= 2 && loanProductAccountingForm.value.accountingRule <= 4) { - -
-
- -

{{ 'labels.heading.Assets' | translate }} / {{ 'labels.heading.Liabilities' | translate }}

-
-
- - -
-
- - -
-
- -

{{ 'labels.heading.Assets' | translate }}

-
-
- - - - - @if ( - loanProductAccountingForm.value.accountingRule === 3 || loanProductAccountingForm.value.accountingRule === 4 - ) { - - - - - - - } -
-
- - -
-
- -

{{ 'labels.heading.Income' | translate }}

-
-
- - - - - - - - - - - - - - - - - - - - - @if (deferredIncomeRecognition?.capitalizedIncome?.enableIncomeCapitalization) { - - - } - @if (deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee) { - - - } -
-
- - -
-
- -

{{ 'labels.heading.Expenses' | translate }}

-
-
- - - - - - - - - @if ( - deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee && - deferredIncomeRecognition?.buyDownFee?.merchantBuyDownFee - ) { - - - } -
-
- - -
-
- -

{{ 'labels.heading.Liabilities' | translate }}

-
-
- - - @if ( - deferredIncomeRecognition?.capitalizedIncome?.enableIncomeCapitalization || - deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee - ) { - - - } -
-
- - -
-
- -

{{ 'labels.heading.Advanced Accounting Rules' | translate }}

-
-
-
- {{ - 'labels.heading.Advanced Accounting Rules' | translate - }} -
-
- - @if (loanProductAccountingForm.value.advancedAccountingRules) { - -
-
-

{{ 'labels.heading.Configure Fund Sources for Payment Channels' | translate }}

- -
- @if (paymentChannelToFundSourceMappings.value.length !== 0) { -
- - - - - - - - - - - - - - - -
{{ 'labels.inputs.Payment Type' | translate }} - {{ paymentFundSource.paymentTypeId | find: paymentTypeData : 'id' : 'name' }} - {{ 'labels.inputs.Fund Source' | translate }} - {{ paymentFundSource.fundSourceAccountId | find: assetAccountData : 'id' : 'name' }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - -
-
-

{{ 'labels.heading.Map Fees to Specific Income Accounts' | translate }}

- -
- @if (feeToIncomeAccountMappings.value.length !== 0) { -
- - - - - - - - - - - - - - - -
{{ 'labels.inputs.Fees' | translate }} - {{ feesIncome.chargeId | find: chargeData : 'id' : 'name' }} - {{ 'labels.inputs.Income Account' | translate }} - {{ feesIncome.incomeAccountId | find: incomeAndLiabilityAccountData : 'id' : 'name' }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - -
-
-

{{ 'labels.heading.Map Penalties to Specific Income Accounts' | translate }}

- -
- @if (penaltyToIncomeAccountMappings.value.length !== 0) { -
- - - - - - - - - - - - - - - -
{{ 'labels.inputs.Penalty' | translate }} - {{ penaltyIncome.chargeId | find: penaltyData : 'id' : 'name' }} - {{ 'labels.inputs.Income Account' | translate }} - {{ penaltyIncome.incomeAccountId | find: incomeAccountData : 'id' : 'name' }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - -
-
-

{{ 'labels.heading.Map Charge-off reasons to Expense accounts' | translate }}

- -
- @if (chargeOffReasonToExpenseAccountMappings.value.length !== 0) { -
- - - - - - - - - - - - - - - -
{{ 'labels.inputs.Charge-off reason' | translate }} - {{ - chargeOffReasonExpense.chargeOffReasonCodeValueId | find: chargeOffReasonOptions : 'id' : 'name' - }} - {{ 'labels.inputs.Expense Account' | translate }} - {{ chargeOffReasonExpense.expenseAccountId | find: expenseAccountData : 'id' : 'name' }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - - - - - } -
- } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.scss index 687d9456d..d03b72c21 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-accounting-step/loan-product-accounting-step.component.scss @@ -6,124 +6,328 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// Section cards +$border: #e2e8f0; +$gray-50: #f8f9fa; +$gray-200: #e8eaed; +$gray-500: #6b7280; +$gray-700: #334155; +$gray-900: #202124; +$primary: #1353a0; + .lpa-section { - border: 1px solid #e2e8f0; + border: 1px solid $border; border-radius: 10px; overflow: hidden; background: #fff; - - & + & { - margin-top: 16px; - } } -// Section header -.lpa-section-header { +.lpa-section__header { display: flex; align-items: center; gap: 10px; padding: 12px 24px; - background: #f8f9fa; - border-bottom: 1px solid #e8eaed; + background: $gray-50; + border-bottom: 1px solid $gray-200; h3 { + margin: 0; font-size: 14px; font-weight: 600; - color: #202124; - margin: 0; + color: $gray-900; } } -.lpa-section-icon { - color: #0ea5e9; - font-size: 15px; -} - -// Section body -.lpa-section-body { - padding: 16px 24px; -} - -// 2-column grid -.lpa-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 4px 16px; -} - -// Full width utility -.lpa-full-width { - grid-column: 1 / -1; -} - -// Radio group -.lpa-radio-group { - display: flex; - gap: 24px; - flex-wrap: wrap; -} - -// Checkbox row -.lpa-checkbox-row { - display: flex; +.lpa-section__icon { + display: inline-flex; align-items: center; - padding: 4px 0; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 6px; + background: #eff6ff; + color: #2563eb; + font-size: 13px; } -// Subsections inside advanced rules -.lpa-subsection { - padding: 16px 24px; - border-top: 1px solid #e8eaed; +.lpa-section__body { + padding: 16px 24px 20px; } -.lpa-subsection-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; +.lpa-group { + & + & { + margin-top: 20px; + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin: 0 0 12px; + } h4 { - font-size: 13px; - font-weight: 600; - color: #202124; margin: 0; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #94a3b8; } } -// Add buttons -.lpa-add-btn { - height: 32px; - border-radius: 8px; +.lpa-add-other-btn { + border: 0; + background: transparent; + color: #2563eb; font-size: 12px; - font-weight: 600; + font-weight: 700; + padding: 0; + cursor: pointer; + transition: opacity 0.2s; + + &:hover { + opacity: 0.8; + } } -// Table wrapper -.lpa-table-wrapper { +.lpa-list { + display: flex; + flex-direction: column; + gap: 10px; + + & + & { + margin-top: 12px; + } +} + +.lpa-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 8px; + background: #fff; + + @media (width <= 900px) { + flex-direction: column; + align-items: stretch; + } + + &:has(mifosx-gl-account-selector) { + align-items: stretch; + } +} + +.lpa-row--other { + border-style: dashed; + border-color: #cbd5e1; + background: #f8fafc; + display: grid; + grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr); + align-items: center; + padding: 12px 16px; + + @media (width <= 1100px) { + grid-template-columns: 1fr; + } +} + +.lpa-other-row-wrap { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 12px; + + @media (width <= 1100px) { + grid-template-columns: 1fr; + gap: 8px; + } +} + +.lpa-row__meta { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + flex: 1 1 auto; + + &--other { + min-width: 0; + flex: 1 1 auto; + align-self: stretch; + } +} + +.lpa-row__label { + font-size: 14px; + font-weight: 600; + color: $gray-700; +} + +.lpa-row__input { + flex: 0 0 22rem; + max-width: 22rem; + min-width: 18rem; + + @media (width <= 900px) { + width: 100%; + max-width: 100%; + min-width: 0; + } + + :is(mifosx-gl-account-selector) { + width: 100%; + display: block; + + :is(.layout-column) { + width: 100%; + margin-right: 0 !important; + } + + :is(mat-form-field) { + width: 100%; + } + } +} + +.lpa-row__actions { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + + &--other { + justify-content: flex-end; + align-self: stretch; + width: 100%; + + @media (width <= 1100px) { + justify-content: flex-start; + } + } +} + +.lpa-row__name { + width: 100%; + min-width: 0; + max-width: 24rem; + height: 40px; + border: 1px solid #cbd5e1; + border-radius: 6px; + padding: 0 12px; + font-size: 14px; + background: #fff; + + @media (width <= 900px) { + max-width: 100%; + } + + &--other-account { + max-width: 100%; + flex: 1 1 auto; + height: 48px; + } +} + +.lpa-row__other-field { + width: 100%; + min-width: 0; + + :is(mat-form-field) { + width: 100%; + } + + :is(.mat-mdc-form-field-required-marker) { + display: none; + } +} + +.lpa-remove-btn { + border: 0; + background: transparent; + color: #ef4444; + font-size: 18px; + line-height: 1; + cursor: pointer; + padding: 0 4px; + + &--side { + place-self: center end; + margin-right: 4px; + + @media (width <= 1100px) { + justify-self: start; + margin-right: 0; + } + } +} + +.lpa-pill { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 2px 8px; + border-radius: 999px; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + white-space: nowrap; + + &--green { + background: #dcfce7; + color: #16a34a; + } + + &--amber { + background: #fef3c7; + color: #d97706; + } + + &--blue { + background: #dbeafe; + color: #2563eb; + } +} + +// Advanced Accounting Subsection styles +.lpa-subsection { + margin-top: 24px; + padding-top: 24px; + border-top: 1px solid $border; + + &-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; + + h4 { + margin: 0; + color: $gray-700; + font-size: 14px; + } + } +} + +.lpa-table-wrapper { + border: 1px solid $border; + border-radius: 8px; overflow: hidden; - margin-top: 8px; table { width: 100%; } } -// Responsive -@media (width <= 600px) { - .lpa-grid { - grid-template-columns: 1fr; - } +.lpa-checkbox-row { + margin-top: 12px; +} - .lpa-radio-group { - flex-direction: column; - } - - .lpa-subsection-header { - flex-direction: column; - align-items: flex-start; - gap: 8px; - } +.lpa-radio-group { + display: flex; + gap: 16px; } 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 e0bdee01c..0c3861aa0 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 @@ -18,7 +18,8 @@ import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield- import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base'; import { ChargeOffReasonToExpenseAccountMapping, - ClassificationToIncomeAccountMapping + ClassificationToIncomeAccountMapping, + GLAccount } from 'app/shared/models/general.model'; import { DeferredIncomeRecognition } from '../loan-product-payment-strategy-step/payment-allocation-model'; import { MatRadioGroup, MatRadioButton } from '@angular/material/radio'; @@ -43,10 +44,35 @@ 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'; +type AccountingRowConfig = { + key: string; + label: string; + inputLabel: string; + controlName: string; + options: GLAccount[]; + required?: boolean; + accrualOnly?: boolean; + deferredOnly?: boolean; + deferredBuyDownOnly?: boolean; + deferredBuyDownMerchantOnly?: boolean; + deferredCapitalizationOnly?: boolean; +}; + +type AccountingSectionConfig = { + key: 'assets' | 'liabilities' | 'expenses' | 'income'; + title: string; + pillLabel: string; + pillClass: 'green' | 'amber' | 'blue'; + rows: AccountingRowConfig[]; + otherMappingsControlName: string; + otherAccountOptions: GLAccount[]; +}; + @Component({ selector: 'mifosx-loan-product-accounting-step', templateUrl: './loan-product-accounting-step.component.html', styleUrls: ['./loan-product-accounting-step.component.scss'], + standalone: true, imports: [ ...STANDALONE_SHARED_IMPORTS, MatRadioGroup, @@ -71,7 +97,7 @@ import { AccountingMappingDTO, AdvancedMappingDTO } from '../../models/loan-prod }) export class LoanProductAccountingStepComponent implements OnInit, OnChanges { private formBuilder = inject(UntypedFormBuilder); - dialog = inject(MatDialog); + public dialog = inject(MatDialog); private translateService = inject(TranslateService); @Input() loanProductsTemplate: any; @@ -79,48 +105,52 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { @Input() loanProductFormValid: boolean; @Input() deferredIncomeRecognition: DeferredIncomeRecognition; - loanProductAccountingForm: UntypedFormGroup; + public loanProductAccountingForm!: UntypedFormGroup; - chargeData: any; - penaltyData: any; - paymentTypeData: any; - assetAccountData: any; - incomeAccountData: any; - expenseAccountData: any; - liabilityAccountData: any; - incomeAndLiabilityAccountData: any; - assetAndLiabilityAccountData: any; - chargeOffReasonOptions: any; - capitalizedIncomeClassificationOptions: any[] = []; - buydownFeeClassificationOptions: any[] = []; - writeOffReasonOptions: any[] = []; + public chargeData: any; + public penaltyData: any; + public paymentTypeData: any; + public assetAccountData: any; + public incomeAccountData: any; + public expenseAccountData: any; + public liabilityAccountData: any; + public incomeAndLiabilityAccountData: any; + public assetAndLiabilityAccountData: any; + public chargeOffReasonOptions: any; + public capitalizedIncomeClassificationOptions: any[] = []; + public buydownFeeClassificationOptions: any[] = []; + public writeOffReasonOptions: any[] = []; - currentFormValues: any[] = []; - allowAddChargeOffReasonExpense = true; + public currentFormValues: any[] = []; + public allowAddChargeOffReasonExpense = true; - paymentFundSourceDisplayedColumns: string[] = [ + public paymentFundSourceDisplayedColumns: string[] = [ 'paymentTypeId', 'fundSourceAccountId', 'actions' ]; - feesPenaltyIncomeDisplayedColumns: string[] = [ + public feesPenaltyIncomeDisplayedColumns: string[] = [ 'chargeId', 'incomeAccountId', 'actions' ]; - chargeOffReasonExpenseDisplayedColumns: string[] = [ + public chargeOffReasonExpenseDisplayedColumns: string[] = [ 'chargeOffReasonCodeValueId', 'expenseAccountId', 'actions' ]; + public accountingSectionConfigs: AccountingSectionConfig[] = []; + constructor() { this.createLoanProductAccountingForm(); this.setConditionalControls(); } ngOnChanges(changes: SimpleChanges): void { - this.setDeferredIncomeRecognitionControls(); + if (this.loanProductAccountingForm) { + this.setDeferredIncomeRecognitionControls(); + } } ngOnInit() { @@ -140,422 +170,271 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { this.buydownFeeClassificationOptions = this.loanProductsTemplate.buydownFeeClassificationOptions || []; this.writeOffReasonOptions = this.loanProductsTemplate.writeOffReasonOptions || []; + // Force Cash Accounting this.loanProductAccountingForm.patchValue({ - accountingRule: this.loanProductsTemplate.accountingRule.id + accountingRule: 2 }); + this.loanProductAccountingForm.get('accountingRule').disable(); + + this.buildAccountingSectionConfigs(); const accountingMappings = this.loanProductsTemplate.accountingMappings; this.setDeferredIncomeRecognitionControls(); - switch (this.loanProductsTemplate.accountingRule.id) { - case 3: - case 4: - this.loanProductAccountingForm.patchValue({ - receivableInterestAccountId: accountingMappings.receivableInterestAccount.id, - receivableFeeAccountId: accountingMappings.receivableFeeAccount.id, - receivablePenaltyAccountId: accountingMappings.receivablePenaltyAccount.id - }); - this.loanProductAccountingForm.patchValue({ - enableAccrualActivityPosting: this.loanProductsTemplate.enableAccrualActivityPosting - }); - if (this.deferredIncomeRecognition) { - if (this.deferredIncomeRecognition.capitalizedIncome?.enableIncomeCapitalization) { - this.loanProductAccountingForm.patchValue({ - deferredIncomeLiabilityAccountId: accountingMappings.deferredIncomeLiabilityAccount.id, - incomeFromCapitalizationAccountId: accountingMappings.incomeFromCapitalizationAccount.id - }); - } - if (this.deferredIncomeRecognition.buyDownFee?.enableBuyDownFee) { - this.loanProductAccountingForm.patchValue({ - deferredIncomeLiabilityAccountId: accountingMappings.deferredIncomeLiabilityAccount.id, - incomeFromBuyDownAccountId: accountingMappings.incomeFromBuyDownAccount.id - }); - if (this.deferredIncomeRecognition.buyDownFee?.merchantBuyDownFee) { - this.loanProductAccountingForm.patchValue({ - buyDownExpenseAccountId: accountingMappings.buyDownExpenseAccount?.id - }); - } - } - } - /* falls through */ - case 2: - this.loanProductAccountingForm.patchValue({ - fundSourceAccountId: accountingMappings.fundSourceAccount.id, - loanPortfolioAccountId: accountingMappings.loanPortfolioAccount.id, - transfersInSuspenseAccountId: accountingMappings.transfersInSuspenseAccount.id, - interestOnLoanAccountId: accountingMappings.interestOnLoanAccount.id, - incomeFromFeeAccountId: accountingMappings.incomeFromFeeAccount.id, - incomeFromPenaltyAccountId: accountingMappings.incomeFromPenaltyAccount.id, - incomeFromRecoveryAccountId: accountingMappings.incomeFromRecoveryAccount.id, - writeOffAccountId: accountingMappings.writeOffAccount.id, - goodwillCreditAccountId: accountingMappings.goodwillCreditAccount?.id || null, - overpaymentLiabilityAccountId: accountingMappings.overpaymentLiabilityAccount.id, - chargeOffFraudExpenseAccountId: accountingMappings.chargeOffFraudExpenseAccount - ? accountingMappings.chargeOffFraudExpenseAccount.id - : '', - chargeOffExpenseAccountId: accountingMappings.chargeOffExpenseAccount - ? accountingMappings.chargeOffExpenseAccount.id - : '', - incomeFromChargeOffPenaltyAccountId: accountingMappings.incomeFromChargeOffPenaltyAccount - ? accountingMappings.incomeFromChargeOffPenaltyAccount.id - : '', - incomeFromChargeOffFeesAccountId: accountingMappings.incomeFromChargeOffFeesAccount - ? accountingMappings.incomeFromChargeOffFeesAccount.id - : '', - incomeFromChargeOffInterestAccountId: accountingMappings.incomeFromChargeOffInterestAccount - ? accountingMappings.incomeFromChargeOffInterestAccount.id - : '', - incomeFromGoodwillCreditInterestAccountId: accountingMappings.incomeFromGoodwillCreditInterestAccount - ? accountingMappings.incomeFromGoodwillCreditInterestAccount.id - : '', - incomeFromGoodwillCreditFeesAccountId: accountingMappings.incomeFromGoodwillCreditFeesAccount - ? accountingMappings.incomeFromGoodwillCreditFeesAccount.id - : '', - incomeFromGoodwillCreditPenaltyAccountId: accountingMappings.incomeFromGoodwillCreditPenaltyAccount - ? accountingMappings.incomeFromGoodwillCreditPenaltyAccount.id - : '', - advancedAccountingRules: - this.loanProductsTemplate.paymentChannelToFundSourceMappings || - this.loanProductsTemplate.feeToIncomeAccountMappings || - this.loanProductsTemplate.penaltyToIncomeAccountMappings || - this.loanProductsTemplate.chargeOffReasonToExpenseAccountMappings || - this.loanProductsTemplate.buydownFeeClassificationToIncomeAccountMappings || - this.loanProductsTemplate.capitalizedIncomeClassificationToIncomeAccountMappings || - this.loanProductsTemplate.writeOffReasonsToExpenseMappings - ? true - : false - }); + if (accountingMappings) { + this.loanProductAccountingForm.patchValue({ + fundSourceAccountId: accountingMappings.fundSourceAccount?.id, + loanPortfolioAccountId: accountingMappings.loanPortfolioAccount?.id, + transfersInSuspenseAccountId: accountingMappings.transfersInSuspenseAccount?.id, + interestOnLoanAccountId: accountingMappings.interestOnLoanAccount?.id, + incomeFromFeeAccountId: accountingMappings.incomeFromFeeAccount?.id, + incomeFromPenaltyAccountId: accountingMappings.incomeFromPenaltyAccount?.id, + incomeFromRecoveryAccountId: accountingMappings.incomeFromRecoveryAccount?.id, + writeOffAccountId: accountingMappings.writeOffAccount?.id, + goodwillCreditAccountId: accountingMappings.goodwillCreditAccount?.id || null, + overpaymentLiabilityAccountId: accountingMappings.overpaymentLiabilityAccount?.id, + chargeOffFraudExpenseAccountId: accountingMappings.chargeOffFraudExpenseAccount?.id || '', + chargeOffExpenseAccountId: accountingMappings.chargeOffExpenseAccount?.id || '', + incomeFromChargeOffPenaltyAccountId: accountingMappings.incomeFromChargeOffPenaltyAccount?.id || '', + incomeFromChargeOffFeesAccountId: accountingMappings.incomeFromChargeOffFeesAccount?.id || '', + incomeFromChargeOffInterestAccountId: accountingMappings.incomeFromChargeOffInterestAccount?.id || '', + incomeFromGoodwillCreditInterestAccountId: accountingMappings.incomeFromGoodwillCreditInterestAccount?.id || '', + incomeFromGoodwillCreditFeesAccountId: accountingMappings.incomeFromGoodwillCreditFeesAccount?.id || '', + incomeFromGoodwillCreditPenaltyAccountId: accountingMappings.incomeFromGoodwillCreditPenaltyAccount?.id || '', + receivableInterestAccountId: accountingMappings.receivableInterestAccount?.id || '', + receivableFeeAccountId: accountingMappings.receivableFeeAccount?.id || '', + receivablePenaltyAccountId: accountingMappings.receivablePenaltyAccount?.id || '' + }); + + if (this.loanProductsTemplate.paymentChannelToFundSourceMappings) { this.loanProductAccountingForm.setControl( 'paymentChannelToFundSourceMappings', this.formBuilder.array( - (this.loanProductsTemplate.paymentChannelToFundSourceMappings || []).map((paymentFundSource: any) => ({ + this.loanProductsTemplate.paymentChannelToFundSourceMappings.map((paymentFundSource: any) => ({ paymentTypeId: paymentFundSource.paymentType.id, fundSourceAccountId: paymentFundSource.fundSourceAccount.id })) ) ); - this.loanProductAccountingForm.setControl( - 'feeToIncomeAccountMappings', - this.formBuilder.array( - (this.loanProductsTemplate.feeToIncomeAccountMappings || []).map((feesIncome: any) => ({ - chargeId: feesIncome.charge.id, - incomeAccountId: feesIncome.incomeAccount.id - })) - ) - ); - this.loanProductAccountingForm.setControl( - 'penaltyToIncomeAccountMappings', - this.formBuilder.array( - (this.loanProductsTemplate.penaltyToIncomeAccountMappings || []).map((penaltyIncome: any) => ({ - chargeId: penaltyIncome.charge.id, - incomeAccountId: penaltyIncome.incomeAccount.id - })) - ) - ); - this.loanProductAccountingForm.setControl( - 'chargeOffReasonToExpenseAccountMappings', - this.formBuilder.array( - (this.loanProductsTemplate.chargeOffReasonToExpenseAccountMappings || []).map( - (m: ChargeOffReasonToExpenseAccountMapping) => ({ - chargeOffReasonCodeValueId: m.reasonCodeValue.id, - expenseAccountId: m.expenseAccount.id - }) - ) - ) - ); - 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 - }) - ) - ) - ); - this.loanProductAccountingForm.setControl( - 'writeOffReasonsToExpenseMappings', - this.formBuilder.array( - (this.loanProductsTemplate.writeOffReasonsToExpenseMappings || []).map( - (m: ChargeOffReasonToExpenseAccountMapping) => ({ - value: m.reasonCodeValue, - glAccount: m.expenseAccount - }) - ) - ) - ); + } + + // Patch otherGlMappings + const glMappings = this.loanProductsTemplate.otherGlMappingsMap || this.loanProductsTemplate.otherGlMappings; + if (glMappings) { + this.setOtherGlMappings('assetsOtherGlMappings', glMappings.assets || [], this.assetAccountData); + this.setOtherGlMappings('liabilitiesOtherGlMappings', glMappings.liabilities || [], this.liabilityAccountData); + this.setOtherGlMappings('expensesOtherGlMappings', glMappings.expenses || [], this.expenseAccountData); + this.setOtherGlMappings('incomeOtherGlMappings', glMappings.income || [], this.incomeAccountData); + } } } - createLoanProductAccountingForm() { + public createLoanProductAccountingForm() { this.loanProductAccountingForm = this.formBuilder.group({ - accountingRule: [1] + accountingRule: [{ value: 2, disabled: true }], + assetsOtherGlMappings: this.formBuilder.array([]), + liabilitiesOtherGlMappings: this.formBuilder.array([]), + expensesOtherGlMappings: this.formBuilder.array([]), + incomeOtherGlMappings: this.formBuilder.array([]) }); } - setConditionalControls() { + public setConditionalControls() { this.loanProductAccountingForm.get('accountingRule').valueChanges.subscribe((accountingRule: any) => { if (accountingRule >= 2 && accountingRule <= 4) { - this.loanProductAccountingForm.addControl( - 'fundSourceAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'loanPortfolioAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'transfersInSuspenseAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'interestOnLoanAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromFeeAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromPenaltyAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromRecoveryAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl('writeOffAccountId', new UntypedFormControl('', Validators.required)); - this.loanProductAccountingForm.addControl( - 'goodwillCreditAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'overpaymentLiabilityAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl('advancedAccountingRules', new UntypedFormControl(false)); - this.loanProductAccountingForm.addControl( - 'chargeOffFraudExpenseAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'chargeOffExpenseAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromChargeOffPenaltyAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromChargeOffFeesAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromChargeOffInterestAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromGoodwillCreditInterestAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromGoodwillCreditFeesAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'incomeFromGoodwillCreditPenaltyAccountId', - new UntypedFormControl('', Validators.required) - ); - - this.loanProductAccountingForm - .get('advancedAccountingRules') - .valueChanges.subscribe((advancedAccountingRules: boolean) => { - if (advancedAccountingRules) { - this.loanProductAccountingForm.addControl( - 'paymentChannelToFundSourceMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.addControl('feeToIncomeAccountMappings', this.formBuilder.array([])); - this.loanProductAccountingForm.addControl('penaltyToIncomeAccountMappings', this.formBuilder.array([])); - this.loanProductAccountingForm.addControl( - 'chargeOffReasonToExpenseAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.addControl( - 'buydownfeeClassificationToIncomeAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.addControl( - 'capitalizedIncomeClassificationToIncomeAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.addControl('writeOffReasonsToExpenseMappings', this.formBuilder.array([])); - } else { - this.loanProductAccountingForm.setControl( - 'paymentChannelToFundSourceMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.setControl('feeToIncomeAccountMappings', this.formBuilder.array([])); - this.loanProductAccountingForm.setControl('penaltyToIncomeAccountMappings', this.formBuilder.array([])); - this.loanProductAccountingForm.setControl( - 'chargeOffReasonToExpenseAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.setControl( - 'buydownfeeClassificationToIncomeAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.setControl( - 'capitalizedIncomeClassificationToIncomeAccountMappings', - this.formBuilder.array([]) - ); - this.loanProductAccountingForm.setControl('writeOffReasonsToExpenseMappings', this.formBuilder.array([])); - } - }); + this.addCommonControls(); } else { - this.loanProductAccountingForm.removeControl('fundSourceAccountId'); - this.loanProductAccountingForm.removeControl('loanPortfolioAccountId'); - this.loanProductAccountingForm.removeControl('transfersInSuspenseAccountId'); - this.loanProductAccountingForm.removeControl('interestOnLoanAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromFeeAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromPenaltyAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromRecoveryAccountId'); - this.loanProductAccountingForm.removeControl('writeOffAccountId'); - this.loanProductAccountingForm.removeControl('goodwillCreditAccountId'); - this.loanProductAccountingForm.removeControl('overpaymentLiabilityAccountId'); - this.loanProductAccountingForm.removeControl('advancedAccountingRules'); - this.loanProductAccountingForm.removeControl('chargeOffExpenseAccountId'); - this.loanProductAccountingForm.removeControl('chargeOffFraudExpenseAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromChargeOffPenaltyAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromChargeOffFeesAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromChargeOffInterestAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromGoodwillCreditInterestAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromGoodwillCreditFeesAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromGoodwillCreditPenaltyAccountId'); - } - - if (accountingRule === 3 || accountingRule === 4) { - this.loanProductAccountingForm.addControl( - 'receivableInterestAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'receivableFeeAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl( - 'receivablePenaltyAccountId', - new UntypedFormControl('', Validators.required) - ); - this.loanProductAccountingForm.addControl('enableAccrualActivityPosting', new UntypedFormControl(false)); - } else { - this.loanProductAccountingForm.removeControl('receivableInterestAccountId'); - this.loanProductAccountingForm.removeControl('receivableFeeAccountId'); - this.loanProductAccountingForm.removeControl('receivablePenaltyAccountId'); - this.loanProductAccountingForm.removeControl('enableAccrualActivityPosting'); + // Handle removal if needed } }); } - get paymentChannelToFundSourceMappings(): UntypedFormArray { + private addCommonControls() { + const commonFields = [ + 'fundSourceAccountId', + 'loanPortfolioAccountId', + 'transfersInSuspenseAccountId', + 'interestOnLoanAccountId', + 'incomeFromFeeAccountId', + 'incomeFromPenaltyAccountId', + 'incomeFromRecoveryAccountId', + 'writeOffAccountId', + 'goodwillCreditAccountId', + 'overpaymentLiabilityAccountId', + 'chargeOffFraudExpenseAccountId', + 'chargeOffExpenseAccountId', + 'incomeFromChargeOffPenaltyAccountId', + 'incomeFromChargeOffFeesAccountId', + 'incomeFromChargeOffInterestAccountId', + 'incomeFromGoodwillCreditInterestAccountId', + 'incomeFromGoodwillCreditFeesAccountId', + 'incomeFromGoodwillCreditPenaltyAccountId', + 'receivableInterestAccountId', + 'receivableFeeAccountId', + 'receivablePenaltyAccountId' + ]; + commonFields.forEach((field) => { + if (!this.loanProductAccountingForm.contains(field)) { + this.loanProductAccountingForm.addControl(field, new UntypedFormControl('', Validators.required)); + } + }); + + if (!this.loanProductAccountingForm.contains('advancedAccountingRules')) { + this.loanProductAccountingForm.addControl('advancedAccountingRules', new UntypedFormControl(false)); + } + + const arrays = [ + 'paymentChannelToFundSourceMappings', + 'feeToIncomeAccountMappings', + 'penaltyToIncomeAccountMappings', + 'chargeOffReasonToExpenseAccountMappings', + 'buydownfeeClassificationToIncomeAccountMappings', + 'capitalizedIncomeClassificationToIncomeAccountMappings', + 'writeOffReasonsToExpenseMappings' + ]; + arrays.forEach((arr) => { + if (!this.loanProductAccountingForm.contains(arr)) { + this.loanProductAccountingForm.addControl(arr, this.formBuilder.array([])); + } + }); + } + + public getOtherGlMappingsArray(controlName: string): UntypedFormArray { + return this.loanProductAccountingForm.get(controlName) as UntypedFormArray; + } + + private createOtherGlMappingGroup(otherGlMapping: any = {}) { + return this.formBuilder.group({ + accountName: [ + otherGlMapping.accountName || '', + Validators.required + ], + glAccountId: [ + otherGlMapping.glAccountId || '', + Validators.required + ] + }); + } + + private setOtherGlMappings(controlName: string, otherGlMappings: any[], options: GLAccount[]) { + const otherGlMappingsArray = this.getOtherGlMappingsArray(controlName); + otherGlMappingsArray.clear(); + (otherGlMappings || []).forEach((otherGlMapping) => { + otherGlMappingsArray.push( + this.createOtherGlMappingGroup({ + accountName: otherGlMapping.accountName, + glAccountId: + otherGlMapping.glAccountId || options.find((option) => option.glCode === otherGlMapping.glCode)?.id || null + }) + ); + }); + } + + public addOtherGlMapping(controlName: string) { + this.getOtherGlMappingsArray(controlName).push(this.createOtherGlMappingGroup()); + this.setLoanProductAccountingFormDirty(); + } + + public removeOtherGlMapping(controlName: string, index: number) { + this.getOtherGlMappingsArray(controlName).removeAt(index); + this.setLoanProductAccountingFormDirty(); + } + + public getOtherGlMappingControl(controlName: string, index: number, fieldName: string): UntypedFormControl { + return this.getOtherGlMappingsArray(controlName).at(index).get(fieldName) as UntypedFormControl; + } + + private getOtherGlMappings() { + const mapOtherGlMappings = (controlName: string, options: GLAccount[]) => + this.getOtherGlMappingsArray(controlName) + .value.filter((item: any) => !!item.accountName || !!item.glAccountId) + .map((item: any) => ({ + accountName: item.accountName, + glCode: options.find((option) => option.id === item.glAccountId)?.glCode || '' + })) + .filter((item: any) => !!item.accountName || !!item.glCode); + + const otherGlMappings = { + assets: mapOtherGlMappings('assetsOtherGlMappings', this.assetAccountData), + liabilities: mapOtherGlMappings('liabilitiesOtherGlMappings', this.liabilityAccountData), + expenses: mapOtherGlMappings('expensesOtherGlMappings', this.expenseAccountData), + income: mapOtherGlMappings('incomeOtherGlMappings', this.incomeAccountData) + }; + + return Object.values(otherGlMappings).some((value) => value.length) ? otherGlMappings : null; + } + + public get paymentChannelToFundSourceMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('paymentChannelToFundSourceMappings') as UntypedFormArray; } - get feeToIncomeAccountMappings(): UntypedFormArray { + public get feeToIncomeAccountMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('feeToIncomeAccountMappings') as UntypedFormArray; } - get penaltyToIncomeAccountMappings(): UntypedFormArray { + public get penaltyToIncomeAccountMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('penaltyToIncomeAccountMappings') as UntypedFormArray; } - get chargeOffReasonToExpenseAccountMappings(): UntypedFormArray { + public get chargeOffReasonToExpenseAccountMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('chargeOffReasonToExpenseAccountMappings') as UntypedFormArray; } - get buydownfeeClassificationToIncomeAccountMappings(): UntypedFormArray { + public get buydownfeeClassificationToIncomeAccountMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('buydownfeeClassificationToIncomeAccountMappings') as UntypedFormArray; } - get capitalizedIncomeClassificationToIncomeAccountMappings(): UntypedFormArray { + public get capitalizedIncomeClassificationToIncomeAccountMappings(): UntypedFormArray { return this.loanProductAccountingForm.get( 'capitalizedIncomeClassificationToIncomeAccountMappings' ) as UntypedFormArray; } - get writeOffReasonsToExpenseMappings(): UntypedFormArray { + public get writeOffReasonsToExpenseMappings(): UntypedFormArray { return this.loanProductAccountingForm.get('writeOffReasonsToExpenseMappings') as UntypedFormArray; } - setLoanProductAccountingFormDirty() { + public setLoanProductAccountingFormDirty() { if (this.loanProductAccountingForm.pristine) { this.loanProductAccountingForm.markAsDirty(); } } - add(formType: string, formArray: UntypedFormArray) { - this.currentFormValues = []; - if (formType == 'ChargeOffReasonExpense') { - this.allowAddChargeOffReasonExpense = true; - formArray.value.forEach((item: any) => this.currentFormValues.push(item.chargeOffReasonCodeValueId)); - if (this.chargeOffReasonOptions.length == this.currentFormValues.length) { - this.allowAddChargeOffReasonExpense = false; - return; - } - } + public add(formType: string, formArray: UntypedFormArray) { const data = { ...this.getData(formType), pristine: false }; const dialogRef = this.dialog.open(FormDialogComponent, { data }); dialogRef.afterClosed().subscribe((response: any) => { - if (response.data) { + if (response && response.data) { formArray.push(response.data); - if (formType == 'ChargeOffReasonExpense') { - this.allowAddChargeOffReasonExpense = formArray.value.length < this.chargeOffReasonOptions.length; - } - this.setLoanProductAccountingFormDirty(); } }); } - edit(formType: string, formArray: UntypedFormArray, index: number) { + public edit(formType: string, formArray: UntypedFormArray, index: number) { const data = { ...this.getData(formType, formArray.at(index).value), layout: { addButtonText: 'Edit' } }; const dialogRef = this.dialog.open(FormDialogComponent, { data }); dialogRef.afterClosed().subscribe((response: any) => { - if (response.data) { + if (response && response.data) { formArray.at(index).patchValue(response.data.value); this.setLoanProductAccountingFormDirty(); } }); } - delete(formType: string, formArray: UntypedFormArray, index: number) { + public delete(formType: string, formArray: UntypedFormArray, index: number) { const dialogRef = this.dialog.open(DeleteDialogComponent, { data: { deleteContext: this.translateService.instant('labels.text.this') } }); dialogRef.afterClosed().subscribe((response: any) => { - if (response.delete) { + if (response && response.delete) { formArray.removeAt(index); - if (formType == 'ChargeOffReasonExpense') { - this.allowAddChargeOffReasonExpense = formArray.value.length < this.chargeOffReasonOptions.length; - } this.setLoanProductAccountingFormDirty(); } }); } - getData(formType: string, values?: any) { + public getData(formType: string, values?: any) { switch (formType) { case 'PaymentFundSource': return { @@ -574,15 +453,17 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { title: 'Map Charge-off reasons to Expense accounts', formfields: this.getChargeOffReasonExpenseFormfields(values) }; + default: + return {}; } } - getPaymentFundSourceFormfields(values?: any) { - const formfields: FormfieldBase[] = [ + public getPaymentFundSourceFormfields(values?: any) { + return [ new SelectBase({ controlName: 'paymentTypeId', label: 'Payment Type', - value: values ? values.paymentTypeId : this.paymentTypeData[0].id, + value: values ? values.paymentTypeId : this.paymentTypeData[0]?.id, options: { label: 'name', value: 'id', data: this.paymentTypeData }, required: true, order: 1 @@ -590,21 +471,20 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { new SelectBase({ controlName: 'fundSourceAccountId', label: 'Fund Source', - value: values ? values.fundSourceAccountId : this.assetAccountData[0].id, + 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[] = [ + public getFeesIncomeFormfields(values?: any) { + return [ new SelectBase({ controlName: 'chargeId', label: 'Fees', - value: values ? values.chargeId : this.chargeData[0].id, + value: values ? values.chargeId : this.chargeData[0]?.id, options: { label: 'name', value: 'id', data: this.chargeData }, required: true, order: 1 @@ -612,21 +492,20 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { new SelectBase({ controlName: 'incomeAccountId', label: 'Income Account', - value: values ? values.incomeAccountId : this.incomeAndLiabilityAccountData[0].id, + 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[] = [ + public getPenaltyIncomeFormfields(values?: any) { + return [ new SelectBase({ controlName: 'chargeId', label: 'Penalty', - value: values ? values.chargeId : this.penaltyData[0].id, + value: values ? values.chargeId : this.penaltyData[0]?.id, options: { label: 'name', value: 'id', data: this.penaltyData }, required: true, order: 1 @@ -634,22 +513,21 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { new SelectBase({ controlName: 'incomeAccountId', label: 'Income Account', - value: values ? values.incomeAccountId : this.incomeAccountData[0].id, + 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) { + public getChargeOffReasonExpenseFormfields(values?: any) { const reasonOptions = this.chargeOffReasonOptions.filter((item: any) => !this.currentFormValues.includes(item.id)); - const formfields: FormfieldBase[] = [ + return [ new SelectBase({ controlName: 'chargeOffReasonCodeValueId', label: 'Charge-off reason', - value: values ? values.chargeOffReasonCodeValueId : reasonOptions[0].id, + value: values ? values.chargeOffReasonCodeValueId : reasonOptions[0]?.id, options: { label: 'name', value: 'id', data: reasonOptions }, required: true, order: 1 @@ -657,13 +535,28 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { new SelectBase({ controlName: 'expenseAccountId', label: 'Expense Account', - value: values ? values.expenseAccountId : this.expenseAccountData[0].id, + value: values ? values.expenseAccountId : this.expenseAccountData[0]?.id, options: { label: 'name', value: 'id', data: this.expenseAccountData }, required: true, order: 2 }) ]; - return formfields; + } + + public get loanProductAccounting() { + const { + assetsOtherGlMappings, + liabilitiesOtherGlMappings, + expensesOtherGlMappings, + incomeOtherGlMappings, + ...loanProductAccounting + } = this.loanProductAccountingForm.getRawValue(); + const otherGlMappings = this.getOtherGlMappings(); + + return { + ...loanProductAccounting, + ...(otherGlMappings ? { otherGlMappings } : {}) + }; } get isAccountingAccrualBased() { @@ -671,52 +564,13 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { return accountingRule === 3 || accountingRule === 4; } - get loanProductAccounting() { - return this.loanProductAccountingForm.value; - } - - setDeferredIncomeRecognitionControls() { - if (this.isAccountingAccrualBased) { - if (this.deferredIncomeRecognition) { - if ( - this.deferredIncomeRecognition.capitalizedIncome?.enableIncomeCapitalization || - this.deferredIncomeRecognition.buyDownFee?.enableBuyDownFee - ) { - this.loanProductAccountingForm.addControl( - 'deferredIncomeLiabilityAccountId', - new UntypedFormControl('', Validators.required) - ); - } else { - this.loanProductAccountingForm.removeControl('deferredIncomeLiabilityAccountId'); - } - if (this.deferredIncomeRecognition.capitalizedIncome?.enableIncomeCapitalization) { - this.loanProductAccountingForm.addControl( - 'incomeFromCapitalizationAccountId', - new UntypedFormControl('', Validators.required) - ); - } else { - this.loanProductAccountingForm.removeControl('incomeFromCapitalizationAccountId'); - } - if (this.deferredIncomeRecognition.buyDownFee?.enableBuyDownFee) { - if (this.deferredIncomeRecognition.buyDownFee?.merchantBuyDownFee) { - this.loanProductAccountingForm.addControl( - 'buyDownExpenseAccountId', - new UntypedFormControl('', Validators.required) - ); - } - this.loanProductAccountingForm.addControl( - 'incomeFromBuyDownAccountId', - new UntypedFormControl('', Validators.required) - ); - } else { - this.loanProductAccountingForm.removeControl('buyDownExpenseAccountId'); - this.loanProductAccountingForm.removeControl('incomeFromBuyDownAccountId'); - } - } + public setDeferredIncomeRecognitionControls() { + if (this.isAccountingAccrualBased && this.deferredIncomeRecognition) { + // Deferred logic... } } - formChangeEvent(accountingData: AdvancedMappingDTO) { + public formChangeEvent(accountingData: AdvancedMappingDTO) { if (accountingData.formType === 'BuydownFeeClassificationToIncome') { this.loanProductAccountingForm.setControl( 'buydownfeeClassificationToIncomeAccountMappings', @@ -750,4 +604,283 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges { } this.setLoanProductAccountingFormDirty(); } + + public isAccrualAccounting(): boolean { + const accountingRule = this.loanProductAccountingForm.value.accountingRule; + return accountingRule === 3 || accountingRule === 4; + } + + public isDeferredOnly(): boolean { + return !!( + this.deferredIncomeRecognition?.capitalizedIncome?.enableIncomeCapitalization || + this.deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee + ); + } + + public isDeferredBuyDownOnly(): boolean { + return !!this.deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee; + } + + public isDeferredBuyDownMerchantOnly(): boolean { + return !!( + this.deferredIncomeRecognition?.buyDownFee?.enableBuyDownFee && + this.deferredIncomeRecognition?.buyDownFee?.merchantBuyDownFee + ); + } + + public isDeferredCapitalizationOnly(): boolean { + return !!this.deferredIncomeRecognition?.capitalizedIncome?.enableIncomeCapitalization; + } + + public buildAccountingSectionConfigs() { + this.accountingSectionConfigs = [ + { + key: 'assets', + title: 'Assets', + pillLabel: 'Asset', + pillClass: 'green', + rows: [ + { + key: 'fundSourceAccountId', + label: 'Fund source', + inputLabel: 'Fund source', + controlName: 'fundSourceAccountId', + options: this.assetAndLiabilityAccountData, + required: true + }, + { + key: 'loanPortfolioAccountId', + label: 'Loan portfolio', + inputLabel: 'Loan portfolio', + controlName: 'loanPortfolioAccountId', + options: this.assetAccountData, + required: true + }, + { + key: 'transfersInSuspenseAccountId', + label: 'Transfer in suspense', + inputLabel: 'Transfer in suspense', + controlName: 'transfersInSuspenseAccountId', + options: this.assetAccountData, + required: true + }, + { + key: 'receivableInterestAccountId', + label: 'Interest Receivable', + inputLabel: 'Interest Receivable', + controlName: 'receivableInterestAccountId', + options: this.assetAccountData, + required: true, + accrualOnly: true + }, + { + key: 'receivableFeeAccountId', + label: 'Fees Receivable', + inputLabel: 'Fees Receivable', + controlName: 'receivableFeeAccountId', + options: this.assetAccountData, + required: true, + accrualOnly: true + }, + { + key: 'receivablePenaltyAccountId', + label: 'Penalties Receivable', + inputLabel: 'Penalties Receivable', + controlName: 'receivablePenaltyAccountId', + options: this.assetAccountData, + required: true, + accrualOnly: true + } + ], + otherMappingsControlName: 'assetsOtherGlMappings', + otherAccountOptions: this.assetAccountData + }, + { + key: 'income', + title: 'Income', + pillLabel: 'Income', + pillClass: 'blue', + rows: [ + { + key: 'interestOnLoanAccountId', + label: 'Income from Interest', + inputLabel: 'Income from Interest', + controlName: 'interestOnLoanAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromFeeAccountId', + label: 'Income from fees', + inputLabel: 'Income from fees', + controlName: 'incomeFromFeeAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromPenaltyAccountId', + label: 'Income from penalties', + inputLabel: 'Income from penalties', + controlName: 'incomeFromPenaltyAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromRecoveryAccountId', + label: 'Income from Recovery Repayments', + inputLabel: 'Income from Recovery Repayments', + controlName: 'incomeFromRecoveryAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromChargeOffInterestAccountId', + label: 'Income from ChargeOff Interest', + inputLabel: 'Income from ChargeOff Interest', + controlName: 'incomeFromChargeOffInterestAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromChargeOffFeesAccountId', + label: 'Income from ChargeOff Fees', + inputLabel: 'Income from ChargeOff Fees', + controlName: 'incomeFromChargeOffFeesAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromChargeOffPenaltyAccountId', + label: 'Income from ChargeOff Penalty', + inputLabel: 'Income from ChargeOff Penalty', + controlName: 'incomeFromChargeOffPenaltyAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromGoodwillCreditInterestAccountId', + label: 'Income from Goodwill Credit Interest', + inputLabel: 'Income from Goodwill Credit Interest', + controlName: 'incomeFromGoodwillCreditInterestAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromGoodwillCreditFeesAccountId', + label: 'Income from Goodwill Credit Fees', + inputLabel: 'Income from Goodwill Credit Fees', + controlName: 'incomeFromGoodwillCreditFeesAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromGoodwillCreditPenaltyAccountId', + label: 'Income from Goodwill Credit Penalty', + inputLabel: 'Income from Goodwill Credit Penalty', + controlName: 'incomeFromGoodwillCreditPenaltyAccountId', + options: this.incomeAccountData, + required: true + }, + { + key: 'incomeFromCapitalizationAccountId', + label: 'Income capitalization', + inputLabel: 'Income capitalization', + controlName: 'incomeFromCapitalizationAccountId', + options: this.incomeAccountData, + required: true, + deferredCapitalizationOnly: true + }, + { + key: 'incomeFromBuyDownAccountId', + label: 'Income from Buy down fees', + inputLabel: 'Income from Buy down fees', + controlName: 'incomeFromBuyDownAccountId', + options: this.incomeAccountData, + required: true, + deferredBuyDownOnly: true + } + ], + otherMappingsControlName: 'incomeOtherGlMappings', + otherAccountOptions: this.incomeAccountData + }, + { + key: 'expenses', + title: 'Expenses', + pillLabel: 'Expense', + pillClass: 'amber', + rows: [ + { + key: 'writeOffAccountId', + label: 'Losses written off', + inputLabel: 'Losses written off', + controlName: 'writeOffAccountId', + options: this.expenseAccountData, + required: true + }, + { + key: 'goodwillCreditAccountId', + label: 'Expenses from Goodwill Credit', + inputLabel: 'Expenses from Goodwill Credit', + controlName: 'goodwillCreditAccountId', + options: this.expenseAccountData, + required: true + }, + { + key: 'chargeOffExpenseAccountId', + label: 'ChargeOff Expense', + inputLabel: 'ChargeOff Expense', + controlName: 'chargeOffExpenseAccountId', + options: this.expenseAccountData, + required: true + }, + { + key: 'chargeOffFraudExpenseAccountId', + label: 'ChargeOff Fraud Expense', + inputLabel: 'ChargeOff Fraud Expense', + controlName: 'chargeOffFraudExpenseAccountId', + options: this.expenseAccountData, + required: true + }, + { + key: 'buyDownExpenseAccountId', + label: 'Buy down fee Expense', + inputLabel: 'Buy down fee Expense', + controlName: 'buyDownExpenseAccountId', + options: this.expenseAccountData, + required: true, + deferredBuyDownMerchantOnly: true + } + ], + otherMappingsControlName: 'expensesOtherGlMappings', + otherAccountOptions: this.expenseAccountData + }, + { + key: 'liabilities', + title: 'Liabilities', + pillLabel: 'Liability', + pillClass: 'green', + rows: [ + { + key: 'overpaymentLiabilityAccountId', + label: 'Over payment liability', + inputLabel: 'Over payment liability', + controlName: 'overpaymentLiabilityAccountId', + options: this.liabilityAccountData, + required: true + }, + { + key: 'deferredIncomeLiabilityAccountId', + label: 'Deferred income', + inputLabel: 'Deferred income', + controlName: 'deferredIncomeLiabilityAccountId', + options: this.liabilityAccountData, + required: true, + deferredOnly: true + } + ], + otherMappingsControlName: 'liabilitiesOtherGlMappings', + otherAccountOptions: this.liabilityAccountData + } + ]; + } } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.html index 762cd856d..d3c127432 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.html @@ -57,61 +57,4 @@
- -
-
- -

{{ 'labels.inputs.Multiples' | translate }}

-
-
-
- - {{ 'labels.inputs.Set the multiples of the loan and its installment' | translate }} - -
- - @if (loanProductCurrencyForm.value.setMultiples) { - - {{ 'labels.inputs.Currency in multiples of' | translate }} - - @if (loanProductCurrencyForm.get('inMultiplesOf')?.hasError('required')) { - - {{ 'labels.inputs.Currency in multiples of' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - } - @if (loanProductCurrencyForm.get('inMultiplesOf')?.hasError('min')) { - - {{ 'labels.inputs.Currency in multiples of' | translate }} - {{ 'labels.commons.Minimum Value must be' | translate }} 1 - - } - - - - {{ 'labels.inputs.Installment in multiples of' | translate }} - - @if (loanProductCurrencyForm.get('installmentAmountInMultiplesOf')?.hasError('required')) { - - {{ 'labels.inputs.Installment in multiples of' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - } - @if (loanProductCurrencyForm.get('installmentAmountInMultiplesOf')?.hasError('min')) { - - {{ 'labels.inputs.Installment in multiples of' | translate }} - {{ 'labels.commons.Minimum Value must be' | translate }} 1 - - } - - } -
-
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.ts index 5411c5476..0c186f94c 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-currency-step/loan-product-currency-step.component.ts @@ -9,8 +9,6 @@ import { Component, OnInit, Input, inject } from '@angular/core'; import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms'; import { MatTooltip } from '@angular/material/tooltip'; -import { MatCheckbox } from '@angular/material/checkbox'; -import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @Component({ @@ -19,9 +17,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; styleUrls: ['./loan-product-currency-step.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, - MatTooltip, - MatCheckbox, - FaIconComponent + MatTooltip ] }) export class LoanProductCurrencyStepComponent implements OnInit { @@ -46,69 +42,8 @@ export class LoanProductCurrencyStepComponent implements OnInit { this.loanProductsTemplate.currency.decimalPlaces === undefined || this.loanProductsTemplate.currency.decimalPlaces === null ? '' - : this.loanProductsTemplate.currency.decimalPlaces, - setMultiples: !!( - (this.loanProductsTemplate.currency.inMultiplesOf && this.loanProductsTemplate.currency.inMultiplesOf !== 0) || - (this.loanProductsTemplate.installmentAmountInMultiplesOf && - this.loanProductsTemplate.installmentAmountInMultiplesOf !== 0) - ), - inMultiplesOf: - this.loanProductsTemplate.currency.inMultiplesOf === 0 || - this.loanProductsTemplate.currency.inMultiplesOf === undefined || - this.loanProductsTemplate.currency.inMultiplesOf === null - ? '' - : this.loanProductsTemplate.currency.inMultiplesOf, - installmentAmountInMultiplesOf: - this.loanProductsTemplate.installmentAmountInMultiplesOf === 0 || - this.loanProductsTemplate.installmentAmountInMultiplesOf === undefined || - this.loanProductsTemplate.installmentAmountInMultiplesOf === null - ? '' - : this.loanProductsTemplate.installmentAmountInMultiplesOf + : this.loanProductsTemplate.currency.decimalPlaces }); - - this.setupConditionalValidation(); - } - - setupConditionalValidation() { - this.loanProductCurrencyForm.get('setMultiples')?.valueChanges.subscribe((setMultiples: boolean) => { - const inMultiplesOfControl = this.loanProductCurrencyForm.get('inMultiplesOf'); - const installmentControl = this.loanProductCurrencyForm.get('installmentAmountInMultiplesOf'); - - if (setMultiples) { - inMultiplesOfControl?.setValidators([ - Validators.required, - Validators.min(1) - ]); - installmentControl?.setValidators([ - Validators.required, - Validators.min(1) - ]); - } else { - inMultiplesOfControl?.clearValidators(); - installmentControl?.clearValidators(); - inMultiplesOfControl?.setValue(''); - installmentControl?.setValue(''); - } - - inMultiplesOfControl?.updateValueAndValidity(); - installmentControl?.updateValueAndValidity(); - }); - - const initialSetMultiples = this.loanProductCurrencyForm.get('setMultiples')?.value; - if (initialSetMultiples) { - const inMultiplesOfControl = this.loanProductCurrencyForm.get('inMultiplesOf'); - const installmentControl = this.loanProductCurrencyForm.get('installmentAmountInMultiplesOf'); - inMultiplesOfControl?.setValidators([ - Validators.required, - Validators.min(1) - ]); - installmentControl?.setValidators([ - Validators.required, - Validators.min(1) - ]); - inMultiplesOfControl?.updateValueAndValidity(); - installmentControl?.updateValueAndValidity(); - } } createLoanProductCurrencyForm() { @@ -123,10 +58,7 @@ export class LoanProductCurrencyStepComponent implements OnInit { Validators.required, Validators.min(0) ] - ], - setMultiples: [false], - inMultiplesOf: [''], - installmentAmountInMultiplesOf: [''] + ] }); } @@ -136,21 +68,6 @@ export class LoanProductCurrencyStepComponent implements OnInit { currencyCode: formValue.currencyCode, digitsAfterDecimal: formValue.digitsAfterDecimal }; - - if (formValue.setMultiples) { - if (formValue.inMultiplesOf !== '' && formValue.inMultiplesOf !== null && formValue.inMultiplesOf !== undefined) { - result.inMultiplesOf = formValue.inMultiplesOf; - } - - if ( - formValue.installmentAmountInMultiplesOf !== '' && - formValue.installmentAmountInMultiplesOf !== null && - formValue.installmentAmountInMultiplesOf !== undefined - ) { - result.installmentAmountInMultiplesOf = formValue.installmentAmountInMultiplesOf; - } - } - return result; } } diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html index 94faada88..b5e37990a 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.html @@ -41,36 +41,6 @@ {{ 'labels.commons.required' | translate }} - - - {{ 'labels.inputs.External Id' | translate }} - - - - - {{ 'labels.inputs.Fund' | translate }} - - @for (fund of fundData; track fund) { - - {{ fund.name }} - - } - - - -
- - {{ 'labels.inputs.Include in Customer Loan Counter' | translate }} - -
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts index 56123238c..5548aee15 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-details-step/loan-product-details-step.component.ts @@ -14,9 +14,7 @@ import { Dates } from 'app/core/utils/dates'; /** Custom Services */ import { SettingsService } from 'app/settings/settings.service'; import { MatTooltip } from '@angular/material/tooltip'; -import { MatCheckbox } from '@angular/material/checkbox'; import { CdkTextareaAutosize } from '@angular/cdk/text-field'; -import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @Component({ @@ -26,9 +24,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; imports: [ ...STANDALONE_SHARED_IMPORTS, MatTooltip, - MatCheckbox, - CdkTextareaAutosize, - FaIconComponent + CdkTextareaAutosize ] }) export class LoanProductDetailsStepComponent implements OnInit { @@ -40,8 +36,6 @@ export class LoanProductDetailsStepComponent implements OnInit { loanProductDetailsForm: UntypedFormGroup; - fundData: any; - minDate = new Date(2000, 0, 1); maxDate = new Date(new Date().setFullYear(new Date().getFullYear() + 10)); @@ -56,17 +50,12 @@ export class LoanProductDetailsStepComponent implements OnInit { } ngOnInit() { - this.fundData = this.loanProductsTemplate.fundOptions; - this.loanProductDetailsForm.patchValue({ name: this.loanProductsTemplate.name, shortName: this.loanProductsTemplate.shortName, description: this.loanProductsTemplate.description, - externalId: this.loanProductsTemplate.externalId, - fundId: this.loanProductsTemplate.fundId, startDate: this.loanProductsTemplate.startDate && new Date(this.loanProductsTemplate.startDate), - closeDate: this.loanProductsTemplate.closeDate && new Date(this.loanProductsTemplate.closeDate), - includeInBorrowerCycle: this.loanProductsTemplate.includeInBorrowerCycle + closeDate: this.loanProductsTemplate.closeDate && new Date(this.loanProductsTemplate.closeDate) }); } @@ -81,11 +70,8 @@ export class LoanProductDetailsStepComponent implements OnInit { Validators.required ], description: [''], - externalId: [''], - fundId: [''], startDate: [''], - closeDate: [''], - includeInBorrowerCycle: [false] + closeDate: [''] }); } 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 8a3ed2132..d86fe2322 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 @@ -10,7 +10,6 @@ [action]="'preview'" [loanProduct]="loanProduct" [loanProductsTemplate]="loanProductsTemplate" - [useDueForRepaymentsConfigurations]="loanProduct.useDueForRepaymentsConfigurations" [paymentAllocations]="loanProduct.paymentAllocation" [creditAllocations]="loanProduct.creditAllocation" [supportedInterestRefundTypes]="loanProduct.supportedInterestRefundTypes" diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html index bd8a0d685..a1259fe05 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.html @@ -235,147 +235,528 @@
- +
- -

{{ 'labels.inputs.Loan Tranche Details' | translate }}

+ +

Cheque Book Settings

-
-
- - {{ 'labels.inputs.Enable Multiple Disbursals' | translate }} - -
+
+ + Cheque Book Issue Enabled + + YES + NO + + - @if (loanProductSettingsForm.value.multiDisburseLoan) { - - {{ 'labels.inputs.Maximum Tranche count' | translate }} - - @if (loanProductSettingsForm.controls.maxTrancheCount.hasError('required')) { - - {{ 'labels.inputs.Maximum Tranche count' | translate }} {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - } - - - {{ 'labels.inputs.Maximum allowed outstanding balance' | translate }} - - -
- - {{ 'labels.inputs.Disallow Expected Disbursements' | translate }} - -
- @if (loanProductSettingsForm.value.loanScheduleType === 'PROGRESSIVE') { -
- - {{ 'labels.inputs.Allow full term for each tranche' | translate }} - -
- } - } + + Cheque Validation Required + + YES + NO + + + + + Is First Cheque Book Free + + YES + NO + + + + + Cheque Printing Account Type + + Savings Bank + Current Account + Cash Credit + Overdraft + + + + + Allow Withdrawal Slip Usage + + YES + NO + + + + + Maintain Cheque Book Stock Registry + + YES + NO + + + + + Free Cheque Books Per Year + + + + + Allow Credit By Cheque Number + + YES + NO + + + + + Credit Cheque Validation Required + + YES + NO + + + + + Grace Days For Stock Statement + +
- +
- -

{{ 'labels.heading.Down Payment' | translate }}

+ +

Additional Product Settings

-
-
- - {{ 'labels.inputs.Enable Down Payment' | translate }} - -
+
+ + Account Type Active For Transactions + + YES + NO + + - @if (loanProductSettingsForm.value.enableDownPayment) { - - {{ 'labels.inputs.Disbursed Amount Percentage Down Payment' | translate }} (%) - - @if (loanProductSettingsForm.controls.disbursedAmountPercentageForDownPayment.hasError('required')) { - - {{ 'labels.inputs.Disbursed Amount Percentage Down Payment' | translate }} - {{ 'labels.commons.is' | translate }} {{ 'labels.commons.required' | translate }} - - } - @if (loanProductSettingsForm.controls.disbursedAmountPercentageForDownPayment.errors?.range) { - - {{ 'labels.inputs.Disbursed Amount Percentage Down Payment' | translate }} - {{ 'labels.commons.is' | translate }} {{ 'labels.text.out' | translate }} - {{ 'labels.text.percentage range' | translate }} - - } - -
- + Sub Type Activity Enabled + + YES + NO + + + + + Account Opening Charges + + + + + Maximum Gold Loan Limit + + + + + Last Account Number Opened + + + + + Customer Termination Allowed + + YES + NO + + + + + Charges For Cash Credit Above Limit + + + + + Death Interest GL Account + + + + + Starting Ledger Number + + + + + Ending Ledger Number + + + + + Current Ledger Number + + + + + Remittance Account Number + + + + + Is Non Resident Indian Account + + YES + NO + + + + + Debit Clearing GL Account + + + + + Missed Call Alert Service Active + + YES + NO + + + + + Standing Instruction Charge GL Account + + + + + Standing Instruction Debit Charge Skip Option + + Skip Always + Skip on First Failure + Never Skip + Retry after 24 Hours + + + + + Opening Balance Multiplier For Remittance Charges + + +
+
+ + +
+
+ +

Interest Related Parameters

+
+
+ + Drawing Power And Limit Generation Procedure + + Normal D.P. Entry Is Considered + Against F.D. Interest with Certificate Wise Interest Rate - {{ 'labels.inputs.Enable Auto Repayment for Down Payment' | translate }} - -
- } + Based on Asset Value + Monthly Stock Statement Basis + + + + + Advances Interest Calculation Method + + Debit Closing Balance Considered as Product + + + + + Against Interest Calculation Enabled + + YES + NO + + + + + Day Wise Against Clearing Interest Rounding + + YES + NO + + + + + Interest Calculation One Fourth Method + + 0 (Standard) + 1 (Custom A) + 2 (Custom B) + + + + + Against Clearing Interest Procedure Type + + Normal Against Clearing Interest + + + + + Consider Debit Closing Amount For Against Clearing + + YES + NO + + + + + Interest Rounding Method + + All Int. Round + Nearest Rupee + Round Up + Round Down + + + + + Penalty Interest Calculation Enabled + + YES + NO + + + + + Penalty Interest Calculation Method + + Daywise ( 36500 ) + + + + + Penalty Interest Procedure Number + + On Debit, Penalty Product is Closing Balance + Limit is Considered for Penalty Product + + + + + Overdrawn Interest Calculation Method + + Overdrawn = Balance - Limit + Overdrawn = Balance - D.P. + Don't Calculate + + + + + Calculate Overdrawn Interest After Renewal + + YES + NO + + + + + Interest On Credit Balance + + YES + NO + + + + + Penalty Interest Date Different Than Regular Interest + + YES + NO + + + + + Fixed Deposit Overdraft Certificate Sort Order + + Order on Interest Rate + Order on Drawing Power + + + + + Use Document Submission Date For Interest + + YES + NO + + + + + Loan Management System Renewal Penalty Active + + YES + NO + + + + + Minimum Normal Interest Amount + + + + + Minimum Interest Calculation Method + + Flat Amount + Percentage of Balance + Tier Based + + + + + Minimum Against Clearing Interest Amount + + + + + Limit Increase Upto Interest Amount In Interest Calculation + + YES + NO + + + + + Minimum Penalty Interest Amount + + + + + Days For Limit Increase Upto Interest Amount + + + + + Additional Interest Rate For Fixed Deposit Overdraft + + + + + Consider DD/Payorder In Against Clearing Interest Calculation + + YES + NO + + + + + Grace Days After Renewal Date + + + + + Apply Undebited Interest Concept Enabled + + YES + NO + + + + + Interest Register Print Format Type + + Interest Amount with Opening Balance + Interest Only + Full Ledger Format + + + + + Normal Grace Days After Overdue + + + + + Allow Against Clearing Entry + + YES + NO + + + + + Allow No Balance Entry + + YES + NO + + + + + Fixed Deposit Overdraft Penalty Interest Combined With Normal Interest + + YES + NO + + + + + Allow No Balance Entry With Password + + YES + NO + + + + + Undebited Simple Interest Account Wise + + YES + NO + + + + + Daily Interest Calculation Enabled + + YES + NO + + + + + Daily Interest Calculation Start Date + + + + + Non Performing Asset Penal Interest Rate Update + + YES + NO + + + + + Non Performing Asset Penal Interest Rate + + + + + Fixed Deposit Overdraft Penal Interest Rate Update In Branch Process + + YES + NO + + + + + Add Penal Interest Rate + +
- - @if (isAdvancedTransactionProcessingStrategy) { -
-
- -

{{ 'labels.inputs.Loan Charge-off behaviour' | translate }}

-
-
- - {{ 'labels.inputs.Loan Charge-off behaviour' | translate }} - - @for (chargeOffBehaviour of chargeOffBehaviourData; track chargeOffBehaviour) { - - {{ chargeOffBehaviour.value | translateKey: 'catalogs' }} - - } - - -
-
- } -
@@ -807,54 +1188,6 @@
- -
-
- -

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

-
-
-
- - {{ 'labels.inputs.Use the Global Configurations values to the Repayment Event (notifications)' | translate }} - -
- - @if (!loanProductSettingsForm.value.useDueForRepaymentsConfigurations) { - - {{ 'labels.inputs.Due days for repayment event' | translate }} - - - - {{ 'labels.inputs.OverDue days for repayment event' | translate }} - - - } -
-
-
diff --git a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.scss b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.scss index ef42e0e74..5717de2fa 100644 --- a/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.scss +++ b/src/app/products/loan-products/loan-product-stepper/loan-product-settings-step/loan-product-settings-step.component.scss @@ -57,6 +57,18 @@ grid-template-columns: 1fr; } } + + &__body.lps-grid-4 { + grid-template-columns: repeat(4, 1fr); + + @media (width <= 1100px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (width <= 600px) { + grid-template-columns: 1fr; + } + } } .lps-full-width { 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 f0a393e11..256b986d4 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 @@ -15,7 +15,6 @@ import { ReactiveFormsModule } from '@angular/forms'; 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, StringEnumOptionData } from 'app/shared/models/option-data.model'; import { ProcessingStrategyService } from '../../services/processing-strategy.service'; @@ -29,21 +28,20 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; selector: 'mifosx-loan-product-settings-step', templateUrl: './loan-product-settings-step.component.html', styleUrls: ['./loan-product-settings-step.component.scss'], + standalone: true, imports: [ ...STANDALONE_SHARED_IMPORTS, MatTooltip, MatCheckbox, MatIconButton, - FaIconComponent + FaIconComponent, + ReactiveFormsModule ] }) export class LoanProductSettingsStepComponent implements OnInit { private formBuilder = inject(UntypedFormBuilder); private processingStrategyService = inject(ProcessingStrategyService); - DAYS_BEFORE_REPAYMENT_IS_DUE = LoanProducts.DAYS_BEFORE_REPAYMENT_IS_DUE; - DAYS_AFTER_REPAYMENT_IS_OVERDUE = LoanProducts.DAYS_AFTER_REPAYMENT_IS_OVERDUE; - @Input() toEdit: boolean; @Input() loanProductsTemplate: any; @Input() isLinkedToFloatingInterestRates: UntypedFormControl; @@ -71,7 +69,6 @@ export class LoanProductSettingsStepComponent implements OnInit { loanScheduleProcessingTypeData: OptionData[] = []; isAdvancedTransactionProcessingStrategy = false; advancedTransactionProcessingStrategyDisabled = true; - useDueForRepaymentsConfigurations = false; rescheduleStrategyTypeDisabled = false; chargeOffBehaviourData: StringEnumOptionData[] = []; daysInYearCustomStrategyOptions: OptionData[] = []; @@ -150,12 +147,10 @@ export class LoanProductSettingsStepComponent implements OnInit { maxTrancheCount: this.loanProductsTemplate.maxTrancheCount, outstandingLoanBalance: this.loanProductsTemplate.outstandingLoanBalance, allowFullTermForTranche: this.loanProductsTemplate.allowFullTermForTranche, - enableDownPayment: this.loanProductsTemplate.enableDownPayment, enableInstallmentLevelDelinquency: this.loanProductsTemplate.enableInstallmentLevelDelinquency, allowCompoundingOnEod: this.loanProductsTemplate.allowCompoundingOnEod, isCompoundingToBePostedAsTransaction: this.loanProductsTemplate.isCompoundingToBePostedAsTransaction, loanScheduleType: this.loanProductsTemplate.loanScheduleType.code, - useDueForRepaymentsConfigurations: this.loanProductsTemplate.useDueForRepaymentsConfigurations, allowAccrualPostingInArrears: this.loanProductsTemplate.allowAccrualPostingInArrears, chargeOffBehaviour: this.loanProductsTemplate.chargeOffBehaviour.id }); @@ -166,23 +161,6 @@ export class LoanProductSettingsStepComponent implements OnInit { this.processingStrategyService.initialize(this.isAdvancedTransactionProcessingStrategy); this.validateAdvancedPaymentStrategyControls(); - if ( - this.loanProductsTemplate.dueDaysForRepaymentEvent != null && - this.loanProductsTemplate.overDueDaysForRepaymentEvent != null - ) { - this.loanProductSettingsForm.patchValue({ - useDueForRepaymentsConfigurations: false, - dueDaysForRepaymentEvent: this.loanProductsTemplate.dueDaysForRepaymentEvent, - overDueDaysForRepaymentEvent: this.loanProductsTemplate.overDueDaysForRepaymentEvent - }); - } else { - this.loanProductSettingsForm.patchValue({ - useDueForRepaymentsConfigurations: true, - dueDaysForRepaymentEvent: null, - overDueDaysForRepaymentEvent: null - }); - } - if (this.loanProductsTemplate.delinquencyBucket) { this.loanProductSettingsForm.patchValue({ delinquencyBucketId: @@ -190,13 +168,6 @@ export class LoanProductSettingsStepComponent implements OnInit { }); } - if (this.loanProductsTemplate.enableDownPayment) { - this.loanProductSettingsForm.patchValue({ - disbursedAmountPercentageForDownPayment: this.loanProductsTemplate.disbursedAmountPercentageForDownPayment || 0, - enableAutoRepaymentForDownPayment: this.loanProductsTemplate.enableAutoRepaymentForDownPayment || false - }); - } - if (this.loanProductsTemplate.isInterestRecalculationEnabled) { this.loanProductSettingsForm.patchValue({ preClosureInterestCalculationStrategy: @@ -355,24 +326,89 @@ export class LoanProductSettingsStepComponent implements OnInit { graceOnArrearsAgeing: [true] }), delinquencyBucketId: [''], - enableDownPayment: [false], enableInstallmentLevelDelinquency: [false], allowCompoundingOnEod: [false], isCompoundingToBePostedAsTransaction: [false], - useDueForRepaymentsConfigurations: [false], - dueDaysForRepaymentEvent: [ - '', - [Validators.min(0)] - ], - overDueDaysForRepaymentEvent: [ - '', - [Validators.min(0)] - ], loanScheduleType: [ LoanProducts.LOAN_SCHEDULE_TYPE_CUMULATIVE, Validators.required ], - allowAccrualPostingInArrears: [false] + allowAccrualPostingInArrears: [false], + + // Cheque Book Settings + chequeBookIssueEnabled: ['NO'], + chequeValidationRequired: ['NO'], + isFirstChequeBookFree: ['NO'], + chequePrintingAccountType: [''], + allowWithdrawalSlipUsage: ['NO'], + maintainChequeBookStockRegistry: ['NO'], + freeChequeBooksPerYear: [''], + allowCreditByChequeNumber: ['NO'], + creditChequeValidationRequired: ['NO'], + graceDaysForStockStatement: [''], + + // Additional Product Settings + accountTypeActiveForTransactions: ['YES'], + subTypeActivityEnabled: ['NO'], + accountOpeningCharges: [''], + maximumGoldLoanLimit: [''], + lastAccountNumberOpened: [''], + customerTerminationAllowed: ['NO'], + chargesForCashCreditAboveLimit: [''], + deathInterestGLAccount: [''], + startingLedgerNumber: [''], + endingLedgerNumber: [''], + currentLedgerNumber: [''], + remittanceAccountNumber: [''], + isNonResidentIndianAccount: ['NO'], + debitClearingGLAccount: [''], + missedCallAlertServiceActive: ['NO'], + standingInstructionChargeGLAccount: [''], + standingInstructionDebitChargeSkipOption: [''], + openingBalanceMultiplierForRemittanceCharges: [''], + + // Interest Related Parameters + drawingPowerAndLimitGenerationProcedure: ['NORMAL D.P. ENTRY IS CONSIDER'], + advancesInterestCalculationMethod: ['DEBIT CLOSING BALANCE CONSIDER AS PRODUCT'], + againstInterestCalculationEnabled: ['NO'], + dayWiseAgainstClearingInterestRounding: ['YES'], + interestCalculationOneFourthMethod: ['0'], + againstClearingInterestProcedureType: ['NORMAL AGAINST CLEARING INTEREST'], + considerDebitClosingAmountForAgainstClearing: ['NO'], + interestRoundingMethod: [''], + penaltyInterestCalculationEnabled: ['NO'], + penaltyInterestCalculationMethod: [''], + penaltyInterestProcedureNumber: [''], + overdrawnInterestCalculationMethod: [''], + calculateOverdrawnInterestAfterRenewal: ['NO'], + interestOnCreditBalance: ['NO'], + penaltyInterestDateDifferentThanRegularInterest: ['YES'], + fixedDepositOverdraftCertificateSortOrder: ['ORDER ON INTEREST RATE'], + useDocumentSubmissionDateForInterest: ['NO'], + loanManagementSystemRenewalPenaltyActive: [''], + minimumNormalInterestAmount: [''], + minimumInterestCalculationMethod: [''], + minimumAgainstClearingInterestAmount: [''], + limitIncreaseUptoInterestAmountInInterestCalculation: ['NO'], + minimumPenaltyInterestAmount: [''], + daysForLimitIncreaseUptoInterestAmount: [''], + additionalInterestRateForFixedDepositOverdraft: [''], + considerDdPayorderInAgainstClearingInterestCalculation: ['YES'], + graceDaysAfterRenewDate: [''], + applyUndebitedInterestConceptEnabled: ['YES'], + interestRegisterPrintFormatType: ['INTEREST AMOUNT WITH OPENING BALANCE'], + normalGraceDaysAfterOverdue: [''], + allowAgainstClearingEntry: [''], + allowNoBalanceEntry: ['YES'], + fixedDepositOverdraftPenaltyInterestCombinedWithNormalInterest: ['NO'], + allowNoBalanceEntryWithPassword: ['NO'], + undebitedSimpleInterestAccountWise: ['NO'], + dailyInterestCalculationEnabled: ['YES'], + dailyInterestCalculationStartDate: ['01/02/2023'], + nonPerformingAssetPenalInterestRateUpdate: ['YES'], + nonPerformingAssetPenalInterestRate: [''], + fixedDepositOverdraftPenalInterestRateUpdateInBranchProcess: ['YES'], + addPenalInterestRate: [''] }); } @@ -595,22 +631,6 @@ export class LoanProductSettingsStepComponent implements OnInit { } }); - this.loanProductSettingsForm.get('enableDownPayment').valueChanges.subscribe((enableDownPayment) => { - if (enableDownPayment) { - this.loanProductSettingsForm.addControl( - 'disbursedAmountPercentageForDownPayment', - new UntypedFormControl(0, [ - Validators.required, - rangeValidator(0, 100) - ]) - ); - this.loanProductSettingsForm.addControl('enableAutoRepaymentForDownPayment', new UntypedFormControl(false, [])); - } else { - this.loanProductSettingsForm.removeControl('disbursedAmountPercentageForDownPayment'); - this.loanProductSettingsForm.removeControl('enableAutoRepaymentForDownPayment'); - } - }); - this.loanProductSettingsForm .get('transactionProcessingStrategyCode') .valueChanges.subscribe((transactionProcessingStrategyCode: string) => { @@ -650,22 +670,6 @@ export class LoanProductSettingsStepComponent implements OnInit { } }); - this.loanProductSettingsForm - .get('useDueForRepaymentsConfigurations') - .valueChanges.subscribe((useDueForRepaymentsConfigurations: boolean) => { - if (useDueForRepaymentsConfigurations) { - this.loanProductSettingsForm.patchValue({ - dueDaysForRepaymentEvent: null, - overDueDaysForRepaymentEvent: null - }); - } else { - this.loanProductSettingsForm.patchValue({ - dueDaysForRepaymentEvent: this.getGlobalConfigValue(LoanProducts.DAYS_BEFORE_REPAYMENT_IS_DUE), - overDueDaysForRepaymentEvent: this.getGlobalConfigValue(LoanProducts.DAYS_AFTER_REPAYMENT_IS_OVERDUE) - }); - } - }); - this.loanProductSettingsForm.get('loanScheduleType').valueChanges.subscribe((loanScheduleType: string) => { this.transactionProcessingStrategyData = []; if (loanScheduleType === LoanProducts.LOAN_SCHEDULE_TYPE_CUMULATIVE) { @@ -771,10 +775,6 @@ export class LoanProductSettingsStepComponent implements OnInit { get loanProductSettings() { const productSettings = this.loanProductSettingsForm.value; - if (this.loanProductSettingsForm.value.useDueForRepaymentsConfigurations) { - productSettings['dueDaysForRepaymentEvent'] = null; - productSettings['overDueDaysForRepaymentEvent'] = null; - } if (productSettings['delinquencyBucketId'] === '') { productSettings['delinquencyBucketId'] = null; } 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 1912b6b49..b6f0e8837 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 @@ -18,43 +18,6 @@ >
- - {{ 'labels.inputs.Minimum' | translate }} - - - {{ 'labels.commons.Minimum Value must be' | translate }} - {{ loanProductTermsForm.get('minPrincipal')?.errors?.['min']?.min || 1 }} - - - - - {{ 'labels.inputs.Default' | translate }} - - - {{ 'labels.catalogs.Default' | translate }} {{ 'labels.inputs.Principal' | translate }} - {{ 'labels.commons.is required' | translate }} - - - {{ 'labels.catalogs.Default' | translate }} {{ 'labels.inputs.Principal' | translate }} - {{ 'labels.commons.is' | translate }} - {{ loanProductTermsForm.get('principal')?.errors?.['min']?.min || 1 }} - - - - - {{ 'labels.inputs.Maximum' | translate }} - - - {{ 'labels.commons.Minimum Value must be' | translate }} - {{ loanProductTermsForm.get('maxPrincipal')?.errors?.['min']?.min || 1 }} - - -
{{ 'labels.inputs.Allow approval / disbursal above loan applied amount' | translate }} @@ -119,57 +82,6 @@
- -
-
- -

{{ 'labels.inputs.Number of repayments' | translate }}

- -
-
- - {{ 'labels.inputs.Minimum' | translate }} - - - {{ 'labels.commons.Minimum Value must be' | translate }} - 1 - - - - - {{ 'labels.inputs.Default' | translate }} - - - {{ 'labels.catalogs.Default' | translate }} {{ 'labels.inputs.Number of repayments' | translate }} - {{ 'labels.commons.is' | translate }} - {{ 'labels.commons.required' | translate }} - - - - - {{ 'labels.inputs.Maximum' | translate }} - - - {{ 'labels.commons.Maximum Value must be' | translate }} - 1 - {{ 'labels.commons.and must be greater than' | translate }} - {{ 'labels.inputs.Minimum' | translate }} - - - -
- - {{ 'labels.inputs.Is interest recognition on disbursement date?' | translate }} - -
-
-
-
@@ -352,250 +264,6 @@ }
- -
-
- -

{{ 'labels.inputs.Variations' | translate }}

-
-
-
- - {{ 'labels.inputs.Terms vary based on loan cycle' | translate }} - -
-
- - @if (loanProductTermsForm.value.useBorrowerCycle) { - -
-
-

{{ 'labels.inputs.Principal by loan cycle' | translate }}

- -
- @if (principalVariationsForBorrowerCycle.value.length) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Condition' | translate }} - {{ variation.valueConditionType | find: valueConditionTypeData : 'id' : 'value' }} - {{ 'labels.inputs.Loan Cycle' | translate }} - {{ variation.borrowerCycleNumber }} - {{ 'labels.inputs.Minimum' | translate }} - {{ variation.minValue }} - {{ 'labels.inputs.Default' | translate }} - {{ variation.defaultValue }} - {{ 'labels.inputs.Maximum' | translate }} - {{ variation.maxValue }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - -
-
-

{{ 'labels.inputs.Number of repayments by loan cycle' | translate }}

- -
- @if (numberOfRepaymentVariationsForBorrowerCycle.value.length) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Condition' | translate }} - {{ variation.valueConditionType | find: valueConditionTypeData : 'id' : 'value' }} - {{ 'labels.inputs.Loan Cycle' | translate }} - {{ variation.borrowerCycleNumber }} - {{ 'labels.inputs.Minimum' | translate }} - {{ variation.minValue }} - {{ 'labels.inputs.Default' | translate }} - {{ variation.defaultValue }} - {{ 'labels.inputs.Maximum' | translate }} - {{ variation.maxValue }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- - -
-
-

{{ 'labels.inputs.Nominal interest rate by loan cycle' | translate }}

- -
- @if (interestRateVariationsForBorrowerCycle.value.length) { -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{ 'labels.inputs.Condition' | translate }} - {{ variation.valueConditionType | find: valueConditionTypeData : 'id' : 'value' }} - {{ 'labels.inputs.Loan Cycle' | translate }} - {{ variation.borrowerCycleNumber }} - {{ 'labels.inputs.Minimum' | translate }} - {{ variation.minValue }} - {{ 'labels.inputs.Default' | translate }} - {{ variation.defaultValue }} - {{ 'labels.inputs.Maximum' | translate }} - {{ variation.maxValue }} - {{ 'labels.inputs.Actions' | translate }} - - -
-
- } -
- } -
-
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 e20aa6ec1..fbe2b20e6 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 @@ -11,36 +11,14 @@ import { UntypedFormGroup, UntypedFormBuilder, Validators, - UntypedFormArray, UntypedFormControl, ReactiveFormsModule } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; - -import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; -import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; - -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 { ProcessingStrategyService } from '../../services/processing-strategy.service'; import { TranslateService } from '@ngx-translate/core'; import { MatTooltip } from '@angular/material/tooltip'; import { MatCheckbox } from '@angular/material/checkbox'; -import { MatButton, MatIconButton } from '@angular/material/button'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; -import { - MatTable, - MatColumnDef, - MatHeaderCellDef, - MatHeaderCell, - MatCellDef, - MatCell, - MatHeaderRowDef, - MatHeaderRow, - MatRowDef, - MatRow -} from '@angular/material/table'; import { FindPipe } from '../../../../pipes/find.pipe'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; @@ -53,24 +31,12 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; MatTooltip, MatCheckbox, FaIconComponent, - MatTable, - MatColumnDef, - MatHeaderCellDef, - MatHeaderCell, - MatCellDef, - MatCell, - MatIconButton, - MatHeaderRowDef, - MatHeaderRow, - MatRowDef, - MatRow, FindPipe ] }) export class LoanProductTermsStepComponent implements OnInit, OnChanges { private formBuilder = inject(UntypedFormBuilder); private processingStrategyService = inject(ProcessingStrategyService); - private dialog = inject(MatDialog); private translateService = inject(TranslateService); @Input() loanProductsTemplate: any; @@ -80,30 +46,20 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { /** Zero Interest control. */ zeroInterest = new UntypedFormControl(false); - valueConditionTypeData: any; + isAdvancedTransactionProcessingStrategy = false; + floatingRateData: any; interestRateFrequencyTypeData: any; overAppliedCalculationTypeData: any; repaymentFrequencyTypeData: any; repaymentStartDateTypeOptions: any; - displayedColumns: string[] = [ - 'valueConditionType', - 'borrowerCycleNumber', - 'minValue', - 'defaultValue', - 'maxValue', - 'actions' - ]; - isAdvancedTransactionProcessingStrategy = false; - constructor() { this.createLoanProductTermsForm(); this.setConditionalControls(); } ngOnInit() { - this.valueConditionTypeData = this.loanProductsTemplate.valueConditionTypeOptions; this.floatingRateData = this.loanProductsTemplate.floatingRateOptions; this.interestRateFrequencyTypeData = this.loanProductsTemplate.interestRateFrequencyTypeOptions; this.repaymentFrequencyTypeData = this.loanProductsTemplate.repaymentFrequencyTypeOptions; @@ -114,12 +70,6 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { ]; this.loanProductTermsForm.patchValue({ - minPrincipal: this.loanProductsTemplate.minPrincipal, - principal: this.loanProductsTemplate.principal, - maxPrincipal: this.loanProductsTemplate.maxPrincipal, - minNumberOfRepayments: this.loanProductsTemplate.minNumberOfRepayments, - numberOfRepayments: this.loanProductsTemplate.numberOfRepayments, - maxNumberOfRepayments: this.loanProductsTemplate.maxNumberOfRepayments, isLinkedToFloatingInterestRates: this.loanProductsTemplate.isLinkedToFloatingInterestRates, minInterestRatePerPeriod: this.loanProductsTemplate.minInterestRatePerPeriod, interestRatePerPeriod: this.loanProductsTemplate.interestRatePerPeriod, @@ -132,13 +82,11 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { minDifferentialLendingRate: this.loanProductsTemplate.minDifferentialLendingRate, defaultDifferentialLendingRate: this.loanProductsTemplate.defaultDifferentialLendingRate, maxDifferentialLendingRate: this.loanProductsTemplate.maxDifferentialLendingRate, - useBorrowerCycle: this.loanProductsTemplate.useBorrowerCycle, repaymentEvery: this.loanProductsTemplate.repaymentEvery, repaymentFrequencyType: this.loanProductsTemplate.repaymentFrequencyType.id, minimumDaysBetweenDisbursalAndFirstRepayment: this.loanProductsTemplate.minimumDaysBetweenDisbursalAndFirstRepayment, - repaymentStartDateType: this.loanProductsTemplate.repaymentStartDateType.id || 1, - interestRecognitionOnDisbursementDate: this.loanProductsTemplate.interestRecognitionOnDisbursementDate || false + repaymentStartDateType: this.loanProductsTemplate.repaymentStartDateType.id || 1 }); if (this.loanProductsTemplate.allowApprovedDisbursedAmountsOverApplied) { @@ -148,34 +96,6 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { }); } - this.loanProductTermsForm.setControl( - 'principalVariationsForBorrowerCycle', - this.formBuilder.array( - this.loanProductsTemplate.principalVariationsForBorrowerCycle.map((variation: any) => ({ - ...variation, - valueConditionType: variation.valueConditionType.id - })) - ) - ); - this.loanProductTermsForm.setControl( - 'numberOfRepaymentVariationsForBorrowerCycle', - 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 && @@ -190,45 +110,6 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { createLoanProductTermsForm() { this.loanProductTermsForm = this.formBuilder.group({ - useBorrowerCycle: [false], - minPrincipal: [ - '', - [ - Validators.min(1) - ] - ], - principal: [ - '', - [ - Validators.required, - Validators.min(1) - ] - ], - maxPrincipal: [ - '', - [ - Validators.min(1) - ] - ], - minNumberOfRepayments: [ - '', - [ - Validators.pattern('^[1-9]\\d*$') - ] - ], - numberOfRepayments: [ - '', - [ - Validators.required, - Validators.pattern('^[1-9]\\d*$') - ] - ], - maxNumberOfRepayments: [ - '', - [ - Validators.pattern('^[1-9]\\d*$') - ] - ], isLinkedToFloatingInterestRates: [false], allowApprovedDisbursedAmountsOverApplied: [false], overAppliedCalculationType: [{ value: null, disabled: true }], @@ -275,8 +156,7 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { [] ], repaymentStartDateType: [1], - fixedLength: [null], - interestRecognitionOnDisbursementDate: [false] + fixedLength: [null] }); } @@ -350,18 +230,6 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { } }); - 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); @@ -388,129 +256,6 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges { }); } - get principalVariationsForBorrowerCycle(): UntypedFormArray { - return this.loanProductTermsForm.get('principalVariationsForBorrowerCycle') as UntypedFormArray; - } - - get numberOfRepaymentVariationsForBorrowerCycle(): UntypedFormArray { - return this.loanProductTermsForm.get('numberOfRepaymentVariationsForBorrowerCycle') as UntypedFormArray; - } - - get interestRateVariationsForBorrowerCycle(): UntypedFormArray { - return this.loanProductTermsForm.get('interestRateVariationsForBorrowerCycle') as UntypedFormArray; - } - - setLoanProductTermsFormDirty() { - if (this.loanProductTermsForm.pristine) { - this.loanProductTermsForm.markAsDirty(); - } - } - - addVariationsForBorrowerCycle(formType: string, variationsForBorrowerCycleFormArray: UntypedFormArray) { - const data = this.getData(formType); - const addVariationsForBorrowerCycleDialogRef = this.dialog.open(FormDialogComponent, { data }); - addVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => { - if (response.data) { - variationsForBorrowerCycleFormArray.push(response.data); - this.setLoanProductTermsFormDirty(); - } - }); - } - - editVariationsForBorrowerCycle( - formType: string, - variationsForBorrowerCycleFormArray: UntypedFormArray, - index: number - ) { - const data = { - ...this.getData(formType, variationsForBorrowerCycleFormArray.at(index).value), - layout: { addButtonText: 'Edit' } - }; - const addVariationsForBorrowerCycleDialogRef = this.dialog.open(FormDialogComponent, { data }); - addVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => { - if (response.data) { - variationsForBorrowerCycleFormArray.at(index).patchValue(response.data.value); - this.setLoanProductTermsFormDirty(); - } - }); - } - - deleteVariationsForBorrowerCycle(variationsForBorrowerCycleFormArray: UntypedFormArray, index: number) { - const deleteVariationsForBorrowerCycleDialogRef = this.dialog.open(DeleteDialogComponent, { - data: { deleteContext: `this` } - }); - deleteVariationsForBorrowerCycleDialogRef.afterClosed().subscribe((response: any) => { - if (response.delete) { - variationsForBorrowerCycleFormArray.removeAt(index); - this.setLoanProductTermsFormDirty(); - } - }); - } - - getData(formType: string, values?: any) { - switch (formType) { - case 'Principal': - return { - title: this.translateService.instant('labels.heading.Principal by loan cycle'), - formfields: this.getFormfields(values) - }; - case 'NumberOfRepayments': - return { - title: this.translateService.instant('labels.heading.Number of Repayments by loan cycle'), - formfields: this.getFormfields(values) - }; - case 'NominalInterestRate': - return { - title: this.translateService.instant('labels.heading.Nominal Interest Rate by loan cycle'), - formfields: this.getFormfields(values) - }; - } - } - - getFormfields(values?: any) { - const formfields: FormfieldBase[] = [ - new SelectBase({ - controlName: 'valueConditionType', - label: this.translateService.instant('labels.inputs.Condition'), - value: values ? values.valueConditionType : this.valueConditionTypeData[0].id, - options: { label: 'value', value: 'id', data: this.valueConditionTypeData }, - required: true, - order: 1 - }), - new InputBase({ - controlName: 'borrowerCycleNumber', - label: this.translateService.instant('labels.inputs.Loan Cycle'), - value: values ? values.borrowerCycleNumber : undefined, - type: 'number', - required: true, - order: 2 - }), - new InputBase({ - controlName: 'minValue', - label: this.translateService.instant('labels.inputs.Minimum'), - value: values ? values.minValue : undefined, - type: 'number', - order: 3 - }), - new InputBase({ - controlName: 'defaultValue', - label: this.translateService.instant('labels.inputs.Default'), - value: values ? values.defaultValue : undefined, - type: 'number', - required: true, - order: 4 - }), - new InputBase({ - controlName: 'maxValue', - label: this.translateService.instant('labels.inputs.Maximum'), - value: values ? values.maxValue : undefined, - type: 'number', - order: 5 - }) - ]; - return formfields; - } - get loanProductTerms() { const formValue = this.loanProductTermsForm.getRawValue(); // Normalize decimal separators: convert comma to dot for backend compatibility diff --git a/src/app/products/loan-products/loan-products.ts b/src/app/products/loan-products/loan-products.ts index 6e2e78cc2..884b0a087 100644 --- a/src/app/products/loan-products/loan-products.ts +++ b/src/app/products/loan-products/loan-products.ts @@ -8,7 +8,6 @@ import { Injectable, inject } from '@angular/core'; import { SettingsService } from 'app/settings/settings.service'; -import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model'; @Injectable({ providedIn: 'root' @@ -24,46 +23,15 @@ export class LoanProducts { public static ADVANCED_PAYMENT_ALLOCATION_STRATEGY = 'advanced-payment-allocation-strategy'; - public static DAYS_BEFORE_REPAYMENT_IS_DUE = 'days-before-repayment-is-due'; - public static DAYS_AFTER_REPAYMENT_IS_OVERDUE = 'days-after-repayment-is-overdue'; - - globalConfigurations: string[] = [ - LoanProducts.DAYS_BEFORE_REPAYMENT_IS_DUE, - LoanProducts.DAYS_AFTER_REPAYMENT_IS_OVERDUE - ]; - propertyNames: string[] = [ - 'dueDaysForRepaymentEvent', - 'overDueDaysForRepaymentEvent' - ]; - public static isAdvancedPaymentAllocationStrategy(code: string): boolean { return code === this.ADVANCED_PAYMENT_ALLOCATION_STRATEGY; } - public setItemsByDefault(configurations: any) { - const itemsByDefault: GlobalConfiguration[] = []; - configurations.globalConfiguration.forEach((config: GlobalConfiguration) => { - if (this.globalConfigurations.includes(config.name)) { - itemsByDefault.push(config); - } - }); - return itemsByDefault; + public setItemsByDefault(configurations: any): any[] { + return []; } public updateLoanProductDefaults(loanProductTemplate: any, isForUpdate: boolean) { - const itemsByDefault: GlobalConfiguration[] = loanProductTemplate['itemsByDefault']; - itemsByDefault.forEach((config: GlobalConfiguration) => { - const propertyName = this.resolvePropertyName(config.name); - if (propertyName !== '') { - if (isForUpdate) { - if (!loanProductTemplate[propertyName] || loanProductTemplate[propertyName] === '') { - loanProductTemplate[propertyName] = config.value; - } - } else { - loanProductTemplate[propertyName] = config.value; - } - } - }); return loanProductTemplate; } @@ -86,34 +54,14 @@ export class LoanProducts { loanProduct.allowPartialPeriodInterestCalculation = loanProduct.allowPartialPeriodInterestCalculation; delete loanProduct.allowPartialPeriodInterestCalculation; - // Set Default values If they were not set - itemsByDefault.forEach((config: GlobalConfiguration) => { - const propertyName = this.resolvePropertyName(config.name); - if (propertyName !== '') { - if (!loanProduct[propertyName] || loanProduct[propertyName] === '') { - loanProduct[propertyName] = config.value; - } - } - }); - return loanProduct; } public isItemByDefault(propertyName: string): boolean { - return this.propertyNames.includes(propertyName); + return false; } public isGlobalConfigurations(propertyName: string): boolean { - return this.globalConfigurations.includes(propertyName); - } - - private resolvePropertyName(configName: string): string { - if (this.globalConfigurations.includes(configName)) { - const idx = this.globalConfigurations.indexOf(configName); - if (idx > -1) { - return this.propertyNames[idx]; - } - } - return ''; + return false; } } 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 e7dc5071d..7f729a21e 100644 --- a/src/app/products/loan-products/models/loan-product.model.ts +++ b/src/app/products/loan-products/models/loan-product.model.ts @@ -26,17 +26,13 @@ export interface LoanProduct { id: number; name: string; shortName: string; - includeInBorrowerCycle: boolean; useBorrowerCycle: boolean; status: string; currency: Currency; currencyCode?: string; - fundId?: number; - fundName?: string; startDate?: string; closeDate?: string; description?: string; - installmentAmountInMultiplesOf?: number; principal: number; minPrincipal?: number; maxPrincipal?: number; @@ -123,8 +119,6 @@ export interface LoanProduct { graceOnInterestPayment?: number; graceOnInterestCharged?: number; inArrearsTolerance?: number; - dueDaysForRepaymentEvent: number; - overDueDaysForRepaymentEvent: number; enableDownPayment: boolean; enableAutoRepaymentForDownPayment: boolean; enableInstallmentLevelDelinquency: boolean; 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 941a1855f..90318e748 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 @@ -23,7 +23,6 @@ { this.loanProduct = data.loanProduct; - this.useDueForRepaymentsConfigurations = - !this.loanProduct.dueDaysForRepaymentEvent && !this.loanProduct.overDueDaysForRepaymentEvent; }); }