diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.html
new file mode 100644
index 000000000..018b95331
--- /dev/null
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.html
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+ @if (loadFailed) {
+
+
+ Could not calculate the premature-closure amount for this account. Close this dialog and try again.
+
+ } @else {
+
+ }
+
+
+
+
+
+
+
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.scss
new file mode 100644
index 000000000..289c1380e
--- /dev/null
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.scss
@@ -0,0 +1,388 @@
+/**
+ * Copyright since 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/**
+ * Premature Close Dialog Styles
+ * CBSNexora Core Banking System
+ *
+ * Same shape/spacing/typography as the Payment dialog (.pmt__*), in this Danger Zone action's own red
+ * accent on the header and Confirm button, rather than Payment's blue -- an irreversible, usually
+ * penalty-bearing action reads differently from a routine one, everywhere else stays identical so the
+ * two dialogs feel like the same product.
+ */
+
+.pcd {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ max-height: 92vh;
+ box-sizing: border-box;
+ font-family:
+ 'DM Sans',
+ system-ui,
+ -apple-system,
+ sans-serif;
+ color: #0f172a;
+ border-radius: 16px;
+ overflow: hidden;
+ box-shadow: 0 24px 48px -12px rgb(15 23 42 / 25%);
+}
+
+.pcd__header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 26px 30px;
+ background: linear-gradient(135deg, #7f1d1d 0%, #b91c1c 55%, #dc2626 100%);
+ color: #fff;
+ flex-shrink: 0;
+ position: relative;
+ overflow: hidden;
+
+ &::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: radial-gradient(circle at 88% -20%, rgb(255 255 255 / 22%), transparent 55%);
+ pointer-events: none;
+ }
+
+ &-left {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ position: relative;
+ z-index: 1;
+ }
+
+ &-ico {
+ width: 48px;
+ height: 48px;
+ border-radius: 14px;
+ background: rgb(255 255 255 / 16%);
+ border: 1px solid rgb(255 255 255 / 25%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20px;
+ color: #fff;
+ flex-shrink: 0;
+ }
+
+ &-text {
+ display: flex;
+ flex-direction: column;
+ }
+
+ &-close {
+ position: relative;
+ z-index: 1;
+ background: rgb(255 255 255 / 14%);
+ border: none;
+ color: #fff;
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ flex-shrink: 0;
+
+ &:hover:not(:disabled) {
+ background: rgb(255 255 255 / 28%);
+ transform: rotate(90deg);
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+ }
+}
+
+.pcd__title {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 800;
+ color: #fff;
+ line-height: 1.25;
+ letter-spacing: -0.01em;
+}
+
+.pcd__subtitle {
+ margin: 5px 0 0;
+ font-size: 13px;
+ color: rgb(255 255 255 / 88%);
+ line-height: 1.4;
+}
+
+.pcd__content {
+ padding: 26px 30px !important;
+ overflow-y: auto;
+ flex: 1 1 auto;
+ background: #fbfcfe;
+
+ mat-form-field {
+ width: 100%;
+ }
+}
+
+.pcd__loading {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ color: #64748b;
+ font-size: 13px;
+ padding: 14px 0 18px;
+}
+
+.pcd__preview {
+ display: flex;
+ flex-direction: column;
+ background: #fff;
+ border: 1px solid #e2e8f0;
+ border-radius: 14px;
+ padding: 4px 18px;
+ margin: 0 0 18px;
+
+ &-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 11px 0;
+ border-bottom: 1px solid #f1f5f9;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ &--highlight {
+ .pcd__preview-value {
+ color: #b91c1c;
+ font-weight: 800;
+ }
+ }
+
+ &--total {
+ margin: 0 -18px;
+ padding: 14px 18px;
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
+ border-bottom: none;
+ border-radius: 0 0 12px 12px;
+
+ .pcd__preview-label {
+ color: #b91c1c;
+ font-weight: 700;
+ }
+
+ .pcd__preview-value {
+ color: #0f172a;
+ font-size: 18px;
+ font-weight: 800;
+ }
+ }
+ }
+
+ &-label {
+ font-size: 12.5px;
+ color: #64748b;
+ font-weight: 600;
+ }
+
+ &-value {
+ font-family: 'JetBrains Mono', 'DM Sans', monospace;
+ font-size: 14px;
+ font-weight: 700;
+ color: #0f172a;
+ }
+}
+
+.pcd__section-label {
+ font-size: 12px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: #475569;
+ margin: 10px 0 12px;
+}
+
+/* Action Options Grid - 2 Equal Columns */
+.pcd__options {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 14px;
+ margin-bottom: 18px;
+
+ @media (width <= 560px) {
+ grid-template-columns: 1fr;
+ }
+}
+
+.pcd__option {
+ display: flex;
+ align-items: flex-start;
+ gap: 14px;
+ text-align: left;
+ background: #fff;
+ border: 1.5px solid #e2e8f0;
+ border-radius: 14px;
+ padding: 16px;
+ cursor: pointer;
+ font: inherit;
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+ position: relative;
+
+ &:hover {
+ border-color: #fca5a5;
+ background: #f8fafc;
+ box-shadow: 0 6px 16px rgb(15 23 42 / 6%);
+ transform: translateY(-1px);
+ }
+
+ &--active {
+ border-color: #dc2626;
+ background: #fef2f2;
+ box-shadow: 0 0 0 1.5px #dc2626;
+
+ .pcd__option-ico {
+ background: #dc2626;
+ color: #fff;
+ border-color: #dc2626;
+ }
+ }
+
+ &-ico {
+ width: 38px;
+ height: 38px;
+ min-width: 38px;
+ border-radius: 10px;
+ background: #f1f5f9;
+ border: 1px solid #e2e8f0;
+ color: #dc2626;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 15px;
+ flex-shrink: 0;
+ transition: all 0.2s ease;
+ }
+
+ &-text {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ flex: 1;
+ }
+
+ &-title {
+ font-size: 14px;
+ font-weight: 700;
+ color: #0f172a;
+ line-height: 1.3;
+ }
+
+ &-desc {
+ font-size: 12px;
+ color: #64748b;
+ line-height: 1.4;
+ }
+
+ &-check {
+ color: #dc2626;
+ font-size: 17px;
+ position: absolute;
+ top: 14px;
+ right: 14px;
+ }
+}
+
+.pcd__option-error {
+ color: #b91c1c;
+ font-size: 12px;
+ font-weight: 600;
+ margin: -10px 0 14px;
+}
+
+.pcd__hint {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ background: #f0f9ff;
+ border: 1px solid #bae6fd;
+ border-left: 4px solid #0284c7;
+ border-radius: 10px;
+ color: #0369a1;
+ font-size: 13px;
+ line-height: 1.5;
+ padding: 12px 16px;
+ margin: 0 0 18px;
+
+ &--warn {
+ background: #fffbeb;
+ border-color: #fde68a;
+ border-left-color: #d97706;
+ color: #b45309;
+ }
+}
+
+.pcd__error {
+ display: flex;
+ align-items: flex-start;
+ gap: 10px;
+ background: #fef2f2;
+ border: 1px solid #fecaca;
+ border-left: 4px solid #ef4444;
+ border-radius: 10px;
+ color: #b91c1c;
+ font-size: 13px;
+ padding: 12px 16px;
+ margin: 10px 0;
+}
+
+.pcd__actions {
+ padding: 18px 30px !important;
+ border-top: 1px solid #e2e8f0;
+ background: #fff;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 14px;
+ flex-shrink: 0;
+
+ .pcd__btn-cancel {
+ border-radius: 10px;
+ font-weight: 600;
+ color: #475569;
+ }
+
+ .pcd__btn-submit {
+ border-radius: 10px;
+ font-weight: 700;
+ padding: 0 26px;
+ height: 42px;
+ background: linear-gradient(135deg, #b91c1c 0%, #dc2626 100%);
+ color: #fff;
+ box-shadow: 0 8px 20px rgb(220 38 38 / 30%);
+ transition: all 0.2s ease;
+
+ &:hover:not(:disabled) {
+ box-shadow: 0 10px 24px rgb(220 38 38 / 40%);
+ transform: translateY(-1px);
+ }
+
+ &:disabled {
+ background: #cbd5e1;
+ box-shadow: none;
+ color: #64748b;
+ }
+ }
+}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.ts
new file mode 100644
index 000000000..2122653e5
--- /dev/null
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/premature-close-dialog/premature-close-dialog.component.ts
@@ -0,0 +1,218 @@
+/**
+ * Copyright since 2025 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/** Angular Imports */
+import { Component, OnInit, inject } from '@angular/core';
+import {
+ MAT_DIALOG_DATA,
+ MatDialogRef,
+ MatDialogContent,
+ MatDialogActions,
+ MatDialogClose
+} from '@angular/material/dialog';
+import { CdkScrollable } from '@angular/cdk/scrolling';
+import { CdkTextareaAutosize } from '@angular/cdk/text-field';
+import { FaIconComponent } from '@fortawesome/angular-fontawesome';
+import { UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms';
+
+/** Custom Services */
+import { FixedDepositsService } from '../../../fixed-deposits.service';
+import { SettingsService } from 'app/settings/settings.service';
+import { Dates } from 'app/core/utils/dates';
+import { FormatNumberPipe } from '../../../../../pipes/format-number.pipe';
+import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
+
+/**
+ * "Premature Close" dialog -- Active-only, no reinvest option (the deposit hasn't matured, so there's
+ * nothing to renew): pay out in cash, or transfer the pre-maturity balance to a savings account. Same
+ * `.pcd__*` visual language as the Payment dialog ({@link PaymentDialogComponent}), in this Danger Zone
+ * action's own red accent rather than Payment's blue, since it's an irreversible, usually
+ * penalty-bearing action rather than a routine one.
+ *
+ * The premature amount is date-dependent (unlike Payment's one-shot template load): every change to
+ * Closed On Date re-calls `calculatePrematureAmount`, same as the full-page form this replaces did.
+ */
+@Component({
+ selector: 'mifosx-premature-close-dialog',
+ templateUrl: './premature-close-dialog.component.html',
+ styleUrls: ['./premature-close-dialog.component.scss'],
+ imports: [
+ ...STANDALONE_SHARED_IMPORTS,
+ CdkScrollable,
+ MatDialogContent,
+ MatDialogActions,
+ MatDialogClose,
+ CdkTextareaAutosize,
+ FaIconComponent,
+ FormatNumberPipe
+ ]
+})
+export class PrematureCloseDialogComponent implements OnInit {
+ /** The `onAccountClosureId` codes Premature Close can offer -- same codes Payment/Close use. */
+ private static readonly WITHDRAW_DEPOSIT_ID = 100;
+ private static readonly TRANSFER_TO_SAVINGS_ID = 200;
+
+ private static readonly ACTION_ICONS: Record = {
+ [PrematureCloseDialogComponent.WITHDRAW_DEPOSIT_ID]: 'money-bill-wave',
+ [PrematureCloseDialogComponent.TRANSFER_TO_SAVINGS_ID]: 'wallet'
+ };
+
+ private static readonly ACTION_DESCRIPTIONS: Record = {
+ [PrematureCloseDialogComponent.WITHDRAW_DEPOSIT_ID]: 'Pay out the full balance in cash',
+ [PrematureCloseDialogComponent.TRANSFER_TO_SAVINGS_ID]: 'Move the full balance to another savings account'
+ };
+
+ dialogRef = inject>(MatDialogRef);
+ data: {
+ accountId: number | string;
+ accountNo?: string;
+ depositAmount?: number;
+ nominalAnnualInterestRate?: number;
+ preClosurePenalApplicable?: boolean;
+ preClosurePenalInterest?: number;
+ preClosurePenalInterestOnType?: { value: string };
+ } = inject(MAT_DIALOG_DATA);
+ private formBuilder = inject(UntypedFormBuilder);
+ private fixedDepositsService = inject(FixedDepositsService);
+ private settingsService = inject(SettingsService);
+ private dateUtils = inject(Dates);
+
+ prematureCloseForm: UntypedFormGroup;
+ onAccountClosureOptions: any[] = [];
+ savingsAccountsData: any[] = [];
+ maturityAmount: number = null;
+ loadingAmount = true;
+ loadFailed = false;
+ submitting = false;
+ submitError: string = null;
+ minDate = new Date(2000, 0, 1);
+ maxDate: Date;
+
+ ngOnInit(): void {
+ this.maxDate = this.settingsService.businessDate;
+ this.prematureCloseForm = this.formBuilder.group({
+ closedOnDate: [
+ this.maxDate,
+ Validators.required
+ ]
+ });
+
+ this.prematureCloseForm
+ .get('closedOnDate')
+ .valueChanges.subscribe((date: Date) => this.calculatePrematureAmount(date));
+ // Same date the form already starts with -- without this the amount/options never load unless the
+ // maker happens to touch the date picker, which the full-page form this replaces required too.
+ this.calculatePrematureAmount(this.maxDate);
+ }
+
+ private calculatePrematureAmount(date: Date): void {
+ this.loadingAmount = true;
+ this.loadFailed = false;
+ const locale = this.settingsService.language.code;
+ const dateFormat = this.settingsService.dateFormat;
+ const data = {
+ closedOnDate: this.dateUtils.formatDate(date, dateFormat),
+ dateFormat,
+ locale
+ };
+ this.fixedDepositsService
+ .executeFixedDepositsAccountCommand(this.data.accountId as string, 'calculatePrematureAmount', data)
+ .subscribe({
+ next: (response: any) => {
+ this.savingsAccountsData = response.savingsAccounts ?? [];
+ this.onAccountClosureOptions = response.onAccountClosureOptions ?? [];
+ this.maturityAmount = response.maturityAmount;
+ this.loadingAmount = false;
+ if (!this.prematureCloseForm.contains('onAccountClosureId')) {
+ this.prematureCloseForm.addControl('onAccountClosureId', new UntypedFormControl('', Validators.required));
+ this.prematureCloseForm.addControl('note', new UntypedFormControl(''));
+ this.prematureCloseForm
+ .get('onAccountClosureId')
+ .valueChanges.subscribe((id: number) => this.onClosureTypeChange(id));
+ }
+ },
+ // Without this the spinner would run forever on a failed load, leaving Cancel as the only way out.
+ error: () => {
+ this.loadingAmount = false;
+ this.loadFailed = true;
+ }
+ });
+ }
+
+ /** Adds/removes the destination-account control to match what the chosen type needs. */
+ private onClosureTypeChange(id: number): void {
+ if (id === PrematureCloseDialogComponent.TRANSFER_TO_SAVINGS_ID) {
+ if (!this.prematureCloseForm.contains('toSavingsAccountId')) {
+ this.prematureCloseForm.addControl('toSavingsAccountId', new UntypedFormControl('', Validators.required));
+ this.prematureCloseForm.addControl('transferDescription', new UntypedFormControl(''));
+ }
+ } else {
+ this.prematureCloseForm.removeControl('toSavingsAccountId');
+ this.prematureCloseForm.removeControl('transferDescription');
+ }
+ }
+
+ /** The gap between the deposit and the computed maturity amount -- the actual interest this premature
+ * closure earns, at whatever rate (contracted, or the lower pre-closure-penalty one below) applied. */
+ get interestEarned(): number | null {
+ if (this.maturityAmount === null || this.maturityAmount === undefined || !this.data.depositAmount) {
+ return null;
+ }
+ return Number(this.maturityAmount) - Number(this.data.depositAmount);
+ }
+
+ /** True when the chosen action needs a destination account but the customer has none to pick. */
+ get noDestinationAccounts(): boolean {
+ return (
+ !this.loadingAmount &&
+ this.prematureCloseForm?.contains('toSavingsAccountId') &&
+ this.savingsAccountsData.length === 0
+ );
+ }
+
+ iconFor(id: number): string {
+ return PrematureCloseDialogComponent.ACTION_ICONS[id] ?? 'circle';
+ }
+
+ descriptionFor(id: number): string {
+ return PrematureCloseDialogComponent.ACTION_DESCRIPTIONS[id] ?? '';
+ }
+
+ selectClosureType(id: number): void {
+ this.prematureCloseForm.get('onAccountClosureId').setValue(id);
+ this.prematureCloseForm.get('onAccountClosureId').markAsTouched();
+ }
+
+ submit(): void {
+ const formValue = { ...this.prematureCloseForm.value };
+ const locale = this.settingsService.language.code;
+ const dateFormat = this.settingsService.dateFormat;
+ if (formValue.closedOnDate instanceof Date) {
+ formValue.closedOnDate = this.dateUtils.formatDate(formValue.closedOnDate, dateFormat);
+ }
+ const data = { ...formValue, dateFormat, locale };
+ this.submitting = true;
+ this.submitError = null;
+ this.fixedDepositsService
+ .executeFixedDepositsAccountCommand(this.data.accountId as string, 'prematureClose', data)
+ .subscribe({
+ next: () => {
+ this.dialogRef.close({ confirm: true });
+ },
+ // Keep the dialog open with the entered values and say what went wrong, rather than leaving a
+ // permanently disabled Confirm behind whatever modal the global interceptor threw up.
+ error: (error: any) => {
+ this.submitting = false;
+ this.submitError =
+ error?.error?.defaultUserMessage ??
+ error?.error?.errors?.[0]?.defaultUserMessage ??
+ 'This account could not be prematurely closed. Please review the details and try again.';
+ }
+ });
+ }
+}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposit-account-view.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposit-account-view.component.ts
index 42f39f6c5..a07dde825 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposit-account-view.component.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/fixed-deposit-account-view.component.ts
@@ -17,6 +17,7 @@ import { CalculateInterestDialogComponent } from './custom-dialogs/calculate-int
import { PostInterestDialogComponent } from './custom-dialogs/post-interest-dialog/post-interest-dialog.component';
import { ToggleWithholdTaxDialogComponent } from './custom-dialogs/toggle-withhold-tax-dialog/toggle-withhold-tax-dialog.component';
import { PaymentDialogComponent } from './custom-dialogs/payment-dialog/payment-dialog.component';
+import { PrematureCloseDialogComponent } from './custom-dialogs/premature-close-dialog/premature-close-dialog.component';
import { DepositCertificateDialogComponent } from 'app/shared/deposit-certificate-dialog/deposit-certificate-dialog.component';
/** Custom Button Config. */
@@ -238,7 +239,6 @@ export class FixedDepositAccountViewComponent implements OnInit {
case 'Undo Activation':
case 'Add Charge':
case 'Withdrawn by Client':
- case 'Premature Close':
case 'Deposit':
case 'Withdrawal':
this.router.navigate([`actions/${name}`], { relativeTo: this.route });
@@ -267,12 +267,17 @@ export class FixedDepositAccountViewComponent implements OnInit {
case 'Payment':
this.openPaymentDialog();
break;
+ case 'Premature Close':
+ this.openPrematureCloseDialog();
+ break;
}
}
/**
- * Opens the Payment action -- unlike Premature Close/Close, works whether the account is Active or
- * Matured, and is the only place a Partial Reinvest can be done.
+ * Opens the Payment action -- only offered once the FD has actually Matured (see
+ * FixedDepositsButtonsConfiguration), and the only place a Partial Reinvest can be done. Also the
+ * exact same dialog the FD Dashboard's Renew action opens (see fd-dashboard.component.ts's
+ * openRenewDialog), just without dashboardRenewal data, so its date picker stays visible.
*/
private openPaymentDialog(): void {
const paymentDialogRef = this.dialog.open(PaymentDialogComponent, {
@@ -293,6 +298,34 @@ export class FixedDepositAccountViewComponent implements OnInit {
});
}
+ /**
+ * Opens the Premature Close action -- Active-only, no reinvest option, since the deposit hasn't
+ * matured yet.
+ */
+ private openPrematureCloseDialog(): void {
+ const prematureCloseDialogRef = this.dialog.open(PrematureCloseDialogComponent, {
+ width: '720px',
+ maxWidth: '95vw',
+ data: {
+ accountId: this.fixedDepositsAccountData.id,
+ accountNo: this.fixedDepositsAccountData.fdAccountNumber || this.fixedDepositsAccountData.accountNo,
+ // calculatePrematureAmount only ever returns the final maturityAmount, not how it was reached --
+ // these come from the account's own data (already loaded for this page) so the dialog can show
+ // the breakdown (deposit, contracted rate, penalty, interest earned) behind that one number.
+ depositAmount: this.fixedDepositsAccountData.depositAmount,
+ nominalAnnualInterestRate: this.fixedDepositsAccountData.nominalAnnualInterestRate,
+ preClosurePenalApplicable: this.fixedDepositsAccountData.preClosurePenalApplicable,
+ preClosurePenalInterest: this.fixedDepositsAccountData.preClosurePenalInterest,
+ preClosurePenalInterestOnType: this.fixedDepositsAccountData.preClosurePenalInterestOnType
+ }
+ });
+ prematureCloseDialogRef.afterClosed().subscribe((response: any) => {
+ if (response?.confirm) {
+ this.reload();
+ }
+ });
+ }
+
/**
* Deletes Fixed Deposits Account.
*/
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.html b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.html
index f20e46af8..92b1d954b 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.html
+++ b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.html
@@ -24,9 +24,6 @@
@if (actions['Add Charge']) {
}
-@if (actions['Premature Close']) {
-
-}
@if (actions['Close']) {
}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.ts b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.ts
index e0b47a2f7..b587839e8 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/fixed-deposits-account-actions.component.ts
@@ -15,7 +15,6 @@ import { ActivateFixedDepositsAccountComponent } from './activate-fixed-deposits
import { UndoApprovalFixedDepositsAccountComponent } from './undo-approval-fixed-deposits-account/undo-approval-fixed-deposits-account.component';
import { WithdrawByClientFixedDepositsAccountComponent } from './withdraw-by-client-fixed-deposits-account/withdraw-by-client-fixed-deposits-account.component';
import { AddChargeFixedDepositsAccountComponent } from './add-charge-fixed-deposits-account/add-charge-fixed-deposits-account.component';
-import { PrematureCloseFixedDepositsAccountComponent } from './premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component';
import { CloseFixedDepositsAccountComponent } from './close-fixed-deposits-account/close-fixed-deposits-account.component';
import { FixedDepositsCashTransactionComponent } from './fixed-deposits-cash-transaction/fixed-deposits-cash-transaction.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -35,7 +34,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
UndoApprovalFixedDepositsAccountComponent,
WithdrawByClientFixedDepositsAccountComponent,
AddChargeFixedDepositsAccountComponent,
- PrematureCloseFixedDepositsAccountComponent,
CloseFixedDepositsAccountComponent,
FixedDepositsCashTransactionComponent
]
@@ -52,7 +50,6 @@ export class FixedDepositsAccountActionsComponent {
'Undo Approval': boolean;
'Undo Activation': boolean;
'Add Charge': boolean;
- 'Premature Close': boolean;
'Withdrawn by Client': boolean;
Withdrawal: boolean;
Deposit: boolean;
@@ -64,7 +61,6 @@ export class FixedDepositsAccountActionsComponent {
'Undo Approval': false,
'Undo Activation': false,
'Add Charge': false,
- 'Premature Close': false,
'Withdrawn by Client': false,
Withdrawal: false,
Deposit: false
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.html b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.html
deleted file mode 100644
index 0d204538d..000000000
--- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.html
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.scss b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.scss
deleted file mode 100644
index 7c3f1555b..000000000
--- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Copyright since 2025 Mifos Initiative
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-.container {
- max-width: 37rem;
-}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.ts b/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.ts
deleted file mode 100644
index 5e4d5dc51..000000000
--- a/src/app/deposits/fixed-deposits/fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component.ts
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * Copyright since 2025 Mifos Initiative
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-/** Angular Imports */
-import { Component, OnInit, inject } from '@angular/core';
-import {
- UntypedFormGroup,
- UntypedFormBuilder,
- Validators,
- UntypedFormControl,
- ReactiveFormsModule
-} from '@angular/forms';
-import { ActivatedRoute, Router, RouterLink } from '@angular/router';
-
-/** Custom Services */
-import { FixedDepositsService } from '../../fixed-deposits.service';
-import { SettingsService } from 'app/settings/settings.service';
-import { Dates } from 'app/core/utils/dates';
-import { CdkTextareaAutosize } from '@angular/cdk/text-field';
-import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
-
-/**
- * Premature Close Fixed Deposits Account Component
- */
-@Component({
- selector: 'mifosx-premature-close-fixed-deposits-account',
- templateUrl: './premature-close-fixed-deposits-account.component.html',
- styleUrls: ['./premature-close-fixed-deposits-account.component.scss'],
- imports: [
- ...STANDALONE_SHARED_IMPORTS,
- CdkTextareaAutosize
- ]
-})
-export class PrematureCloseFixedDepositsAccountComponent implements OnInit {
- private formBuilder = inject(UntypedFormBuilder);
- private fixedDepositsService = inject(FixedDepositsService);
- private dateUtils = inject(Dates);
- private route = inject(ActivatedRoute);
- private router = inject(Router);
- private settingsService = inject(SettingsService);
-
- /** Minimum date allowed. */
- minDate = new Date(2000, 0, 1);
- /** Maximum date allowed. */
- maxDate = new Date();
- /** Premature Close FD Account form. */
- prematureCloseAccountForm: UntypedFormGroup;
- /** Savings Account Data */
- savingsAccountsData: any;
- /** On account Closure Options */
- onAccountClosureOptions: any;
- /** Fixed Deposits Account Id */
- accountId: any;
- /** Form submission event */
- isSubmitted = false;
-
- /**
- * @param {FormBuilder} formBuilder Form Builder
- * @param {FixedDepositsService} fixedDepositsService Fixed Deposits Service
- * @param {Dates} dateUtils Date Utils
- * @param {ActivatedRoute} route Activated Route
- * @param {Router} router Router
- * @param {SettingsService} settingsService Settings Service
- */
- constructor() {
- this.accountId = this.route.parent.snapshot.params['fixedDepositAccountId'];
- }
-
- /**
- * Creates the premature close fd account form.
- */
- ngOnInit() {
- this.maxDate = this.settingsService.businessDate;
- this.createPrematureCloseAccountForm();
- this.buildDependencies();
- }
-
- /**
- * Creates the premature close fd account form.
- */
- createPrematureCloseAccountForm() {
- this.prematureCloseAccountForm = this.formBuilder.group({
- closedOnDate: [
- '',
- Validators.required
- ]
- });
- }
-
- /**
- * Subscribes to value changes of parent control.
- */
- buildDependencies() {
- this.prematureCloseAccountForm.get('closedOnDate').valueChanges.subscribe((value: Date) => {
- if (!this.isSubmitted) {
- this.calculatePrematureAmount(value);
- }
- });
- }
-
- /**
- * Calculates prematue amount based on closed on date.
- * @param {Date} date Premature Close Date
- */
- calculatePrematureAmount(date: Date) {
- const locale = this.settingsService.language.code;
- const dateFormat = this.settingsService.dateFormat;
- const data = {
- closedOnDate: this.dateUtils.formatDate(date, dateFormat),
- dateFormat,
- locale
- };
- this.fixedDepositsService
- .executeFixedDepositsAccountCommand(this.accountId, 'calculatePrematureAmount', data)
- .subscribe((response: any) => {
- this.savingsAccountsData = response.savingsAccounts;
- this.onAccountClosureOptions = response.onAccountClosureOptions;
- this.prematureCloseAccountForm.addControl(
- 'maturityAmount',
- new UntypedFormControl({ value: '', disabled: true })
- );
- this.prematureCloseAccountForm.addControl(
- 'onAccountClosureId',
- new UntypedFormControl('', Validators.required)
- );
- this.prematureCloseAccountForm.addControl('note', new UntypedFormControl(''));
- this.prematureCloseAccountForm.get('maturityAmount').patchValue(response.maturityAmount);
- this.addTransferDetails();
- });
- }
-
- /**
- * Subscribes to value changes of `onAccountClosureId` adds and removes transfer details accordingly.
- */
- addTransferDetails() {
- this.prematureCloseAccountForm.get('onAccountClosureId').valueChanges.subscribe((id: any) => {
- if (id === 200) {
- this.prematureCloseAccountForm.addControl(
- 'toSavingsAccountId',
- new UntypedFormControl('', Validators.required)
- );
- this.prematureCloseAccountForm.addControl('transferDescription', new UntypedFormControl(''));
- } else {
- this.prematureCloseAccountForm.removeControl('toSavingsAccountId');
- this.prematureCloseAccountForm.removeControl('transferDescription');
- }
- });
- }
-
- /**
- * Submits the form and premature closes the fd account,
- * if successful redirects to the fd account.
- */
- submit() {
- const prematureCloseAccountFormData = this.prematureCloseAccountForm.value;
- this.isSubmitted = true;
- const locale = this.settingsService.language.code;
- const dateFormat = this.settingsService.dateFormat;
- const prevClosedDate: Date = this.prematureCloseAccountForm.value.closedOnDate;
- if (prematureCloseAccountFormData.closedOnDate instanceof Date) {
- prematureCloseAccountFormData.closedOnDate = this.dateUtils.formatDate(prevClosedDate, dateFormat);
- }
- const data = {
- ...prematureCloseAccountFormData,
- dateFormat,
- locale
- };
- this.fixedDepositsService
- .executeFixedDepositsAccountCommand(this.accountId, 'prematureClose', data)
- .subscribe(() => {
- this.router.navigate(['../../'], { relativeTo: this.route });
- });
- }
-}
diff --git a/src/app/deposits/fixed-deposits/fixed-deposits.module.ts b/src/app/deposits/fixed-deposits/fixed-deposits.module.ts
index 6529b1b04..9aa9db4ba 100644
--- a/src/app/deposits/fixed-deposits/fixed-deposits.module.ts
+++ b/src/app/deposits/fixed-deposits/fixed-deposits.module.ts
@@ -35,7 +35,6 @@ import { ViewTransactionComponent } from './fixed-deposit-account-view/view-tran
import { WaiveChargeDialogComponent } from './fixed-deposit-account-view/custom-dialogs/waive-charge-dialog/waive-charge-dialog.component';
import { InactivateChargeDialogComponent } from './fixed-deposit-account-view/custom-dialogs/inactivate-charge-dialog/inactivate-charge-dialog.component';
import { AddChargeFixedDepositsAccountComponent } from './fixed-deposits-account-actions/add-charge-fixed-deposits-account/add-charge-fixed-deposits-account.component';
-import { PrematureCloseFixedDepositsAccountComponent } from './fixed-deposits-account-actions/premature-close-fixed-deposits-account/premature-close-fixed-deposits-account.component';
import { CloseFixedDepositsAccountComponent } from './fixed-deposits-account-actions/close-fixed-deposits-account/close-fixed-deposits-account.component';
import { CreateFixedDepositAccountComponent } from './create-fixed-deposit-account/create-fixed-deposit-account.component';
import { FixedDepositAccountDetailsStepComponent } from './fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component';
@@ -78,7 +77,6 @@ import { FixedDepositsCashTransactionComponent } from './fixed-deposits-account-
WaiveChargeDialogComponent,
InactivateChargeDialogComponent,
AddChargeFixedDepositsAccountComponent,
- PrematureCloseFixedDepositsAccountComponent,
CloseFixedDepositsAccountComponent,
CreateFixedDepositAccountComponent,
FixedDepositAccountDetailsStepComponent,