feat: view saving product component (#456)

Fixes: #452
This commit is contained in:
Abhay Chawla
2019-12-01 23:20:51 +03:00
parent 8951f15e6f
commit f22be41f87
10 changed files with 490 additions and 2 deletions
@@ -16,6 +16,7 @@ import { ViewLoanProductComponent } from './loan-products/view-loan-product/view
import { EditLoanProductComponent } from './loan-products/edit-loan-product/edit-loan-product.component';
import { SavingProductsComponent } from './saving-products/saving-products.component';
import { CreateSavingProductComponent } from './saving-products/create-saving-product/create-saving-product.component';
import { ViewSavingProductComponent } from './saving-products/view-saving-product/view-saving-product.component';
import { ManageTaxConfigurationsComponent } from './manage-tax-configurations/manage-tax-configurations.component';
import { RecurringDepositProductsComponent } from './recurring-deposit-products/recurring-deposit-products.component';
import { ChargesComponent } from './charges/charges.component';
@@ -30,6 +31,7 @@ import { LoanProductResolver } from './loan-products/loan-product.resolver';
import { LoanProductAndTemplateResolver } from './loan-products/edit-loan-product/loan-product-and-template.resolver';
import { SavingProductsResolver } from './saving-products/saving-products.resolver';
import { SavingProductsTemplateResolver } from './saving-products/saving-products-template.resolver';
import { SavingProductResolver } from './saving-products/saving-product.resolver';
import { RecurringDepositProductsResolver } from './recurring-deposit-products/recurring-deposit-products.resolver';
import { ChargesResolver } from './charges/charges.resolver';
import { FixedDepositProductsResolver } from './fixed-deposit-products/fixed-deposit-products.resolver';
@@ -108,6 +110,19 @@ const routes: Routes = [
savingProductsTemplate: SavingProductsTemplateResolver
}
},
{
path: ':id',
data: { title: extract('View Saving Product'), routeParamBreadcrumb: 'id' },
children: [
{
path: '',
component: ViewSavingProductComponent,
resolve: {
savingProduct: SavingProductResolver
}
}
]
}
]
},
{
@@ -180,6 +195,7 @@ const routes: Routes = [
LoanProductAndTemplateResolver,
SavingProductsResolver,
SavingProductsTemplateResolver,
SavingProductResolver,
RecurringDepositProductsResolver,
ChargesResolver,
FixedDepositProductsResolver,
+3 -1
View File
@@ -35,6 +35,7 @@ import { SavingProductChargesStepComponent } from './saving-products/saving-prod
import { SavingProductAccountingStepComponent } from './saving-products/saving-product-stepper/saving-product-accounting-step/saving-product-accounting-step.component';
import { SavingProductPreviewStepComponent } from './saving-products/saving-product-stepper/saving-product-preview-step/saving-product-preview-step.component';
import { CreateSavingProductComponent } from './saving-products/create-saving-product/create-saving-product.component';
import { ViewSavingProductComponent } from './saving-products/view-saving-product/view-saving-product.component';
/**
* Products Module
@@ -74,7 +75,8 @@ import { CreateSavingProductComponent } from './saving-products/create-saving-pr
SavingProductChargesStepComponent,
SavingProductAccountingStepComponent,
SavingProductPreviewStepComponent,
CreateSavingProductComponent
CreateSavingProductComponent,
ViewSavingProductComponent
],
entryComponents: [
],
+4
View File
@@ -57,6 +57,10 @@ export class ProductsService {
return this.http.get('/savingsproducts/template');
}
getSavingProduct(savingProductId: string): Observable<any> {
return this.http.get(`/savingsproducts/${savingProductId}`);
}
/**
* @returns {Observable<any>} Recurring deposit products data
*/
@@ -0,0 +1,31 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
/** rxjs Imports */
import { Observable } from 'rxjs';
/** Custom Services */
import { ProductsService } from '../products.service';
/**
* Saving Product data resolver.
*/
@Injectable()
export class SavingProductResolver implements Resolve<Object> {
/**
* @param {ProductsService} productsService Products service.
*/
constructor(private productsService: ProductsService) {}
/**
* Returns the saving product data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const id = route.paramMap.get('id');
return this.productsService.getSavingProduct(id);
}
}
@@ -29,7 +29,7 @@
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="[row.id]" class="select-row"></tr>
</table>
@@ -0,0 +1,7 @@
table {
width: 100%;
.select-row:hover {
cursor: pointer;
}
}
@@ -0,0 +1,353 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary">
<fa-icon icon="edit"></fa-icon>&nbsp;&nbsp;
Edit
</button>
</div>
<div class="container">
<mat-card>
<mat-card-content>
<div fxLayout="row wrap" fxLayout.lt-md="column">
<h2 class="mat-h2" fxFlexFill>{{ savingProduct.name }}</h2>
<mat-divider [inset]="true"></mat-divider>
<h3 class="mat-h3" fxFlexFill>Details</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Short Name:</span>
<span fxFlex="60%">{{ savingProduct.shortName }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.description">
<span fxFlex="40%">Description:</span>
<span fxFlex="60%">{{ savingProduct.description }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Currency</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Currency:</span>
<span fxFlex="60%">{{ savingProduct.currency.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Decimal Places:</span>
<span fxFlex="60%">{{ savingProduct.currency.decimalPlaces }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Currency in multiples of:</span>
<span fxFlex="60%">{{ savingProduct.currency.inMultiplesOf }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Terms</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Nominal Annual Interest Rate:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRate }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Compounding Period:</span>
<span fxFlex="60%">{{ savingProduct.interestCompoundingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Posting Period:</span>
<span fxFlex="60%">{{ savingProduct.interestPostingPeriodType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Interest Calculated using:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Days in Year:</span>
<span fxFlex="60%">{{ savingProduct.interestCalculationDaysInYearType.value }}</span>
</div>
<h3 class="mat-h3" fxFlexFill>Settings</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill *ngIf="savingProduct.minRequiredOpeningBalance">
<span fxFlex="40%">Minimum Opening Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredOpeningBalance }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.lockinPeriodFrequency">
<span fxFlex="40%">Lock-in Period:</span>
<span fxFlex="60%">{{ savingProduct.lockinPeriodFrequency + ' ' + savingProduct.lockinPeriodFrequencyType.value }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Apply Withdrawal Fee for Transfers:</span>
<span fxFlex="60%">{{ savingProduct.withdrawalFeeForTransfers ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minBalanceForInterestCalculation">
<span fxFlex="40%">Balance Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minBalanceForInterestCalculation }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enforce Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.enforceMinRequiredBalance ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minRequiredBalance">
<span fxFlex="40%">Minimum Balance:</span>
<span fxFlex="60%">{{ savingProduct.minRequiredBalance }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Withhold Tax is Applicable:</span>
<span fxFlex="60%">{{ savingProduct.withHoldTax ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.withHoldTax">
<span fxFlex="40%">Tax Group:</span>
<span fxFlex="60%">{{ savingProduct.taxGroup.name }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Is Overdraft Allowed:</span>
<span fxFlex="60%">{{ savingProduct.allowOverdraft ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.minOverdraftForInterestCalculation">
<span fxFlex="40%">Minimum Overdraft Required for Interest Calculation:</span>
<span fxFlex="60%">{{ savingProduct.minOverdraftForInterestCalculation }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.nominalAnnualInterestRateOverdraft">
<span fxFlex="40%">Nominal Annual Interest for Overdraft:</span>
<span fxFlex="60%">{{ savingProduct.nominalAnnualInterestRateOverdraft }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.overdraftLimit">
<span fxFlex="40%">Maximum Overdraft Amount Limit:</span>
<span fxFlex="60%">{{ savingProduct.overdraftLimit }}</span>
</div>
<div fxFlexFill>
<span fxFlex="40%">Enable Dormancy Tracking:</span>
<span fxFlex="60%">{{ savingProduct.isDormancyTrackingActive ? 'Yes' : 'No' }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive" fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Number of Days to Inactive sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToInactive }}</span>
<span fxFlex="40%">Number of Days to Dormant sub-status:</span>
<span fxFlex="60%">{{ savingProduct.daysToDormancy }}</span>
<span fxFlex="40%">Number of Days to Escheat:</span>
<span fxFlex="60%">{{ savingProduct.daysToEscheat }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.charges.length" fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Charges</h3>
<mat-divider [inset]="true"></mat-divider>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.charges">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef> Name </th>
<td mat-cell *matCellDef="let charge">
{{ charge.name + ', ' + charge.currency.displaySymbol }}
</td>
</ng-container>
<ng-container matColumnDef="chargeCalculationType">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let charge">
{{ charge.chargeCalculationType.value }}
</td>
</ng-container>
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef> Amount </th>
<td mat-cell *matCellDef="let charge">
{{ charge.amount }}
</td>
</ng-container>
<ng-container matColumnDef="chargeTimeType">
<th mat-header-cell *matHeaderCellDef> Collected On </th>
<td mat-cell *matCellDef="let charge">
{{ charge.chargeTimeType.value }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="chargesDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: chargesDisplayedColumns;"></tr>
</table>
</div>
<h3 class="mat-h3" fxFlexFill>Accounting</h3>
<mat-divider [inset]="true"></mat-divider>
<div fxFlexFill>
<span fxFlex="40%">Type:</span>
<span fxFlex="60%">{{ savingProduct.accountingRule.value }}</span>
</div>
<div fxFlexFill *ngIf="savingProduct.accountingRule.id === 2" fxLayout="row wrap" fxLayout.lt-md="column">
<h4 fxFlexFill class="mat-h4">Assets</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Saving reference:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsReferenceAccount.name }}</span>
<span fxFlex="40%">Overdraft portfolio:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.overdraftPortfolioControl.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Liabilities</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Saving control:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.savingsControlAccount.name }}</span>
<span fxFlex="40%">Savings transfers in suspense:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.transfersInSuspenseAccount.name }}</span>
<div fxFlexFill *ngIf="savingProduct.isDormancyTrackingActive">
<span fxFlex="40%">Escheat liability:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.escheatLiabilityAccount.name }}</span>
</div>
</div>
<h4 fxFlexFill class="mat-h4">Expenses</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Interest on savings:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.interestOnSavingsAccount.name }}</span>
<span fxFlex="40%">Write-off:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.writeOffAccount.name }}</span>
</div>
<h4 fxFlexFill class="mat-h4">Income</h4>
<div fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<span fxFlex="40%">Income from fees:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromFeeAccount.name }}</span>
<span fxFlex="40%">Income from penalties:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromPenaltyAccount.name }}</span>
<span fxFlex="40%">Overdraft Interest Income:</span>
<span fxFlex="60%">{{ savingProduct.accountingMappings.incomeFromInterest.name }}</span>
</div>
<div *ngIf="savingProduct.paymentChannelToFundSourceMappings?.length || savingProduct.feeToIncomeAccountMappings?.length || savingProduct.penaltyToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h3 class="mat-h3" fxFlexFill>Advanced Accounting Rules</h3>
<mat-divider [inset]="true"></mat-divider>
<div *ngIf="savingProduct.paymentChannelToFundSourceMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h4 class="mat-h4" fxFlexFill>Fund Sources for Payment Channels</h4>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.paymentChannelToFundSourceMappings">
<ng-container matColumnDef="paymentTypeId">
<th mat-header-cell *matHeaderCellDef> Payment Type </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.paymentType.name }}
</td>
</ng-container>
<ng-container matColumnDef="fundSourceAccountId">
<th mat-header-cell *matHeaderCellDef> Fund Source </th>
<td mat-cell *matCellDef="let paymentFundSource">
{{ paymentFundSource.fundSourceAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="paymentFundSourceDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: paymentFundSourceDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.feeToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h4 class="mat-h4" fxFlexFill>Fees to Specific Income Accounts</h4>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.feeToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Fees </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let feesIncome">
{{ feesIncome.incomeAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
<div *ngIf="savingProduct.penaltyToIncomeAccountMappings?.length" fxFlexFill fxLayout="row wrap" fxLayout.lt-md="column">
<h4 class="mat-h4" fxFlexFill>Penalties to Specific Income Accounts</h4>
<table fxFlexFill class="mat-elevation-z1" mat-table [dataSource]="savingProduct.penaltyToIncomeAccountMappings">
<ng-container matColumnDef="chargeId">
<th mat-header-cell *matHeaderCellDef> Penalty </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.charge.name }}
</td>
</ng-container>
<ng-container matColumnDef="incomeAccountId">
<th mat-header-cell *matHeaderCellDef> Income Account </th>
<td mat-cell *matCellDef="let penaltyIncome">
{{ penaltyIncome.incomeAccount.name }}
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="feesPenaltyIncomeDisplayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: feesPenaltyIncomeDisplayedColumns;"></tr>
</table>
</div>
</div>
</div>
</div>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,24 @@
table {
width: 100%;
}
.mat-elevation-z1 {
margin: 1em 0 1.5em 0;
}
h2, h3, h4 {
margin: 0;
font-weight: 500;
}
span {
margin: 0.5em 0;
}
.mat-card .mat-divider-horizontal.mat-divider-inset {
margin: 0 0 0.5em 0;
}
.margin-t {
margin-top: 1em;
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewSavingProductComponent } from './view-saving-product.component';
describe('ViewSavingProductComponent', () => {
let component: ViewSavingProductComponent;
let fixture: ComponentFixture<ViewSavingProductComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ViewSavingProductComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ViewSavingProductComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,26 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-view-saving-product',
templateUrl: './view-saving-product.component.html',
styleUrls: ['./view-saving-product.component.scss']
})
export class ViewSavingProductComponent implements OnInit {
savingProduct: any;
chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType'];
paymentFundSourceDisplayedColumns: string[] = ['paymentTypeId', 'fundSourceAccountId'];
feesPenaltyIncomeDisplayedColumns: string[] = ['chargeId', 'incomeAccountId'];
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { savingProduct: any }) => {
this.savingProduct = data.savingProduct;
});
}
ngOnInit() {
}
}