Removes the Undo button and Undo menu item from savings, current, fixed deposit, recurring deposit and loan transactions, the account-transfer Undo, and the Revert Transaction button on the journal entry transaction detail, to prevent accidental reversal of posted entries. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
177 lines
5.2 KiB
TypeScript
177 lines
5.2 KiB
TypeScript
/**
|
|
* 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, ViewChild, inject } from '@angular/core';
|
|
import { ViewJournalEntryComponent } from '../view-journal-entry/view-journal-entry.component';
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { MatSort, MatSortHeader } from '@angular/material/sort';
|
|
import { MatPaginator } from '@angular/material/paginator';
|
|
import {
|
|
MatTableDataSource,
|
|
MatTable,
|
|
MatColumnDef,
|
|
MatHeaderCellDef,
|
|
MatHeaderCell,
|
|
MatCellDef,
|
|
MatCell,
|
|
MatHeaderRowDef,
|
|
MatHeaderRow,
|
|
MatRowDef,
|
|
MatRow
|
|
} from '@angular/material/table';
|
|
import { Location, NgClass } from '@angular/common';
|
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
|
import { DatetimeFormatPipe } from '../../../pipes/datetime-format.pipe';
|
|
import { FormatNumberPipe } from '../../../pipes/format-number.pipe';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
import { YesnoPipe } from '@pipes/yesno.pipe';
|
|
import { MatCardModule } from '@angular/material/card';
|
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
|
|
@Component({
|
|
selector: 'mifosx-view-journal-entry-transaction',
|
|
templateUrl: './view-journal-entry-transaction.component.html',
|
|
styleUrls: ['./view-journal-entry-transaction.component.scss'],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
FaIconComponent,
|
|
MatTable,
|
|
MatSort,
|
|
MatColumnDef,
|
|
MatHeaderCellDef,
|
|
MatHeaderCell,
|
|
MatSortHeader,
|
|
MatCellDef,
|
|
MatCell,
|
|
MatHeaderRowDef,
|
|
MatHeaderRow,
|
|
MatRowDef,
|
|
MatRow,
|
|
NgClass,
|
|
DateFormatPipe,
|
|
DatetimeFormatPipe,
|
|
FormatNumberPipe,
|
|
MatCardModule,
|
|
MatTooltipModule
|
|
]
|
|
})
|
|
export class ViewJournalEntryTransactionComponent implements OnInit {
|
|
private route = inject(ActivatedRoute);
|
|
dialog = inject(MatDialog);
|
|
private location = inject(Location);
|
|
|
|
title: string;
|
|
journalEntriesData: any[];
|
|
transaction: any;
|
|
transactionId: string;
|
|
|
|
displayedColumns: string[] = [
|
|
'id',
|
|
'glAccountType',
|
|
'glAccountCode',
|
|
'glAccountName',
|
|
'savingsAccountNo',
|
|
'entryType',
|
|
'debit',
|
|
'credit'
|
|
];
|
|
|
|
dataSource: MatTableDataSource<any>;
|
|
|
|
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
|
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
|
|
|
isJournalEntryLoaded = false;
|
|
isManualJournalEntry = false;
|
|
|
|
ngOnInit() {
|
|
this.route.data.subscribe((data: { title: string; transaction: any; transferJournalEntryData: any }) => {
|
|
this.title = data.title;
|
|
this.isJournalEntryLoaded = false;
|
|
if (this.isViewTransaction()) {
|
|
this.transaction = data.transaction;
|
|
if (data.transaction.pageItems.length > 0) {
|
|
this.isJournalEntryLoaded = true;
|
|
this.transactionId = data.transaction.pageItems[0].transactionId;
|
|
this.isManualJournalEntry = data.transaction.pageItems[0].manualEntry;
|
|
}
|
|
} else if (this.isViewTransfer()) {
|
|
this.journalEntriesData = data.transferJournalEntryData.journalEntryData.content;
|
|
this.isJournalEntryLoaded = true;
|
|
}
|
|
this.setTransaction();
|
|
});
|
|
}
|
|
|
|
isViewTransaction(): boolean {
|
|
return this.title === 'View Transaction';
|
|
}
|
|
isViewTransfer(): boolean {
|
|
return this.title === 'View Transfer';
|
|
}
|
|
|
|
setTransaction() {
|
|
if (this.journalEntriesData != null) {
|
|
this.dataSource = new MatTableDataSource(this.journalEntriesData);
|
|
} else {
|
|
this.dataSource = new MatTableDataSource(this.transaction.pageItems);
|
|
this.dataSource.sortingDataAccessor = (transaction: any, property: any) => {
|
|
switch (property) {
|
|
case 'glAccountType':
|
|
return transaction.glAccountType.value;
|
|
case 'debit':
|
|
return transaction.amount;
|
|
case 'credit':
|
|
return transaction.amount;
|
|
default:
|
|
return transaction[property];
|
|
}
|
|
};
|
|
}
|
|
this.dataSource.paginator = this.paginator;
|
|
this.dataSource.sort = this.sort;
|
|
}
|
|
|
|
viewJournalEntry(journalEntry: any) {
|
|
this.dialog.open(ViewJournalEntryComponent, { data: { journalEntry } });
|
|
}
|
|
|
|
goBack(): void {
|
|
this.location.back();
|
|
}
|
|
|
|
getTotalDebit(): number {
|
|
return (this.dataSource?.data || [])
|
|
.filter((e) => e.entryType?.value === 'DEBIT')
|
|
.reduce((sum, e) => sum + (e.amount || 0), 0);
|
|
}
|
|
|
|
getTotalCredit(): number {
|
|
return (this.dataSource?.data || [])
|
|
.filter((e) => e.entryType?.value === 'CREDIT')
|
|
.reduce((sum, e) => sum + (e.amount || 0), 0);
|
|
}
|
|
|
|
isBalanced(): boolean {
|
|
return Math.abs(this.getTotalDebit() - this.getTotalCredit()) < 0.01;
|
|
}
|
|
|
|
getAccountTypeBadgeClass(type: string): string {
|
|
const map: Record<string, string> = {
|
|
ASSET: 'txn-badge--asset',
|
|
LIABILITY: 'txn-badge--liability',
|
|
INCOME: 'txn-badge--income',
|
|
EXPENSE: 'txn-badge--expense',
|
|
EQUITY: 'txn-badge--equity'
|
|
};
|
|
return map[type] || 'txn-badge--default';
|
|
}
|
|
}
|