Asset Externalization Owner management for Loans (#1771)

Co-authored-by: Jose Alberto Hernandez <alberto@blue-daemon.local>
This commit is contained in:
José Alberto Hernández
2023-06-21 12:17:47 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 1b60a8f43c
commit bddf46cccd
36 changed files with 704 additions and 126 deletions
@@ -0,0 +1,101 @@
<div fxLayout="row" fxLayoutAlign="end" class="container m-b-20" *ngIf="dataSource?.data[0].manualEntry">
<button mat-raised-button color="primary" (click)="revertTransaction()" *mifosxHasPermission="'REVERSE_JOURNALENTRY'" [disabled]="dataSource?.data[0].reversed">
<span *ngIf="!dataSource.data[0].reversed">
<fa-icon icon="undo" class="m-r-10"></fa-icon>Revert Transaction
</span>
<span *ngIf="dataSource.data[0].reversed">Transaction Reverted</span>
</button>
</div>
<div class="container m-b-20" *ngIf="isViewTransaction()">
<mat-card class="mat-elevation-z8" *ngIf="dataSource">
<mat-card-content>
<div fxLayout="row wrap" class="content">
<div fxFlex="25%" fxFlex.lt-md="50%" class="header">
Office
</div>
<div fxFlex="25%" fxFlex.lt-md="50%">
{{ dataSource.data[0].officeName }}
</div>
<div fxFlex="25%" fxFlex.lt-md="50%" class="header">
Transaction Date
</div>
<div fxFlex="25%" fxFlex.lt-md="50%">
{{ dataSource.data[0].transactionDate | dateFormat }}
</div>
<div fxFlex="25%" fxFlex.lt-md="50%" class="header">
Created By
</div>
<div fxFlex="25%" fxFlex.lt-md="50%">
{{ dataSource.data[0].createdByUserName }}
</div>
<div fxFlex="25%" fxFlex.lt-md="50%" class="header">
Submitted on
</div>
<div fxFlex="25%" fxFlex.lt-md="50%">
{{ dataSource.data[0].submittedOnDate | datetimeFormat }}
</div>
</div>
</mat-card-content>
</mat-card>
</div>
<div class="mat-elevation-z8 container" fxLayoutAlign="center" fxLayout="row wrap">
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="id">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Entry ID </th>
<td mat-cell *matCellDef="let transaction"> {{ transaction.id }} </td>
</ng-container>
<ng-container matColumnDef="glAccountType">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Type </th>
<td mat-cell *matCellDef="let transaction"> {{ transaction.glAccountType.value }} </td>
</ng-container>
<ng-container matColumnDef="glAccountCode">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Account Code </th>
<td mat-cell *matCellDef="let transaction"> {{ transaction.glAccountCode }} </td>
</ng-container>
<ng-container matColumnDef="glAccountName">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Account Name </th>
<td mat-cell *matCellDef="let transaction"> {{ transaction.glAccountName }} </td>
</ng-container>
<ng-container matColumnDef="debit">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Debit </th>
<td mat-cell *matCellDef="let transaction"><span *ngIf="transaction.entryType.value === 'DEBIT'"> {{ (transaction.currency.displaySymbol || transaction.currency.code) }} {{ transaction.amount | formatNumber }} </span></td>
</ng-container>
<ng-container matColumnDef="credit">
<th mat-header-cell class="center" *matHeaderCellDef mat-sort-header> Credit </th>
<td mat-cell *matCellDef="let transaction"><span *ngIf="transaction.entryType.value === 'CREDIT'"> {{ (transaction.currency.displaySymbol || transaction.currency.code) }} {{ transaction.amount | formatNumber }} </span></td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="viewJournalEntry(row)" class="select-row"></tr>
</table>
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px" class="m-t-15 m-b-15">
<button type="button" color="primary" mat-raised-button (click)="goBack()">Back</button>
</div>
</div>
@@ -0,0 +1,7 @@
table {
width: 100%;
.select-row:hover {
cursor: pointer;
}
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewJournalEntryTransactionComponent } from './view-journal-entry-transaction.component';
describe('ViewJournalEntryTransactionComponent', () => {
let component: ViewJournalEntryTransactionComponent;
let fixture: ComponentFixture<ViewJournalEntryTransactionComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ViewJournalEntryTransactionComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ViewJournalEntryTransactionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,125 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ViewJournalEntryComponent } from '../view-journal-entry/view-journal-entry.component';
import { RevertTransactionComponent } from 'app/accounting/revert-transaction/revert-transaction.component';
import { AccountingService } from 'app/accounting/accounting.service';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { MatSort } from '@angular/material/sort';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { Location } from '@angular/common';
@Component({
selector: 'mifosx-view-journal-entry-transaction',
templateUrl: './view-journal-entry-transaction.component.html',
styleUrls: ['./view-journal-entry-transaction.component.scss']
})
export class ViewJournalEntryTransactionComponent implements OnInit {
title: string;
journalEntriesData: any[];
/** Transaction data. */
transaction: any;
/** Transaction ID. */
transactionId: string;
/** Columns to be displayed in transaction table. */
displayedColumns: string[] = ['id', 'glAccountType', 'glAccountCode', 'glAccountName', 'debit', 'credit'];
/** Data source for transaction table. */
dataSource: MatTableDataSource<any>;
/** Paginator for transaction table. */
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
/** Sorter for transaction table. */
@ViewChild(MatSort, { static: true }) sort: MatSort;
/**
* @param {AccountingService} accountingService Accounting Service.
* @param {ActivatedRoute} route Activated Route.
* @param {Router} router Router for navigation.
* @param {MatDialog} dialog Dialog reference.
*/
constructor(private accountingService: AccountingService,
private route: ActivatedRoute,
private router: Router,
public dialog: MatDialog,
private location: Location) { }
/**
* Retrieves the transaction data from `resolve` and sets the transaction table.
*/
ngOnInit() {
this.route.data.subscribe((data: { title: string, transaction: any, transferJournalEntryData: any }) => {
this.title = data.title;
if (this.isViewTransaction()) {
this.transaction = data.transaction;
this.transactionId = data.transaction.pageItems[0].transactionId;
} else if (this.isViewTransfer()) {
this.journalEntriesData = data.transferJournalEntryData.journalEntryData.content;
}
this.setTransaction();
});
}
isViewTransaction(): boolean {
return (this.title === 'View Transaction');
}
isViewTransfer(): boolean {
return (this.title === 'View Transfer');
}
/**
* Initializes the data source for transaction table with journal entries, paginator and sorter.
*/
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;
}
/**
* View details of selected journal entry.
* @param {any} journalEntry Selected journal entry.
*/
viewJournalEntry(journalEntry: any) {
this.dialog.open(ViewJournalEntryComponent, {
data: { journalEntry: journalEntry }
});
}
/**
* Reverts the given transaction and redirects to reverted transaction.
* @param {transactionId} transactionId Transaction ID of transaction to be reverted.
*/
revertTransaction(transactionId?: string) {
const revertTransactionDialogRef = this.dialog.open(RevertTransactionComponent, {
data: { reverted: this.dataSource.data[0].reversed, transactionId: transactionId }
});
revertTransactionDialogRef.afterClosed().subscribe((response: any) => {
if (response.revert) {
this.accountingService.revertTransaction(this.transactionId, response.comments).subscribe((reversedTransaction: any) => {
this.dataSource.data[0].reversed = true;
this.revertTransaction(reversedTransaction.transactionId);
});
} else if (response.redirect) {
this.router.navigate(['../', transactionId], { relativeTo: this.route });
}
});
}
goBack(): void {
this.location.back();
}
}
@@ -0,0 +1,117 @@
<h1 mat-dialog-title>View Journal Entry</h1>
<div mat-dialog-content>
<table>
<tr>
<th>Office</th>
<td>{{ data.journalEntry.officeName }}</td>
</tr>
<tr>
<th>Entry ID</th>
<td>{{ data.journalEntry.id }}</td>
</tr>
<tr>
<th>Transaction ID</th>
<td>{{ data.journalEntry.transactionId }}</td>
</tr>
<tr>
<th>Transaction Date</th>
<td>{{ data.journalEntry.transactionDate | dateFormat }}</td>
</tr>
<tr>
<th>Type</th>
<td>{{ data.journalEntry.glAccountType.value }}</td>
</tr>
<tr>
<th>Account Code</th>
<td>{{ data.journalEntry.glAccountId }}</td>
</tr>
<tr>
<th>Account Name</th>
<td>{{ data.journalEntry.glAccountName }}</td>
</tr>
<tr>
<th>
<span *ngIf="data.journalEntry.entryType.value === 'CREDIT'">Credit</span>
<span *ngIf="data.journalEntry.entryType.value === 'DEBIT'">Debit</span>
</th>
<td>
{{ data.journalEntry.currency.displaySymbol }} {{ data.journalEntry.amount | formatNumber }}
</td>
</tr>
<tr>
<th>Currency</th>
<td>({{ data.journalEntry.currency.code }}) {{ data.journalEntry.currency.name }}</td>
</tr>
<tr *ngIf="data.journalEntry.referenceNumber">
<th>Reference Number</th>
<td>{{ data.journalEntry.referenceNumber }}</td>
</tr>
<tr *ngIf="data.journalEntry.comments">
<th>Comments</th>
<td>{{ data.journalEntry.comments }}</td>
</tr>
<tr *ngIf="existsPaymentDetails">
<th colspan="2" class="header">Payment Details</th>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.paymentType">
<th>Payment Type</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.paymentType.name }}</td>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.accountNumber">
<th>Account Number</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.accountNumber }}</td>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.checkNumber">
<th>Cheque Number</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.checkNumber }}</td>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.routingCode">
<th>Routing Code</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.routingCode }}</td>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.receiptNumber">
<th>Receipt Number</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.receiptNumber }}</td>
</tr>
<tr *ngIf="existsPaymentDetails && data.journalEntry.transactionDetails.paymentDetails?.bankNumber">
<th>Bank Number</th>
<td>{{ data.journalEntry.transactionDetails.paymentDetails.bankNumber }}</td>
</tr>
<tr *ngIf="existsPaymentDetails">
<th>Created by</th>
<td>{{ data.journalEntry.createdByUserName }}</td>
</tr>
<tr>
<th>Submitted on</th>
<td>{{ data.journalEntry.submittedOnDate | datetimeFormat }}</td>
</tr>
</table>
</div>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>Close</button>
</mat-dialog-actions>
@@ -0,0 +1,16 @@
table {
th {
text-align: left;
font-weight: 500;
padding: 0 0.4rem 0 0;
&.header {
text-align: center;
padding: 0.4rem 0.4rem 0 0;
}
}
td {
padding: 0 0.4rem 0 0;
}
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewJournalEntryComponent } from './view-journal-entry.component';
describe('ViewJournalEntryComponent', () => {
let component: ViewJournalEntryComponent;
let fixture: ComponentFixture<ViewJournalEntryComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ViewJournalEntryComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ViewJournalEntryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,29 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* View journal entry dialog component.
*/
@Component({
selector: 'mifosx-view-journal-entry',
templateUrl: './view-journal-entry.component.html',
styleUrls: ['./view-journal-entry.component.scss']
})
export class ViewJournalEntryComponent implements OnInit {
existsPaymentDetails = false;
/**
* @param {MatDialogRef} dialogRef Component reference to dialog.
* @param {any} data Provides journal entry.
*/
constructor(public dialogRef: MatDialogRef<ViewJournalEntryComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.existsPaymentDetails = (data.journalEntry.transactionDetails != null && data.journalEntry.transactionDetails.paymentDetails != null);
}
ngOnInit() {
}
}
@@ -1,6 +1,7 @@
<h1 mat-dialog-title>Cancel</h1>
<div mat-dialog-content>
<p>Are you sure you want to leave this page and lose all inputted data?</p>
<p *ngIf="!data.cancelContext">Are you sure you want to leave this page and lose all inputted data?</p>
<p *ngIf="data.cancelContext">Are you sure you want to cancel {{ data.cancelContext }} ?</p>
</div>
<mat-dialog-actions align="end">
<button mat-raised-button mat-dialog-close>No</button>
@@ -1,6 +1,6 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
/**
* Cancel dialog component.
@@ -15,7 +15,8 @@ export class CancelDialogComponent implements OnInit {
/**
* @param {MatDialogRef} dialogRef Component reference to dialog.
*/
constructor(public dialogRef: MatDialogRef<CancelDialogComponent>) { }
constructor(public dialogRef: MatDialogRef<CancelDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
}
+7 -1
View File
@@ -37,6 +37,8 @@ import { DatatableSingleRowComponent } from './tabs/entity-datatable-tab/datatab
import { DatatableMultiRowComponent } from './tabs/entity-datatable-tab/datatable-multi-row/datatable-multi-row.component';
import { SvgIconComponent } from './svg-icon/svg-icon.component';
import { NgxMatDatetimePickerModule, NgxMatNativeDateModule } from '@angular-material-components/datetime-picker';
import { ViewJournalEntryComponent } from './accounting/view-journal-entry/view-journal-entry.component';
import { ViewJournalEntryTransactionComponent } from './accounting/view-journal-entry-transaction/view-journal-entry-transaction.component';
/**
* Shared Module
@@ -80,7 +82,9 @@ import { NgxMatDatetimePickerModule, NgxMatNativeDateModule } from '@angular-mat
EntityDatatableTabComponent,
DatatableSingleRowComponent,
DatatableMultiRowComponent,
SvgIconComponent
SvgIconComponent,
ViewJournalEntryComponent,
ViewJournalEntryTransactionComponent
],
exports: [
FileUploadComponent,
@@ -102,6 +106,8 @@ import { NgxMatDatetimePickerModule, NgxMatNativeDateModule } from '@angular-mat
EntityNotesTabComponent,
EntityDocumentsTabComponent,
EntityDatatableTabComponent,
ViewJournalEntryComponent,
ViewJournalEntryTransactionComponent,
SvgIconComponent
]
})