feat(fd): add Renewal Grace Period (Days) field to FD product Terms step

Frontend half of the new per-product grace-period field: number input alongside the existing
term fields, and stops forcing real default values (1 / the first period-frequency option) onto
minDepositTerm/minDepositTermTypeId, two hidden fields nobody actually edits through this form
-- left blank like their max-/in-multiples-of- siblings already correctly were, so a governed
product edit's "Proposed changes" diff no longer showed these as changed on every unrelated edit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 16:02:35 +05:30
co-authored by Claude Sonnet 5
parent 865ad8ba88
commit 89c091671f
2 changed files with 43 additions and 3 deletions
@@ -34,6 +34,35 @@
</div>
</div>
<!-- Renewal -->
<div class="fdpt-section">
<div class="fdpt-section__header">
<span class="fdpt-section__badge"><i class="fas fa-sync-alt"></i></span>
<span>Renewal</span>
</div>
<div class="fdpt-body">
<div class="fdpt-banner">
<span class="fdpt-banner__icon">ℹ️</span>
<span>
Leave blank for an FD on this product to keep renewing instantly, the same night it matures, as before. Set a
number of days and a matured "auto renew" FD instead waits on the FD Dashboard for that many days for staff to
renew it manually -- backdated to the original maturity date within that window, or from the day it is
actually renewed (with gap-period interest at the linked Savings account's rate) once the window has passed.
</span>
</div>
<div class="fdpt-grid fdpt-grid--three">
<mat-form-field appearance="outline" class="fdpt-field">
<mat-label>Renewal Grace Period</mat-label>
<input type="number" matInput formControlName="renewalGracePeriodDays" min="0" />
<mat-hint>Days</mat-hint>
@if (fixedDepositProductTermsForm.get('renewalGracePeriodDays').hasError('min')) {
<mat-error>Renewal Grace Period must be zero or greater</mat-error>
}
</mat-form-field>
</div>
</div>
</div>
<!-- Interest Rate & Posting Terms -->
<div class="fdpt-section">
<div class="fdpt-section__header">
@@ -74,7 +74,6 @@ export class FixedDepositProductTermsStepComponent implements OnInit, OnChanges
this.periodFrequencyData = this.fixedDepositProductsTemplate.periodFrequencyTypeOptions || [];
const isEditing = !!this.fixedDepositProductsTemplate.id;
const defaultPeriodTypeId = this.periodFrequencyData[0]?.id ?? null;
const defaultAveragePeriod = this.averagePeriodData[0]?.value || 'Monthly';
const savedEnumTypeId =
this.fixedDepositProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
@@ -85,12 +84,17 @@ export class FixedDepositProductTermsStepComponent implements OnInit, OnChanges
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
this.fixedDepositProductTermsForm.patchValue({
minDepositTerm: this.fixedDepositProductsTemplate.minDepositTerm ?? 1,
minDepositTermTypeId: this.fixedDepositProductsTemplate.minDepositTermType?.id ?? defaultPeriodTypeId,
// Hidden, unedited-by-anyone fields -- left blank (like their max-/in-multiples-of- siblings
// already correctly are) rather than forced to a real default, or every single product edit
// submitted a "real" value against a product that had none, and a checker's "Proposed changes"
// review showed these two as "changed" on every unrelated edit, never anything a maker touched.
minDepositTerm: this.fixedDepositProductsTemplate.minDepositTerm ?? '',
minDepositTermTypeId: this.fixedDepositProductsTemplate.minDepositTermType?.id ?? '',
maxDepositTerm: this.fixedDepositProductsTemplate.maxDepositTerm ?? '',
maxDepositTermTypeId: this.fixedDepositProductsTemplate.maxDepositTermType?.id ?? '',
inMultiplesOfDepositTerm: this.fixedDepositProductsTemplate.inMultiplesOfDepositTerm ?? '',
inMultiplesOfDepositTermTypeId: this.fixedDepositProductsTemplate.inMultiplesOfDepositTermType?.id ?? '',
renewalGracePeriodDays: this.fixedDepositProductsTemplate.renewalGracePeriodDays ?? '',
minDepositAmount: this.fixedDepositProductsTemplate.minDepositAmount,
maxDepositAmount: this.fixedDepositProductsTemplate.maxDepositAmount,
nominalAnnualInterestRate: this.fixedDepositProductsTemplate.nominalAnnualInterestRate ?? 0,
@@ -171,6 +175,13 @@ export class FixedDepositProductTermsStepComponent implements OnInit, OnChanges
maxDepositTermTypeId: [''],
inMultiplesOfDepositTerm: [''],
inMultiplesOfDepositTermTypeId: [''],
// Days after maturity a depositor can still walk in and have their FD renewed backdated to the
// original maturity date, at the currently active rate -- blank/0 keeps today's instant,
// same-night auto-renewal (see FD Dashboard for the manual renewal this enables).
renewalGracePeriodDays: [
'',
Validators.min(0)
],
minDepositAmount: [
'',
Validators.min(0)