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:
Aditya
2026-03-17 18:23:37 +05:30
co-authored by Claude Opus 4.6
parent 73aa69ee02
commit 0a32d7135e
210 changed files with 15048 additions and 155 deletions
@@ -0,0 +1,108 @@
<!--
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="container">
<mat-card>
<form [formGroup]="editTransactionForm" (ngSubmit)="submit()">
<mat-card-content class="layout-column">
<mat-form-field (click)="dueDatePicker.open()">
<mat-label>{{ 'labels.inputs.Transaction Date' | translate }}</mat-label>
<input
matInput
[min]="minDate"
[max]="maxDate"
[matDatepicker]="dueDatePicker"
formControlName="transactionDate"
required
/>
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dueDatePicker></mat-datepicker>
@if (editTransactionForm.controls.transactionDate.hasError('required')) {
<mat-error>
{{ 'labels.inputs.Transaction Date' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
}
</mat-form-field>
<mifosx-input-amount
[currency]="currency"
[isRequired]="true"
[inputFormControl]="editTransactionForm.controls.transactionAmount"
[inputLabel]="'Transaction Amount'"
>
</mifosx-input-amount>
<mat-form-field>
<mat-label>{{ 'labels.inputs.Payment Type' | translate }}</mat-label>
<mat-select formControlName="paymentTypeId">
@for (paymentType of paymentTypeOptions; track paymentType) {
<mat-option [value]="paymentType.id">
{{ paymentType.name }}
</mat-option>
}
</mat-select>
</mat-form-field>
<div class="flex-fill">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75" (click)="addPaymentDetails()">
<mat-slide-toggle>
<div [className]="showPaymentDetails ? 'enabled' : 'disabled'">
<span class="m-l-10">{{ 'labels.inputs.Show Payment Details' | translate }}</span>
</div>
</mat-slide-toggle>
</span>
</div>
@if (editTransactionForm.controls['accountNumber']) {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Account Number' | translate }}</mat-label>
<input type="number" formControlName="accountNumber" matInput />
</mat-form-field>
}
@if (editTransactionForm.controls['checkNumber']) {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Cheque' | translate }}</mat-label>
<input type="number" formControlName="checkNumber" matInput />
</mat-form-field>
}
@if (editTransactionForm.controls['routingCode']) {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Routing Code' | translate }}</mat-label>
<input formControlName="routingCode" matInput />
</mat-form-field>
}
@if (editTransactionForm.controls['receiptNumber']) {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Receipt Number' | translate }}</mat-label>
<input formControlName="receiptNumber" matInput />
</mat-form-field>
}
@if (editTransactionForm.controls['bankNumber']) {
<mat-form-field>
<mat-label>{{ 'labels.inputs.Bank' | translate }}</mat-label>
<input formControlName="bankNumber" matInput />
</mat-form-field>
}
<mat-card-actions class="layout-row layout-xs-column layout-align-center gap-5px">
<button type="button" mat-raised-button [routerLink]="['../']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" [disabled]="!editTransactionForm.valid">
{{ 'labels.buttons.Submit' | translate }}
</button>
</mat-card-actions>
</mat-card-content>
</form>
</mat-card>
</div>
@@ -0,0 +1,11 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.container {
width: 37rem;
}
@@ -0,0 +1,164 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/** Angular Imports */
import { Component, OnInit, inject } from '@angular/core';
import {
UntypedFormGroup,
UntypedFormBuilder,
Validators,
UntypedFormControl,
ReactiveFormsModule
} from '@angular/forms';
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
import { Dates } from 'app/core/utils/dates';
/** Custom Services */
import { CurrentAccountsService } from 'app/current-accounts/current-accounts.service';
import { SettingsService } from 'app/settings/settings.service';
import { Currency } from 'app/shared/models/general.model';
import { InputAmountComponent } from '../../../../shared/input-amount/input-amount.component';
import { MatSlideToggle } from '@angular/material/slide-toggle';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
/**
* Edit Transaction component.
*/
@Component({
selector: 'mifosx-edit-transaction',
templateUrl: './edit-transaction.component.html',
styleUrls: ['./edit-transaction.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
InputAmountComponent,
MatSlideToggle
]
})
export class EditTransactionComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private route = inject(ActivatedRoute);
private router = inject(Router);
private dateUtils = inject(Dates);
private currentAccountsService = inject(CurrentAccountsService);
private settingsService = inject(SettingsService);
/** Minimum Due Date allowed. */
minDate = new Date(2000, 0, 1);
/** Maximum Due Date allowed. */
maxDate = new Date();
/** Current account transaction form. */
editTransactionForm: UntypedFormGroup;
/** current account transaction payment options. */
paymentTypeOptions: {
id: number;
name: string;
description: string;
isCashPayment: boolean;
position: number;
}[];
/** Flag to enable payment details fields. */
showPaymentDetails: Boolean = false;
/** current account's Id */
currentAccountId: string;
/** Transaction Template */
transactionTemplateData: any;
currency: Currency;
/**
* Retrieves the Current Account transaction template data from `resolve`.
* @param {FormBuilder} formBuilder Form Builder.
* @param {CurrentAccountsService} currentAccountsService Current Accounts Service.
* @param {ActivatedRoute} route Activated Route.
* @param {Dates} dateUtils Date Utils.
* @param {Router} router Router for navigation.
* @param {SettingsService} settingsService Setting service
*/
constructor() {
this.route.data.subscribe((data: { currentAccountTransactionTemplate: any }) => {
this.transactionTemplateData = data.currentAccountTransactionTemplate;
if (data.currentAccountTransactionTemplate.currency) {
this.currency = data.currentAccountTransactionTemplate.currency;
}
this.paymentTypeOptions = this.transactionTemplateData.paymentTypeOptions;
});
this.currentAccountId = this.route.snapshot.params['currentAccountId'];
}
/**
* Creates the Current Account account transaction form when component loads.
*/
ngOnInit() {
this.maxDate = this.settingsService.businessDate;
this.createEditTransactionForm();
this.editTransactionForm.patchValue({
transactionDate: this.transactionTemplateData.date && new Date(this.transactionTemplateData.date),
transactionAmount: this.transactionTemplateData.amount,
paymentTypeId: this.transactionTemplateData.paymentTypeId
});
}
/**
* Method to create the Current Account Transaction Form.
*/
createEditTransactionForm() {
this.editTransactionForm = this.formBuilder.group({
transactionDate: [
'',
Validators.required
],
transactionAmount: [
'',
Validators.required
],
paymentTypeId: ['']
});
}
/**
* Method to add payment detail fields to the UI.
*/
addPaymentDetails() {
this.showPaymentDetails = !this.showPaymentDetails;
if (this.showPaymentDetails) {
this.editTransactionForm.addControl('accountNumber', new UntypedFormControl(''));
this.editTransactionForm.addControl('checkNumber', new UntypedFormControl(''));
this.editTransactionForm.addControl('routingCode', new UntypedFormControl(''));
this.editTransactionForm.addControl('receiptNumber', new UntypedFormControl(''));
this.editTransactionForm.addControl('bankNumber', new UntypedFormControl(''));
} else {
this.editTransactionForm.removeControl('accountNumber');
this.editTransactionForm.removeControl('checkNumber');
this.editTransactionForm.removeControl('routingCode');
this.editTransactionForm.removeControl('receiptNumber');
this.editTransactionForm.removeControl('bankNumber');
}
}
/**
* Method to submit the transaction details.
*/
submit() {
const editTransactionFormData = this.editTransactionForm.value;
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const prevTransactionDate: Date = this.editTransactionForm.value.transactionDate;
if (editTransactionFormData.transactionDate instanceof Date) {
editTransactionFormData.transactionDate = this.dateUtils.formatDate(prevTransactionDate, dateFormat);
}
const data = {
...editTransactionFormData,
dateFormat,
locale
};
this.currentAccountsService
.executeCurrentAccountTransactionsCommand(this.currentAccountId, 'modify', data, this.transactionTemplateData.id)
.subscribe((res) => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,33 @@
<!--
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/.
-->
<mat-card class="container">
<div class="back-button">
<button type="button" color="primary" mat-raised-button [routerLink]="['../../']">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>Back
</button>
</div>
<div *ngIf="pentahoUrl; else noData" class="pdf-container">
<iframe [src]="pentahoUrl" class="pdf-iframe"></iframe>
</div>
<ng-template #noData>
<div class="error-message">
<h3>Unable to Load Receipt</h3>
<p>The transaction receipt could not be loaded. This may be due to:</p>
<ul>
<li>Missing or incomplete report configuration</li>
<li>Report generation service unavailable</li>
<li>Invalid transaction data</li>
</ul>
<p><strong>Please contact your system administrator for assistance.</strong></p>
<p class="console-hint">Technical details may be available in the browser console (F12).</p>
</div>
</ng-template>
</mat-card>
@@ -0,0 +1,58 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.container {
max-width: 50rem;
margin: 0 auto;
.back-button {
margin-bottom: 1rem;
}
.pdf-container {
width: 100%;
height: calc(100vh - 250px);
min-height: 600px;
display: flex;
justify-content: center;
align-items: center;
background-color: #525659;
.pdf-iframe {
width: 100%;
height: 100%;
border: none;
}
}
.error-message {
padding: 2rem;
text-align: center;
background-color: #fff3cd;
border: 1px solid #ffc107;
border-radius: 4px;
margin: 1rem 0;
h3 {
color: #856404;
margin-bottom: 1rem;
}
ul {
text-align: left;
display: inline-block;
margin: 1rem 0;
}
.console-hint {
font-size: 0.875rem;
color: #6c757d;
margin-top: 1rem;
}
}
}
@@ -0,0 +1,72 @@
/**
* 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, OnDestroy, inject } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { HttpResponse } from '@angular/common/http';
/**
* View Transaction Reciept Component
*/
@Component({
selector: 'mifosx-view-reciept',
templateUrl: './view-reciept.component.html',
styleUrls: ['./view-reciept.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent
]
})
export class ViewRecieptComponent implements OnInit, OnDestroy {
private sanitizer = inject(DomSanitizer);
private route = inject(ActivatedRoute);
/** trusted resource url for pentaho output */
pentahoUrl: SafeResourceUrl | null = null;
/** Transaction Reciept Data */
transactionRecieptData: HttpResponse<Blob>;
/** Blob URL for cleanup */
private blobUrl: string | null = null;
/**
* Fetches transaction reciept `resolve`
* @param {DomSanitizer} sanitizer DOM Sanitizer
* @param {ActivatedRoute} route Activated Route
*/
constructor() {
this.route.data.subscribe((data: { currentAccountTransactionReciept: HttpResponse<Blob> }) => {
this.transactionRecieptData = data.currentAccountTransactionReciept;
});
}
ngOnInit() {
if (!this.transactionRecieptData || !this.transactionRecieptData.body) {
return;
}
const contentType = this.transactionRecieptData.headers.get('Content-Type');
const file = new Blob([this.transactionRecieptData.body], { type: contentType });
if (file.size === 0) {
return;
}
this.blobUrl = URL.createObjectURL(file);
this.pentahoUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.blobUrl);
}
ngOnDestroy() {
if (this.blobUrl) {
URL.revokeObjectURL(this.blobUrl);
}
}
}
@@ -0,0 +1,9 @@
<!--
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/.
-->
<p>{{ 'labels.heading.current-account-transaction-datatable-tab works' | translate }}!</p>
@@ -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/.
*/
@@ -0,0 +1,22 @@
/**
* 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 } from '@angular/core';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-current-account-transaction-datatable-tab',
templateUrl: './current-accounts-transaction-datatable-tab.component.html',
styleUrls: ['./current-accounts-transaction-datatable-tab.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS
]
})
export class CurrentAccountTransactionDatatableTabComponent {
constructor() {}
}
@@ -0,0 +1,128 @@
<!--
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="container transaction-buttons m-b-20 align-end-center gap-2percent" align="end">
@if (
!(
transactionData.interestedPostedAsOn === false &&
(transactionData.transactionType.id === 17 || transactionData.transactionType.id === 3)
)
) {
<span>
<span *mifosxHasPermission="'ADJUSTTRANSACTION_CURRENTACCOUNT'">
@if (
transactionData.transactionType.value === 'Transfer' ||
transactionData.reversed === 'true' ||
transactionData.transactionType.id === 3 ||
transactionData.transactionType.id === 17
) {
<button mat-raised-button color="primary" [routerLink]="'edit'">
<fa-icon icon="edit" class="m-r-10"></fa-icon>{{ 'labels.buttons.Edit' | translate }}
</button>
}
</span>
</span>
}
@if (!transactionData.reversed && transactionData.transactionType.amountHold) {
<span>
<button
mat-raised-button
color="primary"
*mifosxHasPermission="'RELEASEAMOUNT_CURRENTACCOUNT'"
(click)="releaseAmount()"
>
<fa-icon icon="lock-open" class="m-r-10"></fa-icon>{{ 'labels.buttons.Release Amount' | translate }}
</button>
</span>
}
@if (allowUndo()) {
<span>
<button
mat-raised-button
color="warn"
*mifosxHasPermission="'UNDOTRANSACTION_CURRENTACCOUNT'"
(click)="undoTransaction()"
>
<fa-icon icon="undo" class="m-r-10"></fa-icon>{{ 'labels.buttons.Undo' | translate }}
</button>
</span>
}
</div>
<div class="container">
<mat-card>
<mat-card-content>
<div class="layout-row-wrap card-content">
<div class="flex-100" [ngClass]="transactionColor()"></div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Id' | translate }}
</div>
<div class="flex-50">
{{ transactionData.id }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Transaction Type' | translate }}
</div>
<div class="flex-50">
{{ transactionData.transactionType.value | translateKey: 'catalogs' }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Transaction Date' | translate }}
</div>
<div class="flex-50">
{{ transactionData.date | dateFormat }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Currency' | translate }}
</div>
<div class="flex-50">
{{ transactionData.currency.displayLabel }}
</div>
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Amount' | translate }}
</div>
<div class="flex-50">
{{ transactionData.amount | currency: transactionData.currency.code : 'symbol-narrow' : '1.2-2' }}
</div>
@if (transactionData.note) {
<div class="flex-50 mat-body-strong">
{{ 'labels.inputs.Note' | translate }}
</div>
}
@if (transactionData.note) {
<div class="flex-50">
{{ transactionData.note }}
</div>
}
@if (transactionData.paymentDetailData) {
<mifosx-transaction-payment-detail [paymentDetailData]="transactionData.paymentDetailData">
</mifosx-transaction-payment-detail>
}
</div>
<div class="layout-row layout-align-center gap-2percent column-on-mobile">
<button type="button" mat-raised-button color="primary" [routerLink]="['../../']">
{{ 'labels.buttons.Back' | translate }}
</button>
</div>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,45 @@
/**
* 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/.
*/
@use 'assets/styles/helper';
@use 'assets/styles/colours' as *;
.container {
max-width: 37rem;
.card-content {
div {
margin: 1rem 0;
word-wrap: break-word;
}
mifosx-transaction-payment-detail {
width: 100%;
margin-bottom: 20px;
}
}
}
.transaction-buttons {
margin-top: 3%;
}
.active {
background-color: $status-active;
height: 4px;
}
.linked {
background-color: $status-approved;
height: 4px;
}
.undo {
background-color: $status-active-overdue;
height: 4px;
}
@@ -0,0 +1,106 @@
/**
* 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, inject } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { Dates } from 'app/core/utils/dates';
import { ReleaseAmountDialogComponent } from 'app/current-accounts/current-account-view/custom-dialogs/release-amount-dialog/release-amount-dialog.component';
import { UndoTransactionDialogComponent } from 'app/current-accounts/current-account-view/custom-dialogs/undo-transaction-dialog/undo-transaction-dialog.component';
import { CurrentAccountsService } from 'app/current-accounts/current-accounts.service';
import { SettingsService } from 'app/settings/settings.service';
import { NgClass, CurrencyPipe } from '@angular/common';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { TransactionPaymentDetailComponent } from '../../../../../shared/transaction-payment-detail/transaction-payment-detail.component';
import { DateFormatPipe } from '../../../../../pipes/date-format.pipe';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-current-account-transaction-general-tab',
templateUrl: './current-accounts-transaction-general-tab.component.html',
styleUrls: ['./current-accounts-transaction-general-tab.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
FaIconComponent,
NgClass,
TransactionPaymentDetailComponent,
CurrencyPipe,
DateFormatPipe
]
})
export class CurrentAccountTransactionGeneralTabComponent {
private currentAccountsService = inject(CurrentAccountsService);
private route = inject(ActivatedRoute);
private dateUtils = inject(Dates);
private router = inject(Router);
dialog = inject(MatDialog);
private settingsService = inject(SettingsService);
accountId: string;
transactionId: string;
transactionData: any;
constructor() {
this.route.data.subscribe((data: { currentAccountTransaction: any }) => {
this.accountId = this.route.parent.snapshot.params['currentAccountId'];
this.transactionData = data.currentAccountTransaction;
});
}
allowUndo(): boolean {
if (this.transactionData.reversed && this.transactionData.transactionType.amountHold) {
return false;
}
return !this.transactionData.reversed;
}
releaseAmount(): void {
const releaseAmountDialogRef = this.dialog.open(ReleaseAmountDialogComponent);
releaseAmountDialogRef.afterClosed().subscribe((response: any) => {
if (response.confirm) {
const data = {};
this.currentAccountsService
.executeCurrentAccountTransactionsCommand(this.accountId, 'releaseAmount', data, this.transactionData.id)
.subscribe(() => {
this.router.navigate(['../..'], { relativeTo: this.route });
});
}
});
}
undoTransaction(): void {
const undoTransactionAccountDialogRef = this.dialog.open(UndoTransactionDialogComponent);
undoTransactionAccountDialogRef.afterClosed().subscribe((response: any) => {
if (response.confirm) {
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const data = {
transactionDate: this.dateUtils.formatDate(
this.transactionData.date && new Date(this.transactionData.date),
dateFormat
),
transactionAmount: 0,
dateFormat,
locale
};
this.currentAccountsService
.executeCurrentAccountTransactionsCommand(this.accountId, 'undo', data, this.transactionData.id)
.subscribe(() => {
this.router.navigate(['../..'], { relativeTo: this.route });
});
}
});
}
transactionColor(): string {
if (this.transactionData.reversed) {
return 'undo';
}
return 'active';
}
}
@@ -0,0 +1,17 @@
<!--
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="tab-container mat-typography">
<mifosx-entity-datatable-tab
entityType="Current Account Transaction"
[entityId]="entityId"
[multiRowDatatableFlag]="multiRowDatatableFlag"
[entityDatatable]="entityDatatable"
>
</mifosx-entity-datatable-tab>
</div>
@@ -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/.
*/
@@ -0,0 +1,39 @@
/**
* 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, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { EntityDatatableTabComponent } from '../../../../../shared/tabs/entity-datatable-tab/entity-datatable-tab.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-datatable-transaction-tab',
templateUrl: './datatable-transaction-tab.component.html',
styleUrls: ['./datatable-transaction-tab.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
EntityDatatableTabComponent
]
})
export class DatatableTransactionTabComponent {
private route = inject(ActivatedRoute);
/** Transaction Id */
entityId: string;
/** Current Account Datatable */
entityDatatable: any;
/** Multi Row Datatable Flag */
multiRowDatatableFlag: boolean;
constructor() {
this.entityId = this.route.parent.parent.snapshot.paramMap.get('id');
this.route.data.subscribe((data: { transactionDatatable: any }) => {
this.entityDatatable = data.transactionDatatable;
this.multiRowDatatableFlag = this.entityDatatable.columnHeaders[0].columnName === 'id' ? true : false;
});
}
}
@@ -0,0 +1,37 @@
<!--
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="container">
<nav mat-tab-nav-bar class="navigation-tabs" [tabPanel]="tabPanel">
<a
mat-tab-link
[routerLink]="['./general']"
routerLinkActive
#general="routerLinkActive"
[active]="general.isActive"
>
{{ 'labels.heading.General' | translate }}
</a>
@for (entityDatatable of entityDatatables; track entityDatatable) {
<a
mat-tab-link
[routerLink]="['./datatables', entityDatatable.registeredTableName]"
routerLinkActive
#datatable="routerLinkActive"
[active]="datatable.isActive"
*mifosxHasPermission="'READ_' + entityDatatable.registeredTableName"
>
{{ entityDatatable.registeredTableName }}
</a>
}
</nav>
<mat-tab-nav-panel #tabPanel>
<router-outlet></router-outlet>
</mat-tab-nav-panel>
</div>
@@ -0,0 +1,18 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
.container {
max-width: 37rem;
.content {
div {
margin: 1rem 0;
word-wrap: break-word;
}
}
}
@@ -0,0 +1,50 @@
/**
* 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, inject } from '@angular/core';
import { ActivatedRoute, RouterLinkActive, RouterLink, RouterOutlet } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { MatTabNav, MatTabLink, MatTabNavPanel } from '@angular/material/tabs';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-view-transaction',
templateUrl: './view-transaction.component.html',
styleUrls: ['./view-transaction.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatTabNav,
MatTabLink,
RouterLinkActive,
MatTabNavPanel,
RouterOutlet
]
})
export class ViewTransactionComponent {
private route = inject(ActivatedRoute);
dialog = inject(MatDialog);
/** Transaction data. */
transactionData: any;
accountId: any;
/** Transaction Data Tables */
entityDatatables: any;
/**
* @param {Router} router Router for navigation.
* @param {MatDialog} dialog Dialog reference.
*/
constructor() {
this.route.data.subscribe((data: { transactionDatatables: any }) => {
this.accountId = this.route.snapshot.params['currentAccountId'];
this.entityDatatables = data.transactionDatatables;
});
}
}