From d51bd6466ab518d0fd41010af7b8f2328214b21c Mon Sep 17 00:00:00 2001 From: Karan Takalkar Date: Mon, 15 Jun 2020 21:18:14 +0530 Subject: [PATCH] feat: add create shares account page (#877) Fixes: #223 --- .../clients-view/clients-view.component.html | 2 +- src/app/shared/icons.module.ts | 6 +- .../shares-account-template.resolver.ts | 32 +++++ .../create-shares-account.component.html | 75 ++++++++++ .../create-shares-account.component.scss | 0 .../create-shares-account.component.spec.ts | 25 ++++ .../create-shares-account.component.ts | 117 ++++++++++++++++ ...shares-account-charges-step.component.html | 77 ++++++++++ ...shares-account-charges-step.component.scss | 11 ++ ...res-account-charges-step.component.spec.ts | 25 ++++ .../shares-account-charges-step.component.ts | 117 ++++++++++++++++ ...shares-account-details-step.component.html | 46 ++++++ ...shares-account-details-step.component.scss | 0 ...res-account-details-step.component.spec.ts | 25 ++++ .../shares-account-details-step.component.ts | 78 +++++++++++ ...shares-account-preview-step.component.html | 132 ++++++++++++++++++ ...shares-account-preview-step.component.scss | 24 ++++ ...res-account-preview-step.component.spec.ts | 25 ++++ .../shares-account-preview-step.component.ts | 31 ++++ .../shares-account-terms-step.component.html | 105 ++++++++++++++ .../shares-account-terms-step.component.scss | 8 ++ ...hares-account-terms-step.component.spec.ts | 25 ++++ .../shares-account-terms-step.component.ts | 79 +++++++++++ src/app/shares/shares-routing.module.ts | 13 +- src/app/shares/shares.module.ts | 12 +- src/app/shares/shares.service.ts | 18 +++ 26 files changed, 1103 insertions(+), 5 deletions(-) create mode 100644 src/app/shares/common-resolvers/shares-account-template.resolver.ts create mode 100644 src/app/shares/create-shares-account/create-shares-account.component.html create mode 100644 src/app/shares/create-shares-account/create-shares-account.component.scss create mode 100644 src/app/shares/create-shares-account/create-shares-account.component.spec.ts create mode 100644 src/app/shares/create-shares-account/create-shares-account.component.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.html create mode 100644 src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.scss create mode 100644 src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.spec.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.html create mode 100644 src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.scss create mode 100644 src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.spec.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.html create mode 100644 src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.scss create mode 100644 src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.spec.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.html create mode 100644 src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.scss create mode 100644 src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.spec.ts create mode 100644 src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.ts diff --git a/src/app/clients/clients-view/clients-view.component.html b/src/app/clients/clients-view/clients-view.component.html index d43c3438d..4757f518c 100644 --- a/src/app/clients/clients-view/clients-view.component.html +++ b/src/app/clients/clients-view/clients-view.component.html @@ -37,7 +37,7 @@ - + diff --git a/src/app/shared/icons.module.ts b/src/app/shared/icons.module.ts index a6150a454..508607644 100644 --- a/src/app/shared/icons.module.ts +++ b/src/app/shared/icons.module.ts @@ -88,7 +88,8 @@ import { faLaptop, faDonate, faMoneyBill, - faChartLine + faChartLine, + faPen } from '@fortawesome/free-solid-svg-icons'; /** Add icons to the library for convenient access in other components. */ @@ -176,7 +177,8 @@ library.add( faLaptop, faDonate, faMoneyBill, - faChartLine + faChartLine, + faPen ); /** diff --git a/src/app/shares/common-resolvers/shares-account-template.resolver.ts b/src/app/shares/common-resolvers/shares-account-template.resolver.ts new file mode 100644 index 000000000..95a42ae55 --- /dev/null +++ b/src/app/shares/common-resolvers/shares-account-template.resolver.ts @@ -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 { SharesService } from '../shares.service'; + +/** + * Shares Account Template resolver. + */ +@Injectable() +export class SharesAccountTemplateResolver implements Resolve { + + /** + * @param {SharesService} SharesService Shares service. + */ + constructor(private sharesService: SharesService) { } + + /** + * Returns the Shares Account Template. + * @param {ActivatedRouteSnapshot} route Route Snapshot + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const clientId = route.parent.parent.paramMap.get('clientId'); + return this.sharesService.getSharesAccountTemplate(clientId); + } + +} diff --git a/src/app/shares/create-shares-account/create-shares-account.component.html b/src/app/shares/create-shares-account/create-shares-account.component.html new file mode 100644 index 000000000..8322c89bd --- /dev/null +++ b/src/app/shares/create-shares-account/create-shares-account.component.html @@ -0,0 +1,75 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + DETAILS + + + + + + + + + TERMS + + + + + + + + CHARGES + + + + + + + + + PREVIEW + + + + + + + + +
diff --git a/src/app/shares/create-shares-account/create-shares-account.component.scss b/src/app/shares/create-shares-account/create-shares-account.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shares/create-shares-account/create-shares-account.component.spec.ts b/src/app/shares/create-shares-account/create-shares-account.component.spec.ts new file mode 100644 index 000000000..8c23811c5 --- /dev/null +++ b/src/app/shares/create-shares-account/create-shares-account.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateSharesAccountComponent } from './create-shares-account.component'; + +describe('CreateSharesAccountComponent', () => { + let component: CreateSharesAccountComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CreateSharesAccountComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateSharesAccountComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shares/create-shares-account/create-shares-account.component.ts b/src/app/shares/create-shares-account/create-shares-account.component.ts new file mode 100644 index 000000000..64fa0c909 --- /dev/null +++ b/src/app/shares/create-shares-account/create-shares-account.component.ts @@ -0,0 +1,117 @@ +/** Angular Imports */ +import { Component, ViewChild } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { DatePipe } from '@angular/common'; + +/** Custom Components */ +import { SharesAccountDetailsStepComponent } from '../shares-account-stepper/shares-account-details-step/shares-account-details-step.component'; +import { SharesAccountTermsStepComponent } from '../shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component'; +import { SharesAccountChargesStepComponent } from '../shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component'; + +/** Custom Services */ +import { SharesService } from '../shares.service'; + +/** + * Create Shares Account Component + */ +@Component({ + selector: 'mifosx-create-shares-account', + templateUrl: './create-shares-account.component.html', + styleUrls: ['./create-shares-account.component.scss'] +}) +export class CreateSharesAccountComponent { + + /** Shares Account Template */ + sharesAccountTemplate: any; + /** Shares Account Product Template */ + sharesAccountProductTemplate: any; + + /** Shares Account Details Step */ + @ViewChild(SharesAccountDetailsStepComponent) sharesAccountDetailsStep: SharesAccountDetailsStepComponent; + /** Shares Account Terms Step */ + @ViewChild(SharesAccountTermsStepComponent) sharesAccountTermsStep: SharesAccountTermsStepComponent; + /** Shares Account Charges Step */ + @ViewChild(SharesAccountChargesStepComponent) sharesAccountChargesStep: SharesAccountChargesStepComponent; + + /** + * Fetches shares account template from `resolve` + * @param {ActivatedRoute} route Activated Route + * @param {Router} router Router + * @param {DatePipe} datePipe Date Pipe + * @param {SharesService} sharesService Shares Service + */ + constructor(private route: ActivatedRoute, + private router: Router, + private datePipe: DatePipe, + private sharesService: SharesService) { + this.route.data.subscribe((data: { sharesAccountTemplate: any }) => { + this.sharesAccountTemplate = data.sharesAccountTemplate; + }); + } + + /** + * Sets shares account product template. + * @param {any} $event API response + */ + setTemplate($event: any) { + this.sharesAccountProductTemplate = $event; + } + + /** + * Retrieves shares account details form. + */ + get sharesAccountDetailsForm() { + return this.sharesAccountDetailsStep.sharesAccountDetailsForm; + } + + /** + * Retrieves shares account terms form. + */ + get sharesAccountTermsForm() { + return this.sharesAccountTermsStep.sharesAccountTermsForm; + } + + /** + * Checks validity of overall shares account form. + */ + get sharesAccountFormValid() { + return ( + this.sharesAccountDetailsForm.valid && + this.sharesAccountTermsForm.valid + ); + } + + /** + * Retrieves shares account object. + */ + get sharesAccount() { + return { + ...this.sharesAccountDetailsStep.sharesAccountDetails, + ...this.sharesAccountTermsStep.sharesAccountTerms, + ...this.sharesAccountChargesStep.sharesAccountCharges + }; + } + + /** + * Creates a new share account. + */ + submit() { + // TODO: Update once language and date settings are setup + const locale = 'en'; + const dateFormat = 'dd MMMM yyyy'; + const sharesAccount = { + ...this.sharesAccount, + clientId: this.sharesAccountTemplate.clientId, + charges: this.sharesAccount.charges.map((charge: any) => ({ chargeId: charge.chargeId, amount: charge.amount })), + applicationDate: this.datePipe.transform(this.sharesAccount.applicationDate, dateFormat), + submittedDate: this.datePipe.transform(this.sharesAccount.submittedDate, dateFormat), + unitPrice: this.sharesAccountTermsForm.get('unitPrice').value, + dateFormat, + locale + }; + this.sharesService.createSharesAccount(sharesAccount).subscribe((response: any) => { + this.router.navigate(['../', response.resourceId], { relativeTo: this.route }); + }); + } + +} diff --git a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.html b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.html new file mode 100644 index 000000000..65b063bf4 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.html @@ -0,0 +1,77 @@ +
+ + + Charge + + + {{ charge.name }} + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + {{ charge.name + ', ' + charge.currency.displaySymbol }} + Type + {{ charge.chargeCalculationType.value }} + Amount + {{ charge.amount }} + + Collected On + {{ charge.chargeTimeType.value }} + Actions + +
+ +
+ +
+ + +
diff --git a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.scss b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.scss new file mode 100644 index 000000000..dd420676a --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.scss @@ -0,0 +1,11 @@ +table { + width: 100%; +} + +.mat-elevation-z1 { + margin: 1em 0 1.5em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.spec.ts b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.spec.ts new file mode 100644 index 000000000..71bd37ed8 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SharesAccountChargesStepComponent } from './shares-account-charges-step.component'; + +describe('SharesAccountChargesStepComponent', () => { + let component: SharesAccountChargesStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SharesAccountChargesStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SharesAccountChargesStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts new file mode 100644 index 000000000..fb4366147 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component.ts @@ -0,0 +1,117 @@ +/** Angular Imports */ +import { Component, OnInit, OnChanges, Input } from '@angular/core'; +import { MatDialog } from '@angular/material'; +import { FormControl } from '@angular/forms'; + +/** Custom Dialogs */ +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; +import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; + +/** Custom Models */ +import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; +import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base'; + +/** + * Shares Account Charges Step + */ +@Component({ + selector: 'mifosx-shares-account-charges-step', + templateUrl: './shares-account-charges-step.component.html', + styleUrls: ['./shares-account-charges-step.component.scss'] +}) +export class SharesAccountChargesStepComponent implements OnInit, OnChanges { + + /** Shares Account Product Template */ + @Input() sharesAccountProductTemplate: any; + /** Shares Account Template */ + @Input() sharesAccountTemplate: any; + /** Currency Code */ + @Input() currencyCode: FormControl; + + /** Charge Data */ + chargeData: any; + /** Charges Data Source */ + chargesDataSource: {}[]; + /** Display columns for charges table */ + displayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType', 'action']; + + /** + * @param {MatDialog} dialog Mat Dialog + */ + constructor(public dialog: MatDialog) {} + + ngOnInit() { + this.chargeData = this.sharesAccountTemplate.chargeOptions.map((charge: any) => { + charge.chargeId = charge.id; + delete charge.id; + return charge; + }); + this.currencyCode.valueChanges.subscribe(() => this.chargesDataSource = []); + } + + ngOnChanges() { + this.chargesDataSource = this.sharesAccountProductTemplate ? (this.sharesAccountProductTemplate.charges || []) : []; + } + + /** + * Adds the charge to charges table + * @param {any} charge Charge + */ + addCharge(charge: any) { + this.chargesDataSource = this.chargesDataSource.concat([charge.value]); + charge.value = ''; + } + + /** + * Edits Charge Amount + * @param {any} charge Charge + */ + editCharge(charge: any) { + const formfields: FormfieldBase[] = [ + new InputBase({ + controlName: 'amount', + label: 'Amount', + value: charge.amount, + type: 'text', + required: true, + }), + ]; + const data = { + title: 'Edit Charge', + layout: { addButtonText: 'Confirm' }, + formfields: formfields + }; + const editNoteDialogRef = this.dialog.open(FormDialogComponent, { data }); + editNoteDialogRef.afterClosed().subscribe((response: any) => { + if (response.data) { + const newCharge = { ...charge, amount: response.data.value.amount }; + this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1, newCharge); + this.chargesDataSource = this.chargesDataSource.concat([]); + } + }); + } + + /** + * Removes the charge from charges table + * @param {any} charge Charge + */ + deleteCharge(charge: any) { + const deleteChargeDialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `charge ${charge.name}` } + }); + deleteChargeDialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + this.chargesDataSource.splice(this.chargesDataSource.indexOf(charge), 1); + this.chargesDataSource = this.chargesDataSource.concat([]); + } + }); + } + + /** + * Returns shares account charges + */ + get sharesAccountCharges() { + return { charges: this.chargesDataSource }; + } + +} diff --git a/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.html b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.html new file mode 100644 index 000000000..209fd2843 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.html @@ -0,0 +1,46 @@ +
+ +
+ + + Product Name + + + {{ product.name }} + + + + Product Name is required + + + + + Submitted On + + + + + Submission Date is required + + + + + External ID + + + +
+ +
+ + +
+ +
diff --git a/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.scss b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.spec.ts b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.spec.ts new file mode 100644 index 000000000..828994ccb --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SharesAccountDetailsStepComponent } from './shares-account-details-step.component'; + +describe('SharesAccountDetailsStepComponent', () => { + let component: SharesAccountDetailsStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SharesAccountDetailsStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SharesAccountDetailsStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.ts b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.ts new file mode 100644 index 000000000..042600fbf --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-details-step/shares-account-details-step.component.ts @@ -0,0 +1,78 @@ +/** Angular Imports */ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; + +/** Custom Services */ +import { SharesService } from 'app/shares/shares.service'; + +/** + * Shares Account Details Step + */ +@Component({ + selector: 'mifosx-shares-account-details-step', + templateUrl: './shares-account-details-step.component.html', + styleUrls: ['./shares-account-details-step.component.scss'] +}) +export class SharesAccountDetailsStepComponent implements OnInit { + + /** Shares Account Template */ + @Input() sharesAccountTemplate: any; + + /** Minimum date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum date allowed. */ + maxDate = new Date(); + /** Product Data */ + productData: any; + /** Shares Account Details Form */ + sharesAccountDetailsForm: FormGroup; + + /** Shares Account Template with product data */ + @Output() sharesAccountProductTemplate = new EventEmitter(); + + /** + * Sets share account details form. + * @param {FormBuilder} formBuilder Form Builder. + * @param {SharesService} sharesService Shares Service. + */ + constructor(private formBuilder: FormBuilder, + private sharesService: SharesService) { + this.createSharesAccountDetailsForm(); + } + + ngOnInit() { + this.productData = this.sharesAccountTemplate.productOptions; + this.buildDependencies(); + } + + /** + * Creates shares account details form. + */ + createSharesAccountDetailsForm() { + this.sharesAccountDetailsForm = this.formBuilder.group({ + 'productId': ['', Validators.required], + 'submittedDate': ['', Validators.required], + 'externalId': [''] + }); + } + + /** + * Fetches shares account product template on productId value changes + */ + buildDependencies() { + const clientId = this.sharesAccountTemplate.clientId; + this.sharesAccountDetailsForm.get('productId').valueChanges.subscribe((productId: string) => { + this.sharesService.getSharesAccountTemplate(clientId, productId).subscribe((response: any) => { + this.sharesAccountProductTemplate.emit(response); + }); + }); + } + + /** + * Returns shares account form value. + */ + get sharesAccountDetails() { + return this.sharesAccountDetailsForm.value; + } + +} diff --git a/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.html b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.html new file mode 100644 index 000000000..c6d03cf9b --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.html @@ -0,0 +1,132 @@ +
+ +

Details

+ + + +
+ Product + {{ sharesAccount.productId | find:sharesAccountTemplate.productOptions:'id':'name' }} +
+ +
+ Submitted On + {{ sharesAccount.submittedDate | date }} +
+ +
+ External Id + {{ sharesAccount.externalId || 'Unassigned' }} +
+ +

Terms

+ + + +
+ Currency + {{ sharesAccountTermsForm.get('currencyCode').value | find:[sharesAccountProductTemplate.currency]:'code':'displayLabel' }} +
+ +
+ Decimal Places + {{ sharesAccountTermsForm.get('decimal').value }} +
+ +
+ Total Number of Shares + {{ sharesAccount.requestedShares }} +
+ +
+ Today's Price + {{ sharesAccountTermsForm.get('unitPrice').value }} +
+ +
+ Currency in multiples of + {{ sharesAccountTermsForm.get('currencyMultiple').value }} +
+ +
+ Default Savings Account + {{ sharesAccount.savingsAccountId | find:sharesAccountProductTemplate.clientSavingsAccounts:'id':'accountNo' }} +
+ +
+ Minimum Active Period + {{sharesAccount.minimumActivePeriod}} {{sharesAccount.minimumActivePeriodFrequencyType | find:sharesAccountProductTemplate.minimumActivePeriodFrequencyTypeOptions:'id':'value'}} +
+ +
+ Lock-in Period + {{sharesAccount.lockinPeriodFrequency}} {{sharesAccount.lockinPeriodFrequencyType | find:sharesAccountProductTemplate.lockinPeriodFrequencyTypeOptions:'id':'value'}} +
+ +
+ Application Date + {{ sharesAccount.applicationDate | date }} +
+ +
+ Allow dividends for inactive clients + {{ sharesAccount.allowDividendCalculationForInactiveClients }} +
+ +
+ +

Charges

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + {{ charge.name + ', ' + charge.currency.displaySymbol }} + Type + {{ charge.chargeCalculationType.value }} + Amount + {{ charge.amount }} + Collected On + {{ charge.chargeTimeType.value }} +
+ +
+ +
+ +
+ + + +
diff --git a/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.scss b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.scss new file mode 100644 index 000000000..87258dacb --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.scss @@ -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-divider { + margin: 0 0 0.5em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.spec.ts b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.spec.ts new file mode 100644 index 000000000..047029a76 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SharesAccountPreviewStepComponent } from './shares-account-preview-step.component'; + +describe('SharesAccountPreviewStepComponent', () => { + let component: SharesAccountPreviewStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SharesAccountPreviewStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SharesAccountPreviewStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.ts b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.ts new file mode 100644 index 000000000..7463d33ec --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Component, Input, Output, EventEmitter } from '@angular/core'; + +/** + * Shares account preview step + */ +@Component({ + selector: 'mifosx-shares-account-preview-step', + templateUrl: './shares-account-preview-step.component.html', + styleUrls: ['./shares-account-preview-step.component.scss'] +}) +export class SharesAccountPreviewStepComponent { + + /** Shares Account Product Template */ + @Input() sharesAccountProductTemplate: any; + /** Shares Account Template */ + @Input() sharesAccountTemplate: any; + /** Shares Account Terms Form */ + @Input() sharesAccountTermsForm: any; + /** Shares Account */ + @Input() sharesAccount: any; + + /** Display columns for charges table. */ + chargesDisplayedColumns: string[] = ['name', 'chargeCalculationType', 'amount', 'chargeTimeType']; + + /** Form submission event */ + @Output() submit = new EventEmitter(); + + constructor() { } + +} diff --git a/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.html b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.html new file mode 100644 index 000000000..43665e9dc --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.html @@ -0,0 +1,105 @@ +
+ +
+ + + Currency + + + + + Decimal Places + + + + + Total Number of Shares + + + Total Number of Shares is required + + + + + Today's Price + + + + + Currency in multiples of + + + + + Default Savings Account + + + {{ account.accountNo }} + + + + Default Savings Account is required + + + + + Application Date + + + + + Application Date is required + + + + + Allow dividends for inactive clients + + +

Minimum Active Period

+ + + Frequency + + + + + Type + + + {{ minimumActivePeriodFrequencyType.value }} + + + + +

Lock-in Period

+ + + Frequency + + + + + Type + + + {{ lockinPeriodFrequencyType.value }} + + + + +
+ +
+ + +
+ +
diff --git a/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.scss b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.scss new file mode 100644 index 000000000..236656059 --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.scss @@ -0,0 +1,8 @@ +h4 { + font-weight: 500; + margin: 1em 0; +} + +.margin-t { + margin-top: 1em; +} diff --git a/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.spec.ts b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.spec.ts new file mode 100644 index 000000000..cf269b09c --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SharesAccountTermsStepComponent } from './shares-account-terms-step.component'; + +describe('SharesAccountTermsStepComponent', () => { + let component: SharesAccountTermsStepComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SharesAccountTermsStepComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SharesAccountTermsStepComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.ts b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.ts new file mode 100644 index 000000000..65d04371d --- /dev/null +++ b/src/app/shares/shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component.ts @@ -0,0 +1,79 @@ +/** Angular Imports */ +import { Component, OnChanges, Input } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; + +/** + * Shares Account Terms Step + */ +@Component({ + selector: 'mifosx-shares-account-terms-step', + templateUrl: './shares-account-terms-step.component.html', + styleUrls: ['./shares-account-terms-step.component.scss'] +}) +export class SharesAccountTermsStepComponent implements OnChanges { + + /** Shares Account and Product Template */ + @Input() sharesAccountProductTemplate: any; + + /** Minimum date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum date allowed. */ + maxDate = new Date(); + /** Shares Account Terms Form */ + sharesAccountTermsForm: FormGroup; + /** Minimum Active Period Frequency Type Data */ + minimumActivePeriodFrequencyTypeData: any; + /** Lockin Period Frequency Type Data */ + lockinPeriodFrequencyTypeData: any; + /** Savings Accounts Data */ + savingsAccountsData: any; + + /** + * @param {FormBuilder} formBuilder Form Builder + */ + constructor(private formBuilder: FormBuilder) { + this.createSharesAccountTermsForm(); + } + + ngOnChanges() { + if (this.sharesAccountProductTemplate) { + this.sharesAccountTermsForm.patchValue({ + 'currencyCode': this.sharesAccountProductTemplate.currency.code, + 'decimal': this.sharesAccountProductTemplate.currency.decimalPlaces, + 'currencyMultiple': this.sharesAccountProductTemplate.currency.inMultiplesOf, + 'unitPrice': this.sharesAccountProductTemplate.currentMarketPrice + }); + this.minimumActivePeriodFrequencyTypeData = this.sharesAccountProductTemplate.minimumActivePeriodFrequencyTypeOptions; + this.lockinPeriodFrequencyTypeData = this.sharesAccountProductTemplate.lockinPeriodFrequencyTypeOptions; + this.savingsAccountsData = this.sharesAccountProductTemplate.clientSavingsAccounts; + } + } + + /** + * Creates shares account terms form. + */ + createSharesAccountTermsForm() { + this.sharesAccountTermsForm = this.formBuilder.group({ + 'currencyCode': [{value: '', disabled: true}], + 'decimal': [{value: '', disabled: true}], + 'requestedShares': ['', Validators.required], + 'unitPrice': [{value: '', disabled: true}], + 'currencyMultiple': [{value: '', disabled: true}], + 'savingsAccountId': ['', Validators.required], + 'minimumActivePeriod': [''], + 'minimumActivePeriodFrequencyType': [''], + 'lockinPeriodFrequency': [''], + 'lockinPeriodFrequencyType': [''], + 'applicationDate': ['', Validators.required], + 'allowDividendCalculationForInactiveClients': [false], + }); + } + + /** + * Returns shares account terms form value. + */ + get sharesAccountTerms() { + return this.sharesAccountTermsForm.value; + } + +} diff --git a/src/app/shares/shares-routing.module.ts b/src/app/shares/shares-routing.module.ts index 69c9605b2..8c5e2f03a 100644 --- a/src/app/shares/shares-routing.module.ts +++ b/src/app/shares/shares-routing.module.ts @@ -10,15 +10,25 @@ import { SharesAccountViewComponent } from './shares-account-view/shares-account import { TransactionsTabComponent } from './shares-account-view/transactions-tab/transactions-tab.component'; import { ChargesTabComponent } from './shares-account-view/charges-tab/charges-tab.component'; import { DividendsTabComponent } from './shares-account-view/dividends-tab/dividends-tab.component'; +import { CreateSharesAccountComponent } from './create-shares-account/create-shares-account.component'; /** Custom Resolvers */ import { SharesAccountViewResolver } from './common-resolvers/share-account-view.resolver'; +import { SharesAccountTemplateResolver } from './common-resolvers/shares-account-template.resolver'; const routes: Routes = [ { path: '', data: { title: extract('Shares'), breadcrumb: 'Shares', routeParamBreadcrumb: false }, children: [ + { + path: 'create-shares-account', + data: { title: extract('Create Shares Account'), breadcrumb: 'Create Shares Account' }, + component: CreateSharesAccountComponent, + resolve: { + sharesAccountTemplate: SharesAccountTemplateResolver + } + }, { path: ':shareAccountId', data: { title: extract('Shares Account View'), routeParamBreadcrumb: 'shareAccountId' }, @@ -52,7 +62,8 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - SharesAccountViewResolver + SharesAccountViewResolver, + SharesAccountTemplateResolver ] }) export class SharesRoutingModule { } diff --git a/src/app/shares/shares.module.ts b/src/app/shares/shares.module.ts index e8839b245..06928e4a3 100644 --- a/src/app/shares/shares.module.ts +++ b/src/app/shares/shares.module.ts @@ -11,6 +11,11 @@ import { SharesAccountViewComponent } from './shares-account-view/shares-account import { TransactionsTabComponent } from './shares-account-view/transactions-tab/transactions-tab.component'; import { DividendsTabComponent } from './shares-account-view/dividends-tab/dividends-tab.component'; import { ChargesTabComponent } from './shares-account-view/charges-tab/charges-tab.component'; +import { CreateSharesAccountComponent } from './create-shares-account/create-shares-account.component'; +import { SharesAccountDetailsStepComponent } from './shares-account-stepper/shares-account-details-step/shares-account-details-step.component'; +import { SharesAccountTermsStepComponent } from './shares-account-stepper/shares-account-terms-step/shares-account-terms-step.component'; +import { SharesAccountChargesStepComponent } from './shares-account-stepper/shares-account-charges-step/shares-account-charges-step.component'; +import { SharesAccountPreviewStepComponent } from './shares-account-stepper/shares-account-preview-step/shares-account-preview-step.component'; /** * Shares Module @@ -28,7 +33,12 @@ import { ChargesTabComponent } from './shares-account-view/charges-tab/charges-t SharesAccountViewComponent, TransactionsTabComponent, DividendsTabComponent, - ChargesTabComponent + ChargesTabComponent, + CreateSharesAccountComponent, + SharesAccountDetailsStepComponent, + SharesAccountTermsStepComponent, + SharesAccountChargesStepComponent, + SharesAccountPreviewStepComponent ], providers: [DatePipe] }) diff --git a/src/app/shares/shares.service.ts b/src/app/shares/shares.service.ts index 1e267e0ab..26ec9d258 100644 --- a/src/app/shares/shares.service.ts +++ b/src/app/shares/shares.service.ts @@ -23,4 +23,22 @@ export class SharesService { return this.http.get(`/accounts/share/${accountId}`); } + /** + * @param clientId Client Id assosciated with shares account. + * @returns {Observable} Shares account template. + */ + getSharesAccountTemplate(clientId: string, productId?: string): Observable { + let httpParams = new HttpParams().set('clientId', clientId); + httpParams = productId ? httpParams.set('productId', productId) : httpParams; + return this.http.get('/accounts/share/template', { params: httpParams }); + } + + /** + * @param {any} sharesAccount Shares Account + * @returns {Observable} + */ + createSharesAccount(sharesAccount: any): Observable { + return this.http.post('/accounts/share', sharesAccount); + } + }