feat: add view savings account page (#862)
- Transactions Tab - Charges Tab - Standing Instructions Tab Fixes: #221
This commit is contained in:
@@ -49,11 +49,6 @@ const routes: Routes = [
|
||||
path: '',
|
||||
component: ClientsComponent,
|
||||
},
|
||||
{
|
||||
path: ':clientId/savingsaccounts',
|
||||
data: { routeParamBreadcrumb: 'clientId'},
|
||||
loadChildren: '../savings/savings.module#SavingsModule'
|
||||
},
|
||||
{
|
||||
path: ':clientId',
|
||||
component: ClientsViewComponent,
|
||||
@@ -177,7 +172,11 @@ const routes: Routes = [
|
||||
path: 'loans',
|
||||
data: { title: extract('Loans'), breadcrumb: 'loans', routeParamBreadcrumb: false },
|
||||
loadChildren: '../loans/loans.module#LoansModule'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'savingsaccounts',
|
||||
loadChildren: '../savings/savings.module#SavingsModule'
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -243,7 +243,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="openSavingsColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: openSavingsColumns;"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: openSavingsColumns;" [routerLink]="['../', 'savingsaccounts', row.id, 'transactions']"></tr>
|
||||
</table>
|
||||
|
||||
<!-- Closed Saving Accounts -->
|
||||
|
||||
@@ -47,48 +47,57 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: ':groupId',
|
||||
component: GroupsViewComponent,
|
||||
data: { title: extract('View Group'), routeParamBreadcrumb: 'groupId' },
|
||||
resolve: {
|
||||
groupViewData: GroupViewResolver,
|
||||
groupDatatables: GroupDatatablesResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'general',
|
||||
component: GeneralTabComponent,
|
||||
data: { title: extract('General'), breadcrumb: 'General', routeParamBreadcrumb: false },
|
||||
path: '',
|
||||
component: GroupsViewComponent,
|
||||
resolve: {
|
||||
groupAccountsData: GroupAccountsResolver,
|
||||
groupSummary: GroupSummaryResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'notes',
|
||||
component: NotesTabComponent,
|
||||
data: { title: extract('Notes'), breadcrumb: 'Notes', routeParamBreadcrumb: false },
|
||||
resolve: {
|
||||
groupNotes: GroupNotesResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'committee',
|
||||
component: CommitteeTabComponent,
|
||||
data: { title: extract('Committee'), breadcrumb: 'Committee', routeParamBreadcrumb: false }
|
||||
},
|
||||
{
|
||||
path: 'datatables',
|
||||
groupViewData: GroupViewResolver,
|
||||
groupDatatables: GroupDatatablesResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':datatableName',
|
||||
component: DatatableTabsComponent,
|
||||
data: { title: extract('View Data Table'), routeParamBreadcrumb: 'datatableName' },
|
||||
path: 'general',
|
||||
component: GeneralTabComponent,
|
||||
data: { title: extract('General'), breadcrumb: 'General', routeParamBreadcrumb: false },
|
||||
resolve: {
|
||||
groupDatatable: GroupDatatableResolver
|
||||
groupAccountsData: GroupAccountsResolver,
|
||||
groupSummary: GroupSummaryResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'notes',
|
||||
component: NotesTabComponent,
|
||||
data: { title: extract('Notes'), breadcrumb: 'Notes', routeParamBreadcrumb: false },
|
||||
resolve: {
|
||||
groupNotes: GroupNotesResolver
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'committee',
|
||||
component: CommitteeTabComponent,
|
||||
data: { title: extract('Committee'), breadcrumb: 'Committee', routeParamBreadcrumb: false }
|
||||
},
|
||||
{
|
||||
path: 'datatables',
|
||||
children: [
|
||||
{
|
||||
path: ':datatableName',
|
||||
component: DatatableTabsComponent,
|
||||
data: { title: extract('View Data Table'), routeParamBreadcrumb: 'datatableName' },
|
||||
resolve: {
|
||||
groupDatatable: GroupDatatableResolver
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'savingsaccounts',
|
||||
loadChildren: '../savings/savings.module#SavingsModule'
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -255,7 +255,7 @@
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="openSavingsColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: openSavingsColumns;"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: openSavingsColumns;" [routerLink]="['../', 'savingsaccounts', row.id, 'transactions']"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="profile-image-container">
|
||||
<div>
|
||||
<img mat-card-md-image class="profile-image"
|
||||
src="../../../assets/images/group_placeholder.png">
|
||||
src="assets/images/group_placeholder.png">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
background-color: #0a6aac;
|
||||
p {
|
||||
color: white;
|
||||
padding-inline: 10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
}
|
||||
}
|
||||
#timescale {
|
||||
margin-block: 2%;
|
||||
margin-inline: 35%;
|
||||
margin: 2% 35%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from '../savings.service';
|
||||
|
||||
/**
|
||||
* Savings Account data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SavingsAccountViewResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {SavingsService} SavingsService Savings service.
|
||||
*/
|
||||
constructor(private savingsService: SavingsService) { }
|
||||
|
||||
/**
|
||||
* Returns the Savings Account data.
|
||||
* @param {ActivatedRouteSnapshot} route Route Snapshot
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const savingAccountId = route.paramMap.get('savingAccountId');
|
||||
return this.savingsService.getSavingsAccountData(savingAccountId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<div class="tab-container mat-typography">
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start">
|
||||
<div class="m-b-10">
|
||||
<h3>All Charges</h3>
|
||||
</div>
|
||||
<div class="action-button m-b-20" *ngIf="this.chargesData">
|
||||
<button mat-raised-button color="primary" (click)="toggleCharges()">
|
||||
{{showInactiveCharges?'View Active Charges':'View Inactive Charges'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mat-elevation-z1 m-b-25">
|
||||
|
||||
<table mat-table #chargesTable [dataSource]="dataSource" >
|
||||
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{ charge.name }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="feeOrPenalty">
|
||||
<th mat-header-cell *matHeaderCellDef> Fee/Penalty </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{ charge.penalty === true ? 'Penalty' : 'Fee' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="paymentDueAt">
|
||||
<th mat-header-cell *matHeaderCellDef> Payment Due At </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{ charge.chargeTimeType.value }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="dueAsOf">
|
||||
<th mat-header-cell *matHeaderCellDef> Due As Of </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{ charge.dueDate | date }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="repeatsOn">
|
||||
<th mat-header-cell *matHeaderCellDef> Repeats On (M/d) </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{ charge.feeOnMonthDay ? charge.feeOnMonthDay.join('/') : '' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="calculationType">
|
||||
<th mat-header-cell *matHeaderCellDef> Calculation Type </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{charge.chargeCalculationType.value}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="due">
|
||||
<th mat-header-cell *matHeaderCellDef> Due </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{charge.currency.displaySymbol}} {{charge.amount}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="paid">
|
||||
<th mat-header-cell *matHeaderCellDef> Paid </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{charge.currency.displaySymbol}} {{charge.amountPaid}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="waived">
|
||||
<th mat-header-cell *matHeaderCellDef> Waived </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{charge.currency.displaySymbol}} {{charge.amountWaived}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="outstanding">
|
||||
<th mat-header-cell *matHeaderCellDef> Outstanding </th>
|
||||
<td mat-cell *matCellDef="let charge"> {{charge.currency.displaySymbol}} {{charge.amountOutstanding}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let charge">
|
||||
<span *ngIf="savingsAccountData.status.value === 'Submitted and pending approval'">
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="Edit Charge"
|
||||
*mifosxHasPermission="'UPDATE_SAVINGSACCOUNTCHARGE'">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</button>
|
||||
<button class="account-action-button" mat-raised-button color="warn" matTooltip="Delete Charge"
|
||||
*mifosxHasPermission="'DELETE_SAVINGSACCOUNTCHARGE'">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="savingsAccountData.status.value === 'Active' && charge.amountOutstanding !== 0">
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="Pay Charge"
|
||||
*mifosxHasPermission="'PAY_SAVINGSACCOUNTCHARGE'">
|
||||
<i class="fa fa-dollar"></i>
|
||||
</button>
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="Waive Charge"
|
||||
*mifosxHasPermission="'WAIVE_SAVINGSACCOUNTCHARGE'">
|
||||
<i class="fa fa-flag"></i>
|
||||
</button>
|
||||
<ng-container *ngIf="isRecurringCharge(charge)">
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="Inactivate Charge"
|
||||
*mifosxHasPermission="'INACTIVATE_SAVINGSACCOUNTCHARGE'">
|
||||
<i class="fa fa-ban"></i>
|
||||
</button>
|
||||
</ng-container>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
.tab-container {
|
||||
padding: 1%;
|
||||
margin: 1%;
|
||||
h3 {
|
||||
margin:1% auto;
|
||||
}
|
||||
.action-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
.account-action-button{
|
||||
min-width: 26px;
|
||||
padding: 0 6px;
|
||||
margin: 4px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ChargesTabComponent } from './charges-tab.component';
|
||||
|
||||
describe('ChargesTabComponent', () => {
|
||||
let component: ChargesTabComponent;
|
||||
let fixture: ComponentFixture<ChargesTabComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ChargesTabComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ChargesTabComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatTableDataSource, MatTable } from '@angular/material';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/**
|
||||
* Charges Tab Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-charges-tab',
|
||||
templateUrl: './charges-tab.component.html',
|
||||
styleUrls: ['./charges-tab.component.scss']
|
||||
})
|
||||
export class ChargesTabComponent implements OnInit {
|
||||
|
||||
/** Savings Account Data */
|
||||
savingsAccountData: any;
|
||||
/** Charges Data */
|
||||
chargesData: any[];
|
||||
/** Data source for charges table. */
|
||||
dataSource: MatTableDataSource<any>;
|
||||
/** Toggles Charges Table */
|
||||
showInactiveCharges = false;
|
||||
/** Columns to be displayed in charges table. */
|
||||
displayedColumns: string[] = [
|
||||
'name',
|
||||
'feeOrPenalty',
|
||||
'paymentDueAt',
|
||||
'dueAsOf',
|
||||
'repeatsOn',
|
||||
'calculationType',
|
||||
'due',
|
||||
'paid',
|
||||
'waived',
|
||||
'outstanding',
|
||||
'actions'
|
||||
];
|
||||
|
||||
/** Charges Table Reference */
|
||||
@ViewChild('chargesTable') chargesTableRef: MatTable<Element>;
|
||||
|
||||
/**
|
||||
* Retrieves Savings Account Data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute) {
|
||||
this.route.parent.data.subscribe((data: { savingsAccountData: any }) => {
|
||||
this.savingsAccountData = data.savingsAccountData;
|
||||
this.chargesData = this.savingsAccountData.charges;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const activeCharges = this.chargesData ? this.chargesData.filter(charge => charge.isActive) : [];
|
||||
this.dataSource = new MatTableDataSource(activeCharges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles datasource for active/inactive charges.
|
||||
*/
|
||||
toggleCharges() {
|
||||
this.showInactiveCharges = !this.showInactiveCharges;
|
||||
if (!this.showInactiveCharges) {
|
||||
const activeCharges = this.chargesData.filter(charge => charge.isActive);
|
||||
this.dataSource.data = activeCharges;
|
||||
} else {
|
||||
const inActiveCharges = this.chargesData.filter(charge => !charge.isActive);
|
||||
this.dataSource.data = inActiveCharges;
|
||||
}
|
||||
this.chargesTableRef.renderRows();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if charge is recurring.
|
||||
* @param {any} charge Charge
|
||||
*/
|
||||
isRecurringCharge(charge: any) {
|
||||
return charge.chargeTimeType.value === 'Monthly Fee' || charge.chargeTimeType.value === 'Annual Fee' || charge.chargeTimeType.value === 'Weekly Fee';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
@import "~@angular/material/theming";
|
||||
|
||||
@mixin savings-account-view-component-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
mifosx-savings-account-view {
|
||||
.savings-account-card {
|
||||
.header {
|
||||
background-color: mat-color($primary, 500);
|
||||
}
|
||||
.table-headers {
|
||||
background-color: mat-color($primary, 500);
|
||||
color: mat-contrast($primary, 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
<mat-card class="savings-account-card">
|
||||
|
||||
<mat-card-header fxLayout="column" class="header">
|
||||
|
||||
<mat-card-title-group class="header-title-group">
|
||||
|
||||
<div class="profile-image-container">
|
||||
<div>
|
||||
<img mat-card-md-image class="profile-image"
|
||||
matTooltip="Savings Account"
|
||||
src="assets/images/savings_account_placeholder.png">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mat-typography account-card-title">
|
||||
<mat-card-title>
|
||||
<h3>
|
||||
<i class="fa fa-stop" [ngClass]="savingsAccountData.status.code | statusLookup" [matTooltip]="savingsAccountData.status.value"></i>
|
||||
Account Name : {{savingsAccountData.savingsProductName}}
|
||||
</h3>
|
||||
</mat-card-title>
|
||||
<mat-card-subtitle>
|
||||
<p>
|
||||
Account #: {{savingsAccountData.accountNo}} | Client Name: {{savingsAccountData.clientName}}<br/>
|
||||
<span *ngIf="!savingsAccountData.status.rejected && !savingsAccountData.status.submittedAndPendingApproval">
|
||||
Current Balance: {{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.accountBalance}}<br/>
|
||||
Available Balance: {{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.availableBalance}}<br/>
|
||||
</span>
|
||||
</p>
|
||||
</mat-card-subtitle>
|
||||
</div>
|
||||
|
||||
</mat-card-title-group>
|
||||
|
||||
<mat-card-actions class="account-actions">
|
||||
|
||||
<ng-container *ngFor="let button of buttonConfig.singleButtons">
|
||||
<button mat-raised-button *mifosxHasPermission="button.taskPermissionName">
|
||||
<i class="{{button.icon}}"></i> {{button.name}}</button>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="buttonConfig.options">
|
||||
<button mat-raised-button [matMenuTriggerFor]="More">More</button>
|
||||
<mat-menu #More="matMenu">
|
||||
<span *ngFor="let button of buttonConfig.options">
|
||||
<button mat-menu-item *mifosxHasPermission="button.taskPermissionName">{{button.name}}</button>
|
||||
</span>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
|
||||
</mat-card-actions>
|
||||
|
||||
</mat-card-header>
|
||||
|
||||
<mat-card-content class="content">
|
||||
|
||||
<div class="savings-account-tables" fxLayout="row" fxLayoutGap="2%">
|
||||
|
||||
<div fxFlex="49%" *ngIf="!savingsAccountData.status.rejected && !savingsAccountData.status.submittedAndPendingApproval; else alternateSummary">
|
||||
<h4 class="table-headers">Account Summary</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr *ngIf="savingsAccountData.summary.totalWithdrawals">
|
||||
<td>Total Withdrawls</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalWithdrawals}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalWithdrawalFees">
|
||||
<td>Withdrawals Fees</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalWithdrawalFees}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalAnnualFees">
|
||||
<td>Annual Fees</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalAnnualFees}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalInterestEarned >= 0">
|
||||
<td>Interest Earned</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalInterestEarned}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalInterestPosted">
|
||||
<td>Interest Posted</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalInterestPosted}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.interestNotPosted >= 0">
|
||||
<td>Interest Earned Not Posted</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.interestNotPosted}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalOverdraftInterestDerived">
|
||||
<td>Interest On Overdraft</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalOverdraftInterestDerived}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.interestNotPosted < 0">
|
||||
<td>Overdraft Interest Not Posted</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.interestNotPosted}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nominal Interest Rate</td>
|
||||
<td>{{savingsAccountData.nominalAnnualInterestRate}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interest Compounding Period</td>
|
||||
<td>{{savingsAccountData.interestCompoundingPeriodType.value}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interest Posting Period</td>
|
||||
<td>{{savingsAccountData.interestPostingPeriodType.value}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Interest Calculated Using</td>
|
||||
<td>{{savingsAccountData.interestCalculationType.value}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td># Days in Year</td>
|
||||
<td>{{savingsAccountData.interestCalculationDaysInYearType.value}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.withdrawalFee">
|
||||
<td>Withdrawal Fee</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.withdrawalFee.amount}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.lastActiveTransactionDate">
|
||||
<td>Last Active Transaction Date</td>
|
||||
<td>{{savingsAccountData.lastActiveTransactionDate | date}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="!(savingsAccountData.subStatus.id === 0)">
|
||||
<td>Substatus</td>
|
||||
<td>{{savingsAccountData.subStatus.value}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.daysToInactive">
|
||||
<td>Days to Inactive</td>
|
||||
<td>{{savingsAccountData.daysToInactive}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.daysToDormancy">
|
||||
<td>Days to Dormancy</td>
|
||||
<td>{{savingsAccountData.daysToDormancy}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.daysToEscheat">
|
||||
<td>Days to Escheat</td>
|
||||
<td>{{savingsAccountData.daysToEscheat}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.annualFee">
|
||||
<td>Annual Fee</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.annualFee.amount}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.allowOverdraft">
|
||||
<td>Over Draft Limit</td>
|
||||
<td>{{savingsAccountData.overdraftLimit}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.allowOverdraft">
|
||||
<td>Min Overdraft Required for Interest Calculation</td>
|
||||
<td>{{savingsAccountData.minOverdraftForInterestCalculation}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.minBalanceForInterestCalculation">
|
||||
<td>Min Balance Required for Interest Calculation</td>
|
||||
<td>{{savingsAccountData.minBalanceForInterestCalculation}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.minRequiredBalance">
|
||||
<td>Minimum Required Balance</td>
|
||||
<td>{{savingsAccountData.minRequiredBalance}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.enforceMinRequiredBalance">
|
||||
<td>Enforce Minimum Required Balance</td>
|
||||
<td>{{savingsAccountData.enforceMinRequiredBalance}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.lastInterestCalculationDate">
|
||||
<td>Interest Recalculation Date</td>
|
||||
<td>{{savingsAccountData.summary.lastInterestCalculationDate | date}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.onHoldFunds">
|
||||
<td>On Hold Funds</td>
|
||||
<td><a *mifosxHasPermission="'READ_SAVINGSACCOUNT'">{{savingsAccountData.onHoldFunds}}</a></td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.withHoldTax">
|
||||
<td>Withhold Tax Group</td>
|
||||
<td>{{savingsAccountData.taxGroup.name}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<ng-template #alternateSummary>
|
||||
<div fxFlex="49%" >
|
||||
<h4 class="table-headers">Account Summary</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Activated On</td>
|
||||
<td>{{savingsAccountData.timeline.activatedOnDate ? (savingsAccountData.timeline.activatedOnDate | date) : 'Not Activated'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Currency</td>
|
||||
<td>{{savingsAccountData.currency.name}} [{{savingsAccountData.currency.code}}]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>External Id</td>
|
||||
<td>{{savingsAccountData.externalId ? savingsAccountData.externalId : 'Not Provided'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div fxFlex="49%" fxLayout="column" fxLayoutGap="4%">
|
||||
|
||||
<div *ngIf="!savingsAccountData.status.rejected && !savingsAccountData.status.submittedAndPendingApproval; else alternateDetails">
|
||||
<h4 class="table-headers">Savings Details</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Activated On</td>
|
||||
<td>{{savingsAccountData.timeline.activatedOnDate ? (savingsAccountData.timeline.activatedOnDate | date) : 'Not Activated'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Field Officer</td>
|
||||
<td>{{savingsAccountData.fieldOfficerName ? savingsAccountData.fieldOfficerName : 'Unassigned'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>External Id</td>
|
||||
<td>{{savingsAccountData.externalId ? savingsAccountData.externalId : 'Not Provided'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Currency</td>
|
||||
<td>{{savingsAccountData.currency.name}} [{{savingsAccountData.currency.code}}]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nominal Interest Rate</td>
|
||||
<td>{{savingsAccountData.nominalAnnualInterestRate}}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<ng-template #alternateDetails>
|
||||
<div>
|
||||
<h4 class="table-headers">Savings Details</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Field Officer</td>
|
||||
<td>{{savingsAccountData.fieldOfficerName ? savingsAccountData.fieldOfficerName : 'Unassigned'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Balance</td>
|
||||
<td>{{savingsAccountData.summary.accountBalance}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="!savingsAccountData.status.rejected && !savingsAccountData.status.submittedAndPendingApproval">
|
||||
<h4 class="table-headers">Performance History</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr *ngIf="savingsAccountData.summary.totalDeposits">
|
||||
<td>Total Deposits</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalDeposits}}</td>
|
||||
</tr>
|
||||
<tr *ngIf="savingsAccountData.summary.totalInterestEarned >= 0">
|
||||
<td>Total Interest Earned</td>
|
||||
<td>{{savingsAccountData.currency.displaySymbol}} {{savingsAccountData.summary.totalInterestEarned}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<nav mat-tab-nav-bar class="navigation-tabs">
|
||||
<a mat-tab-link [routerLink]="['./transactions']" routerLinkActive #transactions="routerLinkActive"
|
||||
[active]="transactions.isActive">
|
||||
Transactions
|
||||
</a>
|
||||
<a mat-tab-link [routerLink]="['./charges']" routerLinkActive #charges="routerLinkActive"
|
||||
[active]="charges.isActive">
|
||||
Charges
|
||||
</a>
|
||||
<ng-container *mifosxHasPermission="'READ_SAVINGSACCOUNTCHARGE'">
|
||||
<a mat-tab-link [routerLink]="['./standing-instructions']" routerLinkActive #standingInstructions="routerLinkActive"
|
||||
[active]="standingInstructions.isActive" *ngIf="savingsAccountData.clientId">
|
||||
Standing Instructions
|
||||
</a>
|
||||
</ng-container>
|
||||
</nav>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
</mat-card>
|
||||
@@ -0,0 +1,53 @@
|
||||
.savings-account-card {
|
||||
margin: 0 auto;
|
||||
max-width: 80rem;
|
||||
width: 90%;
|
||||
padding: 0;
|
||||
.header {
|
||||
padding: 1%;
|
||||
.header-title-group {
|
||||
.account-card-title {
|
||||
color: white;
|
||||
width: 90%;
|
||||
p {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
.profile-image-container {
|
||||
margin: 1%;
|
||||
.profile-image {
|
||||
object-fit: cover;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
.account-actions {
|
||||
align-self: flex-end;
|
||||
margin: 0 1%;
|
||||
i {
|
||||
margin-bottom: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigation-tabs {
|
||||
background-color: #f2f2f2;
|
||||
overflow: auto;
|
||||
}
|
||||
.content {
|
||||
.savings-account-tables {
|
||||
padding: 1%;
|
||||
margin: 1%;
|
||||
.table-headers {
|
||||
margin: 0;
|
||||
padding: 6px;
|
||||
}
|
||||
td {
|
||||
padding: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
i, img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SavingsAccountViewComponent } from './savings-account-view.component';
|
||||
|
||||
describe('SavingsAccountViewComponent', () => {
|
||||
let component: SavingsAccountViewComponent;
|
||||
let fixture: ComponentFixture<SavingsAccountViewComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SavingsAccountViewComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SavingsAccountViewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { SavingsButtonsConfiguration } from './savings-buttons.config';
|
||||
|
||||
/**
|
||||
* Savings Account View Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-view',
|
||||
templateUrl: './savings-account-view.component.html',
|
||||
styleUrls: ['./savings-account-view.component.scss']
|
||||
})
|
||||
export class SavingsAccountViewComponent implements OnInit {
|
||||
|
||||
/** Savings Account Data */
|
||||
savingsAccountData: any;
|
||||
/** Button Configurations */
|
||||
buttonConfig: SavingsButtonsConfiguration;
|
||||
|
||||
/**
|
||||
* Fetches savings account data from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
*/
|
||||
constructor(private route: ActivatedRoute) {
|
||||
this.route.data.subscribe((data: { savingsAccountData: any }) => {
|
||||
this.savingsAccountData = data.savingsAccountData;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setConditionalButtons();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options to button config. conditionaly.
|
||||
*/
|
||||
setConditionalButtons() {
|
||||
const status = this.savingsAccountData.status.value;
|
||||
this.buttonConfig = new SavingsButtonsConfiguration(status);
|
||||
if (this.savingsAccountData.clientId) {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Transfer Funds',
|
||||
taskPermissionName: 'CREATE_ACCOUNTTRANSFER'
|
||||
});
|
||||
}
|
||||
if (!this.savingsAccountData.fieldOfficerId) {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Assign Staff',
|
||||
taskPermissionName: 'UPDATESAVINGSOFFICER_SAVINGSACCOUNT'
|
||||
});
|
||||
} else {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Unassign Staff',
|
||||
taskPermissionName: 'REMOVESAVINGSOFFICER_SAVINGSACCOUNT'
|
||||
});
|
||||
}
|
||||
if (this.savingsAccountData.charges) {
|
||||
const charges: any[] = this.savingsAccountData.charges;
|
||||
charges.forEach((charge: any) => {
|
||||
if (charge.name === 'Annual fee - INR') {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Apply Anuual Fees',
|
||||
taskPermissionName: 'APPLYANNUALFEE_SAVINGSACCOUNT'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.savingsAccountData.taxGroup) {
|
||||
if (this.savingsAccountData.withHoldTax) {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Disable Withhold Tax',
|
||||
taskPermissionName: 'UPDATEWITHHOLDTAX_SAVINGSACCOUNT'
|
||||
});
|
||||
} else {
|
||||
this.buttonConfig.addOption({
|
||||
name: 'Enable Withhold Tax',
|
||||
taskPermissionName: 'UPDATEWITHHOLDTAX_SAVINGSACCOUNT'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/** Savings Account Buttons Configuration */
|
||||
export class SavingsButtonsConfiguration {
|
||||
|
||||
optionArray: {
|
||||
name: string,
|
||||
taskPermissionName: string,
|
||||
action?: Function
|
||||
}[];
|
||||
|
||||
buttonsArray: {
|
||||
name: string,
|
||||
icon: string,
|
||||
taskPermissionName: string,
|
||||
action?: Function
|
||||
}[];
|
||||
|
||||
constructor(status: string) {
|
||||
this.setOptions(status);
|
||||
this.setButtons(status);
|
||||
}
|
||||
|
||||
get singleButtons() {
|
||||
return this.buttonsArray;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.optionArray;
|
||||
}
|
||||
|
||||
setButtons(status: string) {
|
||||
switch (status) {
|
||||
case 'Active':
|
||||
this.buttonsArray = [
|
||||
{
|
||||
name: 'Post Interest As On',
|
||||
icon: 'fa fa-arrow-right',
|
||||
taskPermissionName: 'POSTINTEREST_SAVINGSACCOUNT',
|
||||
},
|
||||
{
|
||||
name: 'Deposit',
|
||||
icon: 'fa fa-arrow-up',
|
||||
taskPermissionName: 'DEPOSIT_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Withdraw',
|
||||
icon: 'fa fa-arrow-down',
|
||||
taskPermissionName: 'WITHDRAW_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Calculate Interest',
|
||||
icon: 'fa fa-table',
|
||||
taskPermissionName: 'CALCULATEINTEREST_SAVINGSACCOUNT'
|
||||
}
|
||||
];
|
||||
break;
|
||||
case 'Submitted and pending approval':
|
||||
this.buttonsArray = [
|
||||
{
|
||||
name: 'Modify Application',
|
||||
icon: 'fa fa-pencil ',
|
||||
taskPermissionName: 'UPDATE_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Approve',
|
||||
icon: 'fa fa-check',
|
||||
taskPermissionName: 'APPROVE_SAVINGSACCOUNT'
|
||||
}
|
||||
];
|
||||
break;
|
||||
case 'Approved':
|
||||
this.buttonsArray = [
|
||||
{
|
||||
name: 'Undo Approval',
|
||||
icon: 'fa fa-undo',
|
||||
taskPermissionName: 'APPROVALUNDO_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Activate',
|
||||
icon: 'fa fa-check',
|
||||
taskPermissionName: 'ACTIVATE_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Add Charge',
|
||||
icon: 'fa fa-plus',
|
||||
taskPermissionName: 'CREATE_SAVINGSACCOUNTCHARGE'
|
||||
}
|
||||
];
|
||||
break;
|
||||
default:
|
||||
this.buttonsArray = [];
|
||||
}
|
||||
}
|
||||
|
||||
setOptions(status: string) {
|
||||
switch (status) {
|
||||
case 'Active':
|
||||
this.optionArray = [
|
||||
{
|
||||
name: 'Post Interest',
|
||||
taskPermissionName: 'POSTINTEREST_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Add Charge',
|
||||
taskPermissionName: 'CREATE_SAVINGSACCOUNTCHARGE'
|
||||
},
|
||||
{
|
||||
name: 'Close',
|
||||
taskPermissionName: 'CLOSE_SAVINGSACCOUNT'
|
||||
}
|
||||
];
|
||||
break;
|
||||
case 'Submitted and pending approval':
|
||||
this.optionArray = [
|
||||
{
|
||||
name: 'Reject',
|
||||
taskPermissionName: 'REJECT_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Withdraw',
|
||||
taskPermissionName: 'WITHDRAW_SAVINGSACCOUNT'
|
||||
},
|
||||
{
|
||||
name: 'Add Charge',
|
||||
taskPermissionName: 'CREATE_SAVINGSACCOUNTCHARGE'
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
taskPermissionName: 'DELETE_SAVINGSACCOUNT'
|
||||
}
|
||||
];
|
||||
break;
|
||||
case 'Approved':
|
||||
default:
|
||||
this.optionArray = [];
|
||||
}
|
||||
}
|
||||
|
||||
addOption(option: {name: string, taskPermissionName: string}) {
|
||||
this.optionArray.push(option);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
<div class="tab-container mat-typography">
|
||||
|
||||
<div class="m-b-10">
|
||||
<h3>All Standing Instructions</h3>
|
||||
</div>
|
||||
|
||||
<div class="mat-elevation-z1 m-b-25">
|
||||
|
||||
<table mat-table #instructionsTable [dataSource]="dataSource">
|
||||
|
||||
<ng-container matColumnDef="client">
|
||||
<th mat-header-cell *matHeaderCellDef> Client </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.fromClient.displayName}}-{{instruction.fromClient.id}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="fromAccount">
|
||||
<th mat-header-cell *matHeaderCellDef> From Account </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.fromAccount.accountNo}} ({{instruction.fromAccountType.value}})</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="beneficiary">
|
||||
<th mat-header-cell *matHeaderCellDef> Beneficiary </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.toClient.displayName}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="toAccount">
|
||||
<th mat-header-cell *matHeaderCellDef> To Account </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.toAccount.accountNo}} ({{instruction.toAccountType.value}})</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="amount">
|
||||
<th mat-header-cell *matHeaderCellDef> Amount </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.instructionType.value}}/{{instruction.amount}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="validity">
|
||||
<th mat-header-cell *matHeaderCellDef> Validity </th>
|
||||
<td mat-cell *matCellDef="let instruction">{{instruction.validFrom | date}} to {{instruction.validTill | date}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let instruction">
|
||||
<span *ngIf="instruction.status.value!=='Deleted'">
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="Edit Standing Instruction"
|
||||
*mifosxHasPermission="'UPDATE_STANDINGINSTRUCTION'">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
</span>
|
||||
<span *ngIf="instruction.status.value!=='Deleted'">
|
||||
<button class="account-action-button" mat-raised-button color="warn" matTooltip="Delete Standing Instruction"
|
||||
*mifosxHasPermission="'DELETE_STANDINGINSTRUCTION'">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</span>
|
||||
<button class="account-action-button" mat-raised-button color="primary" matTooltip="View Standing Instruction"
|
||||
*mifosxHasPermission="'READ_STANDINGINSTRUCTION'">
|
||||
<i class="fa fa-eye"></i>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
.tab-container {
|
||||
padding: 1%;
|
||||
margin: 1%;
|
||||
h3 {
|
||||
margin:1% auto;
|
||||
}
|
||||
.action-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
.account-action-button{
|
||||
min-width: 26px;
|
||||
padding: 0 6px;
|
||||
margin: 4px;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { StandingInstructionsTabComponent } from './standing-instructions-tab.component';
|
||||
|
||||
describe('StandingInstructionsTabComponent', () => {
|
||||
let component: StandingInstructionsTabComponent;
|
||||
let fixture: ComponentFixture<StandingInstructionsTabComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ StandingInstructionsTabComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(StandingInstructionsTabComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatTableDataSource, MatTable } from '@angular/material';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/** Custom Services */
|
||||
import { SavingsService } from 'app/savings/savings.service';
|
||||
|
||||
/**
|
||||
* Savings Standing Instructions Tab
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-standing-instructions-tab',
|
||||
templateUrl: './standing-instructions-tab.component.html',
|
||||
styleUrls: ['./standing-instructions-tab.component.scss']
|
||||
})
|
||||
export class StandingInstructionsTabComponent implements OnInit {
|
||||
|
||||
/** Savings Data */
|
||||
savingsData: any;
|
||||
/** Instructions Data */
|
||||
instructionsData: any[];
|
||||
/** Data source for instructions table. */
|
||||
dataSource = new MatTableDataSource();
|
||||
/** Columns to be displayed in instructions table. */
|
||||
displayedColumns: string[] = [
|
||||
'client',
|
||||
'fromAccount',
|
||||
'beneficiary',
|
||||
'toAccount',
|
||||
'amount',
|
||||
'validity',
|
||||
'actions'
|
||||
];
|
||||
|
||||
/** Instruction Table Reference */
|
||||
@ViewChild('instructionsTable') instructionTableRef: MatTable<Element>;
|
||||
|
||||
/**
|
||||
* Retrieves Savings Account Data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private savingsService: SavingsService) {
|
||||
this.route.parent.data.subscribe((data: { savingsAccountData: any }) => {
|
||||
this.savingsData = data.savingsAccountData;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.getStandingInstructions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves standing instructions and initializes instructions table.
|
||||
*/
|
||||
getStandingInstructions() {
|
||||
const clientId = this.savingsData.clientId;
|
||||
const clientName = this.savingsData.clientName;
|
||||
const accountId = this.savingsData.id;
|
||||
const locale = 'en';
|
||||
const dateFormat = 'dd MMMM yyyy';
|
||||
this.savingsService.getStandingInstructions(clientId, clientName, accountId, locale, dateFormat).subscribe((response: any) => {
|
||||
this.instructionsData = response.pageItems;
|
||||
this.dataSource.data = this.instructionsData;
|
||||
this.instructionTableRef.renderRows();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<div class="tab-container mat-typography">
|
||||
|
||||
<div fxLayout="row" fxLayoutAlign="start">
|
||||
<div class="m-b-10">
|
||||
<h3>All Transactions</h3>
|
||||
</div>
|
||||
<div class="action-button m-b-20" fxLayout="row" fxLayoutGap="20px" *ngIf="checkStatus()">
|
||||
<button mat-raised-button color="primary">View Journal Entries</button>
|
||||
<button mat-raised-button color="primary">Export</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mat-elevation-z1 m-b-25">
|
||||
|
||||
<table mat-table [dataSource]="dataSource" >
|
||||
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef> ID </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ transaction.id }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="transactionDate">
|
||||
<th mat-header-cell *matHeaderCellDef> Transaction Date </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ transaction.date | date }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="transactionType">
|
||||
<th mat-header-cell *matHeaderCellDef> Transaction Type </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ transaction.transactionType.value }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="debit">
|
||||
<th mat-header-cell *matHeaderCellDef> Debit </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ isDebit(transaction.transactionType) ? transaction.amount : 'N/A'}} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="credit">
|
||||
<th mat-header-cell *matHeaderCellDef> Credit </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ !isDebit(transaction.transactionType) ? transaction.amount : 'N/A' }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="balance">
|
||||
<th mat-header-cell *matHeaderCellDef> Balance </th>
|
||||
<td mat-cell *matCellDef="let transaction"> {{ transaction.runningBalance }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="viewReciept">
|
||||
<th mat-header-cell *matHeaderCellDef> View Reciept </th>
|
||||
<td mat-cell *matCellDef="let transaction">
|
||||
<button class="account-action-button" mat-raised-button color="primary">
|
||||
<i class="fa fa-file" matTooltip="View Reciept"></i>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
.tab-container {
|
||||
padding: 1%;
|
||||
margin: 1%;
|
||||
h3 {
|
||||
margin:1% auto;
|
||||
}
|
||||
.action-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
.account-action-button{
|
||||
min-width: 26px;
|
||||
padding: 0 0 3px 0;
|
||||
margin: 0 10%;
|
||||
line-height: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TransactionsTabComponent } from './transactions-tab.component';
|
||||
|
||||
describe('TransactionsTabComponent', () => {
|
||||
let component: TransactionsTabComponent;
|
||||
let fixture: ComponentFixture<TransactionsTabComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ TransactionsTabComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TransactionsTabComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/**
|
||||
* Transactions Tab Component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-transactions-tab',
|
||||
templateUrl: './transactions-tab.component.html',
|
||||
styleUrls: ['./transactions-tab.component.scss']
|
||||
})
|
||||
export class TransactionsTabComponent implements OnInit {
|
||||
|
||||
/** Savings Account Status */
|
||||
status: any;
|
||||
/** Transactions Data */
|
||||
transactionsData: any;
|
||||
/** Columns to be displayed in transactions table. */
|
||||
displayedColumns: string[] = ['id', 'transactionDate', 'transactionType', 'debit', 'credit', 'balance', 'viewReciept'];
|
||||
/** Data source for transactions table. */
|
||||
dataSource: MatTableDataSource<any>;
|
||||
|
||||
/**
|
||||
* Retrieves savings account data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute) {
|
||||
this.route.parent.data.subscribe((data: { savingsAccountData: any }) => {
|
||||
this.transactionsData = data.savingsAccountData.transactions;
|
||||
this.status = data.savingsAccountData.status.value;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.dataSource = new MatTableDataSource(this.transactionsData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if transaction is debit.
|
||||
* @param {any} transactionType Transaction Type
|
||||
*/
|
||||
isDebit(transactionType: any) {
|
||||
return transactionType.withdrawal === true || transactionType.feeDeduction === true
|
||||
|| transactionType.overdraftInterest === true || transactionType.withholdTax === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks transaction status.
|
||||
*/
|
||||
checkStatus() {
|
||||
if (this.status === 'Active' || this.status === 'Closed' || this.status === 'Transfer in progress' ||
|
||||
this.status === 'Transfer on hold' || this.status === 'Premature Closed' || this.status === 'Matured') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,32 +6,50 @@ import { Routes, RouterModule } from '@angular/router';
|
||||
import { extract } from '../core/i18n/i18n.service';
|
||||
|
||||
/** Custom Components */
|
||||
import { SavingsAccountViewComponent } from './savings-account-view/savings-account-view.component';
|
||||
import { TransactionsTabComponent } from './savings-account-view/transactions-tab/transactions-tab.component';
|
||||
import { SavingAccountActionsComponent } from './saving-account-actions/saving-account-actions.component';
|
||||
import { AddSavingsChargeComponent } from './add-savings-charge/add-savings-charge.component';
|
||||
import { ChargesTabComponent } from './savings-account-view/charges-tab/charges-tab.component';
|
||||
import { StandingInstructionsTabComponent } from './savings-account-view/standing-instructions-tab/standing-instructions-tab.component';
|
||||
|
||||
/** Custom Resolvers */
|
||||
import { SavingAccountTransactionTemplateResolver } from './common-resolvers/saving-transaction-template.resolver';
|
||||
import { SavingsChargeTemplateResolver } from './common-resolvers/savings-charge-template.resolver';
|
||||
import { SavingsAccountViewResolver } from './common-resolvers/savings-account-view.resolver';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
data: { title: extract('All Savings'), breadcrumb: 'Savings', routeParamBreadcrumb: false },
|
||||
// Component to view all saving accounts Comes Here
|
||||
children: [
|
||||
{
|
||||
path: ':savingAccountId',
|
||||
data: { title: extract('Saving Account View'), routeParamBreadcrumb: 'savingAccountId' },
|
||||
// Component For Saving Account View Comes Here
|
||||
component: SavingsAccountViewComponent,
|
||||
resolve: {
|
||||
savingsAccountData: SavingsAccountViewResolver
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'transactions',
|
||||
component: TransactionsTabComponent,
|
||||
data: { title: extract('Savings Account Transactions'), breadcrumb: 'Transactions', routeParamBreadcrumb: false }
|
||||
},
|
||||
{
|
||||
path: 'charges',
|
||||
component: ChargesTabComponent,
|
||||
data: { title: extract('Savings Account Charges'), breadcrumb: 'Charges', routeParamBreadcrumb: false }
|
||||
},
|
||||
{
|
||||
path: 'standing-instructions',
|
||||
component: StandingInstructionsTabComponent,
|
||||
data: { title: extract('Savings Account SIH'), breadcrumb: 'Standing Instructions', routeParamBreadcrumb: false }
|
||||
},
|
||||
{
|
||||
path: 'add-savings-charge',
|
||||
component: AddSavingsChargeComponent,
|
||||
data: {
|
||||
title: extract('Add Savings Charge'),
|
||||
breadcrumb: 'Add Savings Charge',
|
||||
routeParamBreadcrumb: false
|
||||
},
|
||||
data: { title: extract('Add Savings Charge'), breadcrumb: 'Add Savings Charge', routeParamBreadcrumb: false },
|
||||
resolve: {
|
||||
savingsChargeTemplate: SavingsChargeTemplateResolver
|
||||
}
|
||||
@@ -53,6 +71,8 @@ const routes: Routes = [
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
declarations: [],
|
||||
providers: [SavingAccountTransactionTemplateResolver, SavingsChargeTemplateResolver]
|
||||
providers: [SavingAccountTransactionTemplateResolver,
|
||||
SavingsChargeTemplateResolver,
|
||||
SavingsAccountViewResolver]
|
||||
})
|
||||
export class SavingsRoutingModule {}
|
||||
|
||||
@@ -5,11 +5,17 @@ import { DatePipe } from '@angular/common';
|
||||
/** Custom Modules */
|
||||
import { SavingsRoutingModule } from './savings-routing.module';
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
import { PipesModule } from '../pipes/pipes.module';
|
||||
import { DirectivesModule } from '../directives/directives.module';
|
||||
|
||||
/** Custom Components */
|
||||
import { SavingAccountActionsComponent } from './saving-account-actions/saving-account-actions.component';
|
||||
import { SavingsAccountTransactionsComponent } from './saving-account-actions/savings-account-transactions/savings-account-transactions.component';
|
||||
import { AddSavingsChargeComponent } from './add-savings-charge/add-savings-charge.component';
|
||||
import { TransactionsTabComponent } from './savings-account-view/transactions-tab/transactions-tab.component';
|
||||
import { SavingsAccountViewComponent } from './savings-account-view/savings-account-view.component';
|
||||
import { ChargesTabComponent } from './savings-account-view/charges-tab/charges-tab.component';
|
||||
import { StandingInstructionsTabComponent } from './savings-account-view/standing-instructions-tab/standing-instructions-tab.component';
|
||||
|
||||
/**
|
||||
* Savings Module
|
||||
@@ -19,12 +25,18 @@ import { AddSavingsChargeComponent } from './add-savings-charge/add-savings-char
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
PipesModule,
|
||||
DirectivesModule,
|
||||
SavingsRoutingModule
|
||||
],
|
||||
declarations: [
|
||||
SavingAccountActionsComponent,
|
||||
SavingsAccountTransactionsComponent,
|
||||
AddSavingsChargeComponent
|
||||
AddSavingsChargeComponent,
|
||||
TransactionsTabComponent,
|
||||
SavingsAccountViewComponent,
|
||||
ChargesTabComponent,
|
||||
StandingInstructionsTabComponent
|
||||
],
|
||||
providers: [DatePipe]
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable } from 'rxjs';
|
||||
@@ -12,7 +12,9 @@ import { Observable } from 'rxjs';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SavingsService {
|
||||
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
/**
|
||||
* @param {string} savingAccountId is saving account's Id.
|
||||
* @returns {Observable<any>}
|
||||
@@ -55,4 +57,35 @@ export class SavingsService {
|
||||
const params = { template: 'true' };
|
||||
return this.http.get(`/charges/${chargeId}`, { params: params });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param groupId Group Id of group to get data for.
|
||||
* @returns {Observable<any>} Group data.
|
||||
*/
|
||||
getSavingsAccountData(accountId: string): Observable<any> {
|
||||
const httpParams = new HttpParams().set('associations', 'all');
|
||||
return this.http.get(`/savingsaccounts/${accountId}`, { params: httpParams });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientId Client Id
|
||||
* @param clientName Client Name
|
||||
* @param fromAccountId Account Id
|
||||
* @param locale Locale
|
||||
* @param dateFormat Date Format
|
||||
* @returns {Observable<any>} Standing Instructions
|
||||
*/
|
||||
getStandingInstructions(
|
||||
clientId: string, clientName: string, fromAccountId: string,
|
||||
locale: string, dateFormat: string): Observable<any> {
|
||||
const httpParams = new HttpParams()
|
||||
.set('clientId', clientId)
|
||||
.set('clientName', clientName)
|
||||
.set('fromAccountId', fromAccountId)
|
||||
.set('fromAccountType', '2')
|
||||
.set('locale', locale)
|
||||
.set('dateFormat', dateFormat);
|
||||
return this.http.get(`/standinginstructions`, { params: httpParams });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
@@ -5,6 +5,7 @@
|
||||
@import '../../app/centers/centers-view/centers-view.component-theme.scss';
|
||||
@import '../../app/home/dashboard/dashboard.component-theme.scss';
|
||||
@import '../../app/loans/loans-view/loans-view.component-theme.scss';
|
||||
@import '../../app/savings/savings-account-view/savings-account-view.component-theme.scss';
|
||||
|
||||
// Custom material palette
|
||||
$mat-denim: (
|
||||
@@ -55,3 +56,4 @@ $mifosx-app-theme: mat-light-theme($mifosx-app-primary, $mifosx-app-accent, $mif
|
||||
@include centers-view-component-theme($mifosx-app-theme);
|
||||
@include dashboard-component-theme($mifosx-app-theme);
|
||||
@include loans-view-component-theme($mifosx-app-theme);
|
||||
@include savings-account-view-component-theme($mifosx-app-theme);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
@import '../../app/centers/centers-view/centers-view.component-theme.scss';
|
||||
@import '../../app/home/dashboard/dashboard.component-theme.scss';
|
||||
@import '../../app/loans/loans-view/loans-view.component-theme.scss';
|
||||
@import '../../app/savings/savings-account-view/savings-account-view.component-theme.scss';
|
||||
|
||||
// Custom material palette
|
||||
$mat-picton-blue: (
|
||||
@@ -55,3 +56,4 @@ $mifosx-app-theme: mat-light-theme($mifosx-app-primary, $mifosx-app-accent, $mif
|
||||
@include centers-view-component-theme($mifosx-app-theme);
|
||||
@include dashboard-component-theme($mifosx-app-theme);
|
||||
@include loans-view-component-theme($mifosx-app-theme);
|
||||
@include savings-account-view-component-theme($mifosx-app-theme);
|
||||
|
||||
Reference in New Issue
Block a user