update some logic show option
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e212bf7d79
commit
e9a9377148
+4
-4
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -61,14 +61,14 @@
|
||||
@if (CurrentProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ CurrentProduct.effectiveTo }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (CurrentProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ CurrentProduct.endDate }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
@if (CurrentProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ CurrentProduct.lastInterestDate }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from '@angular/material/table';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
@@ -47,6 +48,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatRow,
|
||||
FaIconComponent,
|
||||
FindPipe,
|
||||
DateFormatPipe,
|
||||
FormatNumberPipe,
|
||||
YesnoPipe
|
||||
]
|
||||
|
||||
-5
@@ -100,11 +100,6 @@
|
||||
<span>Account Closed with Zero Balance in Day Closing</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="sps-toggle sps-toggle--inline">
|
||||
<mat-checkbox formControlName="withHoldTax">
|
||||
<span>Withhold Tax is Applicable</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+2
-2
@@ -54,7 +54,6 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
||||
withdrawalFeeForTransfers: this.CurrentProductsTemplate.withdrawalFeeForTransfers,
|
||||
enforceMinRequiredBalance: this.CurrentProductsTemplate.enforceMinRequiredBalance,
|
||||
minRequiredBalance: this.CurrentProductsTemplate.minRequiredBalance,
|
||||
withHoldTax: this.CurrentProductsTemplate.withHoldTax ?? false,
|
||||
balanceFrequency: isEditing ? this.CurrentProductsTemplate.balanceFrequency || defaultPeriod : defaultPeriod,
|
||||
enableLockinPeriod: !!(
|
||||
this.CurrentProductsTemplate.lockinPeriodFrequency && this.CurrentProductsTemplate.lockinPeriodFrequency > 0
|
||||
@@ -107,7 +106,6 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
||||
'',
|
||||
[Validators.min(0)]
|
||||
],
|
||||
withHoldTax: [false],
|
||||
balanceFrequency: ['Monthly'],
|
||||
enableLockinPeriod: [false],
|
||||
lockinPeriodFrequency: [''],
|
||||
@@ -159,6 +157,8 @@ export class CurrentProductSettingsStepComponent implements OnInit {
|
||||
get CurrentProductSettings() {
|
||||
const formValue = { ...this.CurrentProductSettingsForm.value };
|
||||
|
||||
delete formValue.withHoldTax;
|
||||
|
||||
if (!formValue.allowOverdraft) {
|
||||
delete formValue.overdraftLimit;
|
||||
delete formValue.nominalAnnualInterestRateOverdraft;
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -75,21 +75,21 @@
|
||||
@if (currentProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ currentProduct.effectiveDate }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (currentProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ currentProduct.effectiveTo }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (currentProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ currentProduct.endDate }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
@if (currentProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ currentProduct.lastInterestDate }}</span>
|
||||
<span class="flex-60">{{ CurrentProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
@@ -47,6 +48,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatRow,
|
||||
ViewSavingsAccountingDetailsComponent,
|
||||
FormatNumberPipe,
|
||||
DateFormatPipe,
|
||||
YesnoPipe
|
||||
]
|
||||
})
|
||||
|
||||
+5
-5
@@ -150,11 +150,11 @@ export class EditRecurringDepositProductComponent {
|
||||
this.recurringDepositProductTermsForm.valid &&
|
||||
this.recurringDepositProductSettingsForm.valid &&
|
||||
this.recurringDepositProductAccountingForm.valid &&
|
||||
(this.recurringDepositProductDetailsForm.pristine ||
|
||||
this.recurringDepositProductCurrencyForm.pristine ||
|
||||
this.recurringDepositProductTermsForm.pristine ||
|
||||
this.recurringDepositProductSettingsForm.pristine ||
|
||||
this.recurringDepositProductAccountingForm.pristine)
|
||||
(!this.recurringDepositProductDetailsForm.pristine ||
|
||||
!this.recurringDepositProductCurrencyForm.pristine ||
|
||||
!this.recurringDepositProductTermsForm.pristine ||
|
||||
!this.recurringDepositProductSettingsForm.pristine ||
|
||||
!this.recurringDepositProductAccountingForm.pristine)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+26
-15
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -7,10 +7,10 @@
|
||||
-->
|
||||
|
||||
<div class="responsive-column">
|
||||
<!-- ── Product Name ── -->
|
||||
<!-- ── Product Name ── -->
|
||||
<h2 class="mat-h2 flex-100">{{ recurringDepositProduct.name }}</h2>
|
||||
|
||||
<!-- ── Details ── -->
|
||||
<!-- ── Details ── -->
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Details' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Product Classification ── -->
|
||||
<!-- ── Product Classification ── -->
|
||||
@if (
|
||||
recurringDepositProduct.accountType ||
|
||||
recurringDepositProduct.customerSegment ||
|
||||
@@ -63,14 +63,14 @@
|
||||
@if (recurringDepositProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveTo }}</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (recurringDepositProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.endDate }}</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
<!-- ── Currency ── -->
|
||||
<!-- ── Currency ── -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Currency' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Terms ── -->
|
||||
<!-- ── Terms ── -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Terms' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
@@ -169,11 +169,22 @@
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@if (recurringDepositProduct.interestCalculateSelector) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
recurringDepositProduct.interestCalculateSelector
|
||||
| find: recurringDepositProductsTemplate.interestCalculationTypeOption : 'id' : 'value'
|
||||
}}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
recurringDepositProduct.interestCalculationType
|
||||
| find: recurringDepositProductsTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
||||
| translateKey: 'catalogs'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@@ -188,7 +199,7 @@
|
||||
@if (recurringDepositProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate }}</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -218,11 +229,11 @@
|
||||
@if (recurringDepositProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.lastInterestDate }}</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Settings ── -->
|
||||
<!-- ── Settings ── -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Settings' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
@@ -313,7 +324,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Interest Rate Slabs ── -->
|
||||
<!-- ── Interest Rate Slabs ── -->
|
||||
@if (recurringDepositProduct.rdSlabs?.length) {
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Interest Rate Charts' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
@@ -334,7 +345,7 @@
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="toPeriod">
|
||||
<th mat-header-cell *matHeaderCellDef>To</th>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
||||
<td mat-cell *matCellDef="let slab">{{ slab.toPeriod ?? '—' }}</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="periodTypeLabel">
|
||||
<th mat-header-cell *matHeaderCellDef>Period</th>
|
||||
@@ -352,7 +363,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Charges ── -->
|
||||
<!-- ── Charges ── -->
|
||||
@if (recurringDepositProduct.charges?.length) {
|
||||
<div class="layout-row-wrap responsive-column flex-100">
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.inputs.Charges' | translate }}</h3>
|
||||
@@ -380,7 +391,7 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- ── Accounting ── -->
|
||||
<!-- ── Accounting ── -->
|
||||
<h3 class="mat-h3 flex-100">{{ 'labels.heading.Accounting' | translate }}</h3>
|
||||
<mat-divider class="flex-100"></mat-divider>
|
||||
|
||||
|
||||
+16
-19
@@ -54,14 +54,22 @@
|
||||
<mat-form-field appearance="outline" class="rdpt-field">
|
||||
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||
<mat-select
|
||||
formControlName="interestCalculationType"
|
||||
formControlName="interestCalculateSelector"
|
||||
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
||||
required
|
||||
>
|
||||
@for (interestCalculationType of interestCalculationTypeOptionData; track interestCalculationType.id) {
|
||||
<mat-option [value]="interestCalculationType.id">
|
||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
@for (opt of interestCalculationTypeOptionData; track opt.id) {
|
||||
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="rdpt-grid rdpt-grid--three">
|
||||
<mat-form-field appearance="outline" class="rdpt-field">
|
||||
<mat-label>Interest Calculate</mat-label>
|
||||
<mat-select formControlName="interestCalculationType" required>
|
||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||
<mat-option [value]="mode.id">{{ mode.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
@@ -69,19 +77,8 @@
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="rdpt-grid rdpt-grid--three">
|
||||
<mat-form-field appearance="outline" class="rdpt-field">
|
||||
<mat-label>Interest Calculate</mat-label>
|
||||
<mat-select formControlName="interestCalculateSelector" required>
|
||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||
<mat-option [value]="mode.id">{{ mode.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@if (recurringDepositProductTermsForm.value.interestCalculateSelector === 2) {
|
||||
@if (recurringDepositProductTermsForm.value.interestCalculationType === 2) {
|
||||
<mat-form-field appearance="outline" class="rdpt-field">
|
||||
<mat-label>Average Period</mat-label>
|
||||
<mat-select formControlName="averagePeriod">
|
||||
@@ -142,7 +139,7 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (recurringDepositProductTermsForm.value.interestCalculateSelector === 1) {
|
||||
@if (recurringDepositProductTermsForm.value.interestCalculationType === 1) {
|
||||
<div class="rdpt-note">
|
||||
<span>⚠️</span>
|
||||
<span
|
||||
|
||||
+15
-23
@@ -49,10 +49,10 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.interestCompoundingPeriodTypeData = this.recurringDepositProductsTemplate.interestCompoundingPeriodTypeOptions;
|
||||
this.interestPostingPeriodTypeData = this.recurringDepositProductsTemplate.interestPostingPeriodTypeOptions;
|
||||
// Standard enum (1=Daily, 2=Average) → "Interest Calculate" dropdown → interestCalculationType form control
|
||||
this.interestCalculationTypeData = this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
|
||||
this.interestCalculationTypeOptionData =
|
||||
this.recurringDepositProductsTemplate.interestCalculationTypeOption ||
|
||||
this.recurringDepositProductsTemplate.interestCalculationTypeOptions;
|
||||
// CBS code values → "Interest Calculation Type" dropdown → interestCalculateSelector form control
|
||||
this.interestCalculationTypeOptionData = this.recurringDepositProductsTemplate.interestCalculationTypeOption || [];
|
||||
this.interestCalculationDaysInYearTypeData =
|
||||
this.recurringDepositProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
||||
this.periodFrequencyData = this.recurringDepositProductsTemplate.periodFrequencyOptions || [];
|
||||
@@ -60,37 +60,33 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
|
||||
const isEditing = !!this.recurringDepositProductsTemplate.id;
|
||||
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||
const savedEnumTypeId =
|
||||
this.recurringDepositProductsTemplate.interestCalculationType?.id ||
|
||||
this.interestCalculationTypeData?.[0]?.id ||
|
||||
1;
|
||||
const savedSelectorId = isEditing
|
||||
? (this.recurringDepositProductsTemplate.interestCalculateSelector ??
|
||||
this.interestCalculationTypeOptionData?.[0]?.id ??
|
||||
null)
|
||||
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
|
||||
|
||||
this.recurringDepositProductTermsForm.patchValue({
|
||||
nominalAnnualInterestRate: this.recurringDepositProductsTemplate.nominalAnnualInterestRate ?? 0,
|
||||
interestCompoundingPeriodType: this.recurringDepositProductsTemplate.interestCompoundingPeriodType?.id,
|
||||
interestPostingPeriodType: this.recurringDepositProductsTemplate.interestPostingPeriodType?.id,
|
||||
interestCalculationType: isEditing
|
||||
? this.recurringDepositProductsTemplate.interestCalculationTypeOption?.id ||
|
||||
this.interestCalculationTypeOptionData?.[0]?.id ||
|
||||
1
|
||||
: this.interestCalculationTypeOptionData?.[0]?.id || 1,
|
||||
interestCalculationType: savedEnumTypeId,
|
||||
interestCalculationDaysInYearType: this.recurringDepositProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||
effectiveDate: this.recurringDepositProductsTemplate.effectiveDate
|
||||
? this.parseDate(this.recurringDepositProductsTemplate.effectiveDate)
|
||||
: '',
|
||||
interestCalculateSelector: isEditing
|
||||
? this.recurringDepositProductsTemplate.interestCalculateSelector || 1
|
||||
: this.recurringDepositProductsTemplate.interestCalculationType?.id ||
|
||||
this.interestCalculationTypeData?.[0]?.id ||
|
||||
1,
|
||||
interestCalculateSelector: savedSelectorId,
|
||||
averagePeriod: isEditing ? this.recurringDepositProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
||||
autoPost: isEditing ? (this.recurringDepositProductsTemplate.autoPost ?? true) : true,
|
||||
frequencyFixDay: isEditing ? this.recurringDepositProductsTemplate.frequencyFixDay || 30 : 30,
|
||||
frequencyFixDay: isEditing ? (this.recurringDepositProductsTemplate.frequencyFixDay ?? 30) : 30,
|
||||
lastInterestDate: this.recurringDepositProductsTemplate.lastInterestDate
|
||||
? this.parseDate(this.recurringDepositProductsTemplate.lastInterestDate)
|
||||
: ''
|
||||
});
|
||||
|
||||
const currentInterestCalcType = this.recurringDepositProductTermsForm.get('interestCalculationType').value;
|
||||
if (currentInterestCalcType && currentInterestCalcType > 2) {
|
||||
this.recurringDepositProductTermsForm.patchValue({ interestCalculationType: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private parseDate(dateValue: any): Date | null {
|
||||
@@ -163,10 +159,6 @@ export class RecurringDepositProductTermsStepComponent implements OnInit {
|
||||
if (formValue.lastInterestDate instanceof Date) {
|
||||
formValue.lastInterestDate = this.dateUtils.formatDate(formValue.lastInterestDate, dateFormat) || '';
|
||||
}
|
||||
if (formValue.interestCalculationType && formValue.interestCalculationType > 2) {
|
||||
formValue.interestCalculationType = 1;
|
||||
}
|
||||
|
||||
for (const key in formValue) {
|
||||
if (formValue[key] === '') {
|
||||
delete formValue[key];
|
||||
|
||||
+7
-7
@@ -141,16 +141,16 @@
|
||||
<span class="flex-60">{{ recurringDepositProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType.value }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.interestCalculateSelector !== undefined) {
|
||||
@if (recurringDepositProduct.interestCalculationTypeName) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Interest Calculate:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculateSelector }}</span>
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationTypeName }}</span>
|
||||
</div>
|
||||
}
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Interest Calculate:</span>
|
||||
<span class="flex-60">{{ recurringDepositProduct.interestCalculationType?.value }}</span>
|
||||
</div>
|
||||
@if (recurringDepositProduct.averagePeriod) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Average Period:</span>
|
||||
|
||||
+8
-9
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -61,14 +61,14 @@
|
||||
@if (savingProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveTo }}</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.endDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -158,9 +158,7 @@
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
savingProduct.interestCalculationType
|
||||
| find: interestCalculationTypeOptionData : 'id' : 'value'
|
||||
| translateKey: 'catalogs'
|
||||
savingProduct.interestCalculateSelector | find: interestCalculationTypeOptionData : 'id' : 'value'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@@ -176,15 +174,16 @@
|
||||
@if (savingProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Interest Calculate:</span>
|
||||
<span class="flex-60">{{
|
||||
savingProduct.interestCalculateSelector
|
||||
savingProduct.interestCalculationType
|
||||
| find: savingProductsTemplate?.interestCalculationTypeOptions : 'id' : 'value'
|
||||
| translateKey: 'catalogs'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@@ -214,7 +213,7 @@
|
||||
@if (savingProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.lastInterestDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from '@angular/material/table';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { FindPipe } from '../../../../pipes/find.pipe';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
@@ -47,6 +48,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatRow,
|
||||
FaIconComponent,
|
||||
FindPipe,
|
||||
DateFormatPipe,
|
||||
FormatNumberPipe,
|
||||
YesnoPipe
|
||||
]
|
||||
|
||||
+7
-3
@@ -82,11 +82,15 @@ export class SavingProductSettingsStepComponent implements OnInit {
|
||||
this.savingProductsTemplate.lockinPeriodFrequency &&
|
||||
this.savingProductsTemplate.lockinPeriodFrequency > 0
|
||||
) {
|
||||
// Backend returns lockinPeriodFrequencyType.id as integer (0=DAYS,1=WEEKS,2=MONTHS,3=YEARS)
|
||||
// but the form dropdown uses string IDs, so reverse-map the number back to string
|
||||
const reverseFrequencyTypeMap: { [key: number]: string } = { 0: 'DAYS', 1: 'WEEKS', 2: 'MONTHS', 3: 'YEARS' };
|
||||
const freqTypeId = this.savingProductsTemplate.lockinPeriodFrequencyType?.id;
|
||||
const freqTypeString = reverseFrequencyTypeMap[freqTypeId] ?? '';
|
||||
|
||||
this.savingProductSettingsForm.patchValue({
|
||||
lockinPeriodFrequency: this.savingProductsTemplate.lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType:
|
||||
this.savingProductsTemplate.lockinPeriodFrequencyType &&
|
||||
this.savingProductsTemplate.lockinPeriodFrequencyType.id
|
||||
lockinPeriodFrequencyType: freqTypeString
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+16
-19
@@ -54,14 +54,22 @@
|
||||
<mat-form-field appearance="outline" class="spt-field">
|
||||
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||
<mat-select
|
||||
formControlName="interestCalculationType"
|
||||
formControlName="interestCalculateSelector"
|
||||
matTooltip="{{ 'tooltips.The method used to calculate interest' | translate }}"
|
||||
required
|
||||
>
|
||||
@for (interestCalculationType of interestCalculationTypeOptionData; track interestCalculationType.id) {
|
||||
<mat-option [value]="interestCalculationType.id">
|
||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
@for (opt of interestCalculationTypeOptionData; track opt.id) {
|
||||
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="spt-grid spt-grid--three">
|
||||
<mat-form-field appearance="outline" class="spt-field">
|
||||
<mat-label>Interest Calculate</mat-label>
|
||||
<mat-select formControlName="interestCalculationType" required>
|
||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||
<mat-option [value]="mode.id">{{ mode.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
@@ -69,19 +77,8 @@
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="spt-grid spt-grid--three">
|
||||
<mat-form-field appearance="outline" class="spt-field">
|
||||
<mat-label>Interest Calculate</mat-label>
|
||||
<mat-select formControlName="interestCalculateSelector" required>
|
||||
@for (mode of interestCalculationTypeData; track mode.id) {
|
||||
<mat-option [value]="mode.id">{{ mode.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@if (savingProductTermsForm.value.interestCalculateSelector === 2) {
|
||||
@if (savingProductTermsForm.value.interestCalculationType === 2) {
|
||||
<mat-form-field appearance="outline" class="spt-field">
|
||||
<mat-label>Average Period</mat-label>
|
||||
<mat-select formControlName="averagePeriod">
|
||||
@@ -142,7 +139,7 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (savingProductTermsForm.value.interestCalculateSelector === 1) {
|
||||
@if (savingProductTermsForm.value.interestCalculationType === 1) {
|
||||
<div class="spt-note">
|
||||
<span>⚠️</span>
|
||||
<span
|
||||
|
||||
+19
-41
@@ -59,59 +59,43 @@ export class SavingProductTermsStepComponent implements OnInit {
|
||||
ngOnInit() {
|
||||
this.interestCompoundingPeriodTypeData = this.savingProductsTemplate.interestCompoundingPeriodTypeOptions;
|
||||
this.interestPostingPeriodTypeData = this.savingProductsTemplate.interestPostingPeriodTypeOptions;
|
||||
// interestCalculationTypeData = standard enum (1=Daily, 2=Average) → "Interest Calculate" dropdown → interestCalculationType form control
|
||||
this.interestCalculationTypeData = this.savingProductsTemplate.interestCalculationTypeOptions;
|
||||
this.interestCalculationTypeOptionData =
|
||||
this.savingProductsTemplate.interestCalculationTypeOption ||
|
||||
this.savingProductsTemplate.interestCalculationTypeOptions;
|
||||
// interestCalculationTypeOptionData = CBS code values from DB → "Interest Calculation Type" dropdown → interestCalculateSelector form control
|
||||
this.interestCalculationTypeOptionData = this.savingProductsTemplate.interestCalculationTypeOption || [];
|
||||
this.interestCalculationDaysInYearTypeData = this.savingProductsTemplate.interestCalculationDaysInYearTypeOptions;
|
||||
this.periodFrequencyData = this.savingProductsTemplate.periodFrequencyOptions || [];
|
||||
this.setYesNoOptions(this.savingProductsTemplate?.yesNoOptions);
|
||||
|
||||
const isEditing = !!this.savingProductsTemplate.id;
|
||||
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||
// interestCalculationType = standard enum id (1=Daily Balance, 2=Average Daily Balance)
|
||||
const savedEnumTypeId =
|
||||
this.savingProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
|
||||
// interestCalculateSelector = CBS code value id from interestCalculationTypeOption
|
||||
const savedSelectorId = isEditing
|
||||
? (this.savingProductsTemplate.interestCalculateSelector ??
|
||||
this.interestCalculationTypeOptionData?.[0]?.id ??
|
||||
null)
|
||||
: (this.interestCalculationTypeOptionData?.[0]?.id ?? null);
|
||||
|
||||
this.savingProductTermsForm.patchValue({
|
||||
nominalAnnualInterestRate: isEditing
|
||||
? this.savingProductsTemplate.nominalAnnualInterestRate
|
||||
: this.savingProductsTemplate.nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType: isEditing
|
||||
? this.savingProductsTemplate.interestCompoundingPeriodType?.id
|
||||
: this.savingProductsTemplate.interestCompoundingPeriodType?.id,
|
||||
interestPostingPeriodType: isEditing
|
||||
? this.savingProductsTemplate.interestPostingPeriodType?.id
|
||||
: this.savingProductsTemplate.interestPostingPeriodType?.id,
|
||||
interestCalculationType: isEditing
|
||||
? this.savingProductsTemplate.interestCalculationTypeOption?.id ||
|
||||
this.interestCalculationTypeOptionData?.[0]?.id ||
|
||||
1
|
||||
: this.interestCalculationTypeOptionData?.[0]?.id || 1,
|
||||
interestCalculationDaysInYearType: isEditing
|
||||
? this.savingProductsTemplate.interestCalculationDaysInYearType?.id
|
||||
: this.savingProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||
nominalAnnualInterestRate: this.savingProductsTemplate.nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType: this.savingProductsTemplate.interestCompoundingPeriodType?.id,
|
||||
interestPostingPeriodType: this.savingProductsTemplate.interestPostingPeriodType?.id,
|
||||
interestCalculationType: savedEnumTypeId,
|
||||
interestCalculationDaysInYearType: this.savingProductsTemplate.interestCalculationDaysInYearType?.id,
|
||||
effectiveDate: this.savingProductsTemplate.effectiveDate
|
||||
? new Date(this.savingProductsTemplate.effectiveDate)
|
||||
: '',
|
||||
interestCalculateSelector: isEditing
|
||||
? this.savingProductsTemplate.interestCalculateSelector || 1
|
||||
: this.savingProductsTemplate.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1,
|
||||
interestCalculateSelector: savedSelectorId,
|
||||
averagePeriod: isEditing ? this.savingProductsTemplate.averagePeriod || defaultPeriod : defaultPeriod,
|
||||
autoPost: isEditing ? (this.savingProductsTemplate.autoPost ?? true) : true,
|
||||
frequencyFixDay: isEditing ? this.savingProductsTemplate.frequencyFixDay || 30 : 30,
|
||||
frequencyFixDay: isEditing ? (this.savingProductsTemplate.frequencyFixDay ?? 30) : 30,
|
||||
lastInterestDate: this.savingProductsTemplate.lastInterestDate
|
||||
? new Date(this.savingProductsTemplate.lastInterestDate)
|
||||
: ''
|
||||
});
|
||||
|
||||
// Map code value IDs to backend enum values for interestCalculationType
|
||||
// Backend expects: 1=Daily Balance, 2=Average Daily Balance
|
||||
const currentInterestCalcType = this.savingProductTermsForm.get('interestCalculationType').value;
|
||||
if (currentInterestCalcType && currentInterestCalcType > 2) {
|
||||
// If it's a code value ID (like 340, 341), map it to backend enum
|
||||
// Default to 1 (Daily Balance) for custom code values
|
||||
this.savingProductTermsForm.patchValue({
|
||||
interestCalculationType: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setYesNoOptions(options: any[]) {
|
||||
@@ -198,12 +182,6 @@ export class SavingProductTermsStepComponent implements OnInit {
|
||||
formValue.lastInterestDate = this.dateUtils.formatDate(prevLastInterestDate, dateFormat) || '';
|
||||
}
|
||||
|
||||
// Convert interestCalculationType from code value ID to backend enum value
|
||||
// Backend expects: 1=Daily Balance, 2=Average Daily Balance
|
||||
if (formValue.interestCalculationType && formValue.interestCalculationType > 2) {
|
||||
formValue.interestCalculationType = 1; // Default to Daily Balance
|
||||
}
|
||||
|
||||
return {
|
||||
...formValue
|
||||
};
|
||||
|
||||
+14
-7
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -75,21 +75,21 @@
|
||||
@if (savingProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingProduct.effectiveTo) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective To:</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveTo }}</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingProduct.endDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">End Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.endDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -164,6 +164,13 @@
|
||||
<span class="flex-60">{{ savingProduct.interestPostingPeriodType.value | translateKey: 'catalogs' }}</span>
|
||||
</div>
|
||||
|
||||
@if (savingProduct.interestCalculationTypeName) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculation Type' | translate }}:</span>
|
||||
<span class="flex-60">{{ savingProduct.interestCalculationTypeName }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||
<span class="flex-60">{{ savingProduct.interestCalculationType.value | translateKey: 'catalogs' }}</span>
|
||||
@@ -177,13 +184,13 @@
|
||||
@if (savingProduct.effectiveDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Effective Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Interest Calculate:</span>
|
||||
<span class="flex-60">{{ savingProduct.interestCalculateSelector }}</span>
|
||||
<span class="flex-60">{{ savingProduct.interestCalculationType?.value | translateKey: 'catalogs' }}</span>
|
||||
</div>
|
||||
|
||||
@if (savingProduct.averagePeriod) {
|
||||
@@ -208,7 +215,7 @@
|
||||
@if (savingProduct.lastInterestDate) {
|
||||
<div class="flex-100 layout-row">
|
||||
<span class="flex-40">Last Interest Date:</span>
|
||||
<span class="flex-60">{{ savingProduct.lastInterestDate }}</span>
|
||||
<span class="flex-60">{{ savingProduct.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { ViewSavingsAccountingDetailsComponent } from '../../../../shared/accounting/view-savings-accounting-details/view-savings-accounting-details.component';
|
||||
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
||||
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
@@ -48,6 +49,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatRow,
|
||||
ViewSavingsAccountingDetailsComponent,
|
||||
FormatNumberPipe,
|
||||
DateFormatPipe,
|
||||
YesnoPipe
|
||||
]
|
||||
})
|
||||
|
||||
@@ -34,8 +34,9 @@
|
||||
<fa-icon icon="eye" size="sm"></fa-icon>
|
||||
</ng-template>
|
||||
|
||||
<!-- Step 1: Product Classification + Details -->
|
||||
<mat-step [stepControl]="savingsAccountDetailsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
|
||||
<ng-template matStepLabel>Product Classification</ng-template>
|
||||
|
||||
<mifosx-savings-account-details-step
|
||||
[savingsAccountTemplate]="savingsAccountTemplate"
|
||||
@@ -44,14 +45,83 @@
|
||||
</mifosx-savings-account-details-step>
|
||||
</mat-step>
|
||||
|
||||
<mat-step [stepControl]="savingsAccountTermsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.TERMS' | translate }}</ng-template>
|
||||
<!-- Step 2: Currency -->
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>Currency</ng-template>
|
||||
|
||||
<mifosx-savings-account-terms-step
|
||||
[savingsAccountProductTemplate]="savingsAccountProductTemplate"
|
||||
></mifosx-savings-account-terms-step>
|
||||
<mifosx-savings-account-currency-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||
</mifosx-savings-account-currency-step>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<!-- Step 3: Term -->
|
||||
<mat-step [stepControl]="savingsAccountTermsForm">
|
||||
<ng-template matStepLabel>Term</ng-template>
|
||||
|
||||
<mifosx-savings-account-term-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||
</mifosx-savings-account-term-step>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext [disabled]="savingsAccountTermsForm.invalid">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<!-- Step 4: Settings -->
|
||||
<mat-step [stepControl]="savingsAccountSettingsForm">
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.SETTINGS' | translate }}</ng-template>
|
||||
|
||||
<mifosx-savings-account-settings-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||
</mifosx-savings-account-settings-step>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<!-- Step 5: Digital -->
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>Digital</ng-template>
|
||||
|
||||
<mifosx-savings-account-digital-step [savingsAccountProductTemplate]="savingsAccountProductTemplate">
|
||||
</mifosx-savings-account-digital-step>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
<button mat-raised-button matStepperPrevious>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-step>
|
||||
|
||||
<!-- Step 6: Charges -->
|
||||
<mat-step>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.CHARGES' | translate }}</ng-template>
|
||||
|
||||
@@ -63,6 +133,7 @@
|
||||
</mifosx-savings-account-charges-step>
|
||||
</mat-step>
|
||||
|
||||
<!-- Step 7: Preview -->
|
||||
@if (savingsAccountFormValid) {
|
||||
<mat-step completed>
|
||||
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
|
||||
|
||||
@@ -12,8 +12,11 @@ import { Router, ActivatedRoute } from '@angular/router';
|
||||
|
||||
/** Custom Components */
|
||||
import { SavingsAccountDetailsStepComponent } from '../savings-account-stepper/savings-account-details-step/savings-account-details-step.component';
|
||||
import { SavingsAccountTermsStepComponent } from '../savings-account-stepper/savings-account-terms-step/savings-account-terms-step.component';
|
||||
import { SavingsAccountTermStepComponent } from '../savings-account-stepper/savings-account-term-step/savings-account-term-step.component';
|
||||
import { SavingsAccountSettingsStepComponent } from '../savings-account-stepper/savings-account-settings-step/savings-account-settings-step.component';
|
||||
import { SavingsAccountChargesStepComponent } from '../savings-account-stepper/savings-account-charges-step/savings-account-charges-step.component';
|
||||
import { SavingsAccountCurrencyStepComponent } from '../savings-account-stepper/savings-account-currency-step/savings-account-currency-step.component';
|
||||
import { SavingsAccountDigitalStepComponent } from '../savings-account-stepper/savings-account-digital-step/savings-account-digital-step.component';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from '../savings.service';
|
||||
@@ -39,8 +42,11 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
MatStep,
|
||||
MatStepLabel,
|
||||
SavingsAccountDetailsStepComponent,
|
||||
SavingsAccountTermsStepComponent,
|
||||
SavingsAccountCurrencyStepComponent,
|
||||
SavingsAccountTermStepComponent,
|
||||
SavingsAccountSettingsStepComponent,
|
||||
SavingsAccountChargesStepComponent,
|
||||
SavingsAccountDigitalStepComponent,
|
||||
SavingsAccountPreviewStepComponent
|
||||
]
|
||||
})
|
||||
@@ -56,75 +62,58 @@ export class CreateSavingsAccountComponent {
|
||||
/** Savings Account Product Template */
|
||||
savingsAccountProductTemplate: any;
|
||||
|
||||
/** Savings Account Details Step */
|
||||
@ViewChild(SavingsAccountDetailsStepComponent, { static: true })
|
||||
savingsAccountDetailsStep: SavingsAccountDetailsStepComponent;
|
||||
/** Savings Account Terms Step */
|
||||
@ViewChild(SavingsAccountTermsStepComponent, { static: true })
|
||||
savingsAccountTermsStep: SavingsAccountTermsStepComponent;
|
||||
/** Savings Account Charges Step */
|
||||
|
||||
@ViewChild(SavingsAccountTermStepComponent, { static: true })
|
||||
savingsAccountTermStep: SavingsAccountTermStepComponent;
|
||||
|
||||
@ViewChild(SavingsAccountSettingsStepComponent, { static: true })
|
||||
savingsAccountSettingsStep: SavingsAccountSettingsStepComponent;
|
||||
|
||||
@ViewChild(SavingsAccountChargesStepComponent, { static: true })
|
||||
savingsAccountChargesStep: SavingsAccountChargesStepComponent;
|
||||
|
||||
/**
|
||||
* Fetches savings account template from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
* @param {Router} router Router
|
||||
* @param {Dates} dateUtils Date Utils
|
||||
* @param {SavingsService} savingsService Savings Service
|
||||
* @param {SettingsService} settingsService Settings Service
|
||||
*/
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { savingsAccountTemplate: any }) => {
|
||||
this.savingsAccountTemplate = data.savingsAccountTemplate;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets savings account product template.
|
||||
* @param {any} $event API response
|
||||
*/
|
||||
setTemplate($event: any) {
|
||||
this.savingsAccountProductTemplate = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves savings account details form.
|
||||
*/
|
||||
get savingsAccountDetailsForm() {
|
||||
return this.savingsAccountDetailsStep.savingsAccountDetailsForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves savings account terms form.
|
||||
*/
|
||||
get savingsAccountTermsForm() {
|
||||
return this.savingsAccountTermsStep.savingsAccountTermsForm;
|
||||
return this.savingsAccountTermStep.savingsAccountTermForm;
|
||||
}
|
||||
|
||||
get savingsAccountSettingsForm() {
|
||||
return this.savingsAccountSettingsStep.savingsAccountSettingsForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks validity of overall savings account form.
|
||||
*/
|
||||
get savingsAccountFormValid() {
|
||||
return this.savingsAccountDetailsForm.valid && this.savingsAccountTermsForm.valid;
|
||||
return (
|
||||
this.savingsAccountDetailsForm.valid &&
|
||||
this.savingsAccountTermsForm.valid &&
|
||||
this.savingsAccountSettingsForm.valid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves savings account object.
|
||||
*/
|
||||
get savingsAccount() {
|
||||
return {
|
||||
...this.savingsAccountDetailsStep.savingsAccountDetails,
|
||||
...this.savingsAccountTermsStep.savingsAccountTerms,
|
||||
...this.savingsAccountTermStep.savingsAccountTerms,
|
||||
...this.savingsAccountSettingsStep.savingsAccountSettings,
|
||||
...this.savingsAccountChargesStep.savingsAccountCharges
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new share account.
|
||||
*/
|
||||
submit() {
|
||||
// TODO: Update once language and date settings are setup
|
||||
const locale = this.settingsService.language.code;
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const monthDayFormat = 'dd MMMM';
|
||||
|
||||
+15
-11
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, OnChanges, Input, inject } from '@angular/core';
|
||||
import { Component, OnInit, Input, inject } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
|
||||
@@ -69,13 +69,25 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
FormatNumberPipe
|
||||
]
|
||||
})
|
||||
export class SavingsAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
export class SavingsAccountChargesStepComponent implements OnInit {
|
||||
private dialog = inject(MatDialog);
|
||||
private dateUtils = inject(Dates);
|
||||
private translateService = inject(TranslateService);
|
||||
|
||||
private _savingsAccountProductTemplate: any;
|
||||
|
||||
/** Savings Account Product Template */
|
||||
@Input() savingsAccountProductTemplate: any;
|
||||
@Input()
|
||||
set savingsAccountProductTemplate(value: any) {
|
||||
this._savingsAccountProductTemplate = value;
|
||||
if (value) {
|
||||
this.chargeData = value.chargeOptions || [];
|
||||
this.chargesDataSource = (value.charges || []).map((charge: any) => ({ ...charge, id: charge.chargeId }));
|
||||
}
|
||||
}
|
||||
get savingsAccountProductTemplate(): any {
|
||||
return this._savingsAccountProductTemplate;
|
||||
}
|
||||
/** Savings Account Template */
|
||||
@Input() savingsAccountTemplate: any;
|
||||
/** Currency Code */
|
||||
@@ -126,14 +138,6 @@ export class SavingsAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.savingsAccountProductTemplate) {
|
||||
this.chargeData = this.savingsAccountProductTemplate.chargeOptions;
|
||||
this.chargesDataSource =
|
||||
this.savingsAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the charge to charges table
|
||||
* @param {any} charge Charge
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<div class="sac-section">
|
||||
<div class="sac-section__header">
|
||||
<i class="fas fa-coins"></i>
|
||||
<span>Currency & Account Status Configuration</span>
|
||||
</div>
|
||||
<div class="sac-body">
|
||||
<div class="sac-grid">
|
||||
<mat-form-field appearance="outline" class="sac-field">
|
||||
<mat-label>Currency</mat-label>
|
||||
<input matInput [value]="savingsAccountProductTemplate?.currency?.name || ''" readonly />
|
||||
<mat-hint>Inherited from selected product</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sac-field">
|
||||
<mat-label>Decimal Places</mat-label>
|
||||
<input matInput [value]="savingsAccountProductTemplate?.currency?.decimalPlaces || ''" readonly />
|
||||
<mat-hint>Inherited from selected product</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="sac-alert">
|
||||
<div class="sac-alert__content">
|
||||
<h4>Dormancy Alert</h4>
|
||||
<p>Enable automated notifications when an account becomes inactive based on product rules.</p>
|
||||
</div>
|
||||
<span class="sac-badge" [class.sac-badge--yes]="savingsAccountProductTemplate?.dormancyAlert">
|
||||
{{ savingsAccountProductTemplate?.dormancyAlert ? 'Yes' : 'No' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sac-alert">
|
||||
<div class="sac-alert__content">
|
||||
<h4>Nomination Facility</h4>
|
||||
<p>Allow account holders to designate individuals to receive funds in the event of account holder's death.</p>
|
||||
</div>
|
||||
<span class="sac-badge" [class.sac-badge--yes]="savingsAccountProductTemplate?.nominationFacility">
|
||||
{{ savingsAccountProductTemplate?.nominationFacility ? 'Yes' : 'No' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$accent: #0ea5e9;
|
||||
$border: #e2e8f0;
|
||||
$gray-50: #f8f9fa;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-900: #202124;
|
||||
|
||||
.sac-section {
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: $gray-50;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sac-body {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.sac-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 4px 16px;
|
||||
padding: 16px 24px;
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sac-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sac-alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 18px 24px;
|
||||
border-top: 1px solid $border;
|
||||
background: #f8fafc;
|
||||
|
||||
@media (width <= 760px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__content {
|
||||
h4 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: #64748b;
|
||||
max-width: 540px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sac-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 48px;
|
||||
padding: 4px 14px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
border: 1px solid #fecaca;
|
||||
|
||||
&--yes {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-currency-step',
|
||||
templateUrl: './savings-account-currency-step.component.html',
|
||||
styleUrls: ['./savings-account-currency-step.component.scss'],
|
||||
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||
})
|
||||
export class SavingsAccountCurrencyStepComponent {
|
||||
@Input() savingsAccountProductTemplate: any;
|
||||
}
|
||||
+106
-50
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright since 2025 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -7,63 +7,119 @@
|
||||
-->
|
||||
|
||||
<form [formGroup]="savingsAccountDetailsForm">
|
||||
<div class="layout-row-wrap gap-2px responsive-column">
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||
<mat-select formControlName="productId" required>
|
||||
@for (product of productData; track product) {
|
||||
<mat-option [value]="product.id">
|
||||
{{ product.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field class="flex-48" (click)="submittedOnDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="submittedOnDatePicker"
|
||||
formControlName="submittedOnDate"
|
||||
required
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||||
<!-- Account Details -->
|
||||
<div class="sad-section">
|
||||
<div class="sad-section__header">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<h3>Account Details</h3>
|
||||
</div>
|
||||
<div class="sad-grid">
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>{{ 'labels.inputs.Product Name' | translate }}</mat-label>
|
||||
<mat-select formControlName="productId" required>
|
||||
@for (product of productData; track product) {
|
||||
<mat-option [value]="product.id">{{ product.name }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
{{ 'labels.inputs.Product Name' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||||
<mat-select formControlName="fieldOfficerId">
|
||||
@for (officer of fieldOfficerData; track officer) {
|
||||
<mat-option [value]="officer.id">
|
||||
{{ officer.displayName }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field appearance="outline" class="sad-field" (click)="submittedOnDatePicker.open()">
|
||||
<mat-label>{{ 'labels.inputs.Submitted On' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
[matDatepicker]="submittedOnDatePicker"
|
||||
formControlName="submittedOnDate"
|
||||
required
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="submittedOnDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #submittedOnDatePicker></mat-datepicker>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||||
<input matInput formControlName="externalId" />
|
||||
</mat-form-field>
|
||||
}
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>{{ 'labels.inputs.Field Officer' | translate }}</mat-label>
|
||||
<mat-select formControlName="fieldOfficerId">
|
||||
@for (officer of fieldOfficerData; track officer) {
|
||||
<mat-option [value]="officer.id">{{ officer.displayName }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (savingsProductSelected) {
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||||
<input matInput formControlName="externalId" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Classification (read-only, shown after product is selected) -->
|
||||
@if (savingsProductSelected) {
|
||||
<div class="sad-section sad-section--mt">
|
||||
<div class="sad-section__header">
|
||||
<i class="fas fa-tags"></i>
|
||||
<h3>Product Classification</h3>
|
||||
<span class="sad-badge">From Product</span>
|
||||
</div>
|
||||
<div class="sad-grid sad-grid--classification">
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>Account Type</mat-label>
|
||||
<input matInput [value]="productTemplate.accountType || ''" readonly />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>Customer Segment</mat-label>
|
||||
<input matInput [value]="productTemplate.customerSegment || ''" readonly />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>Product Code</mat-label>
|
||||
<input matInput [value]="productTemplate.productCode || ''" readonly />
|
||||
</mat-form-field>
|
||||
|
||||
@if (productTemplate.effectiveDate) {
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>Effective Date</mat-label>
|
||||
<input matInput [value]="formatTemplateDate(productTemplate.effectiveDate)" readonly />
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (productTemplate.endDate) {
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>End Date</mat-label>
|
||||
<input matInput [value]="formatTemplateDate(productTemplate.endDate)" readonly />
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<mat-form-field appearance="outline" class="sad-field">
|
||||
<mat-label>Introducer Required</mat-label>
|
||||
<input matInput [value]="productTemplate.introducerRequired ? 'Yes' : 'No'" readonly />
|
||||
</mat-form-field>
|
||||
|
||||
<div class="sad-field sad-info-row">
|
||||
<span class="sad-info-label">Share Linking Enabled</span>
|
||||
<span class="sad-badge" [class.sad-badge--yes]="productTemplate.shareLinkingEnabled">
|
||||
{{ productTemplate.shareLinkingEnabled ? 'Yes' : 'No' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
<button mat-raised-button matStepperPrevious disabled>
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
|
||||
+98
@@ -5,3 +5,101 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$accent: #0ea5e9;
|
||||
$border: #e2e8f0;
|
||||
$gray-50: #f8f9fa;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-900: #202124;
|
||||
|
||||
.sad-section {
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
&--mt {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: $gray-50;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sad-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 4px 16px;
|
||||
padding: 16px 24px;
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sad-grid--classification {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
|
||||
@media (width <= 900px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sad-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sad-info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-height: 56px;
|
||||
}
|
||||
|
||||
.sad-info-label {
|
||||
font-size: 14px;
|
||||
color: $gray-900;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.sad-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3px 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
border: 1px solid #bfdbfe;
|
||||
|
||||
&--yes {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
border-color: #bbf7d0;
|
||||
}
|
||||
}
|
||||
|
||||
+37
-3
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
@@ -7,12 +7,13 @@
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, Output, EventEmitter, inject } from '@angular/core';
|
||||
import { Component, OnInit, Input, Output, EventEmitter, inject, ChangeDetectorRef } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from 'app/savings/savings.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
@@ -35,6 +36,8 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private savingsService = inject(SavingsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private dateUtils = inject(Dates);
|
||||
private cdr = inject(ChangeDetectorRef);
|
||||
|
||||
/** Savings Account Template */
|
||||
@Input() savingsAccountTemplate: any;
|
||||
@@ -57,6 +60,9 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
||||
/** Savings Account Template with product data */
|
||||
@Output() savingsAccountProductTemplate = new EventEmitter();
|
||||
|
||||
/** Local copy of product template for displaying classification info */
|
||||
productTemplate: any = null;
|
||||
|
||||
/**
|
||||
* Sets share account details form.
|
||||
* @param {FormBuilder} formBuilder Form Builder.
|
||||
@@ -115,9 +121,11 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
||||
this.savingsService
|
||||
.getSavingsAccountTemplate(entityId, productId, this.savingsAccountTemplate.groupId ? true : false)
|
||||
.subscribe((response: any) => {
|
||||
this.productTemplate = response;
|
||||
this.savingsAccountProductTemplate.emit(response);
|
||||
this.fieldOfficerData = response.fieldOfficerOptions;
|
||||
this.savingsProductSelected = true;
|
||||
this.cdr.detectChanges();
|
||||
if (!this.isFieldOfficerPatched && this.savingsAccountTemplate.fieldOfficerId) {
|
||||
this.savingsAccountDetailsForm.get('fieldOfficerId').patchValue(this.savingsAccountTemplate.fieldOfficerId);
|
||||
this.isFieldOfficerPatched = true;
|
||||
@@ -128,6 +136,32 @@ export class SavingsAccountDetailsStepComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats template dates into the current CBS date format.
|
||||
* Handles backend date arrays like [2026, 4, 20] as well as strings and Date objects.
|
||||
*/
|
||||
formatTemplateDate(value: any): string {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let dateValue: Date | null = null;
|
||||
|
||||
if (value instanceof Date) {
|
||||
dateValue = value;
|
||||
} else if (Array.isArray(value) && value.length >= 3) {
|
||||
dateValue = new Date(value[0], value[1] - 1, value[2]);
|
||||
} else {
|
||||
dateValue = new Date(value);
|
||||
}
|
||||
|
||||
if (!dateValue || Number.isNaN(dateValue.getTime())) {
|
||||
return Array.isArray(value) ? value.join('/') : String(value);
|
||||
}
|
||||
|
||||
return this.dateUtils.formatDate(dateValue, 'M/d/yyyy') || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns savings account form value.
|
||||
*/
|
||||
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<div class="spl-section">
|
||||
<div class="spl-section__header">
|
||||
<span class="spl-section__icon">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</span>
|
||||
<div class="spl-section__title">Transaction & Product Limits</div>
|
||||
<div class="spl-section__badge">FROM PRODUCT</div>
|
||||
</div>
|
||||
|
||||
<div class="spl-section__body">
|
||||
<div class="spl-features">
|
||||
<div class="spl-features__header">Account Features (as per RBI guidelines)</div>
|
||||
|
||||
<div class="spl-features__grid">
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-automobile"></i> ATM Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.atmFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-wallet"></i> ATM Withdrawal</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.atmWithdrawalFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-mobile-alt"></i> UPI Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.upiFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-exchange-alt"></i> POS Transactions</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.posTransactionsFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-book"></i> Cheque Book</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.chequeBook" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-bolt"></i> IMPS Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.impsFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-sms"></i> SMS Alerts</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.smsAlerts" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-bell"></i> BBPS Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.bbpsFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-shield-alt"></i> Insurance Cover</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.insuranceCover" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-bolt"></i> BEPG Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.bepgFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-money-check-alt"></i> NEFT Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.neftFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-money-check"></i> RTGS Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.rtgsFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-balance-scale"></i> Inter-Bank Total Cap</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.interBankTotalCapFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-arrow-right-arrow-left"></i> Withdrawal Facility</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.withdrawalFacility" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-id-card"></i> Deposit Facility (KYC)</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.depositFacilityKyc" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="spl-feature">
|
||||
<span class="spl-feature__label"><i class="fas fa-credit-card"></i> Virtual Debit Card</span>
|
||||
<select class="spl-select" [ngModel]="accountFacilityConfig.virtualDebitCard" disabled>
|
||||
<option [ngValue]="false">No</option>
|
||||
<option [ngValue]="true">Yes</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (accountFacilityLimits.length > 0) {
|
||||
<div class="spl-table-wrap">
|
||||
<table class="spl-table">
|
||||
<colgroup>
|
||||
<col class="spl-table__col--label" />
|
||||
<col class="spl-table__col--value" />
|
||||
<col class="spl-table__col--value" />
|
||||
<col class="spl-table__col--value" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Limit Type</th>
|
||||
<th>General Limit (Daily)</th>
|
||||
<th>Minor Account Limit</th>
|
||||
<th>Small Saving Limit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (row of accountFacilityLimits; track row.limitType) {
|
||||
<tr>
|
||||
<td>{{ row.limitType }}</td>
|
||||
<td><input type="text" class="spl-input" [value]="row.generalLimit" disabled /></td>
|
||||
<td><input type="text" class="spl-input" [value]="row.minorAccountLimit" disabled /></td>
|
||||
<td><input type="text" class="spl-input" [value]="row.smallSavingLimit" disabled /></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (depositLimitKyc?.general) {
|
||||
<div class="spl-table-wrap spl-table-wrap--kyc">
|
||||
<h4 class="mat-h4">Deposit Limit (KYC)</h4>
|
||||
<table class="spl-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Account Type</th>
|
||||
<th>KYC Type</th>
|
||||
<th>Limit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>General</td>
|
||||
<td>
|
||||
<select class="spl-select" [ngModel]="depositLimitKyc.general.kycType" disabled>
|
||||
<option>{{ depositLimitKyc.general.kycType }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" class="spl-input" [value]="depositLimitKyc.general.limit" disabled /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Minor</td>
|
||||
<td>
|
||||
<select class="spl-select" [ngModel]="depositLimitKyc.minor.kycType" disabled>
|
||||
<option>{{ depositLimitKyc.minor.kycType }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="number" class="spl-input" [value]="depositLimitKyc.minor.limit" disabled /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Small Saving</td>
|
||||
<td>
|
||||
<select class="spl-select" [ngModel]="depositLimitKyc.smallSaving.kycType" disabled>
|
||||
<option>{{ depositLimitKyc.smallSaving.kycType }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="number" class="spl-input" [value]="depositLimitKyc.smallSaving.limit" disabled /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="spl-note">
|
||||
<strong>Note:</strong>
|
||||
These facilities are defined at product level and applied to this account. Small Saving Account limits are
|
||||
strictly capped at Rs.1,00,000 maximum balance per RBI Master Circular.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$border: #d9e2ec;
|
||||
$border-strong: #c7d2de;
|
||||
$text: #24364b;
|
||||
$muted: #64748b;
|
||||
$header-bg: #f8fafc;
|
||||
|
||||
.spl-section {
|
||||
border: 1px solid $border;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.spl-section__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 20px;
|
||||
background: $header-bg;
|
||||
border-bottom: 1px solid $border;
|
||||
}
|
||||
|
||||
.spl-section__icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background: #eff6ff;
|
||||
color: #2563eb;
|
||||
font-size: 14px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.spl-section__title {
|
||||
flex: 1 1 auto;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.spl-section__badge {
|
||||
flex: 0 0 auto;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.spl-section__body {
|
||||
padding: 18px 20px 20px;
|
||||
}
|
||||
|
||||
.spl-table-wrap {
|
||||
overflow-x: auto;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.spl-table {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid $border;
|
||||
|
||||
col.spl-table__col--label {
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
col.spl-table__col--value {
|
||||
width: 26%;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid $border;
|
||||
padding: 6px 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f8fafc;
|
||||
color: #64748b;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: $text;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.spl-input,
|
||||
.spl-select {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.spl-input {
|
||||
height: 30px;
|
||||
border: 1px solid $border-strong;
|
||||
border-radius: 2px;
|
||||
padding: 0 8px;
|
||||
background: #f8fafc;
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.spl-select {
|
||||
height: 30px;
|
||||
border: 1px solid $border-strong;
|
||||
border-radius: 2px;
|
||||
padding: 0 6px;
|
||||
background: #f8fafc;
|
||||
color: $muted;
|
||||
}
|
||||
|
||||
.spl-features {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.spl-features__header {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: $muted;
|
||||
margin-bottom: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.spl-features__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px 16px;
|
||||
|
||||
@media (width <= 900px) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media (width <= 640px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.spl-feature {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
font-size: 13px;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.spl-feature__label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
|
||||
i {
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.spl-note {
|
||||
margin-top: 16px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid $border;
|
||||
border-radius: 8px;
|
||||
background: #f8fafc;
|
||||
color: $muted;
|
||||
font-size: 11px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
const DEFAULT_FACILITY_CONFIG = {
|
||||
atmFacility: false,
|
||||
atmWithdrawalFacility: false,
|
||||
upiFacility: false,
|
||||
posTransactionsFacility: false,
|
||||
chequeBook: false,
|
||||
impsFacility: false,
|
||||
smsAlerts: false,
|
||||
bbpsFacility: false,
|
||||
insuranceCover: false,
|
||||
bepgFacility: false,
|
||||
neftFacility: false,
|
||||
rtgsFacility: false,
|
||||
interBankTotalCapFacility: false,
|
||||
withdrawalFacility: false,
|
||||
depositFacilityKyc: false,
|
||||
virtualDebitCard: false
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-digital-step',
|
||||
templateUrl: './savings-account-digital-step.component.html',
|
||||
styleUrls: ['./savings-account-digital-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FormsModule
|
||||
]
|
||||
})
|
||||
export class SavingsAccountDigitalStepComponent {
|
||||
private _savingsAccountProductTemplate: any;
|
||||
|
||||
@Input()
|
||||
set savingsAccountProductTemplate(value: any) {
|
||||
this._savingsAccountProductTemplate = value;
|
||||
}
|
||||
get savingsAccountProductTemplate(): any {
|
||||
return this._savingsAccountProductTemplate;
|
||||
}
|
||||
|
||||
get accountFacilityConfig() {
|
||||
return this._savingsAccountProductTemplate?.accountFacilityConfig || DEFAULT_FACILITY_CONFIG;
|
||||
}
|
||||
|
||||
get accountFacilityLimits(): any[] {
|
||||
return this._savingsAccountProductTemplate?.accountFacilityLimits || [];
|
||||
}
|
||||
|
||||
get depositLimitKyc() {
|
||||
return (
|
||||
this._savingsAccountProductTemplate?.depositLimitKyc || {
|
||||
general: { kycType: '', limit: '' },
|
||||
minor: { kycType: '', limit: '' },
|
||||
smallSaving: { kycType: '', limit: '' }
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
+86
-1
@@ -22,6 +22,27 @@
|
||||
<span class="flex-60">{{ savingsAccount.submittedOnDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountProductTemplate?.effectiveDate) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Effective Date</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.effectiveDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccountProductTemplate?.effectiveTo) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Effective To</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.effectiveTo | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccountProductTemplate?.endDate) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">End Date</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.endDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.External Id' | translate }}</span>
|
||||
@if (savingsAccount.externalId) {
|
||||
@@ -74,7 +95,7 @@
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
||||
<span class="flex-60">{{ savingsAccountTermsForm.get('decimal').value }}</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate?.currency?.decimalPlaces }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
@@ -82,6 +103,49 @@
|
||||
<span class="flex-60">{{ savingsAccount.nominalAnnualInterestRate | formatNumber }} %</span>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountProductTemplate?.interestCalculationTypeName) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Interest Calculation Mode</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.interestCalculationTypeName }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccountProductTemplate?.lastInterestDate) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Last Interest Date</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.lastInterestDate | dateFormat }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Interest Calculation Selector</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
savingsAccount.interestCalculateSelector
|
||||
| find: savingsAccountProductTemplate.interestCalculationTypeOption : 'id' : 'value'
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@if (savingsAccount.averagePeriod) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Average Period</span>
|
||||
<span class="flex-60">{{ savingsAccount.averagePeriod }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Auto Post</span>
|
||||
<span class="flex-60">{{ savingsAccount.autoPost | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (savingsAccount.frequencyFixDay !== undefined && savingsAccount.frequencyFixDay !== null) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Frequency Fix Day</span>
|
||||
<span class="flex-60">{{ savingsAccount.frequencyFixDay }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
@@ -181,6 +245,27 @@
|
||||
<span class="flex-60">{{ savingsAccount.enforceMinRequiredBalance | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountProductTemplate?.enableLockinPeriod !== undefined) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Enable Lock-in Period</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.enableLockinPeriod | yesNo }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccountProductTemplate?.balanceFrequency) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Balance Frequency</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.balanceFrequency }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccountProductTemplate?.accountClosedZeroBalanceInDayClosing !== undefined) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">Account Closed Zero Balance In Day Closing</span>
|
||||
<span class="flex-60">{{ savingsAccountProductTemplate.accountClosedZeroBalanceInDayClosing | yesNo }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (savingsAccount.minRequiredBalance) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Minimum Balance' | translate }}:</span>
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<form [formGroup]="savingsAccountSettingsForm">
|
||||
<div class="sps-section">
|
||||
<div class="sps-section__header">
|
||||
<span class="sps-section__badge"><i class="fas fa-balance-scale"></i></span>
|
||||
<span>Balance & Minimum Balance</span>
|
||||
</div>
|
||||
<div class="sps-body">
|
||||
<div class="sps-layout sps-layout--balance">
|
||||
<div class="sps-row sps-row--three">
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>{{ 'labels.inputs.Minimum Opening Balance' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minRequiredOpeningBalance" min="0" step="0.01" />
|
||||
</mat-form-field>
|
||||
|
||||
<div class="sps-toggle sps-toggle--inline">
|
||||
<mat-checkbox labelPosition="before" formControlName="enforceMinRequiredBalance">
|
||||
<span>Enforce Minimum Balance</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>Minimum Balance (₹)</mat-label>
|
||||
<input type="number" matInput formControlName="minRequiredBalance" min="0" step="0.01" />
|
||||
<mat-hint>Penal charge applies on breach</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="sps-row sps-row--two">
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>Balance Frequency</mat-label>
|
||||
<mat-select formControlName="balanceFrequency">
|
||||
@for (freq of periodFrequencyData; track freq.value) {
|
||||
<mat-option [value]="freq.value">{{ freq.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="sps-toggle sps-toggle--inline sps-toggle--right">
|
||||
<mat-checkbox labelPosition="before" formControlName="withdrawalFeeForTransfers">
|
||||
<span>{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sps-section">
|
||||
<div class="sps-section__header">
|
||||
<span class="sps-section__badge"><i class="fas fa-lock"></i></span>
|
||||
<span>Lock-in Period</span>
|
||||
</div>
|
||||
<div class="sps-body">
|
||||
<div class="sps-lockin-row">
|
||||
<span>Enable Lock-in Period</span>
|
||||
<mat-checkbox formControlName="enableLockinPeriod"></mat-checkbox>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountSettingsForm.value.enableLockinPeriod) {
|
||||
<div class="sps-lockin-fields">
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}*</mat-label>
|
||||
<input type="number" matInput formControlName="lockinPeriodFrequency" min="1" step="1" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>{{ 'labels.inputs.Type' | translate }}*</mat-label>
|
||||
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||
@for (type of lockinPeriodFrequencyTypeData; track type) {
|
||||
<mat-option [value]="type.id">{{ type.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sps-section">
|
||||
<div class="sps-section__header">
|
||||
<span class="sps-section__badge"><i class="fas fa-cog"></i></span>
|
||||
<span>Additional Rules</span>
|
||||
</div>
|
||||
<div class="sps-body">
|
||||
<div class="sps-layout sps-layout--additional">
|
||||
<div class="sps-toggle sps-toggle--full">
|
||||
<mat-checkbox formControlName="accountClosedZeroBalanceInDayClosing">
|
||||
<span>Account Closed with Zero balance in Day Closing</span>
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountProductTemplate?.minBalanceForInterestCalculation != null) {
|
||||
<mat-form-field appearance="outline" class="sps-field sps-field--compact">
|
||||
<mat-label>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[value]="savingsAccountProductTemplate.minBalanceForInterestCalculation | formatNumber"
|
||||
readonly
|
||||
/>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$accent: #0ea5e9;
|
||||
$border: #e2e8f0;
|
||||
$gray-50: #f8f9fa;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-900: #202124;
|
||||
|
||||
.sps-section {
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: $gray-50;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
i {
|
||||
font-size: 14px;
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sps-section__badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 6px;
|
||||
background: #eff6ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sps-body {
|
||||
background: #fff;
|
||||
padding: 18px 24px 24px;
|
||||
}
|
||||
|
||||
.sps-field {
|
||||
width: 100%;
|
||||
|
||||
&--full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
&--compact {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sps-layout {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.sps-layout--balance {
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.sps-layout--additional {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
align-items: start;
|
||||
|
||||
@media (width <= 1100px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sps-row {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.sps-row--three {
|
||||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
|
||||
.sps-field--compact:first-child {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sps-toggle--inline {
|
||||
align-self: center;
|
||||
min-height: 56px;
|
||||
}
|
||||
}
|
||||
|
||||
.sps-row--two {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
|
||||
.sps-field--compact {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sps-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 56px;
|
||||
|
||||
mat-checkbox {
|
||||
font-size: 14px;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
&--full {
|
||||
min-height: auto;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&--inline {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&--right {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.sps-lockin-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
font-size: 14px;
|
||||
color: $gray-900;
|
||||
}
|
||||
|
||||
.sps-lockin-row mat-checkbox {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.sps-lockin-fields {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
|
||||
@media (width <= 640px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl } from '@angular/forms';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-settings-step',
|
||||
templateUrl: './savings-account-settings-step.component.html',
|
||||
styleUrls: ['./savings-account-settings-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatCheckbox,
|
||||
FormatNumberPipe
|
||||
]
|
||||
})
|
||||
export class SavingsAccountSettingsStepComponent implements OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
|
||||
private _savingsAccountProductTemplate: any;
|
||||
|
||||
@Input()
|
||||
set savingsAccountProductTemplate(value: any) {
|
||||
this._savingsAccountProductTemplate = value;
|
||||
if (value && this.savingsAccountSettingsForm) {
|
||||
this.applyTemplate(value);
|
||||
}
|
||||
}
|
||||
get savingsAccountProductTemplate(): any {
|
||||
return this._savingsAccountProductTemplate;
|
||||
}
|
||||
|
||||
savingsAccountSettingsForm: UntypedFormGroup;
|
||||
|
||||
lockinPeriodFrequencyTypeData: any[] = [];
|
||||
|
||||
periodFrequencyData: any[] = [
|
||||
{ value: 'Daily' },
|
||||
{ value: 'Weekly' },
|
||||
{ value: 'Monthly' },
|
||||
{ value: 'Quarterly' },
|
||||
{ value: 'Half Yearly' },
|
||||
{ value: 'Yearly' }];
|
||||
|
||||
constructor() {
|
||||
this.createForm();
|
||||
this.buildDependencies();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.savingsAccountSettingsForm = this.formBuilder.group({
|
||||
minRequiredOpeningBalance: [''],
|
||||
withdrawalFeeForTransfers: [false],
|
||||
enforceMinRequiredBalance: [false],
|
||||
minRequiredBalance: [''],
|
||||
balanceFrequency: ['Monthly'],
|
||||
enableLockinPeriod: [false],
|
||||
lockinPeriodFrequency: [''],
|
||||
lockinPeriodFrequencyType: [''],
|
||||
accountClosedZeroBalanceInDayClosing: [false],
|
||||
allowOverdraft: [false]
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this._savingsAccountProductTemplate) {
|
||||
this.applyTemplate(this._savingsAccountProductTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private applyTemplate(t: any) {
|
||||
this.lockinPeriodFrequencyTypeData = t.lockinPeriodFrequencyTypeOptions || [];
|
||||
const hasLockin = !!(t.lockinPeriodFrequency && t.lockinPeriodFrequency > 0);
|
||||
|
||||
this.savingsAccountSettingsForm.patchValue({
|
||||
minRequiredOpeningBalance: t.minRequiredOpeningBalance,
|
||||
withdrawalFeeForTransfers: t.withdrawalFeeForTransfers || false,
|
||||
enforceMinRequiredBalance: t.enforceMinRequiredBalance || false,
|
||||
minRequiredBalance: t.minRequiredBalance,
|
||||
balanceFrequency: t.balanceFrequency || 'Monthly',
|
||||
enableLockinPeriod: hasLockin,
|
||||
lockinPeriodFrequency: hasLockin ? t.lockinPeriodFrequency : '',
|
||||
lockinPeriodFrequencyType: hasLockin ? t.lockinPeriodFrequencyType?.id || '' : '',
|
||||
accountClosedZeroBalanceInDayClosing: t.accountClosedZeroBalanceInDayClosing || false,
|
||||
allowOverdraft: t.allowOverdraft || false
|
||||
});
|
||||
|
||||
if (t.allowOverdraft) {
|
||||
if (!this.savingsAccountSettingsForm.contains('minOverdraftForInterestCalculation')) {
|
||||
this.savingsAccountSettingsForm.addControl('minOverdraftForInterestCalculation', new UntypedFormControl(''));
|
||||
this.savingsAccountSettingsForm.addControl('nominalAnnualInterestRateOverdraft', new UntypedFormControl(''));
|
||||
this.savingsAccountSettingsForm.addControl('overdraftLimit', new UntypedFormControl(''));
|
||||
}
|
||||
this.savingsAccountSettingsForm.patchValue({
|
||||
minOverdraftForInterestCalculation: t.minOverdraftForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft: t.nominalAnnualInterestRateOverdraft,
|
||||
overdraftLimit: t.overdraftLimit
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildDependencies() {
|
||||
this.savingsAccountSettingsForm.get('enableLockinPeriod').valueChanges.subscribe((enabled: boolean) => {
|
||||
if (enabled) {
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').setValidators([
|
||||
Validators.required,
|
||||
Validators.min(1)
|
||||
]);
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').setValidators([Validators.required]);
|
||||
} else {
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').setValue('');
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').setValue('');
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').clearValidators();
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').clearValidators();
|
||||
}
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequency').updateValueAndValidity();
|
||||
this.savingsAccountSettingsForm.get('lockinPeriodFrequencyType').updateValueAndValidity();
|
||||
});
|
||||
|
||||
this.savingsAccountSettingsForm.get('allowOverdraft').valueChanges.subscribe((allowed: boolean) => {
|
||||
if (allowed) {
|
||||
this.savingsAccountSettingsForm.addControl('minOverdraftForInterestCalculation', new UntypedFormControl(''));
|
||||
this.savingsAccountSettingsForm.addControl('nominalAnnualInterestRateOverdraft', new UntypedFormControl(''));
|
||||
this.savingsAccountSettingsForm.addControl('overdraftLimit', new UntypedFormControl(''));
|
||||
} else {
|
||||
this.savingsAccountSettingsForm.removeControl('minOverdraftForInterestCalculation');
|
||||
this.savingsAccountSettingsForm.removeControl('nominalAnnualInterestRateOverdraft');
|
||||
this.savingsAccountSettingsForm.removeControl('overdraftLimit');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get savingsAccountSettings() {
|
||||
const value = { ...this.savingsAccountSettingsForm.getRawValue() };
|
||||
if (!value.enableLockinPeriod) {
|
||||
value.lockinPeriodFrequency = null;
|
||||
value.lockinPeriodFrequencyType = null;
|
||||
}
|
||||
delete value.enableLockinPeriod;
|
||||
delete value.balanceFrequency;
|
||||
delete value.accountClosedZeroBalanceInDayClosing;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<form [formGroup]="savingsAccountTermForm">
|
||||
<div class="sat-section">
|
||||
<div class="sat-section__header">
|
||||
<span class="sat-section__badge"><i class="fas fa-chart-line"></i></span>
|
||||
<span>Interest Rate & Posting Terms</span>
|
||||
</div>
|
||||
<div class="sat-body">
|
||||
<!-- Banner showing current rate -->
|
||||
<div class="sat-banner">
|
||||
<span class="sat-banner__icon">ℹ️</span>
|
||||
<span>
|
||||
Savings Account base rate:
|
||||
<b>{{ savingsAccountTermForm.get('nominalAnnualInterestRate').value || 0 }}%</b>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Row 1: Nominal Interest + Effective Date + Interest Calculation Type -->
|
||||
<div class="sat-grid sat-grid--three">
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>{{ 'labels.inputs.Nominal Annual Interest' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="nominalAnnualInterestRate" required min="0" step="0.01" />
|
||||
@if (savingsAccountTermForm.get('nominalAnnualInterestRate').hasError('required')) {
|
||||
<mat-error>Nominal Annual Interest is <strong>required</strong></mat-error>
|
||||
}
|
||||
@if (savingsAccountTermForm.get('nominalAnnualInterestRate').hasError('min')) {
|
||||
<mat-error>Must be zero or greater</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field" (click)="effectiveDatePicker.open()">
|
||||
<mat-label>Effective Date</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matDatepicker]="effectiveDatePicker"
|
||||
formControlName="effectiveDate"
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="effectiveDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #effectiveDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>{{ 'labels.inputs.Interest Calculation Type' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCalculateSelector">
|
||||
@for (opt of interestCalculateSelectorData; track opt.id) {
|
||||
<mat-option [value]="opt.id">{{ opt.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Interest Calculate (enum) + Average Period (conditional) + Interest Posting Period -->
|
||||
<div class="sat-grid sat-grid--three">
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>Interest Calculate</mat-label>
|
||||
<mat-select formControlName="interestCalculationType" required>
|
||||
@for (opt of interestCalculationTypeData; track opt.id) {
|
||||
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Interest Calculation Type' | translate }} is <strong>required</strong></mat-error
|
||||
>
|
||||
</mat-form-field>
|
||||
|
||||
@if (savingsAccountTermForm.value.interestCalculationType === 2) {
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>Average Period</mat-label>
|
||||
<mat-select formControlName="averagePeriod">
|
||||
@for (period of periodFrequencyData; track period) {
|
||||
<mat-option [value]="period.value">{{ period.value }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestPostingPeriodType" required>
|
||||
@for (opt of interestPostingPeriodTypeData; track opt) {
|
||||
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>{{ 'labels.inputs.Interest Posting Period' | translate }} is <strong>required</strong></mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Row 3: Interest Compounding + Days in Year -->
|
||||
<div class="sat-grid sat-grid--three">
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCompoundingPeriodType" required>
|
||||
@for (opt of interestCompoundingPeriodTypeData; track opt) {
|
||||
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Interest Compounding Period' | translate }} is <strong>required</strong></mat-error
|
||||
>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
||||
@for (opt of interestCalculationDaysInYearTypeData; track opt) {
|
||||
<mat-option [value]="opt.id">{{ opt.value | translateKey: 'catalogs' }}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>{{ 'labels.inputs.Days in Year' | translate }} is <strong>required</strong></mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Footer: Auto Post + Frequency Fix Day + Last Interest Date -->
|
||||
<div class="sat-footer">
|
||||
<div class="sat-footer__grid">
|
||||
<div class="sat-toggle-card">
|
||||
<div>
|
||||
<h4>Auto Interest Post</h4>
|
||||
</div>
|
||||
<div class="sat-toggle-card__choices">
|
||||
@for (option of yesNoOptions; track option.value) {
|
||||
<label class="sat-radio-label">
|
||||
<input type="radio" formControlName="autoPost" [value]="option.value" />
|
||||
<span>{{ option.label }}</span>
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field">
|
||||
<mat-label>Frequency (Fix Day)</mat-label>
|
||||
<input type="number" matInput formControlName="frequencyFixDay" min="0" />
|
||||
<mat-hint>Days</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="sat-field" (click)="lastInterestDatePicker.open()">
|
||||
<mat-label>Last Interest Date</mat-label>
|
||||
<input
|
||||
matInput
|
||||
[matDatepicker]="lastInterestDatePicker"
|
||||
formControlName="lastInterestDate"
|
||||
[min]="minDate"
|
||||
[max]="maxDate"
|
||||
/>
|
||||
<mat-datepicker-toggle matSuffix [for]="lastInterestDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #lastInterestDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (savingsAccountTermForm.value.interestCalculationType === 1) {
|
||||
<div class="sat-note">
|
||||
<span>⚠️</span>
|
||||
<span
|
||||
>Daily product-based interest accrual enabled - interest calculated EOD on daily closing balance.</span
|
||||
>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$accent: #0ea5e9;
|
||||
$border: #e2e8f0;
|
||||
$gray-50: #f8f9fa;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-900: #202124;
|
||||
|
||||
.sat-section {
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
|
||||
& + & {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 24px;
|
||||
background: $gray-50;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $gray-900;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sat-section__badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 6px;
|
||||
background: #eff6ff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sat-body {
|
||||
background: #fff;
|
||||
padding: 18px 24px 24px;
|
||||
}
|
||||
|
||||
.sat-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 18px;
|
||||
border-radius: 8px;
|
||||
background: #eff6ff;
|
||||
border: 1px solid #bfdbfe;
|
||||
font-size: 13px;
|
||||
color: $gray-900;
|
||||
|
||||
&__icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.sat-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 4px 16px;
|
||||
|
||||
@media (width <= 900px) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.sat-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sat-footer {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid $border;
|
||||
|
||||
&__grid {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr 1fr;
|
||||
gap: 4px 16px;
|
||||
align-items: start;
|
||||
|
||||
@media (width <= 900px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sat-toggle-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid $border;
|
||||
border-radius: 8px;
|
||||
background: $gray-50;
|
||||
min-height: 56px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
&__choices {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.sat-radio-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
color: $gray-900;
|
||||
cursor: pointer;
|
||||
|
||||
[type='radio'] {
|
||||
accent-color: $accent;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.sat-note {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
font-size: 13px;
|
||||
color: #92400e;
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
import { Component, Input, OnInit, inject } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-term-step',
|
||||
templateUrl: './savings-account-term-step.component.html',
|
||||
styleUrls: ['./savings-account-term-step.component.scss'],
|
||||
imports: [...STANDALONE_SHARED_IMPORTS]
|
||||
})
|
||||
export class SavingsAccountTermStepComponent implements OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private dateUtils = inject(Dates);
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
private _savingsAccountProductTemplate: any;
|
||||
|
||||
@Input()
|
||||
set savingsAccountProductTemplate(value: any) {
|
||||
this._savingsAccountProductTemplate = value;
|
||||
if (value && this.savingsAccountTermForm) {
|
||||
this.applyTemplate(value);
|
||||
}
|
||||
}
|
||||
get savingsAccountProductTemplate(): any {
|
||||
return this._savingsAccountProductTemplate;
|
||||
}
|
||||
|
||||
savingsAccountTermForm: UntypedFormGroup;
|
||||
|
||||
interestCompoundingPeriodTypeData: any[] = [];
|
||||
interestPostingPeriodTypeData: any[] = [];
|
||||
interestCalculationTypeData: any[] = [];
|
||||
interestCalculationDaysInYearTypeData: any[] = [];
|
||||
interestCalculateSelectorData: any[] = [];
|
||||
periodFrequencyData: any[] = [];
|
||||
|
||||
yesNoOptions = [
|
||||
{ value: true, label: 'Yes' },
|
||||
{ value: false, label: 'No' }
|
||||
];
|
||||
|
||||
minDate = new Date(2000, 0, 1);
|
||||
maxDate = new Date();
|
||||
|
||||
constructor() {
|
||||
this.createForm();
|
||||
}
|
||||
|
||||
createForm() {
|
||||
this.savingsAccountTermForm = this.formBuilder.group({
|
||||
currencyCode: [{ value: '', disabled: true }],
|
||||
nominalAnnualInterestRate: [
|
||||
'',
|
||||
[
|
||||
Validators.required,
|
||||
Validators.min(0)
|
||||
]
|
||||
],
|
||||
interestCompoundingPeriodType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestPostingPeriodType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCalculationType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCalculationDaysInYearType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
effectiveDate: [''],
|
||||
interestCalculateSelector: [1],
|
||||
averagePeriod: ['Monthly'],
|
||||
autoPost: [true],
|
||||
frequencyFixDay: [
|
||||
30,
|
||||
[Validators.min(0)]
|
||||
],
|
||||
lastInterestDate: ['']
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
// Apply template if it was set before the form was ready
|
||||
if (this._savingsAccountProductTemplate) {
|
||||
this.applyTemplate(this._savingsAccountProductTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private applyTemplate(t: any) {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
|
||||
this.interestCompoundingPeriodTypeData = t.interestCompoundingPeriodTypeOptions || [];
|
||||
this.interestPostingPeriodTypeData = t.interestPostingPeriodTypeOptions || [];
|
||||
this.interestCalculationTypeData = t.interestCalculationTypeOptions || [];
|
||||
this.interestCalculateSelectorData = t.interestCalculationTypeOption || [];
|
||||
this.interestCalculationDaysInYearTypeData = t.interestCalculationDaysInYearTypeOptions || [];
|
||||
this.periodFrequencyData = t.periodFrequencyOptions?.length ? t.periodFrequencyOptions : [
|
||||
{ value: 'Monthly' },
|
||||
{ value: 'Quarterly' },
|
||||
{ value: 'Half Yearly' },
|
||||
{ value: 'Yearly' }];
|
||||
|
||||
const defaultPeriod = this.periodFrequencyData[0]?.value || 'Monthly';
|
||||
const savedEnumTypeId = t.interestCalculationType?.id || this.interestCalculationTypeData?.[0]?.id || 1;
|
||||
const savedSelectorId = t.interestCalculateSelector ?? this.interestCalculateSelectorData?.[0]?.id ?? null;
|
||||
|
||||
this.savingsAccountTermForm.patchValue({
|
||||
currencyCode: t.currency?.code,
|
||||
nominalAnnualInterestRate: t.nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType: t.interestCompoundingPeriodType?.id,
|
||||
interestPostingPeriodType: t.interestPostingPeriodType?.id,
|
||||
interestCalculationType: savedEnumTypeId,
|
||||
interestCalculationDaysInYearType: t.interestCalculationDaysInYearType?.id,
|
||||
effectiveDate: t.effectiveDate ? new Date(t.effectiveDate) : '',
|
||||
interestCalculateSelector: savedSelectorId,
|
||||
averagePeriod: t.averagePeriod || defaultPeriod,
|
||||
autoPost: t.autoPost ?? true,
|
||||
frequencyFixDay: t.frequencyFixDay ?? 30,
|
||||
lastInterestDate: t.lastInterestDate ? new Date(t.lastInterestDate) : ''
|
||||
});
|
||||
}
|
||||
|
||||
get savingsAccountTerms() {
|
||||
const value = { ...this.savingsAccountTermForm.getRawValue() };
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
|
||||
if (value.effectiveDate instanceof Date) {
|
||||
value.effectiveDate = this.dateUtils.formatDate(value.effectiveDate, dateFormat) || '';
|
||||
}
|
||||
if (value.lastInterestDate instanceof Date) {
|
||||
value.lastInterestDate = this.dateUtils.formatDate(value.lastInterestDate, dateFormat) || '';
|
||||
}
|
||||
|
||||
delete value.currencyCode;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
-7
@@ -152,13 +152,6 @@
|
||||
<mat-label>{{ 'labels.inputs.Minimum Balance' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minRequiredBalance" />
|
||||
</mat-form-field>
|
||||
|
||||
@if (savingsAccountTermsForm.controls.minBalanceForInterestCalculation.value) {
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Balance Required for Interest Calculation' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minBalanceForInterestCalculation" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="layout-row align-center gap-2percent margin-t responsive-column">
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
||||
/* tslint:disable */
|
||||
export default {
|
||||
'mifos_x': {
|
||||
'version': '260420',
|
||||
'hash': 'fe61c925'
|
||||
'version': '260421',
|
||||
'hash': 'e212bf7d'
|
||||
},
|
||||
'allow_switching_backend_instance': true
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user