update loan product code based on new requirement

This commit is contained in:
Nitesh Yadav
2026-05-14 16:10:06 +05:30
parent d0370781e8
commit 8758b9222f
7 changed files with 199 additions and 9 deletions
@@ -440,7 +440,7 @@
} }
@if (loanProduct.inArrearsTolerance) { @if (loanProduct.inArrearsTolerance) {
<div class="flex-fill layout-row"> <div class="flex-fill layout-row">
<span class="flex-40">{{ 'labels.inputs.Arrears tolerance' | translate }}:</span> <span class="flex-40">Allowed pending EMI amount:</span>
<span class="flex-60">{{ loanProduct.inArrearsTolerance }}</span> <span class="flex-60">{{ loanProduct.inArrearsTolerance }}</span>
</div> </div>
} }
@@ -720,7 +720,7 @@
<span class="flex-60">{{ loanProduct.allowAttributeOverrides.interestCalculationPeriodType | yesNo }}</span> <span class="flex-60">{{ loanProduct.allowAttributeOverrides.interestCalculationPeriodType | yesNo }}</span>
</div> </div>
<div class="flex-fill layout-row"> <div class="flex-fill layout-row">
<span class="flex-40">{{ 'labels.inputs.Arrears tolerance' | translate }}:</span> <span class="flex-40">Allowed pending EMI amount:</span>
<span class="flex-60">{{ loanProduct.allowAttributeOverrides.inArrearsTolerance | yesNo }}</span> <span class="flex-60">{{ loanProduct.allowAttributeOverrides.inArrearsTolerance | yesNo }}</span>
</div> </div>
<div class="flex-fill layout-row"> <div class="flex-fill layout-row">
@@ -203,7 +203,14 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges {
incomeFromGoodwillCreditPenaltyAccountId: accountingMappings.incomeFromGoodwillCreditPenaltyAccount?.id || '', incomeFromGoodwillCreditPenaltyAccountId: accountingMappings.incomeFromGoodwillCreditPenaltyAccount?.id || '',
receivableInterestAccountId: accountingMappings.receivableInterestAccount?.id || '', receivableInterestAccountId: accountingMappings.receivableInterestAccount?.id || '',
receivableFeeAccountId: accountingMappings.receivableFeeAccount?.id || '', receivableFeeAccountId: accountingMappings.receivableFeeAccount?.id || '',
receivablePenaltyAccountId: accountingMappings.receivablePenaltyAccount?.id || '' receivablePenaltyAccountId: accountingMappings.receivablePenaltyAccount?.id || '',
intReceivablesGlAccountId: accountingMappings.intReceivablesGlAccount?.id || '',
intPayableGlAccountId: accountingMappings.intPayableGlAccount?.id || '',
deafIntPayableGlAccountId: accountingMappings.deafIntPayableGlAccount?.id || '',
intOverdueReserveAccountId: accountingMappings.intOverdueReserveAccount?.id || '',
undebitedInterestAccountId: accountingMappings.undebitedInterestAccount?.id || '',
penalIntCrGlAccountId: accountingMappings.penalIntCrGlAccount?.id || '',
rdMaturedGlAccountId: accountingMappings.rdMaturedGlAccount?.id || ''
}); });
if (this.loanProductsTemplate.paymentChannelToFundSourceMappings) { if (this.loanProductsTemplate.paymentChannelToFundSourceMappings) {
@@ -271,7 +278,14 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges {
'incomeFromGoodwillCreditPenaltyAccountId', 'incomeFromGoodwillCreditPenaltyAccountId',
'receivableInterestAccountId', 'receivableInterestAccountId',
'receivableFeeAccountId', 'receivableFeeAccountId',
'receivablePenaltyAccountId' 'receivablePenaltyAccountId',
'intReceivablesGlAccountId',
'intPayableGlAccountId',
'deafIntPayableGlAccountId',
'intOverdueReserveAccountId',
'undebitedInterestAccountId',
'penalIntCrGlAccountId',
'rdMaturedGlAccountId'
]; ];
commonFields.forEach((field) => { commonFields.forEach((field) => {
if (!this.loanProductAccountingForm.contains(field)) { if (!this.loanProductAccountingForm.contains(field)) {
@@ -690,6 +704,38 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges {
options: this.assetAccountData, options: this.assetAccountData,
required: true, required: true,
accrualOnly: true accrualOnly: true
},
{
key: 'intReceivablesGlAccountId',
label: 'Int. Receivables GL A/c',
inputLabel: 'Int. Receivables GL A/c',
controlName: 'intReceivablesGlAccountId',
options: this.assetAccountData,
required: false
},
{
key: 'deafIntPayableGlAccountId',
label: 'Deaf Int. Payble GL A/c',
inputLabel: 'Deaf Int. Payble GL A/c',
controlName: 'deafIntPayableGlAccountId',
options: this.assetAccountData,
required: false
},
{
key: 'intOverdueReserveAccountId',
label: 'Int. Overdue Reserve A/c (AS)',
inputLabel: 'Int. Overdue Reserve A/c',
controlName: 'intOverdueReserveAccountId',
options: this.assetAccountData,
required: false
},
{
key: 'rdMaturedGlAccountId',
label: 'RD Matured GL A/c',
inputLabel: 'RD Matured GL A/c',
controlName: 'rdMaturedGlAccountId',
options: this.assetAccountData,
required: false
} }
], ],
otherMappingsControlName: 'assetsOtherGlMappings', otherMappingsControlName: 'assetsOtherGlMappings',
@@ -798,6 +844,14 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges {
options: this.incomeAccountData, options: this.incomeAccountData,
required: true, required: true,
deferredBuyDownOnly: true deferredBuyDownOnly: true
},
{
key: 'penalIntCrGlAccountId',
label: 'Penal Int. Cr. GL A/c',
inputLabel: 'Penal Int. Cr. GL A/c',
controlName: 'penalIntCrGlAccountId',
options: this.incomeAccountData,
required: false
} }
], ],
otherMappingsControlName: 'incomeOtherGlMappings', otherMappingsControlName: 'incomeOtherGlMappings',
@@ -876,6 +930,22 @@ export class LoanProductAccountingStepComponent implements OnInit, OnChanges {
options: this.liabilityAccountData, options: this.liabilityAccountData,
required: true, required: true,
deferredOnly: true deferredOnly: true
},
{
key: 'intPayableGlAccountId',
label: 'Int. Payable GL A/c',
inputLabel: 'Int. Payable GL A/c',
controlName: 'intPayableGlAccountId',
options: this.liabilityAccountData,
required: false
},
{
key: 'undebitedInterestAccountId',
label: 'UnDebited Interest A/c (LI)',
inputLabel: 'UnDebited Interest A/c',
controlName: 'undebitedInterestAccountId',
options: this.liabilityAccountData,
required: false
} }
], ],
otherMappingsControlName: 'liabilitiesOtherGlMappings', otherMappingsControlName: 'liabilitiesOtherGlMappings',
@@ -91,6 +91,67 @@
</div> </div>
</div> </div>
<!-- Interest Information -->
<div class="lpd-section">
<div class="lpd-section__header">
<i class="fas fa-percentage"></i>
<h3>Interest Information</h3>
</div>
<div class="lpd-grid">
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Debit Int. Calculation</mat-label>
<mat-select formControlName="debitIntCalculation">
@for (opt of yesNoOptions; track opt.value) {
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Separate Penal Int.</mat-label>
<mat-select formControlName="separatePenalInterest">
@for (opt of yesNoOptions; track opt.value) {
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Credit Int. Calculation</mat-label>
<mat-select formControlName="creditIntCalculation">
@for (opt of yesNoOptions; track opt.value) {
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Sub A/c Type Balance</mat-label>
<mat-select formControlName="subAccountTypeBalance">
@for (opt of yesNoOptions; track opt.value) {
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Final Int. Rounding</mat-label>
<mat-select formControlName="finalIntRounding">
@for (opt of yesNoOptions; track opt.value) {
<mat-option [value]="opt.value">{{ opt.label }}</mat-option>
}
</mat-select>
</mat-form-field>
@if (loanProductDetailsForm.get('finalIntRounding')?.value) {
<mat-form-field appearance="outline" class="lpd-field">
<mat-label>Rounding UpTo</mat-label>
<input matInput type="number" formControlName="roundingUpTo" min="0" />
</mat-form-field>
}
</div>
</div>
<!-- Product Classification + GL Account — combined section --> <!-- Product Classification + GL Account — combined section -->
<div class="lpd-section"> <div class="lpd-section">
<div class="lpd-section__header"> <div class="lpd-section__header">
@@ -61,6 +61,15 @@ export class LoanProductDetailsStepComponent implements OnInit {
const mappingOptions = this.loanProductsTemplate?.accountingMappingOptions ?? {}; const mappingOptions = this.loanProductsTemplate?.accountingMappingOptions ?? {};
this.assetAccountOptions = mappingOptions.assetAccountOptions ?? []; this.assetAccountOptions = mappingOptions.assetAccountOptions ?? [];
this.liabilityAccountOptions = mappingOptions.liabilityAccountOptions ?? []; this.liabilityAccountOptions = mappingOptions.liabilityAccountOptions ?? [];
this.loanProductDetailsForm.patchValue({
debitIntCalculation: this.loanProductsTemplate.debitIntCalculation ?? false,
separatePenalInterest: this.loanProductsTemplate.separatePenalInterest ?? false,
creditIntCalculation: this.loanProductsTemplate.creditIntCalculation ?? false,
subAccountTypeBalance: this.loanProductsTemplate.subAccountTypeBalance ?? false,
finalIntRounding: this.loanProductsTemplate.finalIntRounding ?? false,
roundingUpTo: this.loanProductsTemplate.roundingUpTo ?? null
});
} }
/** Product Classification data */ /** Product Classification data */
@@ -121,6 +130,11 @@ export class LoanProductDetailsStepComponent implements OnInit {
return this.productTypeGroups.find((g) => g.id === group)?.label ?? ''; return this.productTypeGroups.find((g) => g.id === group)?.label ?? '';
} }
readonly yesNoOptions = [
{ value: true, label: 'YES' },
{ value: false, label: 'NO' }
];
createLoanProductDetailsForm() { createLoanProductDetailsForm() {
this.loanProductDetailsForm = this.formBuilder.group({ this.loanProductDetailsForm = this.formBuilder.group({
name: [ name: [
@@ -137,7 +151,13 @@ export class LoanProductDetailsStepComponent implements OnInit {
productTypeGroup: [''], productTypeGroup: [''],
productTypeSubGroup: [''], productTypeSubGroup: [''],
glClassification: ['asset'], glClassification: ['asset'],
glAccount: [''] glAccount: [''],
debitIntCalculation: [false],
separatePenalInterest: [false],
creditIntCalculation: [false],
subAccountTypeBalance: [false],
finalIntRounding: [false],
roundingUpTo: [null]
}); });
// Auto-set GL based on product type group // Auto-set GL based on product type group
@@ -163,7 +163,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>{{ 'labels.inputs.Arrears tolerance' | translate }}</mat-label> <mat-label>Allowed pending EMI amount</mat-label>
<input <input
type="number" type="number"
min="0" min="0"
@@ -1328,7 +1328,7 @@
</div> </div>
<div class="lps-checkbox-row"> <div class="lps-checkbox-row">
<mat-checkbox labelPosition="before" formControlName="inArrearsTolerance"> <mat-checkbox labelPosition="before" formControlName="inArrearsTolerance">
{{ 'labels.inputs.Arrears tolerance' | translate }} Allowed pending EMI amount
</mat-checkbox> </mat-checkbox>
</div> </div>
<div class="lps-checkbox-row"> <div class="lps-checkbox-row">
@@ -413,4 +413,37 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
<!-- Account Behaviour -->
<div class="lpt-section">
<div class="lpt-section-header">
<i class="fas fa-toggle-on lpt-section-icon"></i>
<h3>Account Behaviour</h3>
</div>
<div class="lpt-grid">
<mat-form-field appearance="outline">
<mat-label>Debit Balance Allow</mat-label>
<mat-select formControlName="debitBalanceAllow">
<mat-option [value]="true">YES</mat-option>
<mat-option [value]="false">NO</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Allow Debit Trans After Due Date</mat-label>
<mat-select formControlName="allowDebitTransAfterDueDate">
<mat-option [value]="true">YES</mat-option>
<mat-option [value]="false">NO</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Branch Level A/c Type</mat-label>
<mat-select formControlName="branchLevelAccountType">
<mat-option [value]="true">YES</mat-option>
<mat-option [value]="false">NO</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</form> </form>
@@ -108,7 +108,10 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges {
repaymentFrequencyType: this.loanProductsTemplate.repaymentFrequencyType.id, repaymentFrequencyType: this.loanProductsTemplate.repaymentFrequencyType.id,
minimumDaysBetweenDisbursalAndFirstRepayment: minimumDaysBetweenDisbursalAndFirstRepayment:
this.loanProductsTemplate.minimumDaysBetweenDisbursalAndFirstRepayment, this.loanProductsTemplate.minimumDaysBetweenDisbursalAndFirstRepayment,
repaymentStartDateType: this.loanProductsTemplate.repaymentStartDateType.id || 1 repaymentStartDateType: this.loanProductsTemplate.repaymentStartDateType.id || 1,
debitBalanceAllow: this.loanProductsTemplate.debitBalanceAllow ?? false,
allowDebitTransAfterDueDate: this.loanProductsTemplate.allowDebitTransAfterDueDate ?? false,
branchLevelAccountType: this.loanProductsTemplate.branchLevelAccountType ?? false
}); });
if (this.loanProductsTemplate.allowApprovedDisbursedAmountsOverApplied) { if (this.loanProductsTemplate.allowApprovedDisbursedAmountsOverApplied) {
@@ -207,7 +210,10 @@ export class LoanProductTermsStepComponent implements OnInit, OnChanges {
[] []
], ],
repaymentStartDateType: [1], repaymentStartDateType: [1],
fixedLength: [null] fixedLength: [null],
debitBalanceAllow: [false],
allowDebitTransAfterDueDate: [false],
branchLevelAccountType: [false]
}); });
} }