diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html index 646c9f4f9..94cd1ea54 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html @@ -18,7 +18,7 @@ {{ isDashboardRenewalMode ? 'Renew ' + (dashboardRenewalRow?.accountNo ?? 'this matured FD') + '.' - : "Process this Fixed Deposit now -- works whether it's Active or Matured." + : 'Process this matured Fixed Deposit now.' }}

@@ -40,10 +40,10 @@ } @else {
- @if (isDashboardRenewalMode) { + @if (isMaturedRenewal) {

- {{ dashboardRenewalTimingText }} + {{ maturedRenewalTimingText }}

@if (renewalPreviewLoading) { @@ -97,7 +97,7 @@
{{ - isDashboardRenewalMode || !remainderIsApproximate + isMaturedRenewal || !remainderIsApproximate ? ('labels.inputs.Maturity Amount' | translate) : 'Amount available on the selected date' }} @@ -106,7 +106,7 @@
} - @if (!isDashboardRenewalMode) { + @if (!isMaturedRenewal) { {{ 'labels.inputs.Closed On Date' | translate }} } - @if (!isDashboardRenewalMode) { + @if (!isMaturedRenewal) { {{ 'labels.inputs.Note' | translate }} diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts index 789d1e902..d481a87bf 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts @@ -133,29 +133,32 @@ export class PaymentDialogComponent implements OnInit { note: [''] }); - const dashboardRow = this.dashboardRenewalRow; - if (dashboardRow) { - // Computed from the grace window, never picked -- the date field stays in the form (the generic - // submit path and reinvestCap/processingOnOrAfterMaturity both key off it) but its picker is - // hidden in this mode (see the template), so it can't be moved off this value. - this.paymentForm - .get('closedOnDate') - .setValue(dashboardRow.withinGraceWindow ? new Date(dashboardRow.maturityDate) : this.maxDate); - this.gapInterestPending = !dashboardRow.withinGraceWindow; - - this.renewalPreviewLoading = true; - this.fixedDepositsService.getFdRenewalPreview(this.data.accountId).subscribe({ - next: (preview: any) => { - this.renewalPreview = preview; - this.renewalPreviewLoading = false; - }, - // Non-fatal -- the plain timing text above still explains what will happen even if the - // numeric breakdown couldn't be fetched; only the numbers are missing, not the action itself. - error: () => { - this.renewalPreviewLoading = false; + // Fetched every time this dialog opens, not just from the FD Dashboard -- Payment is now only ever + // offered once an FD has Matured (see FixedDepositsButtonsConfiguration), so a plain Payment on it + // is exactly the same "late renewal" scenario the Dashboard's Renew action handles, and needs the + // exact same date-locking/gap-interest treatment, or an FD closed this way instead of from the + // Dashboard would silently skip crediting interest it genuinely earned. + this.renewalPreviewLoading = true; + this.fixedDepositsService.getFdRenewalPreview(this.data.accountId).subscribe({ + next: (preview: any) => { + this.renewalPreview = preview; + this.renewalPreviewLoading = false; + if (preview?.isMatured) { + // Computed from the grace window, never picked -- the date field stays in the form (the + // generic submit path and reinvestCap/processingOnOrAfterMaturity both key off it) but its + // picker is hidden once matured (see the template), so it can't be moved off this value. + this.paymentForm + .get('closedOnDate') + .setValue(preview.withinGrace ? new Date(preview.maturityDate) : this.maxDate); + this.gapInterestPending = !preview.withinGrace; } - }); - } + }, + // Non-fatal -- Confirm still works from here (backend re-derives the same numbers on submit), + // only the numeric preview and its date-locking are unavailable, not the action itself. + error: () => { + this.renewalPreviewLoading = false; + } + }); this.paymentForm.get('onAccountClosureId').valueChanges.subscribe((id: number) => this.onClosureTypeChange(id)); // The cap depends on whether the chosen date is on or after maturity, so a date change can make an @@ -195,13 +198,19 @@ export class PaymentDialogComponent implements OnInit { return this.data.dashboardRenewal?.row ?? null; } - /** What clicking Confirm is actually about to do -- shown above the (hidden-picker) date in this - * mode, since the date itself is no longer visible to explain it. */ - get dashboardRenewalTimingText(): string { - const row = this.dashboardRenewalRow; - if (!row) return ''; - return row.withinGraceWindow - ? `Backdated to its original maturity date (${row.maturityDate}), at the currently active interest rate.` + /** True once the loaded preview confirms this FD has actually Matured -- which, since Payment is + * only ever offered on a Matured account, is every real open of this dialog now, from either entry + * point. Drives the date-locking and gap-interest preview/crediting identically either way. */ + get isMaturedRenewal(): boolean { + return !!this.renewalPreview?.isMatured; + } + + /** What clicking Confirm is actually about to do -- shown above the (hidden-picker) date once + * matured, since the date itself is no longer visible to explain it. */ + get maturedRenewalTimingText(): string { + if (!this.renewalPreview) return ''; + return this.renewalPreview.withinGrace + ? `Backdated to its original maturity date (${this.renewalPreview.maturityDate}), at the currently active interest rate.` : `This FD's grace period has passed -- gap-period interest (for the days since maturity) will be ` + `credited at the linked account's rate first, then this renews from today.`; } diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposits-buttons.config.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposits-buttons.config.ts index 76abd062d..9ce40ede6 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposits-buttons.config.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposits-buttons.config.ts @@ -110,7 +110,18 @@ export class FixedDepositsButtonsConfiguration { setOptions(status: string) { switch (status) { case 'Active': + this.optionArray = [ + { + name: 'Post Interest' + }, + { + name: 'Add Charge' + } + ]; + break; case 'Matured': + // Payment only makes sense once the deposit has actually matured -- an Active FD that needs + // closing early already has Premature Close for that. this.optionArray = [ { name: 'Post Interest'