feat: add Current Account module, DIP verification, charges integration, and translations
- Current Account module with full CRUD, transactions, charges, hold/release, block/unblock - Current Products management under Products module - DIP mobile number verification with real API call to /lookup/check - Sanctions verification dialog - Charges support for Current Account (chargeAppliesTo=5) in create/edit - Fixed permissions from SAVINGSACCOUNT to CURRENTACCOUNT across all current account components - Added all missing translation keys for Current Account in en-US.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
73aa69ee02
commit
0a32d7135e
+131
@@ -0,0 +1,131 @@
|
||||
<!--
|
||||
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="layout-row-wrap gap-2px responsive-column">
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Charge' | translate }}</mat-label>
|
||||
<mat-select #charge>
|
||||
@for (charge of chargeData | chargesFilter: chargesDataSource : currencyCode.value; track charge) {
|
||||
<mat-option [value]="charge">
|
||||
{{ charge.name }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<div class="flex-48 align-center">
|
||||
<button type="button" mat-raised-button color="primary" (click)="addCharge(charge)" [disabled]="!charge.value">
|
||||
<fa-icon icon="plus" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Add' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (chargesDataSource.length > 0) {
|
||||
<table class="flex-98 mat-elevation-z1" mat-table [dataSource]="chargesDataSource">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="chargeCalculationType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeCalculationType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.amount | formatNumber }}
|
||||
<button mat-icon-button color="primary" (click)="editChargeAmount(charge)">
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="chargeTimeType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeTimeType.value | translateKey: 'catalogs' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
@if (charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee') {
|
||||
<span>
|
||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
}
|
||||
@if (charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee') {
|
||||
<span>
|
||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
}
|
||||
@if (
|
||||
!(
|
||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date' ||
|
||||
charge.chargeTimeType.value === 'Weekly Fee'
|
||||
)
|
||||
) {
|
||||
<span> N/A </span>
|
||||
}
|
||||
@if (
|
||||
charge.chargeTimeType.value === 'Weekly Fee' ||
|
||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date'
|
||||
) {
|
||||
<button mat-icon-button color="primary" (click)="editChargeDate(charge)">
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="repaymentsEvery">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
@if (charge.feeInterval) {
|
||||
<span>{{ charge.feeInterval }}</span>
|
||||
}
|
||||
@if (!charge.feeInterval) {
|
||||
<span>{{ 'labels.inputs.Not Provided' | translate }}</span>
|
||||
}
|
||||
@if (charge.chargeTimeType.value === 'Weekly Fee' || charge.chargeTimeType.value === 'Monthly Fee') {
|
||||
<button mat-icon-button color="primary" (click)="editChargeFeeInterval(charge)">
|
||||
<fa-icon icon="pen"></fa-icon>
|
||||
</button>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
<button mat-icon-button color="warn" (click)="deleteCharge(charge)">
|
||||
<fa-icon icon="trash"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
}
|
||||
|
||||
<div class="flex-98 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>
|
||||
</div>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-elevation-z1 {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, OnChanges, Input, inject } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { UntypedFormControl } from '@angular/forms';
|
||||
|
||||
/** Custom Dialogs */
|
||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||
|
||||
/** Custom Models */
|
||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||
import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
|
||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import {
|
||||
MatTableDataSource,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { ChargesFilterPipe } from '../../../pipes/charges-filter.pipe';
|
||||
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
||||
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Current Account Charges Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-current-account-charges-step',
|
||||
templateUrl: './current-account-charges-step.component.html',
|
||||
styleUrls: ['./current-account-charges-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatIconButton,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatStepperPrevious,
|
||||
MatStepperNext,
|
||||
ChargesFilterPipe,
|
||||
DateFormatPipe,
|
||||
FormatNumberPipe
|
||||
]
|
||||
})
|
||||
export class CurrentAccountChargesStepComponent implements OnInit, OnChanges {
|
||||
private dialog = inject(MatDialog);
|
||||
private dateUtils = inject(Dates);
|
||||
private translateService = inject(TranslateService);
|
||||
|
||||
/** Current Account Product Template */
|
||||
@Input() currentAccountProductTemplate: any;
|
||||
/** Current Account Template */
|
||||
@Input() currentAccountTemplate: any;
|
||||
/** Currency Code */
|
||||
@Input() currencyCode: UntypedFormControl;
|
||||
@Input() activeClientMembers?: any;
|
||||
|
||||
/** Charge Data */
|
||||
chargeData: any = [];
|
||||
/** Charges Data Source */
|
||||
chargesDataSource: {}[] = [];
|
||||
/** Component is pristine if there has been no changes by user interaction */
|
||||
pristine = true;
|
||||
/** For Edit Current Account Form */
|
||||
isChargesPatched = false;
|
||||
/** Display columns for charges table */
|
||||
displayedColumns: string[] = [
|
||||
'name',
|
||||
'chargeCalculationType',
|
||||
'amount',
|
||||
'chargeTimeType',
|
||||
'date',
|
||||
'repaymentsEvery',
|
||||
'action'
|
||||
];
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
/** Check for select all the Clients List */
|
||||
selectAllItems = false;
|
||||
/** Loan Purpose Options */
|
||||
loanPurposeOptions: string[] = [];
|
||||
/** Table Displayed Columns */
|
||||
displayedColumn: string[] = [
|
||||
'check',
|
||||
'id',
|
||||
'name'
|
||||
];
|
||||
|
||||
ngOnInit() {
|
||||
if (this.currentAccountTemplate) {
|
||||
if (!this.isChargesPatched && this.currentAccountTemplate.charges) {
|
||||
this.chargesDataSource =
|
||||
this.currentAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
this.isChargesPatched = true;
|
||||
} else {
|
||||
this.chargesDataSource = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.currentAccountProductTemplate) {
|
||||
this.chargeData = this.currentAccountProductTemplate.chargeOptions;
|
||||
this.chargesDataSource =
|
||||
this.currentAccountProductTemplate.charges.map((charge: any) => ({ ...charge, id: charge.chargeId })) || [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the charge to charges table
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
addCharge(charge: any) {
|
||||
this.chargesDataSource = this.chargesDataSource.concat([charge.value]);
|
||||
charge.value = '';
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Amount
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeAmount(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new InputBase({
|
||||
controlName: 'amount',
|
||||
label: this.translateService.instant('labels.inputs.Amount'),
|
||||
value: charge.amount,
|
||||
type: 'number',
|
||||
required: false
|
||||
})
|
||||
];
|
||||
const data = {
|
||||
title: this.translateService.instant('labels.heading.Edit Charge Amount'),
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
const newCharge = { ...charge, amount: response.data.value.amount };
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Date
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeDate(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new DatepickerBase({
|
||||
controlName: 'date',
|
||||
label: this.translateService.instant('labels.inputs.Date'),
|
||||
value: charge.dueDate || charge.feeOnMonthDay || '',
|
||||
type: 'datetime-local',
|
||||
required: false
|
||||
})
|
||||
];
|
||||
const data = {
|
||||
title: this.translateService.instant('labels.heading.Edit Charge Date'),
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
let newCharge: any;
|
||||
const dateFormat = 'dd MMMM yyyy';
|
||||
const date = this.dateUtils.formatDate(response.data.value.date, dateFormat);
|
||||
switch (charge.chargeTimeType.value) {
|
||||
case 'Specified due date':
|
||||
case 'Weekly Fee':
|
||||
newCharge = { ...charge, dueDate: date };
|
||||
break;
|
||||
case 'Annual Fee':
|
||||
case 'Monthly Fee':
|
||||
const dateMonthDay = this.dateUtils.formatDate(response.data.value.date, 'dd MMMM');
|
||||
newCharge = { ...charge, feeOnMonthDay: dateMonthDay };
|
||||
break;
|
||||
}
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the Charge Fee Interval
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
editChargeFeeInterval(charge: any) {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new InputBase({
|
||||
controlName: 'feeInterval',
|
||||
label: 'Fee Interval',
|
||||
value: charge.feeInterval,
|
||||
type: 'text',
|
||||
required: false
|
||||
})
|
||||
];
|
||||
const data = {
|
||||
title: 'Edit Charge Fee Interval',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields: formfields
|
||||
};
|
||||
const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data });
|
||||
editNoteDialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response.data) {
|
||||
const newCharge = { ...charge, feeInterval: response.data.value.feeInterval };
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
}
|
||||
});
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the charge from charges table
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
deleteCharge(charge: any) {
|
||||
this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1);
|
||||
this.chargesDataSource = this.chargesDataSource.concat([]);
|
||||
this.pristine = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current account charges
|
||||
*/
|
||||
get currentAccountCharges() {
|
||||
return { charges: this.chargesDataSource };
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<!--
|
||||
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]="currentAccountDetailsForm">
|
||||
<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 (currentAccountProductSelected) {
|
||||
<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>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Submitted On' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
@if (currentAccountProductSelected) {
|
||||
<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 (currentAccountProductSelected) {
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.External ID' | translate }}</mat-label>
|
||||
<input matInput formControlName="externalId" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</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>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext [disabled]="currentAccountDetailsForm.invalid">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, Input, Output, EventEmitter, inject } from '@angular/core';
|
||||
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
/** Custom Services */
|
||||
import { CurrentAccountsService } from 'app/current-accounts/current-accounts.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';
|
||||
|
||||
/**
|
||||
* Current Account Details Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-current-account-details-step',
|
||||
templateUrl: './current-account-details-step.component.html',
|
||||
styleUrls: ['./current-account-details-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatStepperPrevious,
|
||||
FaIconComponent,
|
||||
MatStepperNext
|
||||
]
|
||||
})
|
||||
export class CurrentAccountDetailsStepComponent implements OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private currentAccountsService = inject(CurrentAccountsService);
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
/** Current Account Template */
|
||||
@Input() currentAccountTemplate: any;
|
||||
|
||||
/** Minimum date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
/** Maximum date allowed. */
|
||||
maxDate = new Date();
|
||||
/** Product Data */
|
||||
productData: any;
|
||||
/** Field Officer Data */
|
||||
fieldOfficerData: any;
|
||||
/** For edit current account form */
|
||||
isFieldOfficerPatched = false;
|
||||
/** Current Account Details Form */
|
||||
currentAccountDetailsForm: UntypedFormGroup;
|
||||
|
||||
currentAccountProductSelected = false;
|
||||
|
||||
/** Current Account Template with product data */
|
||||
@Output() currentAccountProductTemplate = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Sets share account details form.
|
||||
* @param {FormBuilder} formBuilder Form Builder.
|
||||
* @param {CurrentAccountsService} currentAccountsService Current Accounts Service.
|
||||
* @param {SettingsService} settingsService Setting service
|
||||
*/
|
||||
constructor() {
|
||||
this.createCurrentAccountDetailsForm();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
this.buildDependencies();
|
||||
if (this.currentAccountTemplate) {
|
||||
this.productData = this.currentAccountTemplate.productOptions;
|
||||
if (this.currentAccountTemplate.currentAccountProductId) {
|
||||
this.currentAccountDetailsForm.patchValue({
|
||||
productId: this.currentAccountTemplate.currentAccountProductId,
|
||||
submittedOnDate:
|
||||
this.currentAccountTemplate.timeline.submittedOnDate &&
|
||||
new Date(this.currentAccountTemplate.timeline.submittedOnDate),
|
||||
externalId: this.currentAccountTemplate.externalId
|
||||
});
|
||||
} else {
|
||||
this.currentAccountDetailsForm.patchValue({
|
||||
submittedOnDate: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates current account details form.
|
||||
*/
|
||||
createCurrentAccountDetailsForm() {
|
||||
this.currentAccountDetailsForm = this.formBuilder.group({
|
||||
productId: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
submittedOnDate: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
fieldOfficerId: [''],
|
||||
externalId: ['']
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches current account product template on productId value changes
|
||||
*/
|
||||
buildDependencies() {
|
||||
const entityId = this.currentAccountTemplate.groupId || this.currentAccountTemplate.clientId;
|
||||
this.currentAccountDetailsForm.get('productId').valueChanges.subscribe((productId: string) => {
|
||||
this.currentAccountsService
|
||||
.getCurrentAccountTemplate(entityId, productId, this.currentAccountTemplate.groupId ? true : false)
|
||||
.subscribe((response: any) => {
|
||||
this.currentAccountProductTemplate.emit(response);
|
||||
this.fieldOfficerData = response.fieldOfficerOptions;
|
||||
this.currentAccountProductSelected = true;
|
||||
if (!this.isFieldOfficerPatched && this.currentAccountTemplate.fieldOfficerId) {
|
||||
this.currentAccountDetailsForm.get('fieldOfficerId').patchValue(this.currentAccountTemplate.fieldOfficerId);
|
||||
this.isFieldOfficerPatched = true;
|
||||
} else {
|
||||
this.currentAccountDetailsForm.get('fieldOfficerId').patchValue('');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current account form value.
|
||||
*/
|
||||
get currentAccountDetails() {
|
||||
return this.currentAccountDetailsForm.getRawValue();
|
||||
}
|
||||
}
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
<!--
|
||||
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="responsive-column">
|
||||
<h3 class="mat-h3 flex-fill">{{ 'labels.heading.Details' | translate }}</h3>
|
||||
|
||||
<mat-divider class="flex-fill"></mat-divider>
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Product' | translate }}</span>
|
||||
<span class="flex-60">{{
|
||||
currentAccount.productId | find: currentAccountTemplate.productOptions : 'id' : 'name'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Submitted On' | translate }}</span>
|
||||
<span class="flex-60">{{ currentAccount.submittedOnDate | dateFormat }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.External Id' | translate }}</span>
|
||||
@if (currentAccount.externalId) {
|
||||
<span class="flex-60">
|
||||
<mifosx-external-identifier externalId="{{ currentAccount.externalId }}"></mifosx-external-identifier>
|
||||
</span>
|
||||
}
|
||||
@if (!currentAccount.externalId) {
|
||||
<span class="flex-60">
|
||||
{{ 'labels.inputs.Unassigned' | translate }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Field Officer' | translate }}</span>
|
||||
<span class="flex-60">{{
|
||||
currentAccount.fieldOfficerId | find: currentAccountProductTemplate.fieldOfficerOptions : 'id' : 'displayName'
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@if (activeClientMembers) {
|
||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Client Members' | translate }}</h3>
|
||||
<mat-divider class="flex-fill"></mat-divider>
|
||||
<div class="flex-60">
|
||||
<table mat-table #membersTable [dataSource]="dataSource">
|
||||
<!-- ID Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
||||
</ng-container>
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="membersDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: membersDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<h3 class="mat-h3 margin-t flex-fill">{{ 'labels.heading.Terms' | translate }}</h3>
|
||||
|
||||
<mat-divider class="flex-fill"></mat-divider>
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Currency' | translate }}</span>
|
||||
<span class="flex-60">{{ currentAccountTermsForm.get('currencyCode').value }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Decimal Places' | translate }}</span>
|
||||
<span class="flex-60">{{ currentAccountTermsForm.get('decimal').value }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.nominalAnnualInterestRate | formatNumber }} %</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Compounding Period' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
currentAccount.interestCompoundingPeriodType
|
||||
| find: currentAccountProductTemplate.interestCompoundingPeriodTypeOptions : 'id' : 'value'
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Posting Period' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
currentAccount.interestPostingPeriodType
|
||||
| find: currentAccountProductTemplate.interestPostingPeriodTypeOptions : 'id' : 'value'
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Interest Calculated using' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
currentAccount.interestCalculationType
|
||||
| find: currentAccountProductTemplate.interestCalculationTypeOptions : 'id' : 'value'
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Days in Year' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
currentAccount.interestCalculationDaysInYearType
|
||||
| find: currentAccountProductTemplate.interestCalculationDaysInYearTypeOptions : 'id' : 'value'
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
@if (currentAccount.minRequiredOpeningBalance) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Minimum Opening Balance' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.minRequiredOpeningBalance | formatNumber }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.withdrawalFeeForTransfers | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (currentAccount.lockinPeriodFrequency) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Lock-in Period' | translate }}:</span>
|
||||
<span class="flex-60">{{
|
||||
getCatalogTranslation(
|
||||
currentAccount.lockinPeriodFrequency +
|
||||
' ' +
|
||||
(currentAccount.lockinPeriodFrequencyType
|
||||
| find: currentAccountProductTemplate.lockinPeriodFrequencyTypeOptions : 'id' : 'value')
|
||||
)
|
||||
}}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Is Overdraft Allowed' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.allowOverdraft | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (currentAccount.minOverdraftForInterestCalculation) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40"
|
||||
>{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}:</span
|
||||
>
|
||||
<span class="flex-60">{{ currentAccount.minOverdraftForInterestCalculation | formatNumber }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (currentAccount.nominalAnnualInterestRateOverdraft) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Nominal Annual Interest for Overdraft' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.nominalAnnualInterestRateOverdraft | formatNumber }} %</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (currentAccount.overdraftLimit) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Maximum Overdraft Amount Limit' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.overdraftLimit | formatNumber }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Enforce Minimum Balance' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.enforceMinRequiredBalance | yesNo }}</span>
|
||||
</div>
|
||||
|
||||
@if (currentAccount.minRequiredBalance) {
|
||||
<div class="flex-fill layout-row">
|
||||
<span class="flex-40">{{ 'labels.inputs.Minimum Balance' | translate }}:</span>
|
||||
<span class="flex-60">{{ currentAccount.minRequiredBalance }}</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (currentAccount.charges.length) {
|
||||
<div class="layout-row-wrap responsive-column flex-fill">
|
||||
<h3 class="mat-h3 flex-fill">{{ 'labels.inputs.Charges' | translate }}</h3>
|
||||
<mat-divider class="flex-fill"></mat-divider>
|
||||
<table class="flex-fill mat-elevation-z1" mat-table [dataSource]="currentAccount.charges">
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.name + ', ' + charge.currency.displaySymbol }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="chargeCalculationType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Type' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeCalculationType.value | translateKey: 'catalogs' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.amount | formatNumber }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Date' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
@if (charge.chargeTimeType.value === 'Specified due date' || charge.chargeTimeType.value === 'Weekly Fee') {
|
||||
<span>
|
||||
{{ (charge.dueDate | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
}
|
||||
@if (charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee') {
|
||||
<span>
|
||||
{{ (charge.feeOnMonthDay | dateFormat) || 'Unassigned' }}
|
||||
</span>
|
||||
}
|
||||
@if (
|
||||
!(
|
||||
charge.chargeTimeType.value === 'Monthly Fee' ||
|
||||
charge.chargeTimeType.value === 'Annual Fee' ||
|
||||
charge.chargeTimeType.value === 'Specified due date' ||
|
||||
charge.chargeTimeType.value === 'Weekly Fee'
|
||||
)
|
||||
) {
|
||||
<span> N/A </span>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="repaymentsEvery">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Repayments Every' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.feeInterval || 'Not Provided' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="chargeTimeType">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Collected On' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
{{ charge.chargeTimeType.value }}
|
||||
</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
||||
<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 [routerLink]="['../..']">
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="submitEvent.emit()">
|
||||
{{ 'labels.buttons.Submit' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-elevation-z1 {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright since 2025 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, inject } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import {
|
||||
MatTableDataSource,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { ExternalIdentifierComponent } from '../../../shared/external-identifier/external-identifier.component';
|
||||
import { MatStepperPrevious } from '@angular/material/stepper';
|
||||
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';
|
||||
|
||||
/**
|
||||
* Current account preview step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-current-account-account-preview-step',
|
||||
templateUrl: './current-account-preview-step.component.html',
|
||||
styleUrls: ['./current-account-preview-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatDivider,
|
||||
ExternalIdentifierComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatStepperPrevious,
|
||||
FaIconComponent,
|
||||
FindPipe,
|
||||
DateFormatPipe,
|
||||
FormatNumberPipe,
|
||||
YesnoPipe
|
||||
]
|
||||
})
|
||||
export class CurrentAccountPreviewStepComponent implements OnChanges {
|
||||
private translateService = inject(TranslateService);
|
||||
|
||||
/** Current Account Product Template */
|
||||
@Input() currentAccountProductTemplate: any;
|
||||
/** Current Account Template */
|
||||
@Input() currentAccountTemplate: any;
|
||||
/** Current Account Terms Form */
|
||||
@Input() currentAccountTermsForm: any;
|
||||
/** Current Account */
|
||||
@Input() currentAccount: any;
|
||||
@Input() activeClientMembers?: any;
|
||||
|
||||
/** Table Data Source */
|
||||
dataSource: any;
|
||||
|
||||
/** Display columns for charges table */
|
||||
chargesDisplayedColumns: string[] = [
|
||||
'name',
|
||||
'chargeCalculationType',
|
||||
'amount',
|
||||
'chargeTimeType',
|
||||
'date',
|
||||
'repaymentsEvery'
|
||||
];
|
||||
/** Columns to be displayed in active members table. */
|
||||
membersDisplayedColumns: string[] = [
|
||||
'id',
|
||||
'name'
|
||||
];
|
||||
|
||||
/** Form submission event */
|
||||
@Output() submitEvent = new EventEmitter();
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (this.activeClientMembers?.length > 0) {
|
||||
this.dataSource = new MatTableDataSource<any>(this.activeClientMembers.filter((member: any) => member.selected));
|
||||
}
|
||||
}
|
||||
|
||||
getCatalogTranslation(text: string): string {
|
||||
return this.translateService.instant('labels.catalogs.' + text);
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
<!--
|
||||
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]="currentAccountTermsForm">
|
||||
<div class="layout-row-wrap gap-2percent responsive-column align-start-center">
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Currency' | translate }}</mat-label>
|
||||
<input matInput formControlName="currencyCode" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Decimal Places' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="decimal" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Nominal Annual Interest' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="nominalAnnualInterestRate" required />
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Nominal Annual Interest' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Interest Compounding Period' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCompoundingPeriodType" required>
|
||||
@for (interestCompoundingPeriodType of interestCompoundingPeriodTypeData; track interestCompoundingPeriodType) {
|
||||
<mat-option [value]="interestCompoundingPeriodType.id">
|
||||
{{ interestCompoundingPeriodType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Interest Compounding Period' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Interest Posting Period' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestPostingPeriodType" required>
|
||||
@for (interestPostingPeriodType of interestPostingPeriodTypeData; track interestPostingPeriodType) {
|
||||
<mat-option [value]="interestPostingPeriodType.id">
|
||||
{{ interestPostingPeriodType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Interest Posting Period' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Interest Calculated using' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCalculationType" required>
|
||||
@for (interestCalculationType of interestCalculationTypeData; track interestCalculationType) {
|
||||
<mat-option [value]="interestCalculationType.id">
|
||||
{{ interestCalculationType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Interest Calculated using' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Days in Year' | translate }}</mat-label>
|
||||
<mat-select formControlName="interestCalculationDaysInYearType" required>
|
||||
@for (
|
||||
interestCalculationDaysInYearType of interestCalculationDaysInYearTypeData;
|
||||
track interestCalculationDaysInYearType
|
||||
) {
|
||||
<mat-option [value]="interestCalculationDaysInYearType.id">
|
||||
{{ interestCalculationDaysInYearType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
<mat-error>
|
||||
{{ 'labels.inputs.Days in Year' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Minimum Opening Balance' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minRequiredOpeningBalance" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox labelPosition="before" formControlName="withdrawalFeeForTransfers" class="margin-v flex-48">
|
||||
{{ 'labels.inputs.Apply Withdrawal Fee for Transfers' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<h4 class="mat-h4 flex-98">{{ 'labels.inputs.Lock-in Period' | translate }}</h4>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Frequency' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="lockinPeriodFrequency" />
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
|
||||
<mat-select formControlName="lockinPeriodFrequencyType">
|
||||
@for (lockinPeriodFrequencyType of lockinPeriodFrequencyTypeData; track lockinPeriodFrequencyType) {
|
||||
<mat-option [value]="lockinPeriodFrequencyType.id">
|
||||
{{ lockinPeriodFrequencyType.value | translateKey: 'catalogs' }}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-divider class="flex-98"></mat-divider>
|
||||
|
||||
<h3 class="mat-h3 flex-23">{{ 'labels.inputs.Overdraft' | translate }}</h3>
|
||||
|
||||
<mat-checkbox class="flex-73 margin-b" labelPosition="before" formControlName="allowOverdraft">
|
||||
{{ 'labels.inputs.Is Overdraft Allowed' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
@if (currentAccountTermsForm.value.allowOverdraft) {
|
||||
<div class="flex-fill layout-row-wrap gap-2percent responsive-column">
|
||||
<mat-form-field class="flex-31">
|
||||
<mat-label>{{ 'labels.inputs.Minimum Overdraft Required for Interest Calculation' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minOverdraftForInterestCalculation" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-31">
|
||||
<mat-label>{{ 'labels.inputs.Nominal Annual Interest for Overdraft' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="nominalAnnualInterestRateOverdraft" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="flex-31">
|
||||
<mat-label>{{ 'labels.inputs.Maximum Overdraft Amount Limit' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="overdraftLimit" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
|
||||
<mat-divider class="flex-98"></mat-divider>
|
||||
|
||||
<mat-checkbox labelPosition="before" formControlName="enforceMinRequiredBalance" class="margin-v flex-48">
|
||||
{{ 'labels.inputs.Enforce Minimum Balance' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-form-field class="flex-48">
|
||||
<mat-label>{{ 'labels.inputs.Minimum Balance' | translate }}</mat-label>
|
||||
<input type="number" matInput formControlName="minRequiredBalance" />
|
||||
</mat-form-field>
|
||||
|
||||
@if (currentAccountTermsForm.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">
|
||||
<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>
|
||||
</form>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
h4 {
|
||||
font-weight: 500;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
mat-divider {
|
||||
margin: 1em 0 2em;
|
||||
}
|
||||
|
||||
.margin-v {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.margin-b {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
+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/.
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnChanges, OnInit, Input, inject } from '@angular/core';
|
||||
import {
|
||||
UntypedFormGroup,
|
||||
UntypedFormBuilder,
|
||||
Validators,
|
||||
UntypedFormControl,
|
||||
ReactiveFormsModule
|
||||
} from '@angular/forms';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Current Account Terms Step
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-current-account-terms-step',
|
||||
templateUrl: './current-account-terms-step.component.html',
|
||||
styleUrls: ['./current-account-terms-step.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatCheckbox,
|
||||
MatDivider,
|
||||
MatStepperPrevious,
|
||||
FaIconComponent,
|
||||
MatStepperNext
|
||||
]
|
||||
})
|
||||
export class CurrentAccountTermsStepComponent implements OnChanges, OnInit {
|
||||
private formBuilder = inject(UntypedFormBuilder);
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
/** Current Account and Product Template */
|
||||
@Input() currentAccountProductTemplate: any;
|
||||
/** Current Account Template */
|
||||
@Input() currentAccountTemplate: any;
|
||||
|
||||
/** Minimum date allowed. */
|
||||
minDate = new Date(2000, 0, 1);
|
||||
/** Maximum date allowed. */
|
||||
maxDate = new Date();
|
||||
/** Current Account Terms Form */
|
||||
currentAccountTermsForm: UntypedFormGroup;
|
||||
/** Lockin Period Frequency Type Data */
|
||||
lockinPeriodFrequencyTypeData: any;
|
||||
/** Interest Compounding Period Type Data */
|
||||
interestCompoundingPeriodTypeData: any;
|
||||
/** Interest Posting Period Type Data */
|
||||
interestPostingPeriodTypeData: any;
|
||||
/** Interest Calculation Type Data */
|
||||
interestCalculationTypeData: any;
|
||||
/** Interest Calculation Days in Year Data */
|
||||
interestCalculationDaysInYearTypeData: any;
|
||||
|
||||
/**
|
||||
* @param {FormBuilder} formBuilder Form Builder
|
||||
* @param {SettingsService} settingsService Setting service
|
||||
*/
|
||||
constructor() {
|
||||
this.createCurrentAccountTermsForm();
|
||||
this.buildDependencies();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.currentAccountProductTemplate) {
|
||||
this.currentAccountTermsForm.patchValue({
|
||||
currencyCode: this.currentAccountProductTemplate.currency.code,
|
||||
decimal: this.currentAccountProductTemplate.currency.decimalPlaces,
|
||||
minBalanceForInterestCalculation: this.currentAccountProductTemplate.minBalanceForInterestCalculation,
|
||||
nominalAnnualInterestRate: this.currentAccountProductTemplate.nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType: this.currentAccountProductTemplate.interestCompoundingPeriodType.id,
|
||||
interestPostingPeriodType: this.currentAccountProductTemplate.interestPostingPeriodType.id,
|
||||
interestCalculationType: this.currentAccountProductTemplate.interestCalculationType.id,
|
||||
interestCalculationDaysInYearType: this.currentAccountProductTemplate.interestCalculationDaysInYearType.id,
|
||||
minRequiredOpeningBalance: this.currentAccountProductTemplate.minRequiredOpeningBalance,
|
||||
allowOverdraft: this.currentAccountProductTemplate.allowOverdraft,
|
||||
overdraftLimit: this.currentAccountProductTemplate.overdraftLimit,
|
||||
enforceMinRequiredBalance: this.currentAccountProductTemplate.enforceMinRequiredBalance,
|
||||
minOverdraftForInterestCalculation: this.currentAccountProductTemplate.minOverdraftForInterestCalculation,
|
||||
nominalAnnualInterestRateOverdraft: this.currentAccountProductTemplate.nominalAnnualInterestRateOverdraft,
|
||||
minRequiredBalance: this.currentAccountProductTemplate.minRequiredBalance,
|
||||
withdrawalFeeForTransfers: this.currentAccountProductTemplate.withdrawalFeeForTransfers
|
||||
});
|
||||
this.setOptions();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.maxDate = this.settingsService.businessDate;
|
||||
if (this.currentAccountTemplate) {
|
||||
this.currentAccountTermsForm.patchValue({
|
||||
nominalAnnualInterestRate: this.currentAccountTemplate.nominalAnnualInterestRate,
|
||||
interestCompoundingPeriodType: this.currentAccountTemplate.interestCompoundingPeriodType.id,
|
||||
interestPostingPeriodType: this.currentAccountTemplate.interestPostingPeriodType.id,
|
||||
interestCalculationType: this.currentAccountTemplate.interestCalculationType.id,
|
||||
interestCalculationDaysInYearType: this.currentAccountTemplate.interestCalculationDaysInYearType.id,
|
||||
minRequiredOpeningBalance: this.currentAccountTemplate.minRequiredOpeningBalance,
|
||||
withdrawalFeeForTransfers: this.currentAccountTemplate.withdrawalFeeForTransfers,
|
||||
lockinPeriodFrequency: this.currentAccountTemplate.lockinPeriodFrequency,
|
||||
lockinPeriodFrequencyType:
|
||||
this.currentAccountTemplate.lockinPeriodFrequencyType &&
|
||||
this.currentAccountTemplate.lockinPeriodFrequencyType.id,
|
||||
allowOverdraft: this.currentAccountTemplate.allowOverdraft,
|
||||
enforceMinRequiredBalance: this.currentAccountTemplate.enforceMinRequiredBalance,
|
||||
minRequiredBalance: this.currentAccountTemplate.minRequiredBalance
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates current account terms form.
|
||||
*/
|
||||
createCurrentAccountTermsForm() {
|
||||
this.currentAccountTermsForm = this.formBuilder.group({
|
||||
currencyCode: [{ value: '', disabled: true }],
|
||||
decimal: [{ value: '', disabled: true }],
|
||||
nominalAnnualInterestRate: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCompoundingPeriodType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestPostingPeriodType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCalculationType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
interestCalculationDaysInYearType: [
|
||||
'',
|
||||
Validators.required
|
||||
],
|
||||
minRequiredOpeningBalance: [''],
|
||||
withdrawalFeeForTransfers: [false],
|
||||
lockinPeriodFrequency: [''],
|
||||
lockinPeriodFrequencyType: [''],
|
||||
allowOverdraft: [false],
|
||||
enforceMinRequiredBalance: [false],
|
||||
minRequiredBalance: [''],
|
||||
minBalanceForInterestCalculation: [{ value: '', disabled: true }]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all select dropdown options.
|
||||
*/
|
||||
setOptions() {
|
||||
this.lockinPeriodFrequencyTypeData = this.currentAccountProductTemplate.lockinPeriodFrequencyTypeOptions;
|
||||
this.interestCompoundingPeriodTypeData = this.currentAccountProductTemplate.interestCompoundingPeriodTypeOptions;
|
||||
this.interestPostingPeriodTypeData = this.currentAccountProductTemplate.interestPostingPeriodTypeOptions;
|
||||
this.interestCalculationTypeData = this.currentAccountProductTemplate.interestCalculationTypeOptions;
|
||||
this.interestCalculationDaysInYearTypeData =
|
||||
this.currentAccountProductTemplate.interestCalculationDaysInYearTypeOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to value changes and sets new form controls accordingly.
|
||||
*/
|
||||
buildDependencies() {
|
||||
this.currentAccountTermsForm.get('allowOverdraft').valueChanges.subscribe((allowOverdraft: any) => {
|
||||
if (allowOverdraft) {
|
||||
this.currentAccountTermsForm.addControl('minOverdraftForInterestCalculation', new UntypedFormControl(''));
|
||||
this.currentAccountTermsForm.addControl('nominalAnnualInterestRateOverdraft', new UntypedFormControl(''));
|
||||
this.currentAccountTermsForm.addControl('overdraftLimit', new UntypedFormControl(''));
|
||||
} else {
|
||||
this.currentAccountTermsForm.removeControl('minOverdraftForInterestCalculation');
|
||||
this.currentAccountTermsForm.removeControl('nominalAnnualInterestRateOverdraft');
|
||||
this.currentAccountTermsForm.removeControl('overdraftLimit');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current account terms form value.
|
||||
*/
|
||||
get currentAccountTerms() {
|
||||
const payload = this.currentAccountTermsForm.getRawValue();
|
||||
delete payload.currencyCode;
|
||||
delete payload.decimal;
|
||||
delete payload.minBalanceForInterestCalculation; // Backend is not accepting minBalanceForInterestCalculation value
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
@if (activeClientMembers) {
|
||||
<div>
|
||||
<table class="flex-98" mat-table [dataSource]="dataSource">
|
||||
<!-- Check Column -->
|
||||
<ng-container matColumnDef="check">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox (change)="toggleSelects()" [(ngModel)]="selectAllItems"></mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<mat-checkbox (change)="toggleSelect()" [(ngModel)]="element.selected"></mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
<!-- Position Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client ID' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.id }}</td>
|
||||
</ng-container>
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client Name' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.displayName }}</td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumn"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumn"></tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="layout-row responsive-column align-center gap-2px margin-t">
|
||||
<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]="selectedClientMembers.selectedMembers.length === 0">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
</div>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mat-elevation-z1 {
|
||||
margin: 1em 0 1.5em;
|
||||
}
|
||||
|
||||
.margin-t {
|
||||
margin-top: 1em;
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 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, OnInit, OnChanges, Input } from '@angular/core';
|
||||
import {
|
||||
MatTableDataSource,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow
|
||||
} from '@angular/material/table';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-current-account-active-client-members',
|
||||
templateUrl: './current-accounts-active-client-members.component.html',
|
||||
styleUrls: ['./current-accounts-active-client-members.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCheckbox,
|
||||
FormsModule,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatStepperPrevious,
|
||||
FaIconComponent,
|
||||
MatStepperNext
|
||||
]
|
||||
})
|
||||
export class CurrentAccountActiveClientMembersComponent implements OnInit {
|
||||
@Input() activeClientMembers?: any;
|
||||
selectAllItems = false;
|
||||
displayedColumn: string[] = [
|
||||
'check',
|
||||
'id',
|
||||
'name'
|
||||
];
|
||||
|
||||
constructor() {}
|
||||
|
||||
dataSource: any;
|
||||
ngOnInit(): void {
|
||||
this.dataSource = new MatTableDataSource<any>(this.activeClientMembers);
|
||||
}
|
||||
get selectedClientMembers() {
|
||||
return { selectedMembers: this.activeClientMembers.filter((item: any) => item.selected) };
|
||||
}
|
||||
|
||||
/** Toggle all checks */
|
||||
toggleSelects() {
|
||||
for (const member of this.activeClientMembers) {
|
||||
member.selected = this.selectAllItems;
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if all the checks are selected */
|
||||
toggleSelect() {
|
||||
const len = this.activeClientMembers.length;
|
||||
this.selectAllItems =
|
||||
len === 0 ? false : this.activeClientMembers.filter((item: any) => item.selected).length === len;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user