done authentication service
diff --git a/package-lock.json b/package-lock.json
index 67feb85..2766a61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -241,6 +241,29 @@
"tslib": "1.9.0"
}
},
+ "@covalent/core": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@covalent/core/-/core-1.0.1.tgz",
+ "integrity": "sha512-br5KMBT8xXlctkSENGHnXsV4xyJuq0+yXopHH02zz9E0j1d1zlB43hM7zU1FkeGIFtWtPP7peM4tjD6S+ujkXw==",
+ "requires": {
+ "tslib": "1.9.0"
+ }
+ },
+ "@ngrx/core": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@ngrx/core/-/core-1.2.0.tgz",
+ "integrity": "sha1-iCtGq6+i4ObYh8txobLC+j5tDcY="
+ },
+ "@ngrx/effects": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-2.0.5.tgz",
+ "integrity": "sha1-EJhpI7cZOvmwiUToDFpmG6k6eTY="
+ },
+ "@ngrx/store": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-2.2.3.tgz",
+ "integrity": "sha1-570RSfHEQgjxzEdENT8PmKDx9Xs="
+ },
"@ngtools/json-schema": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ngtools/json-schema/-/json-schema-1.2.0.tgz",
@@ -263,6 +286,22 @@
"webpack-sources": "1.1.0"
}
},
+ "@ngx-translate/core": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-10.0.2.tgz",
+ "integrity": "sha512-7nM3DrJaqKswwtJlbu2kuKNl+hE8Isr18sKsKvGGpSxQk+G0gO0reDlx2PhUNus7TJTkA1C59vU/JoN8hIvZ4g==",
+ "requires": {
+ "tslib": "1.9.0"
+ }
+ },
+ "@ngx-translate/http-loader": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-3.0.1.tgz",
+ "integrity": "sha1-ILD5i8bCUyESnT4zAqs8xInApCo=",
+ "requires": {
+ "tslib": "1.9.0"
+ }
+ },
"@schematics/angular": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-0.3.2.tgz",
@@ -4093,7 +4132,8 @@
"jsbn": {
"version": "0.1.1",
"bundled": true,
- "dev": true
+ "dev": true,
+ "optional": true
},
"json-schema": {
"version": "0.2.3",
@@ -7225,6 +7265,11 @@
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
"dev": true
},
+ "ngrx-store-localstorage": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-5.0.0.tgz",
+ "integrity": "sha1-rcW4Nz/umV9rssUIoOUQ/CBHp04="
+ },
"no-case": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
@@ -9054,6 +9099,11 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
+ "reselect": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz",
+ "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc="
+ },
"resolve": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz",
diff --git a/package.json b/package.json
index d33309e..0a94c0c 100644
--- a/package.json
+++ b/package.json
@@ -23,9 +23,17 @@
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
+ "@covalent/core": "^1.0.1",
+ "@ngrx/core": "^1.2.0",
+ "@ngrx/effects": "^2.0.5",
+ "@ngrx/store": "^2.2.3",
+ "@ngx-translate/core": "^10.0.2",
+ "@ngx-translate/http-loader": "0.1.0",
"core-js": "^2.4.1",
"hammerjs": "^2.0.8",
"material-design-icons": "^3.0.1",
+ "ngrx-store-localstorage": "^5.0.0",
+ "reselect": "^3.0.1",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"
},
diff --git a/protractor.conf.js b/protractor.conf.js
index 7ee3b5e..584292a 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -12,7 +12,7 @@
'browserName': 'chrome'
},
directConnect: true,
- baseUrl: 'http://localhost:4200/',
+ baseUrl: 'http://163.172.130.175:8888/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index f257139..e931796 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -2,6 +2,11 @@
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterModule, Routes} from '@angular/router';
+import {HttpModule, Http} from '@angular/http';
+import {CommonModule} from '@angular/common';
+import {TranslateModule, TranslateStore} from '@ngx-translate/core';
+import {CovalentLoadingModule} from '@covalent/core';
+
import {
MatAutocompleteModule,
MatButtonModule,
@@ -38,7 +43,7 @@
} from '@angular/material';
-import { NgModule } from '@angular/core';
+import {LOCALE_ID, NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@@ -64,7 +69,47 @@
import { AddLedgerComponent } from './accounting/add-ledger/add-ledger.component';
import { AccountPayableComponent } from './accounting/account-payable/account-payable.component';
import { AddChequeComponent } from './accounting/add-cheque/add-cheque.component';
-import { AddPayrollComponent } from './accounting/add-payroll/add-payroll.component'
+import { AddPayrollComponent } from './accounting/add-payroll/add-payroll.component';
+
+import {HttpClient} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/http/http.service';
+import {IdentityService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/identity.service';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {CustomerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/customer/customer.service';
+import {AuthenticationService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authn/authentication.service';
+import {CatalogService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/catalog/catalog.service';
+import {AccountingService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/accounting.service';
+import {PortfolioService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/portfolio/portfolio.service';
+import {TranslateLoader, TranslateService} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {PermittableGroupIdMapper} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/permittable-group-id-mapper';
+import {reducer} from './store';
+import {StoreModule} from '@ngrx/store';
+import {EffectsModule} from '@ngrx/effects';
+import {NotificationService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/notification/notification.service';
+import {OfficeSearchApiEffects} from './store/office/effects/service.effects';
+import {EmployeeSearchApiEffects} from './store/employee/effects/service.effects';
+import {RoleSearchApiEffects} from './store/role/effects/service.effects';
+import {CustomerSearchApiEffects} from './store/customer/effects/service.effects';
+import {AccountSearchApiEffects} from './store/account/effects/service.effects';
+import {SecurityRouteEffects} from './store/security/effects/route.effects';
+import {SecurityApiEffects} from './store/security/effects/service.effects';
+import {SecurityNotificationEffects} from './store/security/effects/notification.effects';
+import {LedgerSearchApiEffects} from './store/ledger/effects/service.effects';
+//import {ExistsGuardService} from './common/guards/exists-guard';
+import {CountryService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/country/country.service';
+import {ImageService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/image/image.service';
+import {DepositAccountService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/depositAccount/deposit-account.service';
+import {CurrencyService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/currency/currency.service';
+import {TellerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/teller-service';
+import {ReportingService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/reporting/reporting.service';
+import {getSelectedLanguage} from './common/i18n/translate';
+import {ChequeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/cheque/cheque.service';
+import {PayrollService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/payroll/payroll.service';
+
+export function HttpLoaderFactory(http: Http) {
+ return new TranslateHttpLoader(http, './assets/i18n/', '.json');
+}
+
const appRoutes: Routes = [
{ path: 'login', component: LoginComponent },
@@ -102,10 +147,39 @@
declarations: [
AppComponent,LoginComponent, NavbarComponent, DashboardComponent,
AccountingComponent, GeneralLedgerComponent, AddJournalEntryComponent,
- PayrollsComponent, ChartOfAccountsComponent, AddTransactionTypeComponent, TrialBalanceComponent, ChequeClearingComponent, TransactionTypeComponent, AddMemberComponent, ManageMembersComponent, AddEmployeeComponent, ManageEmployeeComponent, AddOfficeComponent, ViewOfficesComponent, AddLedgerComponent, AccountPayableComponent, AddChequeComponent, AddPayrollComponent
+ PayrollsComponent, ChartOfAccountsComponent, AddTransactionTypeComponent,
+ TrialBalanceComponent, ChequeClearingComponent, TransactionTypeComponent,
+ AddMemberComponent, ManageMembersComponent, AddEmployeeComponent, ManageEmployeeComponent, AddOfficeComponent,
+ ViewOfficesComponent, AddLedgerComponent, AccountPayableComponent, AddChequeComponent, AddPayrollComponent,
+
],
imports: [RouterModule.forRoot(appRoutes),
BrowserModule, BrowserAnimationsModule,
+ CommonModule, TranslateModule,CovalentLoadingModule,
+ FormsModule, ReactiveFormsModule,HttpModule,
+ TranslateModule.forRoot({
+ loader: {
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [Http]
+ }
+ }),
+ StoreModule.provideStore(reducer),
+
+ /**
+ * Root effects
+ */
+ EffectsModule.run(SecurityApiEffects),
+ EffectsModule.run(SecurityRouteEffects),
+ EffectsModule.run(SecurityNotificationEffects),
+
+ EffectsModule.run(OfficeSearchApiEffects),
+ EffectsModule.run(EmployeeSearchApiEffects),
+ EffectsModule.run(CustomerSearchApiEffects),
+ EffectsModule.run(AccountSearchApiEffects),
+ EffectsModule.run(RoleSearchApiEffects),
+ EffectsModule.run(LedgerSearchApiEffects),
+
MatAutocompleteModule,
MatButtonModule,
MatButtonToggleModule,
@@ -139,7 +213,31 @@
MatTabsModule,
MatStepperModule
],
- providers: [],
+ providers: [ HttpClient,
+ AuthenticationService,
+ PermittableGroupIdMapper,
+ IdentityService,
+ OfficeService,
+ CustomerService,
+ CatalogService,
+ AccountingService,
+ PortfolioService,
+ DepositAccountService,
+ TellerService,
+ ReportingService,
+ ChequeService,
+ PayrollService,
+ CountryService,
+ CurrencyService,
+ NotificationService,
+ TranslateService,
+
+ // ExistsGuardService,
+ // ...appRoutingProviders,
+ ImageService,
+ {
+ provide: LOCALE_ID, useFactory: getSelectedLanguage, deps: [TranslateService],
+ } ],
bootstrap: [AppComponent]
})
export class AppModule { }
diff --git a/src/app/common/data-table/data-table.component.html b/src/app/common/data-table/data-table.component.html
new file mode 100644
index 0000000..88eb3c1
--- /dev/null
+++ b/src/app/common/data-table/data-table.component.html
@@ -0,0 +1,74 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<div class="mat-content">
+ <table td-data-table>
+ <thead>
+ <tr td-data-table-column-row>
+ <th td-data-table-column
+ *ngFor="let column of _columns"
+ [numeric]="column.numeric"
+ [sortable]="sortable"
+ [name]="column.name"
+ [active]="currentSort.sortColumn === column.name"
+ [sortOrder]="currentSort.sortColumn === column.name ? currentSort.sortDirection : ''"
+ (sortChange)="sortChanged($event)">
+ {{column.label}}
+ </th>
+ <th td-data-table-column *ngIf="actionColumn"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr td-data-table-row *ngFor="let row of data?.data">
+ <ng-container *ngFor="let column of _columns">
+ <td td-data-table-cell [numeric]="column.numeric" *ngIf="!isBoolean(row[column.name])">
+ {{column.format ? column.format(row[column.name]) : row[column.name]}}
+ </td>
+ <td td-data-table-cell *ngIf="isBoolean(row[column.name])">
+ <mat-icon *ngIf="row[column.name]">check</mat-icon>
+ <mat-icon *ngIf="!row[column.name]">close</mat-icon>
+ </td>
+ </ng-container>
+ <td td-data-table-cell (click)="actionCellClick(row)" *ngIf="actionColumn">
+ <button mat-button class="mat-accent">{{ actionColumnLabel | translate}}</button>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+ <div class="mat-padding" *ngIf="!hasData && !loading" layout="row" layout-align="center center">
+ <h3 translate>No results to display.</h3>
+ </div>
+
+ <div class="mat-padding" *ngIf="loading" layout="row" layout-align="center center">
+ <h3 translate>Fetching results...</h3>
+ </div>
+
+ <td-paging-bar #pagingBar
+ *ngIf="pageable"
+ [pageSize]="currentPage.size"
+ [total]="data?.totalElements"
+ (change)="page($event)">
+ <span hide-xs translate>Rows per page</span>
+ <mat-select [style.width.px]="50" [(ngModel)]="currentPage.size">
+ <mat-option *ngFor="let size of pageSizes" [value]="size">
+ {{size}}
+ </mat-option>
+ </mat-select>
+ {{pagingBar.range}} <span hide-xs translate>of</span><span> {{pagingBar.total}}</span>
+ </td-paging-bar>
+</div>
diff --git a/src/app/common/data-table/data-table.component.spec.ts b/src/app/common/data-table/data-table.component.spec.ts
new file mode 100644
index 0000000..edfc95f
--- /dev/null
+++ b/src/app/common/data-table/data-table.component.spec.ts
@@ -0,0 +1,158 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Component, DebugElement, EventEmitter, ViewChild} from '@angular/core';
+import {DataTableComponent, TableData, TableFetchRequest} from './data-table.component';
+import {
+ CovalentDataTableModule,
+ CovalentPagingModule,
+ ITdDataTableColumn,
+ TdDataTableColumnComponent,
+ TdDataTableSortingOrder
+} from '@covalent/core';
+import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {TranslateModule} from '@ngx-translate/core';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {MatIconModule, MatOptionModule, MatSelectModule} from '@angular/material';
+import {FormsModule} from '@angular/forms';
+
+describe('Test data table component', () => {
+
+ let fixture: ComponentFixture<TestComponent>;
+
+ let testComponent: TestComponent;
+
+ let columns: DebugElement[];
+
+ // function click(element: DebugElement) {
+ // element.triggerEventHandler('click', new Event('click'));
+ // }
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ TestComponent,
+ DataTableComponent
+ ],
+ imports: [
+ TranslateModule.forRoot(),
+ NoopAnimationsModule,
+ FormsModule,
+ MatSelectModule,
+ MatOptionModule,
+ MatIconModule,
+ CovalentDataTableModule,
+ CovalentPagingModule
+ ]
+
+ });
+
+ fixture = TestBed.createComponent(TestComponent);
+ testComponent = fixture.componentInstance;
+
+ fixture.detectChanges();
+
+ columns = fixture.debugElement.queryAll(By.directive(TdDataTableColumnComponent));
+ });
+
+ it('should render 2 columns', () => {
+ expect(columns.length).toEqual(2);
+ });
+
+ it('should sort ascending', fakeAsync(() => {
+ const expectedResult: TableFetchRequest = {
+ sort: { sortDirection: 'DESC', sortColumn: 'identifier' },
+ page: { pageIndex: 0, size: 10 }
+ };
+
+ let result: TableFetchRequest;
+
+ testComponent.fetchRequestEmitter.subscribe(fetchRequest => result = fetchRequest);
+
+ // TODO figure out why click does not come through
+ // click(columns[0]);
+
+ testComponent.dataTableComponent.sortChanged({
+ order: TdDataTableSortingOrder.Ascending,
+ name: 'identifier'
+ });
+
+ fixture.detectChanges();
+
+ tick();
+
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should sort descending', fakeAsync(() => {
+ const expectedResult: TableFetchRequest = {
+ sort: { sortDirection: 'ASC', sortColumn: 'name' },
+ page: { pageIndex: 0, size: 10 }
+ };
+
+ let result: TableFetchRequest;
+
+ testComponent.fetchRequestEmitter.subscribe(fetchRequest => result = fetchRequest);
+
+ // TODO figure out why click does not come through
+ // click(columns[0]);
+
+ testComponent.dataTableComponent.sortChanged({
+ order: TdDataTableSortingOrder.Descending,
+ name: 'name'
+ });
+
+ fixture.detectChanges();
+
+ tick();
+
+ expect(result).toEqual(expectedResult);
+ }));
+
+});
+
+@Component({
+ template: `
+ <fims-data-table #datatable [data]="tableData" [columns]="columns" (onFetch)="onFetch($event)" [sortable]="true" [actionColumn]="false">
+ </fims-data-table>`
+})
+class TestComponent {
+
+ fetchRequestEmitter = new EventEmitter<TableFetchRequest>();
+
+ tableData: TableData = {
+ data: [{
+ identifier: 'test',
+ name: 'test'
+ }],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ columns: ITdDataTableColumn[] = [
+ { name: 'identifier', label: 'identifier', sortable: true },
+ { name: 'name', label: 'name', sortable: true }
+ ];
+
+ @ViewChild('datatable') dataTableComponent: DataTableComponent;
+
+ onFetch(fetchRequest: TableFetchRequest): void {
+ this.fetchRequestEmitter.emit(fetchRequest);
+ }
+}
diff --git a/src/app/common/data-table/data-table.component.ts b/src/app/common/data-table/data-table.component.ts
new file mode 100644
index 0000000..9035975
--- /dev/null
+++ b/src/app/common/data-table/data-table.component.ts
@@ -0,0 +1,129 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Component, EventEmitter, Input, Output} from '@angular/core';
+import {Sort} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/sort.model';
+import {Page} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/page.model';
+import {IPageChangeEvent, ITdDataTableColumn, ITdDataTableSortChangeEvent, TdDataTableSortingOrder} from '@covalent/core';
+import {TranslateService} from '@ngx-translate/core';
+
+export interface TableData {
+ data: any[];
+ totalElements: number;
+ totalPages: number;
+}
+
+export interface TableFetchRequest {
+ page: Page;
+ sort: Sort;
+}
+
+@Component({
+ selector: 'fims-data-table',
+ templateUrl: './data-table.component.html'
+})
+export class DataTableComponent {
+
+ pageSizes: number[] = [10, 15, 20];
+
+ private currentPage: Page = {
+ pageIndex: 0,
+ size: this.pageSizes[0]
+ };
+
+ private currentSort: Sort = {
+ sortColumn: 'identifier',
+ sortDirection: 'ASC'
+ };
+
+ _columns: any[];
+
+ @Input('data') data: TableData = {
+ totalElements: 0,
+ totalPages: 0,
+ data: []
+ };
+
+ @Input() set columns(columns: ITdDataTableColumn[]) {
+ columns.forEach((column) => {
+ this.translate.get(column.label)
+ .subscribe((value) => {
+ column.label = value;
+ column.tooltip = value;
+ });
+ });
+ this._columns = columns;
+ };
+
+ @Input() sortable = false;
+
+ @Input() set sortBy(sortBy: string) {
+ this.currentSort.sortColumn = sortBy;
+ }
+
+ @Input() pageable = false;
+
+ @Input() actionColumn = true;
+
+ @Input() actionColumnLabel = 'SHOW';
+
+ @Input() loading = false;
+
+ @Output() onFetch: EventEmitter<TableFetchRequest> = new EventEmitter<TableFetchRequest>();
+
+ @Output() onActionCellClick: EventEmitter<any> = new EventEmitter<any>();
+
+ constructor(private translate: TranslateService) {}
+
+ page(pagingEvent: IPageChangeEvent): void {
+ this.currentPage = {
+ pageIndex: pagingEvent.page - 1,
+ size: pagingEvent.pageSize
+ };
+ this.fetch();
+ }
+
+ sortChanged(event: ITdDataTableSortChangeEvent): void {
+ this.currentSort = {
+ sortDirection: event.order === TdDataTableSortingOrder.Ascending ? 'DESC' : 'ASC',
+ sortColumn: event.name
+ };
+ this.fetch();
+ }
+
+ private fetch() {
+ const fetchRequest: TableFetchRequest = {
+ page: this.currentPage,
+ sort: this.currentSort
+ };
+ this.onFetch.emit(fetchRequest);
+ }
+
+ actionCellClick(row): void {
+ this.onActionCellClick.emit(row);
+ }
+
+ get hasData(): boolean {
+ return this.data && this.data.data && this.data.data.length > 0;
+ }
+
+ isBoolean(value: any): boolean {
+ return typeof(value) === 'boolean';
+ }
+
+}
diff --git a/src/app/common/i18n/translate.ts b/src/app/common/i18n/translate.ts
new file mode 100644
index 0000000..b96aa8e
--- /dev/null
+++ b/src/app/common/i18n/translate.ts
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TranslateService} from '@ngx-translate/core';
+
+export const TRANSLATE_STORAGE_KEY = 'fims-translate-lang';
+
+export function getSelectedLanguage(translateService: TranslateService): string {
+ const storedLanguage: string = sessionStorage.getItem(TRANSLATE_STORAGE_KEY);
+
+ if (storedLanguage && translateService.getLangs().indexOf(storedLanguage) > -1) {
+ return storedLanguage;
+ } else if (translateService.getLangs().indexOf(translateService.getBrowserLang()) > -1) {
+ return translateService.getBrowserLang();
+ }
+
+ return translateService.getDefaultLang();
+}
diff --git a/src/app/common/regex/escape.ts b/src/app/common/regex/escape.ts
new file mode 100644
index 0000000..3765c77
--- /dev/null
+++ b/src/app/common/regex/escape.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export function escapeRegexPattern(value?: string): string {
+ if (!value) {
+ return '';
+ }
+
+ return value.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
diff --git a/src/app/common/store/action-creator/action-creator.ts b/src/app/common/store/action-creator/action-creator.ts
new file mode 100644
index 0000000..1034751
--- /dev/null
+++ b/src/app/common/store/action-creator/action-creator.ts
@@ -0,0 +1,177 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {type} from '../../../store/util';
+import {
+ LoadAction,
+ LoadActionPayload,
+ LoadAllAction,
+ LoadAllCompleteAction,
+ LoadAllCompleteActionPayload,
+ ResourceAction,
+ ResourceActionPayload,
+ ResourceActions,
+ SelectAction,
+ SelectActionPayload
+} from './actions';
+
+export type Actions<T> = LoadAllAction | LoadAllCompleteAction<T>;
+
+export function createResourceActions<T>(name: string): ResourceActions<T> {
+
+ const LOAD_ALL = type(`[${name}] Load All`);
+ const LOAD_ALL_COMPLETE = type(`[${name}] Load All Complete`);
+
+ const LOAD = type(`[${name}] Load`);
+
+ const SELECT = type(`[${name}] Select`);
+
+ const CREATE = type(`[${name}] Create`);
+ const CREATE_SUCCESS = type(`[${name}] Create Success`);
+ const CREATE_FAIL = type(`[${name}] Create Fail`);
+
+ const UPDATE = type(`[${name}] Update`);
+ const UPDATE_SUCCESS = type(`[${name}] Update Success`);
+ const UPDATE_FAIL = type(`[${name}] Update Fail`);
+
+ const DELETE = type(`[${name}] Delete`);
+ const DELETE_SUCCESS = type(`[${name}] Delete Success`);
+ const DELETE_FAIL = type(`[${name}] Delete Fail`);
+
+ function loadAllAction(payload?: any): LoadAllAction {
+ return {
+ payload,
+ type: LOAD_ALL
+ };
+ }
+
+ function loadAllCompleteAction(payload?: LoadAllCompleteActionPayload<T>): LoadAllCompleteAction<T> {
+ return {
+ payload,
+ type: LOAD_ALL_COMPLETE
+ };
+ }
+
+ function loadAction(payload: LoadActionPayload<T>): LoadAction<T> {
+ return {
+ payload,
+ type: LOAD
+ };
+ }
+
+ function selectAction(payload: SelectActionPayload): SelectAction {
+ return {
+ payload,
+ type: SELECT
+ };
+ }
+
+ function createAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: CREATE
+ };
+ }
+
+ function createSuccessAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: CREATE_SUCCESS
+ };
+ }
+
+ function createFailAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: CREATE_FAIL
+ };
+ }
+
+ function updateAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: UPDATE
+ };
+ }
+
+ function updateSuccessAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: UPDATE_SUCCESS
+ };
+ }
+
+ function updateFailAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: UPDATE_FAIL
+ };
+ }
+
+ function deleteAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: DELETE
+ };
+ }
+
+ function deleteSuccessAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: DELETE_SUCCESS
+ };
+ }
+
+ function deleteFailAction(payload: ResourceActionPayload<T>): ResourceAction<T> {
+ return {
+ payload,
+ type: DELETE_FAIL
+ };
+ }
+
+ return {
+ LOAD_ALL,
+ LOAD_ALL_COMPLETE,
+ LOAD,
+ SELECT,
+ CREATE,
+ CREATE_SUCCESS,
+ CREATE_FAIL,
+ UPDATE,
+ UPDATE_SUCCESS,
+ UPDATE_FAIL,
+ DELETE,
+ DELETE_SUCCESS,
+ DELETE_FAIL,
+ loadAllAction,
+ loadAllCompleteAction,
+ loadAction,
+ selectAction,
+ createAction,
+ createSuccessAction,
+ createFailAction,
+ updateAction,
+ updateSuccessAction,
+ updateFailAction,
+ deleteAction,
+ deleteSuccessAction,
+ deleteFailAction
+ };
+}
+
+
diff --git a/src/app/common/store/action-creator/actions.ts b/src/app/common/store/action-creator/actions.ts
new file mode 100644
index 0000000..f14f743
--- /dev/null
+++ b/src/app/common/store/action-creator/actions.ts
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+
+// Actions
+export interface LoadAllAction extends Action {
+ payload?: any;
+}
+
+export interface LoadAllCompleteAction<T> extends Action {
+ payload?: LoadAllCompleteActionPayload<T>;
+}
+
+export interface LoadAction<T> extends Action {
+ payload: LoadActionPayload<T>;
+}
+
+export interface SelectAction extends Action {
+ payload: SelectActionPayload;
+}
+
+export interface ResourceAction<T> extends Action {
+ payload: ResourceActionPayload<T>;
+}
+
+interface DataPayload {
+ data?: any;
+}
+
+// Payload
+export interface LoadAllCompleteActionPayload<T> extends DataPayload {
+ resources: T[];
+}
+
+export interface LoadActionPayload<T> extends DataPayload {
+ resource: T;
+}
+
+export interface SelectActionPayload extends DataPayload {
+ identifier: string;
+}
+
+export interface ResourceActionPayload<T> extends DataPayload {
+ resource: T;
+}
+
+export interface ResourceSuccessActionPayload<T> extends DataPayload {
+ resource: T;
+}
+
+export interface ResourceFailActionPayload<T> extends DataPayload {
+ resource: T;
+ error: Error;
+}
+
+export interface ResourceActions<T> {
+ LOAD_ALL: string;
+ LOAD_ALL_COMPLETE: string;
+ LOAD: string;
+ SELECT: string;
+ CREATE: string;
+ CREATE_SUCCESS: string;
+ CREATE_FAIL: string;
+ UPDATE: string;
+ UPDATE_SUCCESS: string;
+ UPDATE_FAIL: string;
+ DELETE: string;
+ DELETE_SUCCESS: string;
+ DELETE_FAIL: string;
+
+ loadAllAction(payload?: any): LoadAllAction;
+ loadAllCompleteAction(payload?: LoadAllCompleteActionPayload<T>): LoadAllCompleteAction<T>;
+ loadAction(payload: LoadActionPayload<T>): LoadAction<T>;
+ selectAction(payload: SelectActionPayload): SelectAction;
+ createAction(payload: ResourceActionPayload<T>): ResourceAction<T>;
+ createSuccessAction(payload: ResourceSuccessActionPayload<T>): ResourceAction<T>;
+ createFailAction(payload: ResourceFailActionPayload<T>): ResourceAction<T>;
+ updateAction(payload: ResourceActionPayload<T>): ResourceAction<T>;
+ updateSuccessAction(payload: ResourceSuccessActionPayload<T>): ResourceAction<T>;
+ updateFailAction(payload: ResourceFailActionPayload<T>): ResourceAction<T>;
+ deleteAction(payload: ResourceActionPayload<T>): ResourceAction<T>;
+ deleteSuccessAction(payload: ResourceSuccessActionPayload<T>): ResourceAction<T>;
+ deleteFailAction(payload: ResourceFailActionPayload<T>): ResourceAction<T>;
+}
diff --git a/src/app/common/store/form.reducer.spec.ts b/src/app/common/store/form.reducer.spec.ts
new file mode 100644
index 0000000..494df59
--- /dev/null
+++ b/src/app/common/store/form.reducer.spec.ts
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {createFormReducer, FormState} from './form.reducer';
+import {Error} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/error.model';
+import {Action} from '@ngrx/store';
+
+class CreateSuccessAction implements Action {
+ readonly type = '[Test] Create Success';
+
+ constructor() {}
+}
+
+class CreateFailAction implements Action {
+ readonly type = '[Test] Create Fail';
+
+ constructor(public payload: Error) {}
+}
+
+class UpdateSuccessAction implements Action {
+ readonly type = '[Test] Update Success';
+
+ constructor() {}
+}
+
+class UpdateFailAction implements Action {
+ readonly type = '[Test] Update Fail';
+
+ constructor(public payload: Error) {}
+}
+
+describe('Resources Reducer', () => {
+
+ let reducer;
+
+ beforeEach(() => {
+ reducer = createFormReducer('Test');
+ });
+
+ it('should set error on Create Fail Action', () => {
+ const expectedResult: FormState = {
+ error: new Error(409, 'test', 'test')
+ };
+
+ const result = reducer(undefined, new CreateFailAction(expectedResult.error));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should set error on Update Fail Action', () => {
+ const expectedResult: FormState = {
+ error: new Error(409, 'test', 'test')
+ };
+
+ const result = reducer(undefined, new UpdateFailAction(expectedResult.error));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should reset error on Create Success Action', () => {
+ const expectedResult: FormState = {};
+
+ const result = reducer(undefined, new CreateSuccessAction());
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should reset error on Update Success Action', () => {
+ const expectedResult: FormState = {};
+
+ const result = reducer(undefined, new UpdateSuccessAction());
+
+ expect(result).toEqual(expectedResult);
+ });
+});
diff --git a/src/app/common/store/form.reducer.ts b/src/app/common/store/form.reducer.ts
new file mode 100644
index 0000000..6a28bd0
--- /dev/null
+++ b/src/app/common/store/form.reducer.ts
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action, ActionReducer} from '@ngrx/store';
+import {Error} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/error.model';
+
+export interface FormState {
+ error?: Error;
+}
+
+export const initialState: FormState = {};
+
+export const createFormReducer = (resource: string, reducer?: ActionReducer<FormState>) => {
+
+ return function(state: FormState = initialState, action: Action): FormState {
+
+ switch (action.type) {
+
+ case `[${resource}] Create Fail`:
+ case `[${resource}] Update Fail`: {
+ return Object.assign({}, state, {
+ error: action.payload
+ });
+ }
+
+ case `[${resource}] Reset Form`:
+ case `[${resource}] Create Success`:
+ case `[${resource}] Update Success`: {
+ return initialState;
+ }
+
+ default: {
+ // delegate to wrapped reducer
+ if (reducer) {
+ return reducer(state, action);
+ }
+ return state;
+ }
+
+ }
+ };
+};
+
+export const getFormError = (formState: FormState) => formState.error;
+
diff --git a/src/app/common/store/reducer.helper.spec.ts b/src/app/common/store/reducer.helper.spec.ts
new file mode 100644
index 0000000..e27124c
--- /dev/null
+++ b/src/app/common/store/reducer.helper.spec.ts
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from './reducer.helper';
+
+describe('Reducer Helper', () => {
+
+ it('should create hash with default identifier', () => {
+ const payload = [
+ { identifier: 'a', value: 'test value'},
+ { identifier: 'b', value: 'test value'}
+ ];
+
+ const expectedResult = {
+ a: payload[0],
+ b: payload[1],
+ };
+
+ const result = resourcesToHash(payload);
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should create hash with custom identifier', () => {
+ const payload = [
+ { customIdentifier: 'a', value: 'test value'},
+ { customIdentifier: 'b', value: 'test value'}
+ ];
+
+ const expectedResult = {
+ a: payload[0],
+ b: payload[1],
+ };
+
+ const result = resourcesToHash(payload, 'customIdentifier');
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should create hash with current timestamp', () => {
+ spyOn(Date, 'now').and.returnValue(1000);
+
+ const expectedResult = {
+ a: 1000,
+ b: 1000
+ };
+
+ const ids = ['a', 'b'];
+
+ const result = idsToHashWithCurrentTimestamp(ids);
+
+ expect(result).toEqual(expectedResult);
+ });
+
+});
diff --git a/src/app/common/store/reducer.helper.ts b/src/app/common/store/reducer.helper.ts
new file mode 100644
index 0000000..b0baea4
--- /dev/null
+++ b/src/app/common/store/reducer.helper.ts
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export function resourcesToHash(resources: any[], identifier: string = 'identifier') {
+ const hash = {};
+
+ resources.forEach(resource => hash[resource[identifier]] = resource);
+
+ return hash;
+}
+
+export function idsToHashWithCurrentTimestamp(ids: string[]): { [id: string]: number } {
+ const loadedAt = ids.reduce((entities: { [id: string]: number }, id: string) => {
+ return Object.assign(entities, {
+ [id]: Date.now()
+ });
+ }, {});
+
+ return loadedAt;
+}
diff --git a/src/app/common/store/resource.reducer.spec.ts b/src/app/common/store/resource.reducer.spec.ts
new file mode 100644
index 0000000..aeac748
--- /dev/null
+++ b/src/app/common/store/resource.reducer.spec.ts
@@ -0,0 +1,171 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {
+ createResourceReducer,
+ CreateResourceSuccessPayload,
+ DeleteResourceSuccessPayload,
+ LoadResourcePayload,
+ ResourceState,
+ UpdateResourceSuccessPayload
+} from './resource.reducer';
+import {Action} from '@ngrx/store';
+
+class LoadAction implements Action {
+ readonly type = '[Test] Load';
+
+ constructor(public payload: LoadResourcePayload) { }
+}
+
+class SelectAction implements Action {
+ readonly type = '[Test] Select';
+
+ constructor(public payload: string) { }
+}
+
+class CreateSuccessAction implements Action {
+ readonly type = '[Test] Create Success';
+
+ constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+class UpdateSuccessAction implements Action {
+ readonly type = '[Test] Update Success';
+
+ constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+class DeleteSuccessAction implements Action {
+ readonly type = '[Test] Delete Success';
+
+ constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+describe('Resources Reducer', () => {
+
+ let reducer;
+
+ beforeEach(() => {
+ reducer = createResourceReducer('Test');
+ });
+
+ it('should set selected id', () => {
+ const expectedResult: ResourceState = {
+ selectedId: 'test',
+ entities: {},
+ loadedAt: {},
+ ids: [],
+ };
+
+ const result = reducer(undefined, new SelectAction('test'));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should add resource on load', () => {
+ spyOn(Date, 'now').and.returnValue(1000);
+
+ const resource = {
+ identifier: 'test',
+ name: 'test'
+ };
+
+ const expectedResult: ResourceState = {
+ selectedId: null,
+ entities: {
+ 'test': resource
+ },
+ loadedAt: {
+ 'test': 1000
+ },
+ ids: ['test'],
+ };
+
+ const result = reducer(undefined, new LoadAction({
+ resource: resource
+ }));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should add resource when create success', () => {
+ const resource = {
+ identifier: 'test',
+ name: 'test'
+ };
+
+ const expectedResult: ResourceState = {
+ selectedId: null,
+ entities: {
+ 'test': resource
+ },
+ loadedAt: {},
+ ids: ['test'],
+ };
+
+ const result = reducer(undefined, new CreateSuccessAction({
+ resource: resource,
+ activatedRoute: null
+ }));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should update resource when update success', () => {
+ const updatedResource = {
+ identifier: 'test',
+ name: 'newValue'
+ };
+
+ const initialState: ResourceState = {
+ selectedId: null,
+ entities: {
+ 'test': {
+ name: 'oldValue'
+ }
+ },
+ loadedAt: {},
+ ids: ['test'],
+ };
+
+ const expectedResult: ResourceState = {
+ selectedId: null,
+ entities: {
+ 'test': updatedResource
+ },
+ loadedAt: {},
+ ids: ['test'],
+ };
+
+ const result = reducer(initialState, new UpdateSuccessAction({
+ resource: updatedResource,
+ activatedRoute: null
+ }));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should delete resource when delete success', () => {
+
+ });
+
+ it('should delegate to wrapper reducer on unhandled action', () => {
+
+ });
+
+});
diff --git a/src/app/common/store/resource.reducer.ts b/src/app/common/store/resource.reducer.ts
new file mode 100644
index 0000000..0e360fa
--- /dev/null
+++ b/src/app/common/store/resource.reducer.ts
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action, ActionReducer} from '@ngrx/store';
+import {createSelector} from 'reselect';
+import {RoutePayload} from './route-payload';
+
+export interface Resource {
+ identifier: string;
+}
+
+export interface LoadResourcePayload {
+ resource: any;
+}
+
+export interface SelectResourcePayload {
+ selectedId: string;
+}
+
+export interface CreateResourceSuccessPayload extends RoutePayload {
+ resource: any;
+}
+
+export interface UpdateResourceSuccessPayload extends RoutePayload {
+ resource: any;
+}
+
+export interface DeleteResourceSuccessPayload extends RoutePayload {
+ resource: any;
+}
+
+export interface ResourceState {
+ ids: string[];
+ entities: { [id: string]: any };
+ selectedId: string | null;
+ loadedAt: { [id: string]: number };
+}
+
+const initialState: ResourceState = {
+ ids: [],
+ entities: {},
+ loadedAt: {},
+ selectedId: null
+};
+
+export const createResourceReducer =
+ (resourceId: string, reducer?: ActionReducer<ResourceState>, identifierName: string = 'identifier') => {
+
+ const identifier = (resource: any) => resource[identifierName];
+
+ return function (state: ResourceState = initialState, action: Action): ResourceState {
+
+ switch (action.type) {
+
+ case `[${resourceId}] Load`: {
+ const resource = action.payload.resource;
+
+ const newIds = state.ids.filter(id => id !== identifier(resource));
+
+ return {
+ ids: [...newIds, identifier(resource)],
+ entities: Object.assign({}, state.entities, {
+ [identifier(resource)]: resource
+ }),
+ selectedId: state.selectedId,
+ loadedAt: Object.assign({}, state.entities, {
+ [identifier(resource)]: Date.now()
+ })
+ };
+ }
+
+ case `[${resourceId}] Select`: {
+ return Object.assign({}, state, {
+ selectedId: action.payload
+ });
+ }
+
+ case `[${resourceId}] Create Success`: {
+ const resource = action.payload.resource;
+
+ return {
+ ids: [...state.ids, identifier(resource)],
+ entities: Object.assign({}, state.entities, {
+ [identifier(resource)]: resource
+ }),
+ selectedId: state.selectedId,
+ loadedAt: state.loadedAt
+ };
+ }
+
+ case `[${resourceId}] Update Success`: {
+ const resource = action.payload.resource;
+
+ return {
+ ids: state.ids,
+ entities: Object.assign({}, state.entities, {
+ [identifier(resource)]: resource
+ }),
+ selectedId: state.selectedId,
+ loadedAt: state.loadedAt
+ };
+ }
+
+ case `[${resourceId}] Delete Success`: {
+ const resource = action.payload.resource;
+
+ const newIds = state.ids.filter(id => id !== identifier(resource));
+
+ const newEntities = newIds.reduce((entities: { [id: string]: any }, id: string) => {
+ const entity = state.entities[id];
+ return Object.assign(entities, {
+ [identifier(entity)]: entity
+ });
+ }, {});
+
+ const newLoadedAt = newIds.reduce((entities: { [id: string]: any }, id: string) => {
+ const loadedAt = state.loadedAt[id];
+ return Object.assign(entities, {
+ [id]: loadedAt
+ });
+ }, {});
+
+ return {
+ ids: [...newIds],
+ entities: newEntities,
+ loadedAt: newLoadedAt,
+ selectedId: state.selectedId,
+ };
+ }
+
+ default: {
+ // delegate to wrapped reducer
+ if (reducer) {
+ return reducer(state, action);
+ }
+ return state;
+ }
+ }
+ };
+ };
+
+export const getResourceEntities = (cacheState: ResourceState) => cacheState.entities;
+export const getResourceLoadedAt = (cacheState: ResourceState) => cacheState.loadedAt;
+export const getResourceIds = (cacheState: ResourceState) => cacheState.ids;
+export const getResourceSelectedId = (cacheState: ResourceState) => cacheState.selectedId;
+
+export const getResourceSelected = createSelector(getResourceEntities, getResourceSelectedId, (entities, selectedId) => {
+ return entities[selectedId];
+});
+
+export const getResourceAll = createSelector(getResourceEntities, getResourceIds, (entities, ids) => {
+ return ids.map(id => entities[id]);
+});
diff --git a/src/app/common/store/route-payload.ts b/src/app/common/store/route-payload.ts
new file mode 100644
index 0000000..6df7591
--- /dev/null
+++ b/src/app/common/store/route-payload.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ActivatedRoute} from '@angular/router';
+
+export interface RoutePayload {
+ activatedRoute: ActivatedRoute;
+}
diff --git a/src/app/common/store/search.reducer.ts b/src/app/common/store/search.reducer.ts
new file mode 100644
index 0000000..cc387ce
--- /dev/null
+++ b/src/app/common/store/search.reducer.ts
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action, ActionReducer} from '@ngrx/store';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {createSelector} from 'reselect';
+
+export function emptySearchResult(): SearchResult {
+ return {
+ elements: [],
+ totalElements: 0,
+ totalPages: 0
+ };
+}
+
+export interface SearchPayload {
+ fetchRequest: FetchRequest;
+}
+
+export interface SearchResult {
+ elements: any[];
+ totalElements: number;
+ totalPages: number;
+}
+
+export interface SearchState {
+ entities: any[];
+ totalPages: number;
+ totalElements: number;
+ loading: boolean;
+ fetchRequest: FetchRequest;
+}
+
+const initialState: SearchState = {
+ entities: [],
+ totalPages: 0,
+ totalElements: 0,
+ loading: false,
+ fetchRequest: null
+};
+
+export const createSearchReducer = (entityName: string, reducer?: ActionReducer<SearchState>) => {
+ return function(state: SearchState = initialState, action: Action): SearchState {
+
+ switch (action.type) {
+
+ case `[${entityName}] Search`: {
+ const fetchRequest: FetchRequest = action.payload;
+
+ return Object.assign({}, state, {
+ fetchRequest,
+ loading: true,
+ entities: []
+ });
+ }
+
+ case `[${entityName}] Search Complete`: {
+ const searchResult: SearchResult = action.payload;
+
+ return Object.assign({}, state, {
+ entities: searchResult.elements,
+ totalElements: searchResult.totalElements,
+ totalPages: searchResult.totalPages,
+ loading: false
+ });
+ }
+
+ default: {
+ // delegate to wrapped reducer
+ if (reducer) {
+ return reducer(state, action);
+ }
+ return state;
+ }
+ }
+ };
+};
+
+export const getSearchEntities = (state: SearchState) => state.entities;
+export const getSearchTotalElements = (state: SearchState) => state.totalElements;
+export const getSearchTotalPages = (state: SearchState) => state.totalPages;
+export const getSearchLoading = (state: SearchState) => state.loading;
+
+export const getSearchResult = createSelector(getSearchEntities, getSearchTotalElements, getSearchTotalPages,
+ (elements, totalElements, totalPages) => {
+ return {
+ elements,
+ totalElements,
+ totalPages
+ };
+});
diff --git a/src/app/common/testing/input-fields.ts b/src/app/common/testing/input-fields.ts
new file mode 100644
index 0000000..11f5bb7
--- /dev/null
+++ b/src/app/common/testing/input-fields.ts
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ComponentFixture} from '@angular/core/testing';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {DebugElement} from '@angular/core';
+
+export function setValueByFormControlName(fixture: ComponentFixture<any>, formControlName: string, value: string): Observable<any> {
+ const debugElement: DebugElement = fixture.debugElement.query(By.css(`input[formControlName="${formControlName}"]`));
+
+ if (!debugElement) {
+ throw new Error(`Could not find debug element for form control name: ${formControlName}`);
+ }
+
+ setValue(debugElement, value);
+
+ fixture.detectChanges();
+ return Observable.fromPromise(fixture.whenStable());
+}
+
+export function setValueByCssSelector(fixture: ComponentFixture<any>, selector: string, value: string): Observable<any> {
+ const debugElement: DebugElement = fixture.debugElement.query(By.css(selector));
+
+ if (!debugElement) {
+ throw new Error(`Could not find debug element with selector: ${selector}`);
+ }
+
+ setValue(debugElement, value);
+
+ fixture.detectChanges();
+ return Observable.fromPromise(fixture.whenStable());
+}
+
+function setValue(debugElement: DebugElement, value: string): void {
+ const inputElement: HTMLInputElement = debugElement.nativeElement;
+ inputElement.value = value;
+ inputElement.dispatchEvent(new Event('input'));
+}
diff --git a/src/app/common/testing/permission-stubs.ts b/src/app/common/testing/permission-stubs.ts
new file mode 100644
index 0000000..c7d48cd
--- /dev/null
+++ b/src/app/common/testing/permission-stubs.ts
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Directive, Input} from '@angular/core';
+import {FimsPermission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/fims-permission.model';
+
+@Directive({
+ // tslint:disable-next-line:directive-selector
+ selector: '[hasPermission]'
+})
+export class FimsPermissionStubDirective {
+
+ // tslint:disable-next-line:no-input-rename
+ @Input('hasPermission') permission: FimsPermission;
+
+}
diff --git a/src/app/common/testing/router-stubs.ts b/src/app/common/testing/router-stubs.ts
new file mode 100644
index 0000000..14814fb
--- /dev/null
+++ b/src/app/common/testing/router-stubs.ts
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import {NavigationExtras, Params} from '@angular/router';
+import {Component, Directive, Injectable, Input} from '@angular/core';
+
+@Directive({
+ // tslint:disable-next-line:directive-selector
+ selector: '[routerLink]',
+ // tslint:disable-next-line:use-host-property-decorator
+ host: {
+ '(click)': 'onClick()'
+ }
+})
+export class RouterLinkStubDirective {
+ // tslint:disable-next-line:no-input-rename
+ @Input('routerLink') linkParams: any;
+ navigatedTo: any = null;
+
+ onClick() {
+ this.navigatedTo = this.linkParams;
+ }
+}
+
+
+@Component({
+ // tslint:disable-next-line:component-selector
+ selector: 'router-outlet',
+ template: ''
+})
+export class RouterOutletStubComponent { }
+
+@Injectable()
+export class RouterStub {
+ navigate(commands: any[], extras?: NavigationExtras) { }
+}
+
+@Injectable()
+export class ActivatedRouteStub {
+
+ // ActivatedRoute.paramMap is Observable
+ private subject = new BehaviorSubject(this.testParams);
+
+ params = this.subject.asObservable();
+
+ // Test parameters
+ private _testParams: Params;
+
+ get testParams() { return this._testParams; }
+
+ set testParams(params: {}) {
+ this._testParams = params;
+ this.subject.next(this._testParams);
+ }
+
+ // ActivatedRoute.snapshot.paramMap
+ get snapshot() {
+ return { params: this.testParams };
+ }
+}
diff --git a/src/app/common/testing/select-fields.ts b/src/app/common/testing/select-fields.ts
new file mode 100644
index 0000000..afa7cde
--- /dev/null
+++ b/src/app/common/testing/select-fields.ts
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {By} from '@angular/platform-browser';
+import {ComponentFixture} from '@angular/core/testing';
+
+export function clickOption(fixture: ComponentFixture<any>, optionIndex: number): void {
+ const trigger = fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement;
+
+ trigger.click();
+
+ fixture.detectChanges();
+
+ const options = fixture.debugElement.queryAll(By.css('mat-option'));
+
+ options[optionIndex].nativeElement.click();
+
+ fixture.detectChanges();
+}
diff --git a/src/app/common/util/account-assignments.ts b/src/app/common/util/account-assignments.ts
new file mode 100644
index 0000000..2e25946
--- /dev/null
+++ b/src/app/common/util/account-assignments.ts
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountAssignment} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/portfolio/domain/account-assignment.model';
+
+export function findAccountDesignator(accountAssignments: AccountAssignment[], designator: string): AccountAssignment {
+ return accountAssignments.find(assignment => assignment.designator === designator);
+}
+
+export function createAccountAssignment(identifier: string, designator: string): AccountAssignment {
+ return {
+ accountIdentifier: identifier,
+ designator: designator
+ };
+}
+
+export function createLedgerAssignment(identifier: string, designator: string): AccountAssignment {
+ return {
+ ledgerIdentifier: identifier,
+ designator: designator
+ };
+}
+
+export function accountIdentifier(assignment: AccountAssignment): string {
+ return assignment ? assignment.accountIdentifier : undefined;
+}
+
+export function ledgerIdentifier(assignment: AccountAssignment): string {
+ return assignment ? assignment.ledgerIdentifier : undefined;
+}
diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html
index 9bbfd38..4560c05 100644
--- a/src/app/login/login.component.html
+++ b/src/app/login/login.component.html
@@ -6,10 +6,8 @@
<div class="language">
<span class="change">Change language</span>
<mat-form-field class="select-input">
- <mat-select [(value)]="selected">
- <mat-option value="English">English</mat-option>
- <mat-option value="French">French</mat-option>
- <mat-option value="Portoguese">Portoguese</mat-option>
+ <mat-select [(ngModel)]="currentLanguage" name="language" matTooltip="Change Language" matTooltipPosition="right" (change)="selectLanguage($event)">
+ <mat-option *ngFor="let option of languageOptions" [value]="option.id"> {{option.label}}</mat-option>
</mat-select>
</mat-form-field>
</div>
@@ -19,6 +17,7 @@
<img src="assets/fineract.png" class="image">
</div>
<div class="login-div mat-elevation-z2">
+ <form [formGroup]="form" (ngSubmit)="login()">
<br>
<div class="form-div">
<div class="app-input">
@@ -26,7 +25,11 @@
</div>
<div class="app-input1">
<mat-form-field class="app-input2">
- <input matInput placeholder="Tenant">
+ <input matInput placeholder="Tenant" type="text" formControlName="tenant">
+ <mat-error *ngIf="form.get('tenant').hasError('required')">
+ Required
+ </mat-error>
+
</mat-form-field>
</div>
@@ -37,7 +40,10 @@
</div>
<div class="app-input1">
<mat-form-field class="app-input2">
- <input matInput placeholder="Username">
+ <input matInput placeholder="Username" type="text" formControlName="username">
+ <mat-error *ngIf="form.get('password').hasError('required')">
+ Required
+ </mat-error>
</mat-form-field>
</div>
<br>
@@ -47,18 +53,23 @@
</div>
<div class="app-input1">
<mat-form-field class="app-input2">
- <input matInput placeholder="Enter your password" [type]="hide ? 'password' : 'text'">
+ <input matInput placeholder="Password" type="password" formControlName="password" autocomplete="new-password" [type]="hide ? 'password' : 'text'">
+ <mat-error *ngIf="form.get('password').hasError('required')">
+ Required
+ </mat-error>
<mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 'visibility_off'}}</mat-icon>
</mat-form-field>
</div>
<br>
<br>
<p>
- <button mat-raised-button color="primary" type="submit" class="btn" (click)="onLogin()">Sign In</button>
+ <button mat-raised-button color="primary" type="submit" class="btn" [disabled]="form.invalid">Sign In</button>
</p>
<br>
</div>
+ </form>
</div>
+ </div>
diff --git a/src/app/login/login.component.spec.ts b/src/app/login/login.component.spec.ts
index d6d85a8..aa23f4f 100644
--- a/src/app/login/login.component.spec.ts
+++ b/src/app/login/login.component.spec.ts
@@ -1,25 +1,149 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
+import {LoginComponent} from './login.component';
+import {Observable} from 'rxjs/Observable';
+import {By} from '@angular/platform-browser';
+import {DebugElement} from '@angular/core';
+import {ActivatedRoute, Router} from '@angular/router';
+import {TranslateModule} from '@ngx-translate/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {Store} from '@ngrx/store';
+import {LOGIN} from '../store/security/security.actions';
+import {NoopAnimationsModule} from '@angular/platform-browser/animations';
+import {CovalentLoadingModule} from '@covalent/core';
+import {setValueByFormControlName} from '../common/testing/input-fields';
+import {MatCardModule, MatIconModule, MatInputModule, MatOptionModule, MatSelectModule, MatTooltipModule} from '@angular/material';
-import { LoginComponent } from './login.component';
+describe('Test Login Component', () => {
-describe('LoginComponent', () => {
- let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
+ let loginComponent: LoginComponent;
+
+ let router: Router;
+
beforeEach(async(() => {
+ router = jasmine.createSpyObj('Router', ['navigate']);
+
TestBed.configureTestingModule({
- declarations: [ LoginComponent ]
- })
- .compileComponents();
+ declarations: [LoginComponent],
+ imports: [
+ ReactiveFormsModule,
+ FormsModule,
+ TranslateModule.forRoot(),
+ MatIconModule,
+ MatCardModule,
+ MatInputModule,
+ MatSelectModule,
+ MatOptionModule,
+ MatTooltipModule,
+ NoopAnimationsModule,
+ CovalentLoadingModule
+ ],
+ providers: [
+ {provide: 'tenantId', useValue: 'tenantId'},
+ {provide: Router, useValue: router},
+ {provide: ActivatedRoute, useValue: {
+ queryParams: Observable.of([])
+ }},
+ {
+ provide: Store,
+ useClass: class {
+ dispatch = jasmine.createSpy('dispatch');
+ select = jasmine.createSpy('select').and.returnValue(Observable.empty());
+ }
+ }
+ ]
+ });
+
+ fixture = TestBed.createComponent(LoginComponent);
+ loginComponent = fixture.componentInstance;
}));
- beforeEach(() => {
- fixture = TestBed.createComponent(LoginComponent);
- component = fixture.componentInstance;
+ it('should disable/enable login button', () => {
fixture.detectChanges();
+
+ const button: DebugElement = fixture.debugElement.query(By.css('button'));
+
+ expect(button.properties['disabled']).toBeTruthy('Button should be disabled');
+
+ loginComponent.form.setValue({
+ tenant: 'tenantId',
+ username: 'test',
+ password: 'test'
+ });
+
+ fixture.detectChanges();
+
+ expect(button.properties['disabled']).toBeFalsy('Button should be enabled');
});
- it('should create', () => {
- expect(component).toBeTruthy();
+ it('should show error message', async(inject([Store], (store: Store<any>) => {
+ store.select = jasmine.createSpy('select').and.returnValue(Observable.of({ error: {} }));
+
+ fixture.detectChanges();
+
+ loginComponent.form.setValue({
+ tenant: 'tenantId',
+ username: 'test',
+ password: 'test'
+ });
+
+ fixture.detectChanges();
+
+ const button: DebugElement = fixture.debugElement.query(By.css('button'));
+ button.nativeElement.click();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+
+ const error: DebugElement = fixture.debugElement.query(By.css('p'));
+ expect(error).toBeDefined('Debug element should be defined');
+ expect(error.nativeElement.textContent.length).toBeGreaterThan(0, 'Error message should not be empty');
+ });
+
+ })));
+
+ it('should set the username', (done: DoneFn) => {
+ fixture.detectChanges();
+ setValueByFormControlName(fixture, 'username', 'test').subscribe(() => {
+ expect(loginComponent.form.get('username').value).toBe('test');
+ done();
+ });
});
+
+ it('should set the password', (done: DoneFn) => {
+ fixture.detectChanges();
+ setValueByFormControlName(fixture, 'password', 'test').subscribe(() => {
+ expect(loginComponent.form.get('password').value).toBe('test');
+ done();
+ });
+ });
+
+ it('should send the right user and password', async(inject([Store], (store: Store<any>) => {
+ fixture.detectChanges();
+
+ loginComponent.form.setValue({
+ tenant: 'tenantId',
+ username: 'test',
+ password: 'test'
+ });
+
+ fixture.detectChanges();
+
+ const button: DebugElement = fixture.debugElement.query(By.css('button'));
+
+ button.nativeElement.click();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(store.dispatch).toHaveBeenCalledWith({ type: LOGIN, payload: {
+ username: 'test',
+ password: 'test',
+ tenant: 'tenantId'
+ }});
+ });
+
+ })));
+
});
+
diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts
index 7d1588e..6b92113 100644
--- a/src/app/login/login.component.ts
+++ b/src/app/login/login.component.ts
@@ -1,25 +1,92 @@
-import { Component, OnInit } from '@angular/core';
+import { Component,OnDestroy, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import {Router } from '@angular/router'
-
+import { Subscription, Observable } from 'rxjs';
+import { TdLoadingService, LoadingType } from '@covalent/core';
+import { Store } from '@ngrx/store';
+import { ITdLoadingConfig } from '@covalent/core';
+import { LOGIN } from '../store/security/security.actions';
+import { MatSelectChange } from '@angular/material';
+import * as fromRoot from '../store';
+import { TranslateService } from '@ngx-translate/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {TRANSLATE_STORAGE_KEY} from '../common/i18n/translate';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
- styleUrls: ['./login.component.scss']
+ styleUrls: ['./login.component.scss'],
+
})
export class LoginComponent implements OnInit {
- selected = 'English';
+ private loadingSubscription: Subscription;
+ currentLanguage: string;
+
+ languageOptions: any[] = [
+ {id: 'en', label: 'Welcome to fims'},
+ {id: 'es', label: 'Bienvenido a fims'}
+ ];
+
hide= true;
+ form: FormGroup;
- constructor(private _router: Router){}
+ error$: Observable<string>;
- onLogin(): void{
- this._router.navigate(['/navbar']);
+ constructor(private _loadingService: TdLoadingService, private translate: TranslateService, private formBuilder: FormBuilder,
+ private store: Store<fromRoot.State>) {
}
ngOnInit() {
+ this.currentLanguage = this.translate.currentLang || this.translate.getDefaultLang();
+
+ const options: ITdLoadingConfig = {
+ name: 'login',
+ type: LoadingType.Circular,
+ };
+
+ this._loadingService.create(options);
+
+ this.form = this.formBuilder.group({
+ tenant: ['', Validators.required],
+ username: ['', Validators.required],
+ password: ['', Validators.required]
+ });
+
+ this.error$ = this.store.select(fromRoot.getAuthenticationError)
+ .filter(error => !!error)
+ .do(() => this.form.get('password').setValue(''))
+ .map(error => 'Sorry, that login did not work.');
+
+ this.loadingSubscription = this.store.select(fromRoot.getAuthenticationLoading).subscribe(loading => {
+ if (loading) {
+ this._loadingService.register('login');
+ } else {
+ this._loadingService.resolve('login');
+ }
+ });
}
+ ngOnDestroy(): void {
+ this.loadingSubscription.unsubscribe();
+ }
+
+ login(): void {
+ const tenant = this.form.get('tenant').value;
+ const username = this.form.get('username').value;
+ const password = this.form.get('password').value;
+
+ this.store.dispatch({
+ type: LOGIN, payload: {
+ username,
+ password,
+ tenant
+ }
+ });
+ }
+
+ selectLanguage(selectChange: MatSelectChange): void {
+ sessionStorage.setItem(TRANSLATE_STORAGE_KEY, selectChange.value);
+ location.reload();
+ }
}
diff --git a/src/app/office/store/effects/notification.effects.ts b/src/app/office/store/effects/notification.effects.ts
new file mode 100644
index 0000000..9b4b52d
--- /dev/null
+++ b/src/app/office/store/effects/notification.effects.ts
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as officeActions from '../office.actions';
+import {NotificationService, NotificationType} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/notification/notification.service';
+
+@Injectable()
+export class OfficeNotificationEffects {
+
+ @Effect({ dispatch: false })
+ createOfficeSuccess$: Observable<Action> = this.actions$
+ .ofType(officeActions.CREATE_SUCCESS, officeActions.UPDATE_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Office is going to be saved'
+ }));
+
+ @Effect({ dispatch: false })
+ deleteOfficeSuccess$: Observable<Action> = this.actions$
+ .ofType(officeActions.DELETE_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Office is going to be deleted'
+ }));
+
+ @Effect({ dispatch: false })
+ deleteOfficeFail$: Observable<Action> = this.actions$
+ .ofType(officeActions.DELETE_FAIL)
+ .do(() => this.notificationService.send({
+ type: NotificationType.ALERT,
+ title: 'Office can\'t be deleted',
+ message: 'Office has either branch offices, employees or teller assigned to it.'
+ }));
+
+ constructor(private actions$: Actions, private notificationService: NotificationService) {
+ }
+}
+
diff --git a/src/app/office/store/effects/route.effects.ts b/src/app/office/store/effects/route.effects.ts
new file mode 100644
index 0000000..8064460
--- /dev/null
+++ b/src/app/office/store/effects/route.effects.ts
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as officeActions from '../office.actions';
+import {Router} from '@angular/router';
+
+@Injectable()
+export class OfficeRouteEffects {
+
+ @Effect({ dispatch: false })
+ createOfficeSuccess$: Observable<Action> = this.actions$
+ .ofType(officeActions.CREATE_SUCCESS)
+ .map(action => action.payload)
+ .do(payload => {
+ if (payload.resource.parentIdentifier) {
+ this.router.navigate(['../detail', payload.resource.parentIdentifier], { relativeTo: payload.activatedRoute });
+ } else {
+ this.router.navigate(['../detail', payload.resource.identifier], { relativeTo: payload.activatedRoute });
+ }
+ });
+
+ @Effect({ dispatch: false })
+ updateOfficeSuccess$: Observable<Action> = this.actions$
+ .ofType(officeActions.UPDATE_SUCCESS)
+ .map(action => action.payload)
+ .do(payload => this.router.navigate(['../../', payload.resource.identifier], { relativeTo: payload.activatedRoute }));
+
+ @Effect({ dispatch: false })
+ deleteOfficeSuccess$: Observable<Action> = this.actions$
+ .ofType(officeActions.DELETE_SUCCESS)
+ .map((action) => action.payload)
+ .do(payload => {
+ if (payload.resource.parentIdentifier) {
+ this.router.navigate(['../../', payload.resource.parentIdentifier], { relativeTo: payload.activatedRoute});
+ } else {
+ this.router.navigate(['../../'], { relativeTo: payload.activatedRoute});
+ }
+ });
+
+ constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/office/store/effects/service.effects.ts b/src/app/office/store/effects/service.effects.ts
new file mode 100644
index 0000000..2947d5e
--- /dev/null
+++ b/src/app/office/store/effects/service.effects.ts
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as officeActions from '../office.actions';
+
+@Injectable()
+export class OfficeApiEffects {
+
+ @Effect()
+ createOffice$: Observable<Action> = this.actions$
+ .ofType(officeActions.CREATE)
+ .map((action: officeActions.CreateOfficeAction) => action.payload)
+ .mergeMap(payload =>
+ this.officeService.createOffice(payload.office)
+ .map(() => new officeActions.CreateOfficeSuccessAction({
+ resource: payload.office,
+ activatedRoute: payload.activatedRoute
+ }))
+ .catch((error) => of(new officeActions.CreateOfficeFailAction(error)))
+ );
+
+ @Effect()
+ createBranchOffice$: Observable<Action> = this.actions$
+ .ofType(officeActions.CREATE_BRANCH)
+ .map((action: officeActions.CreateBranchOfficeAction) => action.payload)
+ .mergeMap(payload =>
+ this.officeService.addBranch(payload.office.parentIdentifier, payload.office)
+ .map(() => new officeActions.CreateOfficeSuccessAction({
+ resource: payload.office,
+ activatedRoute: payload.activatedRoute
+ }))
+ .catch((error) => of(new officeActions.CreateOfficeFailAction(error)))
+ );
+
+ @Effect()
+ updateOffice$: Observable<Action> = this.actions$
+ .ofType(officeActions.UPDATE)
+ .map((action: officeActions.UpdateOfficeAction) => action.payload)
+ .mergeMap(payload =>
+ this.officeService.updateOffice(payload.office)
+ .map(() => new officeActions.UpdateOfficeSuccessAction({
+ resource: payload.office,
+ activatedRoute: payload.activatedRoute
+ }))
+ .catch((error) => of(new officeActions.UpdateOfficeFailAction(error)))
+ );
+
+ @Effect()
+ deleteOffice$: Observable<Action> = this.actions$
+ .ofType(officeActions.DELETE)
+ .map((action: officeActions.DeleteOfficeAction) => action.payload)
+ .mergeMap(payload =>
+ this.officeService.deleteOffice(payload.office.identifier)
+ .map(() => new officeActions.DeleteOfficeSuccessAction({
+ resource: payload.office,
+ activatedRoute: payload.activatedRoute
+ }))
+ .catch((error) => of(new officeActions.DeleteOfficeFailAction(error)))
+ );
+
+ constructor(private actions$: Actions, private officeService: OfficeService) {}
+
+}
diff --git a/src/app/office/store/index.ts b/src/app/office/store/index.ts
new file mode 100644
index 0000000..9ae603b
--- /dev/null
+++ b/src/app/office/store/index.ts
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as fromRoot from '../../store';
+import * as fromTellers from '../store/teller/tellers.reducer';
+import * as fromDenominations from '../store/teller/denomination/denominations.reducer';
+import {ActionReducer, Store} from '@ngrx/store';
+import {createReducer} from '../../store/index';
+import {createSelector} from 'reselect';
+import {
+ createResourceReducer,
+ getResourceAll,
+ getResourceEntities,
+ getResourceLoadedAt,
+ getResourceSelected,
+ ResourceState
+} from '../../common/store/resource.reducer';
+import {createFormReducer, FormState, getFormError} from '../../common/store/form.reducer';
+
+export interface State extends fromRoot.State {
+ offices: ResourceState;
+ officeForm: FormState;
+ tellers: ResourceState;
+ tellerForm: FormState;
+ denominations: fromDenominations.State;
+}
+
+const reducers = {
+ offices: createResourceReducer('Office'),
+ officeForm: createFormReducer('Office'),
+ tellers: createResourceReducer('Office Teller', fromTellers.reducer, 'code'),
+ tellerForm: createFormReducer('Office Teller'),
+ denominations: fromDenominations.reducer
+};
+
+export const officeModuleReducer: ActionReducer<State> = createReducer(reducers);
+
+export class OfficesStore extends Store<State> {}
+
+export function officeStoreFactory(appStore: Store<fromRoot.State>) {
+ appStore.replaceReducer(officeModuleReducer);
+ return appStore;
+}
+
+export const getOfficesState = (state: State) => state.offices;
+
+export const getOfficeFormState = (state: State) => state.officeForm;
+export const getOfficeFormError = createSelector(getOfficeFormState, getFormError);
+
+export const getOfficeEntities = createSelector(getOfficesState, getResourceEntities);
+export const getOfficesLoadedAt = createSelector(getOfficesState, getResourceLoadedAt);
+export const getSelectedOffice = createSelector(getOfficesState, getResourceSelected);
+
+export const getTellerState = (state: State) => state.tellers;
+
+export const getTellerFormState = (state: State) => state.tellerForm;
+export const getTellerFormError = createSelector(getTellerFormState, getFormError);
+
+export const getAllTellerEntities = createSelector(getTellerState, getResourceAll);
+
+export const getTellersLoadedAt = createSelector(getTellerState, getResourceLoadedAt);
+export const getSelectedTeller = createSelector(getTellerState, getResourceSelected);
+
+export const getDenominationState = (state: State) => state.denominations;
+export const getDenominationsEntities = createSelector(getDenominationState, fromDenominations.getDenominationEntities);
diff --git a/src/app/office/store/office.actions.ts b/src/app/office/store/office.actions.ts
new file mode 100644
index 0000000..82645b2
--- /dev/null
+++ b/src/app/office/store/office.actions.ts
@@ -0,0 +1,145 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../../store/util';
+import {Office} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/domain/office.model';
+import {Error} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/error.model';
+import {RoutePayload} from '../../common/store/route-payload';
+import {
+ CreateResourceSuccessPayload,
+ DeleteResourceSuccessPayload,
+ LoadResourcePayload,
+ SelectResourcePayload,
+ UpdateResourceSuccessPayload
+} from '../../common/store/resource.reducer';
+
+export const LOAD = type('[Office] Load');
+export const SELECT = type('[Office] Select');
+
+export const CREATE = type('[Office] Create');
+export const CREATE_BRANCH = type('[Office] Create Branch');
+export const CREATE_SUCCESS = type('[Office] Create Success');
+export const CREATE_FAIL = type('[Office] Create Fail');
+
+export const UPDATE = type('[Office] Update');
+export const UPDATE_SUCCESS = type('[Office] Update Success');
+export const UPDATE_FAIL = type('[Office] Update Fail');
+
+export const DELETE = type('[Office] Delete');
+export const DELETE_SUCCESS = type('[Office] Delete Success');
+export const DELETE_FAIL = type('[Office] Delete Fail');
+
+export const RESET_FORM = type('[Office] Reset Form');
+
+export interface OfficeRoutePayload extends RoutePayload {
+ office: Office;
+}
+
+export class LoadAction implements Action {
+ readonly type = LOAD;
+
+ constructor(public payload: LoadResourcePayload) { }
+}
+
+export class SelectAction implements Action {
+ readonly type = SELECT;
+
+ constructor(public payload: SelectResourcePayload) { }
+}
+
+export class CreateOfficeAction implements Action {
+ readonly type = CREATE;
+
+ constructor(public payload: OfficeRoutePayload) { }
+}
+
+export class CreateBranchOfficeAction implements Action {
+ readonly type = CREATE_BRANCH;
+
+ constructor(public payload: OfficeRoutePayload) { }
+}
+
+export class CreateOfficeSuccessAction implements Action {
+ readonly type = CREATE_SUCCESS;
+
+ constructor(public payload: CreateResourceSuccessPayload) { }
+}
+
+export class CreateOfficeFailAction implements Action {
+ readonly type = CREATE_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class UpdateOfficeAction implements Action {
+ readonly type = UPDATE;
+
+ constructor(public payload: OfficeRoutePayload) { }
+}
+
+export class UpdateOfficeSuccessAction implements Action {
+ readonly type = UPDATE_SUCCESS;
+
+ constructor(public payload: UpdateResourceSuccessPayload) { }
+}
+
+export class UpdateOfficeFailAction implements Action {
+ readonly type = UPDATE_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class DeleteOfficeAction implements Action {
+ readonly type = DELETE;
+
+ constructor(public payload: OfficeRoutePayload) { }
+}
+
+export class DeleteOfficeSuccessAction implements Action {
+ readonly type = DELETE_SUCCESS;
+
+ constructor(public payload: DeleteResourceSuccessPayload) { }
+}
+
+export class DeleteOfficeFailAction implements Action {
+ readonly type = DELETE_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class ResetOfficeFormAction implements Action {
+ readonly type = RESET_FORM;
+
+ constructor() {}
+}
+
+export type Actions
+ = LoadAction
+ | SelectAction
+ | CreateOfficeAction
+ | CreateBranchOfficeAction
+ | CreateOfficeSuccessAction
+ | CreateOfficeFailAction
+ | UpdateOfficeAction
+ | UpdateOfficeSuccessAction
+ | UpdateOfficeFailAction
+ | DeleteOfficeAction
+ | DeleteOfficeSuccessAction
+ | DeleteOfficeFailAction
+ | ResetOfficeFormAction;
diff --git a/src/app/office/store/teller/denomination/denomination.actions.ts b/src/app/office/store/teller/denomination/denomination.actions.ts
new file mode 100644
index 0000000..0cb7f42
--- /dev/null
+++ b/src/app/office/store/teller/denomination/denomination.actions.ts
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {type} from '../../../../store/util';
+import {TellerDenomination} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller-denomination.model';
+import {RoutePayload} from '../../../../common/store/route-payload';
+import {Action} from '@ngrx/store';
+
+export const LOAD_DENOMINATION = type('[Teller Denomination] Load All ');
+export const LOAD_DENOMINATION_SUCCESS = type('[Teller Denomination] Load All Success');
+
+export const CREATE_DENOMINATION = type('[Teller Denomination] Create');
+export const CREATE_DENOMINATION_SUCCESS = type('[Teller Denomination] Create Success');
+export const CREATE_DENOMINATION_FAIL = type('[Teller Denomination] Create Fail');
+
+export interface LoadDenominationPayload {
+ officeId: string;
+ tellerCode: string;
+}
+
+export interface DenominationPayload extends RoutePayload {
+ officeId: string;
+ tellerCode: string;
+ denomination: TellerDenomination;
+}
+
+export class LoadDenominationAction implements Action {
+ readonly type = LOAD_DENOMINATION;
+
+ constructor(public payload: LoadDenominationPayload) {}
+}
+
+export class LoadDenominationSuccessAction implements Action {
+ readonly type = LOAD_DENOMINATION_SUCCESS;
+
+ constructor(public payload: TellerDenomination[]) {}
+}
+
+export class CreateDenominationAction implements Action {
+ readonly type = CREATE_DENOMINATION;
+
+ constructor(public payload: DenominationPayload) {}
+}
+
+export class CreateDenominationSuccessAction implements Action {
+ readonly type = CREATE_DENOMINATION_SUCCESS;
+
+ constructor(public payload: DenominationPayload) {}
+}
+
+export class CreateDenominationFailAction implements Action {
+ readonly type = CREATE_DENOMINATION_FAIL;
+
+ constructor(public payload: Error) {}
+}
+
+export type Actions
+ = LoadDenominationAction
+ | LoadDenominationSuccessAction
+ | CreateDenominationAction
+ | CreateDenominationSuccessAction
+ | CreateDenominationFailAction;
diff --git a/src/app/office/store/teller/denomination/denominations.reducer.ts b/src/app/office/store/teller/denomination/denominations.reducer.ts
new file mode 100644
index 0000000..cc5a978
--- /dev/null
+++ b/src/app/office/store/teller/denomination/denominations.reducer.ts
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import * as denominations from './denomination.actions';
+import {DenominationPayload} from './denomination.actions';
+import {TellerDenomination} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller-denomination.model';
+
+export interface State {
+ entities: TellerDenomination[];
+}
+
+export const initialState: State = {
+ entities: [],
+};
+
+export function reducer(state = initialState, action: denominations.Actions): State {
+
+ switch (action.type) {
+
+ case denominations.LOAD_DENOMINATION: {
+ return initialState;
+ }
+
+ case denominations.LOAD_DENOMINATION_SUCCESS: {
+ const denominations: TellerDenomination[] = action.payload;
+
+ return {
+ entities: denominations,
+ };
+ }
+
+ case denominations.CREATE_DENOMINATION_SUCCESS: {
+ const payload: DenominationPayload = action.payload;
+
+ return {
+ entities: state.entities.concat(payload.denomination),
+ };
+ }
+
+ default: {
+ return state;
+ }
+ }
+}
+
+export const getDenominationEntities = (state: State) => state.entities;
diff --git a/src/app/office/store/teller/denomination/effects/notification.effects.ts b/src/app/office/store/teller/denomination/effects/notification.effects.ts
new file mode 100644
index 0000000..1046c48
--- /dev/null
+++ b/src/app/office/store/teller/denomination/effects/notification.effects.ts
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as denominationActions from '../denomination.actions';
+import {NotificationService, NotificationType} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/notification/notification.service';
+
+@Injectable()
+export class TellerDenominationNotificationEffects {
+
+ @Effect({ dispatch: false })
+ createDenominationSuccess$: Observable<Action> = this.actions$
+ .ofType(denominationActions.CREATE_DENOMINATION_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Denomination is going to be saved'
+ }));
+
+ constructor(private actions$: Actions, private notificationService: NotificationService) {
+ }
+}
diff --git a/src/app/office/store/teller/denomination/effects/route.effects.ts b/src/app/office/store/teller/denomination/effects/route.effects.ts
new file mode 100644
index 0000000..4e09777
--- /dev/null
+++ b/src/app/office/store/teller/denomination/effects/route.effects.ts
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Router} from '@angular/router';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as denominationActions from '../denomination.actions';
+
+@Injectable()
+export class TellerDenominationRouteEffects {
+
+ @Effect({ dispatch: false })
+ createDenominationSuccess$: Observable<Action> = this.actions$
+ .ofType(denominationActions.CREATE_DENOMINATION_SUCCESS)
+ .map(action => action.payload)
+ .do(payload => {
+ this.router.navigate(['../'], { relativeTo: payload.activatedRoute });
+ });
+
+ constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/office/store/teller/denomination/effects/service.effects.ts b/src/app/office/store/teller/denomination/effects/service.effects.ts
new file mode 100644
index 0000000..8056788
--- /dev/null
+++ b/src/app/office/store/teller/denomination/effects/service.effects.ts
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as denominationActions from '../denomination.actions';
+import {of} from 'rxjs/observable/of';
+import {TellerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/teller-service';
+
+@Injectable()
+export class TellerDenominationApiEffects {
+
+ @Effect()
+ loadDenomination$: Observable<Action> = this.actions$
+ .ofType(denominationActions.LOAD_DENOMINATION)
+ .map((action: denominationActions.LoadDenominationAction) => action.payload)
+ .mergeMap(payload =>
+ this.tellerService.fetchTellerDenominations(payload.officeId, payload.tellerCode)
+ .map(teller => new denominationActions.LoadDenominationSuccessAction(teller))
+ .catch(error => of(new denominationActions.LoadDenominationSuccessAction([])))
+ );
+
+ @Effect()
+ createDenomination$: Observable<Action> = this.actions$
+ .ofType(denominationActions.CREATE_DENOMINATION)
+ .map((action: denominationActions.CreateDenominationAction) => action.payload)
+ .mergeMap(payload =>
+ this.tellerService.saveTellerDenomination(payload.officeId, payload.tellerCode, payload.denomination)
+ .map(() => new denominationActions.CreateDenominationSuccessAction(payload))
+ .catch((error) => of(new denominationActions.CreateDenominationFailAction(error)))
+ );
+
+ constructor(private actions$: Actions, private tellerService: TellerService) {}
+
+}
diff --git a/src/app/office/store/teller/effects/notification.effects.ts b/src/app/office/store/teller/effects/notification.effects.ts
new file mode 100644
index 0000000..d544943
--- /dev/null
+++ b/src/app/office/store/teller/effects/notification.effects.ts
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {NotificationService, NotificationType} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/notification/notification.service';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as tellerActions from '../teller.actions';
+
+@Injectable()
+export class TellerNotificationEffects {
+
+ @Effect({ dispatch: false })
+ createTellerSuccess$: Observable<Action> = this.actions$
+ .ofType(tellerActions.CREATE_TELLER_SUCCESS, tellerActions.UPDATE_TELLER_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Teller is going to be saved'
+ }));
+
+ @Effect({ dispatch: false })
+ executeCommandSuccess$: Observable<Action> = this.actions$
+ .ofType(tellerActions.EXECUTE_COMMAND_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Teller is going to be updated'
+ }));
+
+ @Effect({ dispatch: false })
+ openCommandFail$: Observable<Action> = this.actions$
+ .ofType(tellerActions.EXECUTE_COMMAND_FAIL)
+ .map(action => action.payload.command)
+ .filter(command => command.action === 'OPEN')
+ .do(action => this.notificationService.send({
+ type: NotificationType.ALERT,
+ title: 'Employee already assigned',
+ message: 'Employees can only be assigned to one teller. Please choose a different employee or unassign the employee first.'
+ })
+ );
+
+ @Effect({ dispatch: false })
+ closeCommandFail$: Observable<Action> = this.actions$
+ .ofType(tellerActions.EXECUTE_COMMAND_FAIL)
+ .map(action => action.payload.command)
+ .filter(command => command.action === 'CLOSE')
+ .do(action => this.notificationService.send({
+ type: NotificationType.ALERT,
+ title: 'Denomination required',
+ message: 'This teller requires a denomination before it can be closed.'
+ })
+ );
+
+ constructor(private actions$: Actions, private notificationService: NotificationService) {
+ }
+}
diff --git a/src/app/office/store/teller/effects/route.effects.ts b/src/app/office/store/teller/effects/route.effects.ts
new file mode 100644
index 0000000..daff2f4
--- /dev/null
+++ b/src/app/office/store/teller/effects/route.effects.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Router} from '@angular/router';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as tellerActions from '../teller.actions';
+
+@Injectable()
+export class TellerRouteEffects {
+
+ @Effect({ dispatch: false })
+ createTellerSuccess$: Observable<Action> = this.actions$
+ .ofType(tellerActions.CREATE_TELLER_SUCCESS, tellerActions.UPDATE_TELLER_SUCCESS)
+ .map(action => action.payload)
+ .do(payload => {
+ this.router.navigate(['../'], { relativeTo: payload.activatedRoute });
+ });
+
+ @Effect({ dispatch: false })
+ executeCommandSuccess$: Observable<Action> = this.actions$
+ .ofType(tellerActions.EXECUTE_COMMAND_SUCCESS)
+ .map(action => action.payload)
+ .do(payload => {
+ this.router.navigate(['../'], { relativeTo: payload.activatedRoute });
+ });
+
+ constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/office/store/teller/effects/service.effects.ts b/src/app/office/store/teller/effects/service.effects.ts
new file mode 100644
index 0000000..2cebcc7
--- /dev/null
+++ b/src/app/office/store/teller/effects/service.effects.ts
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {TellerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/teller-service';
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as tellerActions from '../../teller/teller.actions';
+import {of} from 'rxjs/observable/of';
+
+@Injectable()
+export class TellerApiEffects {
+
+ @Effect()
+ loadTeller$: Observable<Action> = this.actions$
+ .ofType(tellerActions.LOAD_TELLER)
+ .map((action: tellerActions.LoadTellerAction) => action.payload)
+ .mergeMap(officeId =>
+ this.tellerService.fetch(officeId)
+ .map((teller) => new tellerActions.LoadTellerSuccessAction(teller))
+ .catch((error) => of(new tellerActions.LoadTellerSuccessAction([])))
+ );
+
+ @Effect()
+ createTeller$: Observable<Action> = this.actions$
+ .ofType(tellerActions.CREATE_TELLER)
+ .map((action: tellerActions.CreateTellerAction) => action.payload)
+ .mergeMap(payload =>
+ this.tellerService.create(payload.officeId, payload.teller)
+ .map(() => new tellerActions.CreateTellerSuccessAction({
+ activatedRoute: payload.activatedRoute,
+ resource: payload.teller
+ }))
+ .catch((error) => of(new tellerActions.CreateTellerFailAction(error)))
+ );
+
+ @Effect()
+ updateTeller$: Observable<Action> = this.actions$
+ .ofType(tellerActions.UPDATE_TELLER)
+ .map((action: tellerActions.UpdateTellerAction) => action.payload)
+ .mergeMap(payload =>
+ this.tellerService.change(payload.officeId, payload.teller)
+ .map(() => new tellerActions.UpdateTellerSuccessAction({
+ activatedRoute: payload.activatedRoute,
+ resource: payload.teller
+ }))
+ .catch((error) => of(new tellerActions.UpdateTellerFailAction(error)))
+ );
+
+ @Effect()
+ executeCommand$: Observable<Action> = this.actions$
+ .ofType(tellerActions.EXECUTE_COMMAND)
+ .map((action: tellerActions.ExecuteCommandAction) => action.payload)
+ .mergeMap(payload =>
+ this.tellerService.createCommand(payload.officeId, payload.tellerCode, payload.command)
+ .map(() => new tellerActions.ExecuteCommandSuccessAction(payload))
+ .catch((error) => of(new tellerActions.ExecuteCommandFailAction({
+ command: payload.command,
+ error
+ })))
+ );
+
+ constructor(private actions$: Actions, private tellerService: TellerService) {}
+
+}
diff --git a/src/app/office/store/teller/teller.actions.ts b/src/app/office/store/teller/teller.actions.ts
new file mode 100644
index 0000000..5481e2b
--- /dev/null
+++ b/src/app/office/store/teller/teller.actions.ts
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {type} from '../../../store/util';
+import {Teller} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller.model';
+import {Action} from '@ngrx/store';
+import {
+ CreateResourceSuccessPayload,
+ LoadResourcePayload,
+ SelectResourcePayload,
+ UpdateResourceSuccessPayload
+} from '../../../common/store/resource.reducer';
+import {RoutePayload} from '../../../common/store/route-payload';
+import {TellerManagementCommand} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller-management-command.model';
+
+export const LOAD_TELLER = type('[Office Teller] Load All ');
+export const LOAD_TELLER_SUCCESS = type('[Office Teller] Load All Success');
+
+export const LOAD = type('[Office Teller] Load');
+export const SELECT = type('[Office Teller] Select');
+export const CREATE_TELLER = type('[Office Teller] Create');
+export const CREATE_TELLER_SUCCESS = type('[Office Teller] Create Success');
+export const CREATE_TELLER_FAIL = type('[Office Teller] Create Fail');
+
+export const UPDATE_TELLER = type('[Office Teller] Update');
+export const UPDATE_TELLER_SUCCESS = type('[Office Teller] Update Success');
+export const UPDATE_TELLER_FAIL = type('[Office Teller] Update Fail');
+
+export const RESET_FORM = type('[Office Teller] Reset Form');
+
+export const EXECUTE_COMMAND = type('[Office Teller] Execute Command');
+export const EXECUTE_COMMAND_SUCCESS = type('[Office Teller] Execute Command Success');
+export const EXECUTE_COMMAND_FAIL = type('[Office Teller] Execute Command Fail');
+
+export interface LoadTellerSuccessPayload {
+ officeId: string;
+ teller: Teller[];
+}
+
+export interface ExecuteCommandPayload extends RoutePayload {
+ officeId: string;
+ tellerCode: string;
+ command: TellerManagementCommand;
+}
+
+export interface ExecuteCommandFailPayload {
+ command: TellerManagementCommand;
+ error: Error;
+}
+
+export interface TellerPayload extends RoutePayload {
+ officeId: string;
+ teller: Teller;
+}
+
+export class LoadTellerAction implements Action {
+ readonly type = LOAD_TELLER;
+
+ constructor(public payload: string) {}
+}
+
+export class LoadTellerSuccessAction implements Action {
+ readonly type = LOAD_TELLER_SUCCESS;
+
+ constructor(public payload: Teller[]) {}
+}
+
+export class LoadAction implements Action {
+ readonly type = LOAD;
+
+ constructor(public payload: LoadResourcePayload) { }
+}
+
+export class SelectAction implements Action {
+ readonly type = SELECT;
+
+ constructor(public payload: SelectResourcePayload) { }
+}
+
+export class CreateTellerAction implements Action {
+ readonly type = CREATE_TELLER;
+
+ constructor(public payload: TellerPayload) {}
+}
+
+export class CreateTellerSuccessAction implements Action {
+ readonly type = CREATE_TELLER_SUCCESS;
+
+ constructor(public payload: CreateResourceSuccessPayload) {}
+}
+
+export class CreateTellerFailAction implements Action {
+ readonly type = CREATE_TELLER_FAIL;
+
+ constructor(public payload: Error) {}
+}
+
+export class UpdateTellerAction implements Action {
+ readonly type = UPDATE_TELLER;
+
+ constructor(public payload: TellerPayload) {}
+}
+
+export class UpdateTellerSuccessAction implements Action {
+ readonly type = UPDATE_TELLER_SUCCESS;
+
+ constructor(public payload: UpdateResourceSuccessPayload) {}
+}
+
+export class UpdateTellerFailAction implements Action {
+ readonly type = UPDATE_TELLER_FAIL;
+
+ constructor(public payload: Error) {}
+}
+
+export class ExecuteCommandAction implements Action {
+ readonly type = EXECUTE_COMMAND;
+
+ constructor(public payload: ExecuteCommandPayload) {}
+}
+
+export class ExecuteCommandSuccessAction implements Action {
+ readonly type = EXECUTE_COMMAND_SUCCESS;
+
+ constructor(public payload: ExecuteCommandPayload) {}
+}
+
+export class ExecuteCommandFailAction implements Action {
+ readonly type = EXECUTE_COMMAND_FAIL;
+
+ constructor(public payload: ExecuteCommandFailPayload) {}
+}
+
+export type Actions
+ = LoadTellerAction
+ | LoadTellerSuccessAction
+ | CreateTellerAction
+ | CreateTellerSuccessAction
+ | CreateTellerFailAction
+ | UpdateTellerAction
+ | UpdateTellerSuccessAction
+ | UpdateTellerFailAction
+ | ExecuteCommandAction
+ | ExecuteCommandSuccessAction
+ | ExecuteCommandFailAction;
diff --git a/src/app/office/store/teller/tellers.reducer.spec.ts b/src/app/office/store/teller/tellers.reducer.spec.ts
new file mode 100644
index 0000000..28b119d
--- /dev/null
+++ b/src/app/office/store/teller/tellers.reducer.spec.ts
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {reducer} from './tellers.reducer';
+import {ResourceState} from '../../../common/store/resource.reducer';
+import {ExecuteCommandPayload, ExecuteCommandSuccessAction} from './teller.actions';
+import {Status} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller.model';
+
+describe('Tellers Reducer', () => {
+
+ describe('EXECUTE_COMMAND_SUCCESS', () => {
+
+ function createState(state?: Status, assignedEmployee?: string): ResourceState {
+ return {
+ ids: ['testTeller'],
+ entities: {
+ 'testTeller': {
+ code: 'testTeller',
+ state,
+ assignedEmployee
+ }
+ },
+ selectedId: null,
+ loadedAt: {}
+ };
+ }
+
+ it('should add assigned employee on open', () => {
+ const payload: ExecuteCommandPayload = {
+ officeId: 'officeId',
+ tellerCode: 'testTeller',
+ command: {
+ action: 'OPEN',
+ assignedEmployeeIdentifier: 'test'
+ },
+ activatedRoute: null
+ };
+
+ const initialState: ResourceState = createState();
+
+ const expectedResult: ResourceState = createState('OPEN', payload.command.assignedEmployeeIdentifier);
+
+ const result = reducer(initialState, new ExecuteCommandSuccessAction(payload));
+
+ expect(result).toEqual(expectedResult);
+ });
+
+ it('should remove assigned employee on close', () => {
+ const payload: ExecuteCommandPayload = {
+ officeId: 'officeId',
+ tellerCode: 'testTeller',
+ command: {
+ action: 'CLOSE'
+ },
+ activatedRoute: null
+ };
+
+ const initialState: ResourceState = createState();
+
+ const expectedResult: ResourceState = createState('CLOSED', null);
+
+ const result = reducer(initialState, new ExecuteCommandSuccessAction(payload));
+
+ expect(result).toEqual(expectedResult);
+ });
+ });
+
+});
diff --git a/src/app/office/store/teller/tellers.reducer.ts b/src/app/office/store/teller/tellers.reducer.ts
new file mode 100644
index 0000000..174e5b7
--- /dev/null
+++ b/src/app/office/store/teller/tellers.reducer.ts
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ResourceState} from '../../../common/store/resource.reducer';
+import * as tellers from '../teller/teller.actions';
+import {Status, Teller} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller.model';
+import {idsToHashWithCurrentTimestamp, resourcesToHash} from '../../../common/store/reducer.helper';
+import {TellerManagementCommand} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/teller/domain/teller-management-command.model';
+
+export const initialState: ResourceState = {
+ ids: [],
+ entities: {},
+ loadedAt: {},
+ selectedId: null,
+};
+
+export function reducer(state = initialState, action: tellers.Actions): ResourceState {
+
+ switch (action.type) {
+
+ case tellers.LOAD_TELLER: {
+ return initialState;
+ }
+
+ case tellers.LOAD_TELLER_SUCCESS: {
+ const tellers: Teller[] = action.payload;
+
+ const ids = tellers.map(teller => teller.code);
+
+ const entities = resourcesToHash(tellers, 'code');
+
+ const loadedAt = idsToHashWithCurrentTimestamp(ids);
+
+ return {
+ ids: [ ...ids ],
+ entities: entities,
+ loadedAt: loadedAt,
+ selectedId: state.selectedId
+ };
+ }
+
+ case tellers.EXECUTE_COMMAND_SUCCESS: {
+ const payload = action.payload;
+ const tellerCode = payload.tellerCode;
+ const command: TellerManagementCommand = payload.command;
+ const teller: Teller = state.entities[tellerCode];
+
+ let tellerState: Status = null;
+ let assignedEmployee = null;
+
+ if (command.action === 'OPEN') {
+ tellerState = 'OPEN';
+ assignedEmployee = command.assignedEmployeeIdentifier;
+ } else if (command.action === 'CLOSE') {
+ tellerState = 'CLOSED';
+ }
+
+ const newTeller = Object.assign({}, teller, {
+ state: tellerState,
+ assignedEmployee
+ });
+
+ return {
+ ids: [ ...state.ids ],
+ entities: Object.assign({}, state.entities, {
+ [teller.code]: newTeller
+ }),
+ loadedAt: state.loadedAt,
+ selectedId: state.selectedId
+ };
+ }
+
+ default: {
+ return state;
+ }
+ }
+}
diff --git a/src/app/office/view-offices/view-offices.component.html b/src/app/office/view-offices/view-offices.component.html
index afb9f7f..bb12198 100644
--- a/src/app/office/view-offices/view-offices.component.html
+++ b/src/app/office/view-offices/view-offices.component.html
@@ -12,7 +12,7 @@
<br>
<mat-divider></mat-divider>
-<mat-table #table [dataSource]="dataSource">
+<mat-table #table [dataSource]="datasource" >
<!-- Position Column -->
<ng-container matColumnDef="id">
diff --git a/src/app/office/view-offices/view-offices.component.ts b/src/app/office/view-offices/view-offices.component.ts
index aed8376..3ac70b7 100644
--- a/src/app/office/view-offices/view-offices.component.ts
+++ b/src/app/office/view-offices/view-offices.component.ts
@@ -1,13 +1,14 @@
import { Component, OnInit } from '@angular/core';
import {MatTableDataSource} from '@angular/material';
+
+
@Component({
selector: 'app-view-offices',
templateUrl: './view-offices.component.html',
styleUrls: ['./view-offices.component.scss']
})
export class ViewOfficesComponent implements OnInit {
-
displayedColumns = ['id','name','description'];
dataSource = new MatTableDataSource(ELEMENT_DATA);
diff --git a/src/app/sevices/accounting/accounting.service.ts b/src/app/sevices/accounting/accounting.service.ts
new file mode 100644
index 0000000..cb404bb
--- /dev/null
+++ b/src/app/sevices/accounting/accounting.service.ts
@@ -0,0 +1,201 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Ledger} from './domain/ledger.model';
+import {Observable} from 'rxjs/Observable';
+import {Account} from './domain/account.model';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {AccountCommand} from './domain/account-command.model';
+import {JournalEntry} from './domain/journal-entry.model';
+import {TrialBalance} from './domain/trial-balance.model';
+import {AccountEntryPage} from './domain/account-entry-page.model';
+import {AccountPage} from './domain/account-page.model';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {buildDateRangeParam, buildSearchParams} from '../domain/paging/search-param.builder';
+import {LedgerPage} from './domain/ledger-page.model';
+import {ChartOfAccountEntry} from './domain/chart-of-account-entry.model';
+import {TransactionType} from './domain/transaction-type.model';
+import {TransactionTypePage} from './domain/transaction-type-page.model';
+import {AccountType} from './domain/account-type.model';
+import {IncomeStatement} from './domain/income-statement.model';
+import {FinancialCondition} from './domain/financial-condition.model';
+
+@Injectable()
+export class AccountingService {
+
+ constructor(private http: HttpClient, @Inject('accountingBaseUrl') private baseUrl: string) {
+ }
+
+ public createLedger(ledger: Ledger): Observable<void> {
+ return this.http.post(`${this.baseUrl}/ledgers`, ledger);
+ }
+
+ public fetchLedgers(includeSubLedgers = false, fetchRequest?: FetchRequest, type?: AccountType): Observable<LedgerPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ params.append('includeSubLedgers', String(includeSubLedgers));
+ params.append('type', type);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+
+ return this.http.get(`${this.baseUrl}/ledgers`, requestOptions);
+ }
+
+ public findLedger(identifier: string, silent?: boolean): Observable<Ledger> {
+ return this.http.get(`${this.baseUrl}/ledgers/${identifier}`, {}, silent);
+ }
+
+ public addSubLedger(identifier: string, subLedger: Ledger): Observable<void> {
+ return this.http.post(`${this.baseUrl}/ledgers/${identifier}`, subLedger);
+ }
+
+ public modifyLedger(ledger: Ledger): Observable<void> {
+ return this.http.put(`${this.baseUrl}/ledgers/${ledger.identifier}`, ledger);
+ }
+
+ public deleteLedger(identifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/ledgers/${identifier}`);
+ }
+
+ public fetchAccountsOfLedger(identifier: string, fetchRequest?: FetchRequest): Observable<AccountPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/ledgers/${identifier}/accounts`, requestOptions);
+ }
+
+ public createAccount(account: Account): Observable<void> {
+ return this.http.post(`${this.baseUrl}/accounts`, account);
+ }
+
+ public fetchAccounts(fetchRequest?: FetchRequest, type?: AccountType): Observable<AccountPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ params.append('type', type);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/accounts`, requestOptions)
+ .share();
+ }
+
+ public findAccount(identifier: string, silent?: boolean): Observable<Account> {
+ return this.http.get(`${this.baseUrl}/accounts/${identifier}`, {}, silent);
+ }
+
+ public modifyAccount(account: Account): Observable<void> {
+ return this.http.put(`${this.baseUrl}/accounts/${account.identifier}`, account);
+ }
+
+ public deleteAccount(account: Account): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/accounts/${account.identifier}`);
+ }
+
+ public fetchAccountEntries(identifier: string, startDate: string, endDate: string,
+ fetchRequest?: FetchRequest): Observable<AccountEntryPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+ const dateRange = buildDateRangeParam(startDate, endDate);
+ params.append('dateRange', dateRange);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/accounts/${identifier}/entries`, requestOptions);
+ }
+
+ public fetchAccountCommands(identifier: string): Observable<AccountCommand[]> {
+ return this.http.get(`${this.baseUrl}/accounts/${identifier}/commands`);
+ }
+
+ public accountCommand(identifier: string, command: AccountCommand): Observable<void> {
+ return this.http.post(`${this.baseUrl}/accounts/${identifier}/commands`, command);
+ }
+
+ public createJournalEntry(journalEntry: JournalEntry): Observable<void> {
+ return this.http.post(`${this.baseUrl}/journal`, journalEntry);
+ }
+
+ public fetchJournalEntries(startDate: string, endDate: string, account?: string, amount?: string): Observable<JournalEntry[]> {
+ const params: URLSearchParams = new URLSearchParams();
+
+ params.append('dateRange', buildDateRangeParam(startDate, endDate));
+ params.append('account', account && account.length > 0 ? account : undefined);
+ params.append('amount', amount && amount.length > 0 ? amount : undefined);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/journal`, requestOptions);
+ }
+
+ public findJournalEntry(transactionIdentifier: string): Observable<JournalEntry> {
+ return this.http.get(`${this.baseUrl}/journal/${transactionIdentifier}`);
+ }
+
+ public getTrialBalance(includeEmptyEntries?: boolean): Observable<TrialBalance> {
+ const params: URLSearchParams = new URLSearchParams();
+ params.append('includeEmptyEntries', includeEmptyEntries ? 'true' : 'false');
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/trialbalance`, requestOptions);
+ }
+
+ public getChartOfAccounts(): Observable<ChartOfAccountEntry[]> {
+ return this.http.get(`${this.baseUrl}/chartofaccounts`);
+ }
+
+ public findTransactionType(code: string, silent?: boolean): Observable<Account> {
+ return this.http.get(`${this.baseUrl}/transactiontypes/${code}`, {}, silent);
+ }
+
+ public createTransactionType(transactionType: TransactionType): Observable<void> {
+ return this.http.post(`${this.baseUrl}/transactiontypes`, transactionType);
+ }
+
+ public fetchTransactionTypes(fetchRequest?: FetchRequest): Observable<TransactionTypePage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+
+ return this.http.get(`${this.baseUrl}/transactiontypes`, requestOptions);
+ }
+
+ public changeTransactionType(transactionType: TransactionType): Observable<void> {
+ return this.http.put(`${this.baseUrl}/transactiontypes/${transactionType.code}`, transactionType);
+ }
+
+ public getIncomeStatement(): Observable<IncomeStatement> {
+ return this.http.get(`${this.baseUrl}/incomestatement`);
+ }
+
+ public getFinancialCondition(): Observable<FinancialCondition> {
+ return this.http.get(`${this.baseUrl}/financialcondition`);
+ }
+
+}
diff --git a/src/app/sevices/accounting/domain/account-command-action.model.ts b/src/app/sevices/accounting/domain/account-command-action.model.ts
new file mode 100644
index 0000000..aaaab30
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-command-action.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type AccountCommandAction = 'LOCK' | 'UNLOCK' | 'CLOSE' | 'REOPEN';
diff --git a/src/app/sevices/accounting/domain/account-command.model.ts b/src/app/sevices/accounting/domain/account-command.model.ts
new file mode 100644
index 0000000..5a577c3
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-command.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountCommandAction} from './account-command-action.model';
+
+export interface AccountCommand {
+ action: AccountCommandAction;
+ comment: string;
+ createdOn?: string;
+ createdBy?: string;
+}
diff --git a/src/app/sevices/accounting/domain/account-entry-page.model.ts b/src/app/sevices/accounting/domain/account-entry-page.model.ts
new file mode 100644
index 0000000..2801db6
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-entry-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountEntry} from './account-entry.model';
+
+export interface AccountEntryPage {
+ accountEntries: AccountEntry[];
+ totalElements: number;
+ totalPages: number;
+}
diff --git a/src/app/sevices/accounting/domain/account-entry-type.model.ts b/src/app/sevices/accounting/domain/account-entry-type.model.ts
new file mode 100644
index 0000000..db9dee9
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-entry-type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type AccountEntryType = 'DEBIT' | 'CREDIT';
diff --git a/src/app/sevices/accounting/domain/account-entry.model.ts b/src/app/sevices/accounting/domain/account-entry.model.ts
new file mode 100644
index 0000000..38b1ef2
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-entry.model.ts
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountEntryType} from './account-entry-type.model';
+
+export interface AccountEntry {
+ type: AccountEntryType;
+ transactionDate: string;
+ message: string;
+ amount: number;
+ balance: number;
+}
diff --git a/src/app/sevices/accounting/domain/account-page.model.ts b/src/app/sevices/accounting/domain/account-page.model.ts
new file mode 100644
index 0000000..cef3a9e
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Account} from './account.model';
+
+export interface AccountPage {
+ accounts: Account[];
+ totalElements: number;
+ totalPages: number;
+}
diff --git a/src/app/sevices/accounting/domain/account-state.model.ts b/src/app/sevices/accounting/domain/account-state.model.ts
new file mode 100644
index 0000000..11eb6a0
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-state.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type AccountState = 'OPEN' | 'LOCKED' | 'CLOSED';
diff --git a/src/app/sevices/accounting/domain/account-type.model.ts b/src/app/sevices/accounting/domain/account-type.model.ts
new file mode 100644
index 0000000..05a0899
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account-type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type AccountType = 'ASSET' | 'LIABILITY' | 'EQUITY' | 'REVENUE' | 'EXPENSE';
diff --git a/src/app/sevices/accounting/domain/account.model.ts b/src/app/sevices/accounting/domain/account.model.ts
new file mode 100644
index 0000000..dffa0a2
--- /dev/null
+++ b/src/app/sevices/accounting/domain/account.model.ts
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountType} from './account-type.model';
+import {AccountState} from './account-state.model';
+
+export interface Account {
+ type?: AccountType;
+ identifier: string;
+ name: string;
+ holders?: string[];
+ signatureAuthorities?: string[];
+ balance?: number;
+ referenceAccount?: string;
+ ledger: string;
+ alternativeAccountNumber?: string;
+ state?: AccountState;
+ createdOn?: string;
+ createdBy?: string;
+ lastModifiedOn?: string;
+ lastModifiedBy?: string;
+}
diff --git a/src/app/sevices/accounting/domain/chart-of-account-entry.model.ts b/src/app/sevices/accounting/domain/chart-of-account-entry.model.ts
new file mode 100644
index 0000000..cf1c472
--- /dev/null
+++ b/src/app/sevices/accounting/domain/chart-of-account-entry.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface ChartOfAccountEntry {
+ code: string;
+ name: string;
+ level: number;
+ description: string;
+ type: string;
+ chartOfAccountEntries: ChartOfAccountEntry[];
+}
diff --git a/src/app/sevices/accounting/domain/creditor.model.ts b/src/app/sevices/accounting/domain/creditor.model.ts
new file mode 100644
index 0000000..164de3e
--- /dev/null
+++ b/src/app/sevices/accounting/domain/creditor.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Creditor {
+ accountNumber: string;
+ amount: string;
+}
diff --git a/src/app/sevices/accounting/domain/debtor.model.ts b/src/app/sevices/accounting/domain/debtor.model.ts
new file mode 100644
index 0000000..9aae22c
--- /dev/null
+++ b/src/app/sevices/accounting/domain/debtor.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface Debtor {
+ accountNumber: string;
+ amount: string;
+}
diff --git a/src/app/sevices/accounting/domain/financial-condition-entry.model.ts b/src/app/sevices/accounting/domain/financial-condition-entry.model.ts
new file mode 100644
index 0000000..7439538
--- /dev/null
+++ b/src/app/sevices/accounting/domain/financial-condition-entry.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface FinancialConditionEntry {
+ description: string;
+ value: string;
+}
diff --git a/src/app/sevices/accounting/domain/financial-condition-section.model.ts b/src/app/sevices/accounting/domain/financial-condition-section.model.ts
new file mode 100644
index 0000000..c69f3ce
--- /dev/null
+++ b/src/app/sevices/accounting/domain/financial-condition-section.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {FinancialConditionEntry} from './financial-condition-entry.model';
+
+export type Type = 'ASSET' | 'EQUITY' | 'LIABILITY';
+
+export interface FinancialConditionSection {
+ type: Type;
+ description: string;
+ financialConditionEntries: FinancialConditionEntry[];
+ subtotal: string;
+}
diff --git a/src/app/sevices/accounting/domain/financial-condition.model.ts b/src/app/sevices/accounting/domain/financial-condition.model.ts
new file mode 100644
index 0000000..ed58d48
--- /dev/null
+++ b/src/app/sevices/accounting/domain/financial-condition.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {FinancialConditionSection} from './financial-condition-section.model';
+
+export interface FinancialCondition {
+ date: string;
+ financialConditionSections: FinancialConditionSection[];
+ totalAssets: string;
+ totalEquitiesAndLiabilities: string;
+}
diff --git a/src/app/sevices/accounting/domain/income-statement-entry.model.ts b/src/app/sevices/accounting/domain/income-statement-entry.model.ts
new file mode 100644
index 0000000..5167d89
--- /dev/null
+++ b/src/app/sevices/accounting/domain/income-statement-entry.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface IncomeStatementEntry {
+ description: string;
+ value: string;
+}
diff --git a/src/app/sevices/accounting/domain/income-statement-section.model.ts b/src/app/sevices/accounting/domain/income-statement-section.model.ts
new file mode 100644
index 0000000..3237b03
--- /dev/null
+++ b/src/app/sevices/accounting/domain/income-statement-section.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {IncomeStatementEntry} from './income-statement-entry.model';
+
+export type Type = 'INCOME' | 'EXPENSES';
+
+export interface IncomeStatementSection {
+ type: Type;
+ description: string;
+ incomeStatementEntries: IncomeStatementEntry[];
+ subtotal: string;
+}
diff --git a/src/app/sevices/accounting/domain/income-statement.model.ts b/src/app/sevices/accounting/domain/income-statement.model.ts
new file mode 100644
index 0000000..bb9397b
--- /dev/null
+++ b/src/app/sevices/accounting/domain/income-statement.model.ts
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {IncomeStatementSection} from './income-statement-section.model';
+
+export interface IncomeStatement {
+ date: string;
+ incomeStatementSections: IncomeStatementSection[];
+ grossProfit: string;
+ totalExpenses: string;
+ netIncome: string;
+}
diff --git a/src/app/sevices/accounting/domain/journal-entry-state.model.ts b/src/app/sevices/accounting/domain/journal-entry-state.model.ts
new file mode 100644
index 0000000..6a0d5ae
--- /dev/null
+++ b/src/app/sevices/accounting/domain/journal-entry-state.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type JournalEntryState = 'PENDING' | 'PROCESSED';
diff --git a/src/app/sevices/accounting/domain/journal-entry.model.ts b/src/app/sevices/accounting/domain/journal-entry.model.ts
new file mode 100644
index 0000000..b31cef3
--- /dev/null
+++ b/src/app/sevices/accounting/domain/journal-entry.model.ts
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Debtor} from './debtor.model';
+import {Creditor} from './creditor.model';
+import {JournalEntryState} from './journal-entry-state.model';
+
+export interface JournalEntry {
+ transactionIdentifier: string;
+ transactionDate: string;
+ transactionType: string;
+ clerk: string;
+ note?: string;
+ debtors: Debtor[];
+ creditors: Creditor[];
+ state?: JournalEntryState;
+ message?: string;
+}
diff --git a/src/app/sevices/accounting/domain/ledger-page.model.ts b/src/app/sevices/accounting/domain/ledger-page.model.ts
new file mode 100644
index 0000000..80a5c18
--- /dev/null
+++ b/src/app/sevices/accounting/domain/ledger-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Ledger} from './ledger.model';
+
+export interface LedgerPage {
+ ledgers: Ledger[];
+ totalElements: number;
+ totalPages: number;
+}
diff --git a/src/app/sevices/accounting/domain/ledger.model.ts b/src/app/sevices/accounting/domain/ledger.model.ts
new file mode 100644
index 0000000..33c844c
--- /dev/null
+++ b/src/app/sevices/accounting/domain/ledger.model.ts
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountType} from './account-type.model';
+
+export interface Ledger {
+ parentLedgerIdentifier?: string;
+ type: AccountType;
+ identifier: string;
+ name: string;
+ description?: string;
+ subLedgers: Ledger[];
+ showAccountsInChart: boolean;
+ totalValue?: string;
+ createdOn?: string;
+ createdBy?: string;
+ lastModifiedOn?: string;
+ lastModifiedBy?: string;
+}
diff --git a/src/app/sevices/accounting/domain/permittable-group-ids.ts b/src/app/sevices/accounting/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..30d27fc
--- /dev/null
+++ b/src/app/sevices/accounting/domain/permittable-group-ids.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class AccountingPermittableGroupIds {
+ public static readonly ACCOUNT_MANAGEMENT = 'accounting__v1__account';
+ public static readonly JOURNAL_MANAGEMENT = 'accounting__v1__journal';
+ public static readonly LEDGER_MANAGEMENT = 'accounting__v1__ledger';
+ public static readonly TRANSACTION_TYPES = 'accounting__v1__tx_types';
+ public static readonly THOTH_INCOME_STMT = 'accounting__v1__income_stmt';
+ public static readonly THOTH_FIN_CONDITION = 'accounting__v1__fin_condition';
+}
diff --git a/src/app/sevices/accounting/domain/transaction-type-page.model.ts b/src/app/sevices/accounting/domain/transaction-type-page.model.ts
new file mode 100644
index 0000000..3c1ffbf
--- /dev/null
+++ b/src/app/sevices/accounting/domain/transaction-type-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TransactionType} from './transaction-type.model';
+
+export interface TransactionTypePage {
+ transactionTypes: TransactionType[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/accounting/domain/transaction-type.model.ts b/src/app/sevices/accounting/domain/transaction-type.model.ts
new file mode 100644
index 0000000..671242b
--- /dev/null
+++ b/src/app/sevices/accounting/domain/transaction-type.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface TransactionType {
+ code: string;
+ name: string;
+ description?: string;
+}
diff --git a/src/app/sevices/accounting/domain/trial-balance-entry-type.model.ts b/src/app/sevices/accounting/domain/trial-balance-entry-type.model.ts
new file mode 100644
index 0000000..d9d23cc
--- /dev/null
+++ b/src/app/sevices/accounting/domain/trial-balance-entry-type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type TrialBalanceEntryType = 'DEBIT' | 'CREDIT';
diff --git a/src/app/sevices/accounting/domain/trial-balance-entry.model.ts b/src/app/sevices/accounting/domain/trial-balance-entry.model.ts
new file mode 100644
index 0000000..9dcd6d2
--- /dev/null
+++ b/src/app/sevices/accounting/domain/trial-balance-entry.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Ledger} from './ledger.model';
+import {TrialBalanceEntryType} from './trial-balance-entry-type.model';
+
+export interface TrialBalanceEntry {
+ ledger: Ledger;
+ type: TrialBalanceEntryType;
+ amount: number;
+}
diff --git a/src/app/sevices/accounting/domain/trial-balance.model.ts b/src/app/sevices/accounting/domain/trial-balance.model.ts
new file mode 100644
index 0000000..aca7952
--- /dev/null
+++ b/src/app/sevices/accounting/domain/trial-balance.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TrialBalanceEntry} from './trial-balance-entry.model';
+
+export interface TrialBalance {
+ trialBalanceEntries: TrialBalanceEntry[];
+ debitTotal: number;
+ creditTotal: number;
+}
diff --git a/src/app/sevices/anubis/permittable-endpoint.model.ts b/src/app/sevices/anubis/permittable-endpoint.model.ts
new file mode 100644
index 0000000..e28286c
--- /dev/null
+++ b/src/app/sevices/anubis/permittable-endpoint.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface PermittableEndpoint {
+ path: string;
+ method: Method;
+ groupId?: string;
+}
+
+export type Method = 'POST' | 'HEAD' | 'PUT' | 'DELETE';
diff --git a/src/app/sevices/anubis/permittable-group.model.ts b/src/app/sevices/anubis/permittable-group.model.ts
new file mode 100644
index 0000000..ef27616
--- /dev/null
+++ b/src/app/sevices/anubis/permittable-group.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PermittableEndpoint} from './permittable-endpoint.model';
+
+export interface PermittableGroup {
+ identifier: string;
+ permittables: PermittableEndpoint[];
+}
diff --git a/src/app/sevices/catalog/catalog.service.ts b/src/app/sevices/catalog/catalog.service.ts
new file mode 100644
index 0000000..43eb77b
--- /dev/null
+++ b/src/app/sevices/catalog/catalog.service.ts
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Observable} from 'rxjs/Observable';
+import {Catalog} from './domain/catalog.model';
+import {Field} from './domain/field.model';
+
+@Injectable()
+export class CatalogService {
+
+ constructor(@Inject('customerBaseUrl') private baseUrl: string, private http: HttpClient) {
+ }
+
+ fetchCatalogs(): Observable<Catalog[]> {
+ return this.http.get(`${this.baseUrl}/catalogs`);
+ }
+
+ createCatalog(catalog: Catalog): Observable<void> {
+ return this.http.post(`${this.baseUrl}/catalogs`, catalog);
+ }
+
+ updateCatalog(catalog: Catalog): Observable<void> {
+ return this.http.put(`${this.baseUrl}/catalogs/${catalog.identifier}`, catalog);
+ }
+
+ deleteCatalog(catalog: Catalog): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/catalogs/${catalog.identifier}`, {});
+ }
+
+ findCatalog(identifier: string, silent: boolean = false): Observable<Catalog> {
+ return this.http.get(`${this.baseUrl}/catalogs/${identifier}`, {}, silent);
+ }
+
+ updateField(catalogIdentifier: string, field: Field): Observable<void> {
+ return this.http.put(`${this.baseUrl}/catalogs/${catalogIdentifier}/fields/${field.identifier}`, field);
+ }
+
+ deleteField(catalogIdentifier: string, field: Field): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/catalogs/${catalogIdentifier}/fields/${field.identifier}`, );
+ }
+}
diff --git a/src/app/sevices/catalog/domain/catalog.model.ts b/src/app/sevices/catalog/domain/catalog.model.ts
new file mode 100644
index 0000000..280b6d4
--- /dev/null
+++ b/src/app/sevices/catalog/domain/catalog.model.ts
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Field} from './field.model';
+
+export interface Catalog {
+ identifier: string;
+ name: string;
+ description?: string;
+ fields: Field[];
+ createdBy?: string;
+ createdOn?: string;
+ lastModifiedBy?: string;
+ lastModifiedOn?: string;
+}
diff --git a/src/app/sevices/catalog/domain/field.model.ts b/src/app/sevices/catalog/domain/field.model.ts
new file mode 100644
index 0000000..c60520c
--- /dev/null
+++ b/src/app/sevices/catalog/domain/field.model.ts
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Option} from './option.model';
+
+export class Field {
+ identifier: string;
+ dataType: FieldDataType;
+ label: string;
+ hint?: string;
+ description?: string;
+ options: Option[];
+ mandatory?: boolean;
+ length?: number;
+ precision?: number;
+ minValue?: number;
+ maxValue?: number;
+ createdBy?: string;
+ createdOn?: string;
+}
+
+export type FieldDataType = 'TEXT' | 'NUMBER' | 'DATE' | 'SINGLE_SELECTION' | 'MULTI_SELECTION';
diff --git a/src/app/sevices/catalog/domain/option.model.ts b/src/app/sevices/catalog/domain/option.model.ts
new file mode 100644
index 0000000..48bcf35
--- /dev/null
+++ b/src/app/sevices/catalog/domain/option.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Option {
+ label: string;
+ value: number;
+ createdBy?: string;
+ createdOn?: string;
+}
diff --git a/src/app/sevices/catalog/domain/value.model.ts b/src/app/sevices/catalog/domain/value.model.ts
new file mode 100644
index 0000000..1e02c19
--- /dev/null
+++ b/src/app/sevices/catalog/domain/value.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Value {
+ catalogIdentifier: string;
+ fieldIdentifier: string;
+ value: string;
+}
diff --git a/src/app/sevices/cheque/cheque.service.ts b/src/app/sevices/cheque/cheque.service.ts
new file mode 100644
index 0000000..2d6f650
--- /dev/null
+++ b/src/app/sevices/cheque/cheque.service.ts
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Observable} from 'rxjs/Observable';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {Cheque} from './domain/cheque.model';
+import {IssuingCount} from './domain/issuing-count.model';
+import {ChequeProcessingCommand} from './domain/cheque-processing-command';
+import {ChequeTransaction} from './domain/cheque-transaction';
+import {MICRResolution} from './domain/micr-resolution.model';
+import {FimsCheque} from './domain/fims-cheque.model';
+import {mapToFimsCheque, mapToFimsCheques} from './domain/mapper/fims-cheque.mapper';
+
+@Injectable()
+export class ChequeService {
+
+ constructor(private http: HttpClient, @Inject('chequeBaseUrl') private baseUrl: string) {
+ }
+
+ public issue(issuingCount: IssuingCount): Observable<string> {
+ return this.http.post(`${this.baseUrl}/cheques/`, issuingCount);
+ }
+
+ public fetch(state?: string, accountIdentifier?: string): Observable<FimsCheque[]> {
+ const search = new URLSearchParams();
+
+ search.append('state', state);
+ search.append('accountIdentifier', accountIdentifier);
+
+ const requestOptions: RequestOptionsArgs = {
+ search
+ };
+
+ return this.http.get(`${this.baseUrl}/cheques/`, requestOptions)
+ .map((cheques: Cheque[]) => mapToFimsCheques(cheques));
+ }
+
+ public get(identifier: string): Observable<FimsCheque> {
+ return this.http.get(`${this.baseUrl}/cheques/${identifier}`)
+ .map((cheque: Cheque) => mapToFimsCheque(cheque));
+ }
+
+ public process(identifier: string, command: ChequeProcessingCommand): Observable<void> {
+ return this.http.post(`${this.baseUrl}/cheques/${identifier}/commands`, command);
+ }
+
+ public processTransaction(transaction: ChequeTransaction): Observable<void> {
+ return this.http.post(`${this.baseUrl}/transactions/`, transaction);
+ }
+
+ public expandMicr(identifier: string): Observable<MICRResolution> {
+ return this.http.get(`${this.baseUrl}/micr/${identifier}`, {}, true);
+ }
+
+}
diff --git a/src/app/sevices/cheque/domain/action.model.ts b/src/app/sevices/cheque/domain/action.model.ts
new file mode 100644
index 0000000..b6414c4
--- /dev/null
+++ b/src/app/sevices/cheque/domain/action.model.ts
@@ -0,0 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type Action = 'APPROVE' | 'CANCEL';
diff --git a/src/app/sevices/cheque/domain/cheque-processing-command.ts b/src/app/sevices/cheque/domain/cheque-processing-command.ts
new file mode 100644
index 0000000..27d5d76
--- /dev/null
+++ b/src/app/sevices/cheque/domain/cheque-processing-command.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from './action.model';
+
+export interface ChequeProcessingCommand {
+ action: Action;
+}
diff --git a/src/app/sevices/cheque/domain/cheque-transaction.ts b/src/app/sevices/cheque/domain/cheque-transaction.ts
new file mode 100644
index 0000000..21dbc5a
--- /dev/null
+++ b/src/app/sevices/cheque/domain/cheque-transaction.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Cheque} from './cheque.model';
+
+export interface ChequeTransaction {
+ cheque: Cheque;
+ creditorAccountNumber: string;
+}
diff --git a/src/app/sevices/cheque/domain/cheque.model.ts b/src/app/sevices/cheque/domain/cheque.model.ts
new file mode 100644
index 0000000..7260595
--- /dev/null
+++ b/src/app/sevices/cheque/domain/cheque.model.ts
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {MICR} from './micr.model';
+import {State} from './state.model';
+
+export interface Cheque {
+ micr: MICR;
+ drawee: string;
+ drawer: string;
+ payee: string;
+ amount: string;
+ dateIssued: string;
+ openCheque: boolean;
+ state: State;
+ journalEntryIdentifier: string;
+}
diff --git a/src/app/sevices/cheque/domain/fims-cheque.model.ts b/src/app/sevices/cheque/domain/fims-cheque.model.ts
new file mode 100644
index 0000000..249164f
--- /dev/null
+++ b/src/app/sevices/cheque/domain/fims-cheque.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Cheque} from './cheque.model';
+
+export interface FimsCheque extends Cheque {
+ identifier: string;
+}
diff --git a/src/app/sevices/cheque/domain/issuing-count.model.ts b/src/app/sevices/cheque/domain/issuing-count.model.ts
new file mode 100644
index 0000000..98c53c4
--- /dev/null
+++ b/src/app/sevices/cheque/domain/issuing-count.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface IssuingCount {
+ accountIdentifier: string;
+ start?: number;
+ amount: number;
+}
diff --git a/src/app/sevices/cheque/domain/mapper/fims-cheque.mapper.ts b/src/app/sevices/cheque/domain/mapper/fims-cheque.mapper.ts
new file mode 100644
index 0000000..433d4cd
--- /dev/null
+++ b/src/app/sevices/cheque/domain/mapper/fims-cheque.mapper.ts
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {FimsCheque} from '../fims-cheque.model';
+import {Cheque} from '../cheque.model';
+import {MICR} from '../micr.model';
+
+export function mapToFimsCheque(cheque: Cheque): FimsCheque {
+ return Object.assign({}, cheque, {
+ identifier: micrToIdentifier(cheque.micr)
+ });
+}
+
+export function mapToFimsCheques(cheques: Cheque[]): FimsCheque[] {
+ return cheques.map(cheque => mapToFimsCheque(cheque));
+}
+
+export function micrToIdentifier(micr: MICR): string {
+ return toMICRIdentifier(micr.chequeNumber, micr.branchSortCode, micr.accountNumber);
+}
+
+export function toMICRIdentifier(chequeNumber: string, branchSortCode: string, accountNumber: string): string {
+ return `${chequeNumber}~${branchSortCode}~${accountNumber}`;
+}
diff --git a/src/app/sevices/cheque/domain/micr-resolution.model.ts b/src/app/sevices/cheque/domain/micr-resolution.model.ts
new file mode 100644
index 0000000..8319a51
--- /dev/null
+++ b/src/app/sevices/cheque/domain/micr-resolution.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface MICRResolution {
+ office: string;
+ customer: string;
+}
diff --git a/src/app/sevices/cheque/domain/micr.model.ts b/src/app/sevices/cheque/domain/micr.model.ts
new file mode 100644
index 0000000..288d282
--- /dev/null
+++ b/src/app/sevices/cheque/domain/micr.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface MICR {
+ chequeNumber: string;
+ branchSortCode: string;
+ accountNumber: string;
+}
diff --git a/src/app/sevices/cheque/domain/permittable-group-ids.ts b/src/app/sevices/cheque/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..81927b1
--- /dev/null
+++ b/src/app/sevices/cheque/domain/permittable-group-ids.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class ChequePermittableGroupIds {
+ public static readonly CHEQUE_TRANSACTION = 'cheques__v1__transaction';
+ public static readonly CHEQUE_MANAGEMENT = 'cheques__v1__management';
+}
diff --git a/src/app/sevices/cheque/domain/state.model.ts b/src/app/sevices/cheque/domain/state.model.ts
new file mode 100644
index 0000000..adacc2b
--- /dev/null
+++ b/src/app/sevices/cheque/domain/state.model.ts
@@ -0,0 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type State = 'PENDING' | 'PROCESSED' | 'CANCELED';
diff --git a/src/app/sevices/country/country.service.spec.ts b/src/app/sevices/country/country.service.spec.ts
new file mode 100644
index 0000000..8cfde78
--- /dev/null
+++ b/src/app/sevices/country/country.service.spec.ts
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {fakeAsync, tick} from '@angular/core/testing';
+import {CountryService} from './country.service';
+import {MockBackend} from '@angular/http/testing';
+import {BaseRequestOptions, ConnectionBackend, Http, RequestOptions, ResponseOptions} from '@angular/http';
+import {TranslateService} from '@ngx-translate/core';
+import {Observable} from 'rxjs/Observable';
+import {ReflectiveInjector} from '@angular/core';
+
+describe('Test country service', () => {
+
+ beforeEach(() => {
+ const translateService = {
+ onLangChange: Observable.empty()
+ };
+
+ this.injector = ReflectiveInjector.resolveAndCreate([
+ {provide: ConnectionBackend, useClass: MockBackend},
+ {provide: RequestOptions, useClass: BaseRequestOptions},
+ {provide: TranslateService, useValue: translateService},
+ Http,
+ CountryService
+ ]);
+ this.countryService = this.injector.get(CountryService);
+ this.backend = this.injector.get(ConnectionBackend) as MockBackend;
+ this.backend.connections.subscribe((connection: any) => this.lastConnection = connection);
+ });
+
+ xit('should return countries when term contains brackets', fakeAsync(() => {
+ // TODO find out why mock connection returns a rejected promise
+ this.countryService.init();
+
+ const mockResponse = [
+ {name: 'Country (A)', displayName: 'Country (A)', alpha2Code: '', translations: {}},
+ {name: 'Country (B)', displayName: 'Country (B)', alpha2Code: '', translations: {}}
+ ];
+
+ this.lastConnection.mockRespond(new Response(new ResponseOptions({
+ body: JSON.stringify(mockResponse)
+ })));
+
+ tick();
+
+ const result = this.countryService.fetchCountries('Country (A)');
+
+ expect(result.length).toBe(1);
+ expect(result[0]).toEqual(mockResponse[0]);
+ }));
+
+});
diff --git a/src/app/sevices/country/country.service.ts b/src/app/sevices/country/country.service.ts
new file mode 100644
index 0000000..80593ed
--- /dev/null
+++ b/src/app/sevices/country/country.service.ts
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Http} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {Country} from './model/country.model';
+import {TranslateService} from '@ngx-translate/core';
+import {escapeRegexPattern} from '../../common/regex/escape';
+
+@Injectable()
+export class CountryService {
+
+ private countries: Country[] = [];
+
+ constructor(private http: Http, private translateService: TranslateService) {
+ }
+
+ init(): void {
+ this.getCountries()
+ .map(countries => this.translate(countries))
+ .subscribe(countries => this.countries = countries);
+
+ this.translateService.onLangChange
+ .map(() => this.translate(this.countries))
+ .subscribe(countries => this.countries = countries);
+ }
+
+ fetchCountries(term): Country[] {
+ const regTerm = new RegExp(`^${escapeRegexPattern(term)}`, 'gi');
+
+ let result: Country[];
+
+ if (term) {
+ result = this.countries.filter((country: Country) => regTerm.test(country.displayName));
+ } else {
+ result = this.countries.slice();
+ }
+ return result;
+ }
+
+ fetchByCountryCode(countryCode: string): Country {
+ return this.countries.find((country: Country) => country.alpha2Code === countryCode);
+ }
+
+ private translate(countries: Country[]): Country[] {
+ return countries.map(country => this.mapTranslation(country));
+ }
+
+ private mapTranslation(country: Country): Country {
+ const currentLang = this.translateService.currentLang;
+ return Object.assign({}, country, {
+ displayName: currentLang !== 'en' && country.translations[currentLang] ? country.translations[currentLang] : country.name
+ });
+ }
+
+ private getCountries(): Observable<Country[]> {
+ return this.http.get('https://restcountries.eu/rest/v2/all?fields=name;alpha2Code;translations')
+ .map(response => response.json());
+ }
+
+}
diff --git a/src/app/sevices/country/model/country.model.ts b/src/app/sevices/country/model/country.model.ts
new file mode 100644
index 0000000..1ee5eaf
--- /dev/null
+++ b/src/app/sevices/country/model/country.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Country {
+ displayName: string;
+ name: string;
+ alpha2Code: string;
+ translations: { [ key: string ]: string };
+}
diff --git a/src/app/sevices/currency/currency.service.ts b/src/app/sevices/currency/currency.service.ts
new file mode 100644
index 0000000..9072e6b
--- /dev/null
+++ b/src/app/sevices/currency/currency.service.ts
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Currency} from './domain/currency.model';
+
+@Injectable()
+export class CurrencyService {
+
+ private currencies: Currency[] = [
+ {code: 'BZD', name: 'Belize Dollar', sign: '$', digits: 2},
+ {code: 'EUR', name: 'Euro', sign: '€', digits: 2},
+ {code: 'GMD', name: 'Gambian Dalasi', sign: 'D', digits: 2},
+ {code: 'JMD', name: 'Jamaican Dollar', sign: '$', digits: 2},
+ {code: 'MXN', name: 'Mexican Peso', sign: '$', digits: 2},
+ {code: 'USD', name: 'US Dollar', sign: '$', digits: 2},
+ {code: 'TTD', name: 'Trinidad and Tobago Dollar', sign: '$', digits: 2},
+ {code: 'XCD', name: 'East Caribbean Dollar', sign: '$', digits: 2}
+ ];
+
+ fetchCurrencies(): Observable<Currency[]> {
+ return Observable.of(this.currencies.slice(0));
+ }
+
+ getCurrency(code: string): Currency {
+ const foundCurrency = this.currencies.find(currency => currency.code === code);
+ return Object.assign({}, foundCurrency);
+ }
+}
diff --git a/src/app/sevices/currency/domain/currency.model.ts b/src/app/sevices/currency/domain/currency.model.ts
new file mode 100644
index 0000000..18de582
--- /dev/null
+++ b/src/app/sevices/currency/domain/currency.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Currency {
+ code: string;
+ name: string;
+ sign: string;
+ digits: number;
+}
diff --git a/src/app/sevices/customer/customer.service.ts b/src/app/sevices/customer/customer.service.ts
new file mode 100644
index 0000000..f093c2e
--- /dev/null
+++ b/src/app/sevices/customer/customer.service.ts
@@ -0,0 +1,212 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Customer} from './domain/customer.model';
+import {HttpClient} from '../http/http.service';
+import {CustomerPage} from './domain/customer-page.model';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {buildSearchParams} from '../domain/paging/search-param.builder';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {Command} from './domain/command.model';
+import {TaskDefinition} from './domain/task-definition.model';
+import {ImageService} from '../image/image.service';
+import {IdentificationCard} from './domain/identification-card.model';
+import {IdentificationCardScan} from './domain/identification-card-scan.model';
+import {ProcessStep} from './domain/process-step.model';
+import {CustomerDocument} from './domain/customer-document.model';
+
+@Injectable()
+export class CustomerService {
+
+ constructor(@Inject('customerBaseUrl') private baseUrl: string, private http: HttpClient, private imageService: ImageService) {
+ }
+
+ fetchCustomers(fetchRequest: FetchRequest): Observable<CustomerPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.get(`${this.baseUrl}/customers`, requestOptions).share();
+ }
+
+ getCustomer(id: string, silent?: boolean): Observable<Customer> {
+ return this.http.get(`${this.baseUrl}/customers/${id}`, {}, silent);
+ }
+
+ createCustomer(customer: Customer): Observable<Customer> {
+ return this.http.post(`${this.baseUrl}/customers`, customer);
+ }
+
+ updateCustomer(customer: Customer): Observable<Customer> {
+ return this.http.put(`${this.baseUrl}/customers/${customer.identifier}`, customer);
+ }
+
+ executeCustomerCommand(id: string, command: Command): Observable<void> {
+ return this.http.post(`${this.baseUrl}/customers/${id}/commands`, command);
+ }
+
+ listCustomerCommand(id: string): Observable<Command[]> {
+ return this.http.get(`${this.baseUrl}/customers/${id}/commands`);
+ }
+
+ addTaskToCustomer(customerId: string, taskId: string): Observable<void> {
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/tasks/${taskId}`, {});
+ }
+
+ markTaskAsExecuted(customerId: string, taskId: string): Observable<void> {
+ return this.http.put(`${this.baseUrl}/customers/${customerId}/tasks/${taskId}`, {});
+ }
+
+ fetchCustomerTasks(customerId: string, includeExecuted?: boolean): Observable<TaskDefinition[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/tasks`);
+ }
+
+ fetchTasks(): Observable<TaskDefinition[]> {
+ return this.http.get(`${this.baseUrl}/tasks`);
+ }
+
+ getTask(identifier: string): Observable<TaskDefinition> {
+ return this.http.get(`${this.baseUrl}/tasks/${identifier}`);
+ }
+
+ createTask(task: TaskDefinition): Observable<void> {
+ return this.http.post(`${this.baseUrl}/tasks`, task);
+ }
+
+ updateTask(task: TaskDefinition): Observable<void> {
+ return this.http.put(`${this.baseUrl}/tasks/${task.identifier}`, task);
+ }
+
+ fetchProcessSteps(customerId: string): Observable<ProcessStep[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/actions`);
+ }
+
+ /* getPortrait(customerId: string): Observable<Blob> {
+ return this.imageService.getImage(`${this.baseUrl}/customers/${customerId}/portrait`);
+ }
+ */
+
+ uploadPortrait(customerId: string, file: File): Observable<void> {
+ const formData = new FormData();
+
+ formData.append('portrait', file, file.name);
+
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/portrait`, formData);
+ }
+
+ deletePortrait(customerId: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/customers/${customerId}/portrait`);
+ }
+
+ fetchIdentificationCards(customerId: string): Observable<IdentificationCard[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/identifications`);
+ }
+
+ getIdentificationCard(customerId: string, number: string): Observable<IdentificationCard> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/identifications/${number}`);
+ }
+
+ createIdentificationCard(customerId: string, identificationCard: IdentificationCard): Observable<void> {
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/identifications`, identificationCard);
+ }
+
+ updateIdentificationCard(customerId: string, identificationCard: IdentificationCard): Observable<void> {
+ return this.http.put(`${this.baseUrl}/customers/${customerId}/identifications/${identificationCard.number}`, identificationCard);
+ }
+
+ deleteIdentificationCard(customerId: string, number: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/customers/${customerId}/identifications/${number}`);
+ }
+
+ fetchIdentificationCardScans(customerId: string, number: string): Observable<IdentificationCardScan[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/identifications/${number}/scans`);
+ }
+
+ /* getIdentificationCardScanImage(customerId: string, number: string, scanId: string): Observable<Blob> {
+ return this.imageService.getImage(`${this.baseUrl}/customers/${customerId}/identifications/${number}/scans/${scanId}/image`);
+ }
+ */
+
+ uploadIdentificationCardScan(customerId: string, number: string, scan: IdentificationCardScan, file: File): Observable<void> {
+ const formData = new FormData();
+ formData.append('image', file, file.name);
+
+ const params = new URLSearchParams();
+ params.append('scanIdentifier', scan.identifier);
+ params.append('description', scan.description);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/identifications/${number}/scans`, formData, requestOptions);
+ }
+
+ deleteIdentificationCardScan(customerId: string, number: string, scanId: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/customers/${customerId}/identifications/${number}/scans/${scanId}`);
+ }
+
+ getDocuments(customerId: string): Observable<CustomerDocument[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/documents`);
+ }
+
+ getDocument(customerId: string, documentId: string): Observable<CustomerDocument> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/documents/${documentId}`);
+ }
+
+ createDocument(customerId: string, document: CustomerDocument): Observable<void> {
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/documents/${document.identifier}`, document);
+ }
+
+ updateDocument(customerId: string, document: CustomerDocument): Observable<void> {
+ return this.http.put(`${this.baseUrl}/customers/${customerId}/documents/${document.identifier}`, document);
+ }
+
+ deleteDocument(customerId: string, document: CustomerDocument): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/customers/${customerId}/documents/${document.identifier}`);
+ }
+
+ completeDocument(customerId: string, documentId: string, silent: boolean = false): Observable<void> {
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/documents/${documentId}/completed`, true, {}, silent);
+ }
+
+ getDocumentPageNumbers(customerId: string, documentId: string): Observable<number[]> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/documents/${documentId}/pages`);
+ }
+
+ /* getDocumentPage(customerId: string, documentId: string, pageNumber: number): Observable<Blob> {
+ return this.imageService.getImage(`${this.baseUrl}/customers/${customerId}/documents/${documentId}/pages/${pageNumber}`);
+ }
+ */
+
+ createDocumentPage(customerId: string, documentId: string, pageNumber: number, file: File): Observable<void> {
+ const formData = new FormData();
+ formData.append('page', file, file.name);
+
+ return this.http.post(`${this.baseUrl}/customers/${customerId}/documents/${documentId}/pages/${pageNumber}`, formData);
+ }
+
+ deleteDocumentPage(customerId: string, documentId: string, pageNumber: number): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/customers/${customerId}/documents/${documentId}/pages/${pageNumber}`);
+ }
+
+}
diff --git a/src/app/sevices/customer/domain/command.model.ts b/src/app/sevices/customer/domain/command.model.ts
new file mode 100644
index 0000000..c2f87bf
--- /dev/null
+++ b/src/app/sevices/customer/domain/command.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type CommandAction = 'ACTIVATE' | 'LOCK' | 'UNLOCK' | 'CLOSE' | 'REOPEN';
+
+export interface Command {
+ action: CommandAction;
+ comment?: string;
+ createdOn?: string;
+ createdBy?: string;
+}
diff --git a/src/app/sevices/customer/domain/customer-document.model.ts b/src/app/sevices/customer/domain/customer-document.model.ts
new file mode 100644
index 0000000..3ab7dd4
--- /dev/null
+++ b/src/app/sevices/customer/domain/customer-document.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface CustomerDocument {
+ identifier: string;
+ description?: string;
+ completed?: boolean;
+ createdBy?: string;
+ createdOn?: string;
+}
diff --git a/src/app/sevices/customer/domain/customer-page.model.ts b/src/app/sevices/customer/domain/customer-page.model.ts
new file mode 100644
index 0000000..eef7fab
--- /dev/null
+++ b/src/app/sevices/customer/domain/customer-page.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Customer} from './customer.model';
+
+export interface CustomerPage {
+ customers: Customer[];
+ totalElements: number;
+ totalPages: number;
+}
diff --git a/src/app/sevices/customer/domain/customer-state.model.ts b/src/app/sevices/customer/domain/customer-state.model.ts
new file mode 100644
index 0000000..c444393
--- /dev/null
+++ b/src/app/sevices/customer/domain/customer-state.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type CustomerState = 'PENDING' | 'ACTIVE' | 'LOCKED' | 'CLOSED';
diff --git a/src/app/sevices/customer/domain/customer-type.model.ts b/src/app/sevices/customer/domain/customer-type.model.ts
new file mode 100644
index 0000000..b9a1958
--- /dev/null
+++ b/src/app/sevices/customer/domain/customer-type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type CustomerType = 'PERSON' | 'BUSINESS';
diff --git a/src/app/sevices/customer/domain/customer.model.ts b/src/app/sevices/customer/domain/customer.model.ts
new file mode 100644
index 0000000..409bb9b
--- /dev/null
+++ b/src/app/sevices/customer/domain/customer.model.ts
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {CustomerState} from './customer-state.model';
+import {CustomerType} from './customer-type.model';
+import {DateOfBirth} from './date-of-birth.model';
+import {IdentificationCard} from './identification-card.model';
+import {Address} from '../../domain/address/address.model';
+import {ContactDetail} from '../../domain/contact/contact-detail.model';
+import {Value} from '../../catalog/domain/value.model';
+
+export interface Customer {
+ identifier: string;
+ type: CustomerType;
+ givenName: string;
+ middleName?: string;
+ surname: string;
+ dateOfBirth: DateOfBirth;
+ identificationCard?: IdentificationCard;
+ accountBeneficiary?: string;
+ referenceCustomer?: string;
+ assignedOffice?: string;
+ assignedEmployee?: string;
+ address: Address;
+ contactDetails?: ContactDetail[];
+ currentState?: CustomerState;
+ applicationDate?: string;
+ customValues: Value[];
+ member: boolean;
+ createdBy?: string;
+ createdOn?: string;
+ lastModifiedBy?: string;
+ lastModifiedOn?: string;
+}
diff --git a/src/app/sevices/customer/domain/date-of-birth.model.ts b/src/app/sevices/customer/domain/date-of-birth.model.ts
new file mode 100644
index 0000000..6451d52
--- /dev/null
+++ b/src/app/sevices/customer/domain/date-of-birth.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface DateOfBirth {
+ year?: number;
+ month?: number;
+ day?: number;
+}
diff --git a/src/app/sevices/customer/domain/expiration-date.model.ts b/src/app/sevices/customer/domain/expiration-date.model.ts
new file mode 100644
index 0000000..a23b1c2
--- /dev/null
+++ b/src/app/sevices/customer/domain/expiration-date.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface ExpirationDate {
+ year: number;
+ month: number;
+ day: number;
+}
diff --git a/src/app/sevices/customer/domain/identification-card-scan.model.ts b/src/app/sevices/customer/domain/identification-card-scan.model.ts
new file mode 100644
index 0000000..0605777
--- /dev/null
+++ b/src/app/sevices/customer/domain/identification-card-scan.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface IdentificationCardScan {
+ identifier: string;
+ description: string;
+}
diff --git a/src/app/sevices/customer/domain/identification-card.model.ts b/src/app/sevices/customer/domain/identification-card.model.ts
new file mode 100644
index 0000000..27553b0
--- /dev/null
+++ b/src/app/sevices/customer/domain/identification-card.model.ts
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ExpirationDate} from './expiration-date.model';
+
+export interface IdentificationCard {
+ type: string;
+ number: string;
+ expirationDate: ExpirationDate;
+ issuer?: string;
+ createdBy?: string;
+ createdOn?: string;
+ lastModifiedBy?: string;
+ lastModifiedOn?: string;
+}
diff --git a/src/app/sevices/customer/domain/permittable-group-ids.ts b/src/app/sevices/customer/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..9926e4c
--- /dev/null
+++ b/src/app/sevices/customer/domain/permittable-group-ids.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class CustomerPermittableGroupIds {
+ public static readonly CUSTOMER_MANAGEMENT = 'customer__v1__customer';
+ public static readonly TASK_MANAGEMENT = 'customer__v1__task';
+ public static readonly CATALOG_MANAGEMENT = 'catalog__v1__catalog';
+ public static readonly IDENTITY_CARD_MANAGEMENT = 'customer__v1__identifications';
+ public static readonly PORTRAIT_MANAGEMENT = 'customer__v1__portrait';
+ public static readonly CUSTOMER_DOCUMENT = 'customer__v1__documents';
+}
+
+
diff --git a/src/app/sevices/customer/domain/process-step.model.ts b/src/app/sevices/customer/domain/process-step.model.ts
new file mode 100644
index 0000000..b112f22
--- /dev/null
+++ b/src/app/sevices/customer/domain/process-step.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Command} from './command.model';
+import {TaskDefinition} from './task-definition.model';
+
+export interface ProcessStep {
+ command: Command;
+ taskDefinitions: TaskDefinition[];
+}
diff --git a/src/app/sevices/customer/domain/task-definition.model.ts b/src/app/sevices/customer/domain/task-definition.model.ts
new file mode 100644
index 0000000..7cc6f1b
--- /dev/null
+++ b/src/app/sevices/customer/domain/task-definition.model.ts
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type TaskDefinitionType = 'ID_CARD' | 'FOUR_EYES' | 'CUSTOM';
+
+export type TaskDefinitionCommand = 'ACTIVATE' | 'LOCK' | 'UNLOCK' | 'CLOSE' | 'REOPEN';
+
+export interface TaskDefinition {
+ identifier: string;
+ type: TaskDefinitionType;
+ commands: TaskDefinitionCommand[];
+ name: string;
+ description: string;
+ mandatory: boolean;
+ predefined: boolean;
+}
diff --git a/src/app/sevices/depositAccount/deposit-account.service.ts b/src/app/sevices/depositAccount/deposit-account.service.ts
new file mode 100644
index 0000000..02ef3a0
--- /dev/null
+++ b/src/app/sevices/depositAccount/deposit-account.service.ts
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Observable} from 'rxjs/Observable';
+import {ProductDefinition} from './domain/definition/product-definition.model';
+import {ProductDefinitionCommand} from './domain/definition/product-definition-command.model';
+import {ProductInstance} from './domain/instance/product-instance.model';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {Action} from './domain/definition/action.model';
+import {DividendDistribution} from './domain/definition/dividend-distribution.model';
+import {AvailableTransactionType} from './domain/instance/available-transaction-type.model';
+
+@Injectable()
+export class DepositAccountService {
+
+ constructor(private http: HttpClient, @Inject('depositAccountBaseUrl') private baseUrl: string) {
+ }
+
+ createProductDefinition(productDefinition: ProductDefinition): Observable<void> {
+ return this.http.post(`${this.baseUrl}/definitions`, productDefinition);
+ }
+
+ updateProductDefinition(productDefinition: ProductDefinition): Observable<void> {
+ return this.http.put(`${this.baseUrl}/definitions/${productDefinition.identifier}`, productDefinition);
+ }
+
+ deleteProductDefinition(identifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/definitions/${identifier}`);
+ }
+
+ fetchProductDefinitions(): Observable<ProductDefinition[]> {
+ return this.http.get(`${this.baseUrl}/definitions`);
+ }
+
+ findProductDefinition(identifier: string): Observable<ProductDefinition> {
+ return this.http.get(`${this.baseUrl}/definitions/${identifier}`);
+ }
+
+ processCommand(identifier: string, command: ProductDefinitionCommand): Observable<void> {
+ return this.http.post(`${this.baseUrl}/definitions/${identifier}/commands`, command);
+ }
+
+ fetchDividendDistributions(identifier: string): Observable<DividendDistribution[]> {
+ return this.http.get(`${this.baseUrl}/definitions/${identifier}/dividends`);
+ }
+
+ distributeDividend(identifier: string, dividendDistribution: DividendDistribution): Observable<void> {
+ return this.http.post(`${this.baseUrl}/definitions/${identifier}/dividends`, dividendDistribution);
+ }
+
+ createProductInstance(productInstance: ProductInstance): Observable<void> {
+ return this.http.post(`${this.baseUrl}/instances`, productInstance);
+ }
+
+ updateProductInstance(productInstance: ProductInstance): Observable<void> {
+ return this.http.put(`${this.baseUrl}/instances/${productInstance.accountIdentifier}`, productInstance);
+ }
+
+ findProductInstance(identifier: string): Observable<ProductInstance> {
+ return this.http.get(`${this.baseUrl}/instances/${identifier}`);
+ }
+
+ fetchProductInstances(customerIdentifier: string, productIdentifier?: string): Observable<ProductInstance[]> {
+ const search = new URLSearchParams();
+
+ search.append('customer', customerIdentifier);
+ search.append('product', productIdentifier);
+
+ const requestOptions: RequestOptionsArgs = {
+ search
+ };
+
+ return this.http.get(`${this.baseUrl}/instances`, requestOptions);
+ }
+
+ fetchActions(): Observable<Action[]> {
+ return this.http.get(`${this.baseUrl}/actions`);
+ }
+
+ fetchPossibleTransactionTypes(customerIdentifier: string): Observable<AvailableTransactionType[]> {
+ const search = new URLSearchParams();
+
+ search.append('customer', customerIdentifier);
+
+ const requestOptions: RequestOptionsArgs = {
+ search
+ };
+
+ return this.http.get(`${this.baseUrl}/instances/transactiontypes`, requestOptions);
+ }
+
+
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/action.model.ts b/src/app/sevices/depositAccount/domain/definition/action.model.ts
new file mode 100644
index 0000000..7d4a801
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/action.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Action {
+ identifier: string;
+ name: string;
+ description?: string;
+ transactionType: string;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/charge.model.ts b/src/app/sevices/depositAccount/domain/definition/charge.model.ts
new file mode 100644
index 0000000..76ea29b
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/charge.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Charge {
+ actionIdentifier: string;
+ incomeAccountIdentifier: string;
+ name: string;
+ description?: string;
+ proportional?: boolean;
+ amount?: number;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/currency.model.ts b/src/app/sevices/depositAccount/domain/definition/currency.model.ts
new file mode 100644
index 0000000..1e10bf5
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/currency.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Currency {
+ code: string;
+ name: string;
+ sign: string;
+ scale: number;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/dividend-distribution.model.ts b/src/app/sevices/depositAccount/domain/definition/dividend-distribution.model.ts
new file mode 100644
index 0000000..170664c
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/dividend-distribution.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface DividendDistribution {
+ dueDate: {
+ year?: number;
+ month?: number;
+ day?: number;
+ };
+ dividendRate: string;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/product-definition-command.model.ts b/src/app/sevices/depositAccount/domain/definition/product-definition-command.model.ts
new file mode 100644
index 0000000..6aef8c1
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/product-definition-command.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type Action = 'ACTIVATE' | 'DEACTIVATE';
+
+export interface ProductDefinitionCommand {
+ action: Action;
+ note?: string;
+ createdBy?: string;
+ createdOn?: string;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/product-definition.model.ts b/src/app/sevices/depositAccount/domain/definition/product-definition.model.ts
new file mode 100644
index 0000000..b87cfc6
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/product-definition.model.ts
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Type} from '../type.model';
+import {Currency} from './currency.model';
+import {Charge} from './charge.model';
+import {Term} from './term.model';
+
+export interface ProductDefinition {
+ type: Type;
+ identifier: string;
+ name: string;
+ description?: string;
+ currency: Currency;
+ minimumBalance: number;
+ equityLedgerIdentifier?: string;
+ expenseAccountIdentifier: string;
+ cashAccountIdentifier: string;
+ accrueAccountIdentifier?: string;
+ interest?: number;
+ term: Term;
+ charges: Charge[];
+ flexible: boolean;
+ active?: boolean;
+}
diff --git a/src/app/sevices/depositAccount/domain/definition/term.model.ts b/src/app/sevices/depositAccount/domain/definition/term.model.ts
new file mode 100644
index 0000000..d016b9a
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/definition/term.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TimeUnit} from '../time-unit.model';
+import {InterestPayable} from '../interest-payable.model';
+
+export interface Term {
+ period?: number;
+ timeUnit?: TimeUnit;
+ interestPayable: InterestPayable;
+}
diff --git a/src/app/sevices/depositAccount/domain/instance/available-transaction-type.model.ts b/src/app/sevices/depositAccount/domain/instance/available-transaction-type.model.ts
new file mode 100644
index 0000000..e4a7a10
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/instance/available-transaction-type.model.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface AvailableTransactionType {
+ transactionType: string;
+}
diff --git a/src/app/sevices/depositAccount/domain/instance/product-instance.model.ts b/src/app/sevices/depositAccount/domain/instance/product-instance.model.ts
new file mode 100644
index 0000000..db82ca4
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/instance/product-instance.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface ProductInstance {
+ customerIdentifier: string;
+ productIdentifier: string;
+ accountIdentifier?: string;
+ alternativeAccountNumber?: string;
+ beneficiaries?: string[];
+ state?: string;
+ balance?: number;
+ openedOn?: string;
+ lastTransactionDate?: string;
+}
diff --git a/src/app/sevices/depositAccount/domain/interest-payable.model.ts b/src/app/sevices/depositAccount/domain/interest-payable.model.ts
new file mode 100644
index 0000000..716bb74
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/interest-payable.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type InterestPayable = 'MATURITY' | 'ANNUALLY' | 'QUARTERLY' | 'MONTHLY';
diff --git a/src/app/sevices/depositAccount/domain/permittable-group-ids.ts b/src/app/sevices/depositAccount/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..3f9bf3d
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/permittable-group-ids.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class DepositAccountPermittableGroupIds {
+ public static readonly DEFINITION_MANAGEMENT = 'deposit__v1__definition';
+ public static readonly INSTANCE_MANAGEMENT = 'deposit__v1__instance';
+}
diff --git a/src/app/sevices/depositAccount/domain/time-unit.model.ts b/src/app/sevices/depositAccount/domain/time-unit.model.ts
new file mode 100644
index 0000000..9b7f6bf
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/time-unit.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type TimeUnit = 'MONTH' | 'YEAR';
diff --git a/src/app/sevices/depositAccount/domain/type.model.ts b/src/app/sevices/depositAccount/domain/type.model.ts
new file mode 100644
index 0000000..20cea9e
--- /dev/null
+++ b/src/app/sevices/depositAccount/domain/type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type Type = 'CHECKING' | 'SAVINGS' | 'SHARE';
diff --git a/src/app/sevices/domain/address/address.model.ts b/src/app/sevices/domain/address/address.model.ts
new file mode 100644
index 0000000..e378dc7
--- /dev/null
+++ b/src/app/sevices/domain/address/address.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Address {
+ street: string;
+ city: string;
+ region?: string;
+ postalCode?: string;
+ countryCode: string;
+ country: string;
+}
diff --git a/src/app/sevices/domain/contact/contact-detail.model.ts b/src/app/sevices/domain/contact/contact-detail.model.ts
new file mode 100644
index 0000000..ed1170d
--- /dev/null
+++ b/src/app/sevices/domain/contact/contact-detail.model.ts
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface ContactDetail {
+ type: ContactDetailType;
+ group: ContactDetailGroup;
+ value: string;
+ preferenceLevel: number;
+}
+
+export const BUSINESS = 'BUSINESS';
+export const PRIVATE = 'PRIVATE';
+
+export type ContactDetailGroup = 'BUSINESS' | 'PRIVATE';
+
+export const EMAIL = 'EMAIL';
+export const PHONE = 'PHONE';
+export const MOBILE = 'MOBILE';
+
+export type ContactDetailType = 'EMAIL' | 'PHONE' | 'MOBILE';
diff --git a/src/app/sevices/domain/date.converter.ts b/src/app/sevices/domain/date.converter.ts
new file mode 100644
index 0000000..365382f
--- /dev/null
+++ b/src/app/sevices/domain/date.converter.ts
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface FimsDate {
+ year: number;
+ month: number;
+ day: number;
+}
+
+export function dateAsISOString(date: Date): string {
+ return date.toISOString().slice(0, 10);
+}
+
+export function todayAsISOString(): string {
+ return new Date().toISOString().slice(0, 10);
+}
+
+/**
+ * Converts '2017-01-20' to full ISO string e.g. '2017-01-20T00:00:00.000Z'
+ * @param dateString
+ */
+export function toLongISOString(dateString: string): string {
+ const date: Date = parseDate(dateString);
+ return date.toISOString();
+}
+
+export function toShortISOString(dateString: string): string {
+ return `${toLongISOString(dateString).slice(0, 10)}Z`;
+}
+
+export function toEndOfDay(dateString: string): string {
+ const date: Date = parseDate(dateString);
+
+ date.setDate(date.getDate() + 1);
+ date.setTime(date.getTime() - 1);
+
+ return date.toISOString();
+}
+
+export function addCurrentTime(date: Date): Date {
+ const now: Date = new Date();
+
+ date.setUTCHours(now.getUTCHours());
+ date.setUTCMinutes(now.getUTCMinutes());
+ date.setUTCSeconds(now.getUTCSeconds());
+ date.setUTCMilliseconds(now.getUTCMilliseconds());
+
+ return date;
+}
+
+export function parseDate(dateString: string): Date {
+ const millis = Date.parse(dateString);
+ const date: Date = new Date(millis);
+
+ return date;
+}
+
+/**
+ * Converts '2017-01-20' to FimsDate
+ * @param dateString
+ */
+export function toFimsDate(dateString: string): FimsDate {
+ const chunks: string[] = dateString ? dateString.split('-') : [];
+
+ return {
+ year: chunks.length ? Number(chunks[0]) : undefined,
+ month: chunks.length ? Number(chunks[1]) : undefined,
+ day: chunks.length ? Number(chunks[2]) : undefined,
+ };
+}
+
+export function toISOString(fimsDate: FimsDate): string {
+ return formatDate(fimsDate.year, fimsDate.month, fimsDate.day);
+}
+
+function formatDate(year: number, month: number, day: number): string {
+ return `${year}-${addZero(month)}-${addZero(day)}`;
+}
+
+function addZero(value: number): string {
+ return ('0' + value).slice(-2);
+}
+
+
diff --git a/src/app/sevices/domain/error.model.ts b/src/app/sevices/domain/error.model.ts
new file mode 100644
index 0000000..1073ef5
--- /dev/null
+++ b/src/app/sevices/domain/error.model.ts
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Observable} from 'rxjs/Observable';
+import {ErrorObservable} from 'rxjs/observable/ErrorObservable';
+
+export class Error {
+ status: number;
+ statusText: string;
+ message: string;
+
+ static handleError(errorResponse: any): ErrorObservable {
+ const error: Error = new Error(errorResponse.status, errorResponse.statusText, errorResponse.message);
+
+ console.error(error.getErrorMessage());
+
+ return Observable.throw(error);
+ }
+
+ constructor(status: number, statusText: string, message: string) {
+ this.status = status;
+ this.message = message;
+ this.statusText = statusText;
+ }
+
+ getErrorMessage(): string {
+ const errMsg: string = (this.message)
+ ? this.message
+ : this.status ? `${this.status} - ${this.statusText}` : 'Server error';
+ return errMsg;
+ }
+
+}
diff --git a/src/app/sevices/domain/paging/fetch-request.model.ts b/src/app/sevices/domain/paging/fetch-request.model.ts
new file mode 100644
index 0000000..989a365
--- /dev/null
+++ b/src/app/sevices/domain/paging/fetch-request.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Sort} from './sort.model';
+import {Page} from './page.model';
+
+export interface FetchRequest {
+ searchTerm?: string;
+ page?: Page;
+ sort?: Sort;
+}
diff --git a/src/app/sevices/domain/paging/page.model.ts b/src/app/sevices/domain/paging/page.model.ts
new file mode 100644
index 0000000..b8172cf
--- /dev/null
+++ b/src/app/sevices/domain/paging/page.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Page {
+ pageIndex: number;
+ size: number;
+}
diff --git a/src/app/sevices/domain/paging/search-param.builder.ts b/src/app/sevices/domain/paging/search-param.builder.ts
new file mode 100644
index 0000000..258cebd
--- /dev/null
+++ b/src/app/sevices/domain/paging/search-param.builder.ts
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {URLSearchParams} from '@angular/http';
+import {FetchRequest} from './fetch-request.model';
+import {Page} from './page.model';
+import {Sort} from './sort.model';
+
+export function buildSearchParams(fetchRequest?: FetchRequest): URLSearchParams {
+ const params = new URLSearchParams();
+
+ fetchRequest = fetchRequest || {};
+
+ const page: Page = fetchRequest.page || {pageIndex: 0, size: 10};
+ const sort: Sort = fetchRequest.sort || {sortColumn: '', sortDirection: ''};
+
+ params.append('term', fetchRequest.searchTerm ? fetchRequest.searchTerm : undefined);
+
+ params.append('pageIndex', page.pageIndex !== undefined ? page.pageIndex.toString() : undefined);
+ params.append('size', page.size ? page.size.toString() : undefined);
+
+ params.append('sortColumn', sort.sortColumn ? sort.sortColumn : undefined);
+ params.append('sortDirection', sort.sortDirection ? sort.sortDirection : undefined);
+
+ return params;
+}
+
+export function buildDateRangeParam(startDate: string, endDate: string): string {
+ return `${startDate}..${endDate}`;
+}
diff --git a/src/app/sevices/domain/paging/sort.model.ts b/src/app/sevices/domain/paging/sort.model.ts
new file mode 100644
index 0000000..982864b
--- /dev/null
+++ b/src/app/sevices/domain/paging/sort.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface Sort {
+ sortColumn: string;
+ sortDirection: string;
+}
diff --git a/src/app/sevices/http/default-request-options.service.ts b/src/app/sevices/http/default-request-options.service.ts
new file mode 100644
index 0000000..56549c1
--- /dev/null
+++ b/src/app/sevices/http/default-request-options.service.ts
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {BaseRequestOptions, RequestOptions} from '@angular/http';
+import {Injectable} from '@angular/core';
+
+@Injectable()
+export class DefaultRequestOptions extends BaseRequestOptions {
+
+ constructor() {
+ super();
+
+ // this.headers.set('Accept', 'application/json');
+ // this.headers.set('Content-Type', 'application/json');
+ }
+}
+
+export const requestOptionsProvider = {provide: RequestOptions, useClass: DefaultRequestOptions};
diff --git a/src/app/sevices/http/header.service.ts b/src/app/sevices/http/header.service.ts
new file mode 100644
index 0000000..2abcfba
--- /dev/null
+++ b/src/app/sevices/http/header.service.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class HeaderService {
+
+}
diff --git a/src/app/sevices/http/http.service.spec.ts b/src/app/sevices/http/http.service.spec.ts
new file mode 100644
index 0000000..0500f29
--- /dev/null
+++ b/src/app/sevices/http/http.service.spec.ts
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {MockBackend, MockConnection} from '@angular/http/testing';
+import {AUTHORIZATION_HEADER, HttpClient, TENANT_HEADER, USER_HEADER} from './http.service';
+import {
+ BaseRequestOptions,
+ ConnectionBackend,
+ Headers,
+ Http,
+ RequestOptions,
+ RequestOptionsArgs,
+ Response,
+ ResponseOptions
+} from '@angular/http';
+import {Store} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {ReflectiveInjector} from '@angular/core';
+
+describe('Test http client', () => {
+
+ const tenant = 'Reynholm Industries';
+
+ const authenticationState = {
+ username: 'test',
+ tenant: tenant,
+ authentication: {
+ tokenType: 'iDontCare',
+ accessToken: 'accessToken',
+ accessTokenExpiration: new Date().toISOString(),
+ refreshTokenExpiration: new Date().toISOString(),
+ passwordExpiration: new Date().toISOString(),
+ passwordChangedBy: 'moss'
+ }
+ };
+
+ const doPostRequest = function (httpClient: HttpClient, options?: RequestOptionsArgs): void {
+ httpClient.post('/test', {}, options).subscribe(() => {
+ });
+ };
+
+ describe('Test http header', () => {
+
+ beforeEach(() => {
+ this.injector = ReflectiveInjector.resolveAndCreate([
+ {provide: ConnectionBackend, useClass: MockBackend},
+ {provide: RequestOptions, useClass: BaseRequestOptions},
+ {
+ provide: Store, useClass: class {
+ select = jasmine.createSpy('select').and.returnValue(Observable.of(authenticationState));
+ }
+ },
+ Http,
+ HttpClient,
+ ]);
+ this.httpClient = this.injector.get(HttpClient);
+ this.backend = this.injector.get(ConnectionBackend) as MockBackend;
+ });
+
+ it('should send tenant header', (done: DoneFn) => {
+ this.backend.connections.subscribe((connection: MockConnection) => {
+ expect(connection.request.headers.get(TENANT_HEADER)).toBe(tenant);
+ done();
+ });
+ doPostRequest(this.httpClient);
+ });
+
+ it('should send authorization header when logged in', (done: DoneFn) => {
+ this.backend.connections.subscribe((connection: MockConnection) => {
+ expect(connection.request.headers.get(USER_HEADER)).toBe(authenticationState.username);
+ expect(connection.request.headers.get(AUTHORIZATION_HEADER)).toBe(authenticationState.authentication.accessToken);
+ done();
+ });
+ doPostRequest(this.httpClient);
+ });
+
+ it('should send custom headers', (done: DoneFn) => {
+ this.backend.connections.subscribe((connection: MockConnection) => {
+ expect(connection.request.headers.get('Content-Type')).toBe('multipart/form-data');
+ done();
+ });
+ doPostRequest(this.httpClient, {
+ headers: new Headers({
+ 'Content-Type': 'multipart/form-data'
+ })
+ });
+ });
+
+ it('should return json if json', (done: DoneFn) => {
+ const expectedResponse: any = {
+ text: 'text'
+ };
+ this.backend.connections.subscribe((connection: MockConnection) => {
+ const response = new Response(new ResponseOptions({
+ body: JSON.stringify(expectedResponse)
+ }));
+ connection.mockRespond(response);
+ });
+
+ this.httpClient.post('/test', {}).subscribe(response => {
+ expect(response).toEqual(expectedResponse);
+ done();
+ });
+ });
+
+ it('should return text if no json', (done: DoneFn) => {
+ this.backend.connections.subscribe((connection: MockConnection) => {
+ const response = new Response(new ResponseOptions({
+ body: 'text'
+ }));
+ connection.mockRespond(response);
+ });
+
+ this.httpClient.post('/test', {}).subscribe(text => {
+ expect(text).toEqual('text');
+ done();
+ });
+ });
+
+ });
+
+});
diff --git a/src/app/sevices/http/http.service.ts b/src/app/sevices/http/http.service.ts
new file mode 100644
index 0000000..7a33b35
--- /dev/null
+++ b/src/app/sevices/http/http.service.ts
@@ -0,0 +1,124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Headers, Http, Request, RequestMethod, RequestOptions, RequestOptionsArgs, Response} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {Subject} from 'rxjs/Subject';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../store';
+import {LOGOUT} from '../../store/security/security.actions';
+
+export enum Action { QueryStart, QueryStop }
+
+export const TENANT_HEADER = 'X-Tenant-Identifier';
+export const USER_HEADER = 'User';
+export const AUTHORIZATION_HEADER = 'Authorization';
+
+@Injectable()
+export class HttpClient {
+
+ process: Subject<Action> = new Subject<Action>();
+
+ error: Subject<any> = new Subject<any>();
+
+ constructor(private http: Http, private store: Store<fromRoot.State>) {
+ }
+
+ public get(url: string, options?: RequestOptionsArgs, silent?: boolean): Observable<any> {
+ return this.createRequest(RequestMethod.Get, url, undefined, options, silent);
+ }
+
+ public post(url: string, body: any, options?: RequestOptionsArgs, silent?: boolean): Observable<any> {
+ return this.createRequest(RequestMethod.Post, url, body, options, silent);
+ }
+
+ public put(url: string, body: any, options?: RequestOptionsArgs): Observable<any> {
+ return this.createRequest(RequestMethod.Put, url, body, options);
+ }
+
+ public delete(url: string, options?: RequestOptionsArgs): Observable<any> {
+ return this.createRequest(RequestMethod.Delete, url, undefined, options);
+ }
+
+ private _buildRequestOptions(method: RequestMethod, url: string, body: any, tenant: string, username: string,
+ accessToken: string, options?: RequestOptionsArgs): RequestOptions {
+ options = options || {};
+
+ const headers = new Headers();
+
+ if (!(body instanceof FormData)) {
+ headers.set('Accept', 'application/json');
+ headers.set('Content-Type', 'application/json');
+ }
+
+ headers.set(TENANT_HEADER, tenant);
+ headers.set(USER_HEADER, username);
+ headers.set(AUTHORIZATION_HEADER, accessToken);
+
+ const requestOptions: RequestOptions = new RequestOptions({
+ method: method,
+ url: url,
+ body: body,
+ headers: headers
+ });
+
+ return requestOptions.merge(options);
+ }
+
+ private createRequest(method: RequestMethod, url: string, body?: any, options?: RequestOptionsArgs, silent?: boolean): Observable<any> {
+ return this.store.select(fromRoot.getAuthenticationState)
+ .take(1)
+ .map(state => this._buildRequestOptions(method, url, body, state.tenant, state.username, state.authentication.accessToken, options))
+ .flatMap(requestOptions => {
+ this.process.next(Action.QueryStart);
+
+ const request: Observable<any> = this.http.request(new Request(requestOptions))
+ .catch((err: any) => {
+ const error = err.json();
+ if (silent) {
+ return Observable.throw(error);
+ }
+
+ switch (error.status) {
+ case 409:
+ return Observable.throw(error);
+ case 401:
+ case 403:
+ this.store.dispatch({type: LOGOUT});
+ return Observable.throw('User is not authenticated');
+ default:
+ console.error('Error', error);
+ this.error.next(error);
+ return Observable.throw(error);
+ }
+ }).finally(() => this.process.next(Action.QueryStop));
+
+ return request.map((res: Response) => {
+ if (res.text()) {
+ try {
+ return res.json();
+ } catch (err) {
+ return res.text();
+ }
+ }
+ });
+ });
+ }
+
+}
diff --git a/src/app/sevices/identity/domain/authentication.model.ts b/src/app/sevices/identity/domain/authentication.model.ts
new file mode 100644
index 0000000..25ed770
--- /dev/null
+++ b/src/app/sevices/identity/domain/authentication.model.ts
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class Authentication {
+ tokenType: string;
+ accessToken: string;
+ accessTokenExpiration: string;
+ refreshTokenExpiration: string;
+ passwordExpiration: string;
+
+ constructor(tokenType: string,
+ accessToken: string, accessTokenExpiration: string,
+ refreshTokenExpiration: string,
+ passwordExpiration: string) {
+ this.tokenType = tokenType;
+ this.accessToken = accessToken;
+ this.accessTokenExpiration = accessTokenExpiration;
+ this.refreshTokenExpiration = refreshTokenExpiration;
+ this.passwordExpiration = passwordExpiration;
+ }
+}
diff --git a/src/app/sevices/identity/domain/password.model.ts b/src/app/sevices/identity/domain/password.model.ts
new file mode 100644
index 0000000..2b6a48b
--- /dev/null
+++ b/src/app/sevices/identity/domain/password.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class Password {
+ password: string;
+
+ constructor(password: string) {
+ this.password = password;
+ }
+}
diff --git a/src/app/sevices/identity/domain/permission.model.ts b/src/app/sevices/identity/domain/permission.model.ts
new file mode 100644
index 0000000..436f14d
--- /dev/null
+++ b/src/app/sevices/identity/domain/permission.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Permission {
+ permittableEndpointGroupIdentifier: string;
+ allowedOperations: AllowedOperation[];
+}
+
+export type AllowedOperation = 'READ' | 'CHANGE' | 'DELETE';
diff --git a/src/app/sevices/identity/domain/permittable-group-ids.model.ts b/src/app/sevices/identity/domain/permittable-group-ids.model.ts
new file mode 100644
index 0000000..271ae45
--- /dev/null
+++ b/src/app/sevices/identity/domain/permittable-group-ids.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class IdentityPermittableGroupIds {
+ public static readonly IDENTITY_MANAGEMENT = 'identity__v1__users';
+ public static readonly ROLE_MANAGEMENT = 'identity__v1__roles';
+ public static readonly SELF_MANAGEMENT = 'identity__v1__self';
+}
+
+
diff --git a/src/app/sevices/identity/domain/role-identifier.model.ts b/src/app/sevices/identity/domain/role-identifier.model.ts
new file mode 100644
index 0000000..50424b5
--- /dev/null
+++ b/src/app/sevices/identity/domain/role-identifier.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class RoleIdentifier {
+ identifier: string;
+
+ constructor(identifier: string) {
+ this.identifier = identifier;
+ }
+}
diff --git a/src/app/sevices/identity/domain/role.model.ts b/src/app/sevices/identity/domain/role.model.ts
new file mode 100644
index 0000000..123b657
--- /dev/null
+++ b/src/app/sevices/identity/domain/role.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Permission} from './permission.model';
+
+export interface Role {
+ identifier: string;
+ permissions: Permission[];
+}
diff --git a/src/app/sevices/identity/domain/user-with-password.model.ts b/src/app/sevices/identity/domain/user-with-password.model.ts
new file mode 100644
index 0000000..4369788
--- /dev/null
+++ b/src/app/sevices/identity/domain/user-with-password.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class UserWithPassword {
+ identifier: string;
+ role: string;
+ password: string;
+}
diff --git a/src/app/sevices/identity/domain/user.model.ts b/src/app/sevices/identity/domain/user.model.ts
new file mode 100644
index 0000000..ca761c7
--- /dev/null
+++ b/src/app/sevices/identity/domain/user.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class User {
+ identifier: string;
+ role: string;
+}
diff --git a/src/app/sevices/identity/identity.service.ts b/src/app/sevices/identity/identity.service.ts
new file mode 100644
index 0000000..12a8ad6
--- /dev/null
+++ b/src/app/sevices/identity/identity.service.ts
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Error} from '../domain/error.model';
+import {HttpClient} from '../http/http.service';
+import {Password} from './domain/password.model';
+import {UserWithPassword} from './domain/user-with-password.model';
+import {Role} from './domain/role.model';
+import {RoleIdentifier} from './domain/role-identifier.model';
+import {User} from './domain/user.model';
+import {PermittableGroup} from '../anubis/permittable-group.model';
+
+@Injectable()
+export class IdentityService {
+
+ private static encodePassword(password: string): string {
+ return btoa(password);
+ }
+
+ private baseUrl: string = "http://163.172.130.175:8888/"
+
+ constructor(private http: HttpClient ) { // , @Inject('identityBaseUrl') private baseUrl: string) {
+ }
+
+ changePassword(id: string, password: Password): Observable<any> {
+ password.password = IdentityService.encodePassword(password.password);
+ return this.http.put(this.baseUrl + '/users/' + id + '/password', password)
+ .catch(Error.handleError);
+ }
+
+ createUser(user: UserWithPassword): Observable<any> {
+ user.password = IdentityService.encodePassword(user.password);
+ return this.http.post(this.baseUrl + '/users', user)
+ .catch(Error.handleError);
+ }
+
+ getUser(id: string): Observable<User> {
+ return this.http.get(this.baseUrl + '/users/' + id)
+ .catch(Error.handleError);
+ }
+
+ changeUserRole(user: string, roleIdentifier: RoleIdentifier): Observable<any> {
+ return this.http.put(this.baseUrl + '/users/' + user + '/roleIdentifier', roleIdentifier)
+ .catch(Error.handleError);
+ }
+
+ listRoles(): Observable<Role[]> {
+ return this.http.get(this.baseUrl + '/roles')
+ .catch(Error.handleError);
+ }
+
+ getRole(id: string): Observable<Role> {
+ return this.http.get(this.baseUrl + '/roles/' + id)
+ .catch(Error.handleError);
+ }
+
+ createRole(role: Role): Observable<any> {
+ return this.http.post(this.baseUrl + '/roles', role)
+ .catch(Error.handleError);
+ }
+
+ changeRole(role: Role): Observable<any> {
+ return this.http.put(this.baseUrl + '/roles/' + role.identifier, role)
+ .catch(Error.handleError);
+ }
+
+ deleteRole(id: String): Observable<any> {
+ return this.http.delete(this.baseUrl + '/roles/' + id, {})
+ .catch(Error.handleError);
+ }
+
+ createPermittableGroup(permittableGroup: PermittableGroup): Observable<PermittableGroup> {
+ return this.http.post(this.baseUrl + '/permittablegroups', permittableGroup)
+ .catch(Error.handleError);
+ }
+
+ getPermittableGroup(id: string): Observable<PermittableGroup> {
+ return this.http.get(this.baseUrl + '/permittablegroups/' + id)
+ .catch(Error.handleError);
+ }
+
+ getPermittableGroups(): Observable<PermittableGroup[]> {
+ return this.http.get(this.baseUrl + '/permittablegroups')
+ .catch(Error.handleError);
+ }
+}
diff --git a/src/app/sevices/image/image.service.ts b/src/app/sevices/image/image.service.ts
new file mode 100644
index 0000000..2db3b1e
--- /dev/null
+++ b/src/app/sevices/image/image.service.ts
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Headers, Http, Response, ResponseContentType} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../store';
+import {AUTHORIZATION_HEADER, TENANT_HEADER, USER_HEADER} from '../http/http.service';
+import {State} from '../../store/security/authentication.reducer';
+
+@Injectable()
+export class ImageService {
+
+ constructor(private http: Http, private store: Store<fromRoot.State>) {
+ }
+
+ /* public getImage(url: string): Observable<Blob> {
+ return this.store.select(fromRoot.getAuthenticationState)
+ .take(1)
+ .map(this.mapHeader)
+ .switchMap((headers: Headers) =>
+ this.http.get(url, {
+ responseType: ResponseContentType.Blob,
+ headers: headers
+ })
+ .map((response: Response) => response.blob())
+ .catch(() => Observable.empty()));
+
+ }
+ */
+
+
+ private mapHeader(authenticationState: State): Headers {
+ const headers = new Headers();
+
+ headers.set('Accept', 'application/json');
+
+ headers.set(TENANT_HEADER, authenticationState.tenant);
+ headers.set(USER_HEADER, authenticationState.username);
+ headers.set(AUTHORIZATION_HEADER, authenticationState.authentication.accessToken);
+
+ return headers;
+ }
+
+}
diff --git a/src/app/sevices/notification/notification.service.ts b/src/app/sevices/notification/notification.service.ts
new file mode 100644
index 0000000..96ba681
--- /dev/null
+++ b/src/app/sevices/notification/notification.service.ts
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+
+export enum NotificationType {
+ MESSAGE, ALERT
+}
+
+export interface NotificationEvent {
+ type: NotificationType;
+ title?: string;
+ message: string;
+}
+
+@Injectable()
+export class NotificationService {
+
+ private notificationSource = new BehaviorSubject<NotificationEvent>(null);
+
+ notifications$ = this.notificationSource.asObservable();
+
+ send(notification: NotificationEvent) {
+ this.notificationSource.next(notification);
+ }
+}
diff --git a/src/app/sevices/office/domain/employee-page.model.ts b/src/app/sevices/office/domain/employee-page.model.ts
new file mode 100644
index 0000000..5e55739
--- /dev/null
+++ b/src/app/sevices/office/domain/employee-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Employee} from './employee.model';
+
+export interface EmployeePage {
+ employees: Employee[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/office/domain/employee.model.ts b/src/app/sevices/office/domain/employee.model.ts
new file mode 100644
index 0000000..485e515
--- /dev/null
+++ b/src/app/sevices/office/domain/employee.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ContactDetail} from '../../domain/contact/contact-detail.model';
+
+export interface Employee {
+ identifier: string;
+ givenName: string;
+ middleName?: string;
+ surname: string;
+ assignedOffice?: string;
+ contactDetails: ContactDetail[];
+}
diff --git a/src/app/sevices/office/domain/office-page.model.ts b/src/app/sevices/office/domain/office-page.model.ts
new file mode 100644
index 0000000..07d0400
--- /dev/null
+++ b/src/app/sevices/office/domain/office-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Office} from './office.model';
+
+export interface OfficePage {
+ offices: Office[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/office/domain/office.model.ts b/src/app/sevices/office/domain/office.model.ts
new file mode 100644
index 0000000..4fc8019
--- /dev/null
+++ b/src/app/sevices/office/domain/office.model.ts
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Address} from '../../domain/address/address.model';
+
+export interface Office {
+ identifier: string;
+ parentIdentifier?: string;
+ name: string;
+ description?: string;
+ address?: Address;
+ branches?: Office[];
+ tellerIds?: string[];
+ externalReferences?: boolean;
+}
diff --git a/src/app/sevices/office/domain/permittable-group-ids.model.ts b/src/app/sevices/office/domain/permittable-group-ids.model.ts
new file mode 100644
index 0000000..cec0a5a
--- /dev/null
+++ b/src/app/sevices/office/domain/permittable-group-ids.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class OfficePermittableGroupIds {
+ public static readonly OFFICE_MANAGEMENT = 'office__v1__offices';
+ public static readonly EMPLOYEE_MANAGEMENT = 'office__v1__employees';
+ public static readonly SELF_MANAGEMENT = 'office__v1__self';
+}
+
+
diff --git a/src/app/sevices/office/office.service.ts b/src/app/sevices/office/office.service.ts
new file mode 100644
index 0000000..5df8ab2
--- /dev/null
+++ b/src/app/sevices/office/office.service.ts
@@ -0,0 +1,118 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {HttpClient} from '../http/http.service';
+import {Error} from '../domain/error.model';
+import {RequestOptionsArgs, URLSearchParams,} from '@angular/http';
+import {Office} from './domain/office.model';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {OfficePage} from './domain/office-page.model';
+import {EmployeePage} from './domain/employee-page.model';
+import {Employee} from './domain/employee.model';
+import {buildSearchParams} from '../domain/paging/search-param.builder';
+import {ContactDetail} from '../domain/contact/contact-detail.model';
+
+@Injectable()
+export class OfficeService {
+
+ constructor(private http: HttpClient, @Inject('officeBaseUrl') private baseUrl: string) {
+ }
+
+ createOffice(office: Office): Observable<Office> {
+ return this.http.post(this.baseUrl + '/offices', office)
+
+ }
+
+ addBranch(id: string, office: Office): Observable<Office> {
+ return this.http.post(this.baseUrl + '/offices/' + id, office)
+
+ }
+
+ updateOffice(office: Office): Observable<Office> {
+ return this.http.put(this.baseUrl + '/offices/' + office.identifier, office)
+
+ }
+
+ deleteOffice(id: String): Observable<Office> {
+ return this.http.delete(this.baseUrl + '/offices/' + id, {})
+
+ }
+
+ listOffices(fetchRequest?: FetchRequest): Observable<OfficePage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+ return this.http.get(this.baseUrl + '/offices', requestOptions)
+
+ }
+
+ listBranches(parentIdentifier: string, fetchRequest?: FetchRequest): Observable<OfficePage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+ return this.http.get(this.baseUrl + '/offices/' + parentIdentifier + '/branches', requestOptions)
+
+ }
+
+ getOffice(id: string): Observable<Office> {
+ return this.http.get(this.baseUrl + '/offices/' + id)
+
+ }
+
+ listEmployees(fetchRequest?: FetchRequest): Observable<EmployeePage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.get(this.baseUrl + '/employees', requestOptions)
+
+ }
+
+ getEmployee(id: string, silent?: true): Observable<Employee> {
+ return this.http.get(this.baseUrl + '/employees/' + id, {}, silent)
+
+ }
+
+ createEmployee(employee: Employee): Observable<Employee> {
+ return this.http.post(this.baseUrl + '/employees', employee)
+
+ }
+
+ updateEmployee(employee: Employee): Observable<Employee> {
+ return this.http.put(this.baseUrl + '/employees/' + employee.identifier, employee)
+
+ }
+
+ deleteEmployee(id: string): Observable<Employee> {
+ return this.http.delete(this.baseUrl + '/employees/' + id, {})
+
+ }
+
+ setContactDetails(id: string, contactDetails: ContactDetail[]): Observable<void> {
+ return this.http.put(this.baseUrl + '/employees/' + id + '/contacts', contactDetails);
+ }
+
+}
diff --git a/src/app/sevices/payroll/domain/payroll-allocation.model.ts b/src/app/sevices/payroll/domain/payroll-allocation.model.ts
new file mode 100644
index 0000000..c9de4e5
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-allocation.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface PayrollAllocation {
+ accountNumber: string;
+ amount: string;
+ proportional: boolean;
+}
diff --git a/src/app/sevices/payroll/domain/payroll-collection-history.model.ts b/src/app/sevices/payroll/domain/payroll-collection-history.model.ts
new file mode 100644
index 0000000..87c0a30
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-collection-history.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface PayrollCollectionHistory {
+ identifier?: string;
+ sourceAccountNumber: string;
+ createdBy: string;
+ createdOn: string;
+}
diff --git a/src/app/sevices/payroll/domain/payroll-collection-sheet.model.ts b/src/app/sevices/payroll/domain/payroll-collection-sheet.model.ts
new file mode 100644
index 0000000..de2b492
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-collection-sheet.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PayrollPayment} from './payroll-payment.model';
+
+export interface PayrollCollectionSheet {
+ sourceAccountNumber: string;
+ payrollPayments: PayrollPayment[];
+}
diff --git a/src/app/sevices/payroll/domain/payroll-configuration.model.ts b/src/app/sevices/payroll/domain/payroll-configuration.model.ts
new file mode 100644
index 0000000..21fb5ea
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-configuration.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PayrollAllocation} from './payroll-allocation.model';
+
+export interface PayrollConfiguration {
+ mainAccountNumber: string;
+ payrollAllocations: PayrollAllocation[];
+ createdBy?: string;
+ createdOn?: string;
+ lastModifiedBy?: string;
+ lastModifiedOn?: string;
+}
diff --git a/src/app/sevices/payroll/domain/payroll-payment-page.model.ts b/src/app/sevices/payroll/domain/payroll-payment-page.model.ts
new file mode 100644
index 0000000..e0ca049
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-payment-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PayrollPayment} from './payroll-payment.model';
+
+export interface PayrollPaymentPage {
+ payrollPayments: PayrollPayment[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/payroll/domain/payroll-payment.model.ts b/src/app/sevices/payroll/domain/payroll-payment.model.ts
new file mode 100644
index 0000000..252cfba
--- /dev/null
+++ b/src/app/sevices/payroll/domain/payroll-payment.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface PayrollPayment {
+ customerIdentifier: string;
+ employer: string;
+ salary: string;
+}
diff --git a/src/app/sevices/payroll/domain/permittable-group-ids.ts b/src/app/sevices/payroll/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..9bb2e99
--- /dev/null
+++ b/src/app/sevices/payroll/domain/permittable-group-ids.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class PayrollPermittableGroupIds {
+ public static readonly CONFIGURATION = 'payroll__v1__configuration';
+ public static readonly DISTRIBUTION = 'payroll__v1__distribution';
+}
diff --git a/src/app/sevices/payroll/payroll.service.ts b/src/app/sevices/payroll/payroll.service.ts
new file mode 100644
index 0000000..c9236b8
--- /dev/null
+++ b/src/app/sevices/payroll/payroll.service.ts
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {RequestOptionsArgs} from '@angular/http';
+import {buildSearchParams} from '../domain/paging/search-param.builder';
+import {PayrollPaymentPage} from './domain/payroll-payment-page.model';
+import {Observable} from 'rxjs/Observable';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {PayrollCollectionHistory} from './domain/payroll-collection-history.model';
+import {PayrollCollectionSheet} from './domain/payroll-collection-sheet.model';
+import {HttpClient} from '../http/http.service';
+import {PayrollConfiguration} from './domain/payroll-configuration.model';
+
+@Injectable()
+export class PayrollService {
+
+ constructor(private http: HttpClient, @Inject('payrollBaseUrl') private baseUrl: string) {
+ }
+
+ public distribute(sheet: PayrollCollectionSheet): Observable<void> {
+ return this.http.post(`${this.baseUrl}/distribution`, sheet);
+ }
+
+ public fetchDistributionHistory(): Observable<PayrollCollectionHistory[]> {
+ return this.http.get(`${this.baseUrl}/distribution`);
+ }
+
+ public fetchPayments(identifier: string, fetchRequest?: FetchRequest): Observable<PayrollPaymentPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+ return this.http.get(`${this.baseUrl}/distribution/${identifier}/payments`, requestOptions);
+ }
+
+ setPayrollConfiguration(customerId: string, configuration: PayrollConfiguration): Observable<void> {
+ return this.http.put(`${this.baseUrl}/customers/${customerId}/payroll`, configuration);
+ }
+
+ findPayrollConfiguration(customerId: string, silent: boolean = false): Observable<PayrollConfiguration> {
+ return this.http.get(`${this.baseUrl}/customers/${customerId}/payroll`, {}, silent);
+ }
+}
diff --git a/src/app/sevices/portfolio/domain/account-assignment.model.ts b/src/app/sevices/portfolio/domain/account-assignment.model.ts
new file mode 100644
index 0000000..7cb183e
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/account-assignment.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface AccountAssignment {
+ designator: string;
+ accountIdentifier?: string;
+ ledgerIdentifier?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/balance-range.model.ts b/src/app/sevices/portfolio/domain/balance-range.model.ts
new file mode 100644
index 0000000..0c4b5aa
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/balance-range.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface BalanceRange {
+ minimum: number;
+ maximum: number;
+}
diff --git a/src/app/sevices/portfolio/domain/balance-segment-set.model.ts b/src/app/sevices/portfolio/domain/balance-segment-set.model.ts
new file mode 100644
index 0000000..d39cb5c
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/balance-segment-set.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface BalanceSegmentSet {
+ identifier: string;
+ segments: number[];
+ segmentIdentifiers: string[];
+}
diff --git a/src/app/sevices/portfolio/domain/case-command.model.ts b/src/app/sevices/portfolio/domain/case-command.model.ts
new file mode 100644
index 0000000..6318513
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/case-command.model.ts
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountAssignment} from './account-assignment.model';
+
+export interface CaseCommand {
+ oneTimeAccountAssignments?: AccountAssignment[];
+ paymentSize?: number;
+ note?: string;
+ createdOn: string;
+ createdBy?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/case-customer-documents.model.ts b/src/app/sevices/portfolio/domain/case-customer-documents.model.ts
new file mode 100644
index 0000000..6aec456
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/case-customer-documents.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Document {
+ customerId: string;
+ documentId: string;
+}
+
+export interface CaseCustomerDocuments {
+ documents: Document[]
+}
diff --git a/src/app/sevices/portfolio/domain/case-page.model.ts b/src/app/sevices/portfolio/domain/case-page.model.ts
new file mode 100644
index 0000000..90fbf31
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/case-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Case} from './case.model';
+
+export interface CasePage {
+ elements: Case[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/portfolio/domain/case-state.model.ts b/src/app/sevices/portfolio/domain/case-state.model.ts
new file mode 100644
index 0000000..91defff
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/case-state.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type CaseState = 'CREATED' | 'PENDING' | 'APPROVED' | 'ACTIVE' | 'CLOSED';
diff --git a/src/app/sevices/portfolio/domain/case.model.ts b/src/app/sevices/portfolio/domain/case.model.ts
new file mode 100644
index 0000000..6856b44
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/case.model.ts
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AccountAssignment} from './account-assignment.model';
+import {CaseState} from './case-state.model';
+
+export interface Case {
+ identifier: string;
+ productIdentifier: string;
+ interest: number;
+ parameters: string;
+ accountAssignments: AccountAssignment[];
+ currentState?: CaseState;
+ createdOn?: string;
+ createdBy?: string;
+ lastModifiedOn?: string;
+ lastModifiedBy?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/charge-definition.model.ts b/src/app/sevices/portfolio/domain/charge-definition.model.ts
new file mode 100644
index 0000000..599593e
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/charge-definition.model.ts
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ChargeMethod} from './charge-method.model';
+import {ChronoUnit} from './chrono-unit.model';
+import {WorkflowAction} from './individuallending/workflow-action.model';
+
+export interface ChargeDefinition {
+ identifier: string;
+ name: string;
+ description: string;
+ chargeAction: WorkflowAction;
+ chargeMethod: ChargeMethod;
+ amount: number;
+ fromAccountDesignator: string;
+ toAccountDesignator: string;
+ forCycleSizeUnit: ChronoUnit;
+ accrualAccountDesignator?: string;
+ accrueAction?: WorkflowAction;
+ readOnly?: boolean;
+ proportionalTo: string;
+ forSegmentSet?: string;
+ fromSegment?: string;
+ toSegment?: string;
+ chargeOnTop?: boolean;
+}
diff --git a/src/app/sevices/portfolio/domain/charge-method.model.ts b/src/app/sevices/portfolio/domain/charge-method.model.ts
new file mode 100644
index 0000000..1bc00c9
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/charge-method.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type ChargeMethod = 'FIXED' | 'PROPORTIONAL' | 'INTEREST';
diff --git a/src/app/sevices/portfolio/domain/chrono-unit.model.ts b/src/app/sevices/portfolio/domain/chrono-unit.model.ts
new file mode 100644
index 0000000..7fb845e
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/chrono-unit.model.ts
@@ -0,0 +1,20 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type ChronoUnit = 'WEEKS' | 'MONTHS' | 'YEARS';
diff --git a/src/app/sevices/portfolio/domain/cost-component.model.ts b/src/app/sevices/portfolio/domain/cost-component.model.ts
new file mode 100644
index 0000000..8c955f1
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/cost-component.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class CostComponent {
+ chargeIdentifier: string;
+ amount: number;
+}
diff --git a/src/app/sevices/portfolio/domain/fims-case-page.model.ts b/src/app/sevices/portfolio/domain/fims-case-page.model.ts
new file mode 100644
index 0000000..37f4a75
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/fims-case-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {FimsCase} from './fims-case.model';
+
+export interface FimsCasePage {
+ elements: FimsCase[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/portfolio/domain/fims-case.model.ts b/src/app/sevices/portfolio/domain/fims-case.model.ts
new file mode 100644
index 0000000..36985e8
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/fims-case.model.ts
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {CaseParameters} from './individuallending/case-parameters.model';
+import {CaseState} from './case-state.model';
+
+export interface FimsCase {
+ identifier: string;
+ productIdentifier: string;
+ interest: number;
+ parameters: CaseParameters;
+ depositAccountIdentifier: string;
+ customerLoanAccountIdentifier?: string;
+ currentState?: CaseState;
+ createdOn?: string;
+ createdBy?: string;
+ lastModifiedOn?: string;
+ lastModifiedBy?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/account-designators.model.ts b/src/app/sevices/portfolio/domain/individuallending/account-designators.model.ts
new file mode 100644
index 0000000..aefeb4a
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/account-designators.model.ts
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * A debit is an accounting entry that either increases an asset or expense account, or decreases a liability or equity account.
+ * It is positioned to the left in an accounting entry.
+ *
+ * A credit is an accounting entry that either increases a liability or equity account, or decreases an asset or expense account.
+ */
+
+export class AccountDesignators {
+
+ public static readonly CUSTOMER_LOAN_GROUP = 'cll';
+
+ public static readonly CUSTOMER_LOAN_PRINCIPAL = 'clp';
+
+ public static readonly CUSTOMER_LOAN_INTEREST = 'cli';
+
+ public static readonly CUSTOMER_LOAN_FEES = 'clf';
+
+ public static readonly LOAN_FUNDS_SOURCE = 'ls';
+
+ public static readonly PROCESSING_FEE_INCOME = 'pfi';
+
+ public static readonly ORIGINATION_FEE_INCOME = 'ofi';
+
+ public static readonly DISBURSEMENT_FEE_INCOME = 'dfi';
+
+ public static readonly INTEREST_INCOME = 'ii';
+
+ public static readonly INTEREST_ACCRUAL = 'ia';
+
+ public static readonly LATE_FEE_INCOME = 'lfi';
+
+ public static readonly LATE_FEE_ACCRUAL = 'lfa';
+
+ public static readonly PRODUCT_LOSS_ALLOWANCE = 'pa';
+
+ public static readonly GENERAL_LOSS_ALLOWANCE = 'aa';
+
+ public static readonly GENERAL_EXPENSE = 'ge';
+
+ public static readonly ENTRY = 'ey';
+
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/case-parameters.model.ts b/src/app/sevices/portfolio/domain/individuallending/case-parameters.model.ts
new file mode 100644
index 0000000..40bbc43
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/case-parameters.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TermRange} from '../term-range.model';
+import {PaymentCycle} from '../payment-cycle.model';
+import {CreditWorthinessSnapshot} from './credit-worthiness-snapshot.model';
+
+export interface CaseParameters {
+ customerIdentifier: string;
+ termRange: TermRange;
+ maximumBalance: number;
+ paymentCycle: PaymentCycle;
+ creditWorthinessSnapshots: CreditWorthinessSnapshot[];
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/charge-name.model.ts b/src/app/sevices/portfolio/domain/individuallending/charge-name.model.ts
new file mode 100644
index 0000000..3d41229
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/charge-name.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface ChargeName {
+ identifier: string;
+ name: string;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/charge-proportional-designators.model.ts b/src/app/sevices/portfolio/domain/individuallending/charge-proportional-designators.model.ts
new file mode 100644
index 0000000..3c5f513
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/charge-proportional-designators.model.ts
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class ChargeProportionalDesignators {
+ public static readonly NOT_PROPORTIONAL = '{notproportional}';
+
+ public static readonly MAXIMUM_BALANCE_DESIGNATOR = '{maximumbalance}';
+
+ public static readonly RUNNING_BALANCE_DESIGNATOR = '{runningbalance}';
+
+ public static readonly PRINCIPAL_DESIGNATOR = '{principal}';
+
+ public static readonly REQUESTED_DISBURSEMENT_DESIGNATOR = '{requesteddisbursement}';
+
+ public static readonly TO_ACCOUNT_DESIGNATOR = '{toAccount}';
+
+ public static readonly FROM_ACCOUNT_DESIGNATOR = '{fromAccount}';
+
+ public static readonly REQUESTED_REPAYMENT_DESIGNATOR = '{requestedrepayment}';
+
+ public static readonly CONTRACTUAL_REPAYMENT_DESIGNATOR = '{contractualrepayment}';
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-factor.model.ts b/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-factor.model.ts
new file mode 100644
index 0000000..d128f4b
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-factor.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface CreditWorthinessFactor {
+ description?: string;
+ amount: string;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts b/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts
new file mode 100644
index 0000000..fca8446
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/credit-worthiness-snapshot.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {CreditWorthinessFactor} from './credit-worthiness-factor.model';
+
+export interface CreditWorthinessSnapshot {
+ forCustomer: string;
+ incomeSources: CreditWorthinessFactor[];
+ assets: CreditWorthinessFactor[];
+ debts: CreditWorthinessFactor[];
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/document.model.ts b/src/app/sevices/portfolio/domain/individuallending/document.model.ts
new file mode 100644
index 0000000..d77a8eb
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/document.model.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Document {
+ description: string;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/moratorium.model.ts b/src/app/sevices/portfolio/domain/individuallending/moratorium.model.ts
new file mode 100644
index 0000000..50f910a
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/moratorium.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ChronoUnit} from '../chrono-unit.model';
+
+export class Moratorium {
+ chargeTask: string;
+ temporalUnit: ChronoUnit;
+ period: number;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/planned-payment-page.model.ts b/src/app/sevices/portfolio/domain/individuallending/planned-payment-page.model.ts
new file mode 100644
index 0000000..9c7a0c8
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/planned-payment-page.model.ts
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PlannedPayment} from './planned-payment.model';
+import {ChargeName} from './charge-name.model';
+
+export class PlannedPaymentPage {
+ elements: PlannedPayment[];
+ chargeNames: ChargeName[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/planned-payment.model.ts b/src/app/sevices/portfolio/domain/individuallending/planned-payment.model.ts
new file mode 100644
index 0000000..6b68557
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/planned-payment.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Payment} from '../payment.model';
+
+export interface PlannedPayment {
+ payment: Payment;
+ balances: { [id: string]: number };
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/product-parameters.model.ts b/src/app/sevices/portfolio/domain/individuallending/product-parameters.model.ts
new file mode 100644
index 0000000..5e8a507
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/product-parameters.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Moratorium} from './moratorium.model';
+
+export class ProductParameters {
+ moratoriums: Moratorium[];
+ maximumDispersalCount: number;
+ maximumDispersalAmount: number;
+ minimumDispersalAmount: number;
+}
diff --git a/src/app/sevices/portfolio/domain/individuallending/workflow-action.model.ts b/src/app/sevices/portfolio/domain/individuallending/workflow-action.model.ts
new file mode 100644
index 0000000..69a7127
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/individuallending/workflow-action.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type WorkflowAction =
+ 'OPEN'
+ | 'DENY'
+ | 'APPROVE'
+ | 'ACCEPT_PAYMENT'
+ | 'DISBURSE'
+ | 'MARK_LATE'
+ | 'APPLY_INTEREST'
+ | 'WRITE_OFF'
+ | 'CLOSE'
+ | 'RECOVER';
diff --git a/src/app/sevices/portfolio/domain/interest-basis.model.ts b/src/app/sevices/portfolio/domain/interest-basis.model.ts
new file mode 100644
index 0000000..fbad706
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/interest-basis.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type InterestBasis = 'CURRENT_BALANCE' | 'BEGINNING_BALANCE';
diff --git a/src/app/sevices/portfolio/domain/interest-range.model.ts b/src/app/sevices/portfolio/domain/interest-range.model.ts
new file mode 100644
index 0000000..fb97846
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/interest-range.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface InterestRange {
+ minimum: number;
+ maximum: number;
+}
diff --git a/src/app/sevices/portfolio/domain/loss-provision-configuration.model.ts b/src/app/sevices/portfolio/domain/loss-provision-configuration.model.ts
new file mode 100644
index 0000000..08c8a77
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/loss-provision-configuration.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {LossProvisionStep} from './loss-provision-step.model';
+
+export interface LossProvisionConfiguration {
+ lossProvisionSteps: LossProvisionStep[];
+}
diff --git a/src/app/sevices/portfolio/domain/loss-provision-step.model.ts b/src/app/sevices/portfolio/domain/loss-provision-step.model.ts
new file mode 100644
index 0000000..b11e708
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/loss-provision-step.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface LossProvisionStep {
+ daysLate: number;
+ percentProvision: number;
+}
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-case-page.mapper.ts b/src/app/sevices/portfolio/domain/mapper/fims-case-page.mapper.ts
new file mode 100644
index 0000000..1fbd52a
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/mapper/fims-case-page.mapper.ts
@@ -0,0 +1,35 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {mapToFimsCase} from './fims-case.mapper';
+import {FimsCasePage} from '../fims-case-page.model';
+import {CasePage} from '../case-page.model';
+
+export function mapToFimsCasePage(casePage: CasePage): FimsCasePage {
+ const elements = [];
+
+ for (const caseInstance of casePage.elements) {
+ elements.push(mapToFimsCase(caseInstance));
+ }
+
+ return {
+ elements: elements,
+ totalPages: casePage.totalPages,
+ totalElements: casePage.totalElements
+ };
+}
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-case.mapper.ts b/src/app/sevices/portfolio/domain/mapper/fims-case.mapper.ts
new file mode 100644
index 0000000..9c6ad56
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/mapper/fims-case.mapper.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Case} from '../case.model';
+import {AccountDesignators} from '../individuallending/account-designators.model';
+import {accountIdentifier, findAccountDesignator} from '../../../../common/util/account-assignments';
+import {FimsCase} from '../fims-case.model';
+
+export function mapToCase(caseInstance: FimsCase): Case {
+ return Object.assign({}, caseInstance, {
+ parameters: JSON.stringify(caseInstance.parameters),
+ accountAssignments: [
+ {accountIdentifier: caseInstance.depositAccountIdentifier, designator: AccountDesignators.ENTRY}
+ ]
+ });
+}
+
+export function mapToFimsCase(caseInstance: Case): FimsCase {
+ const entryDesignator = findAccountDesignator(caseInstance.accountAssignments, AccountDesignators.ENTRY);
+ const customerLoanDesignator = findAccountDesignator(caseInstance.accountAssignments, AccountDesignators.CUSTOMER_LOAN_PRINCIPAL);
+
+ return Object.assign({}, caseInstance, {
+ parameters: JSON.parse(caseInstance.parameters),
+ depositAccountIdentifier: accountIdentifier(entryDesignator),
+ customerLoanAccountIdentifier: accountIdentifier(customerLoanDesignator),
+ });
+}
+
+export function mapToFimsCases(caseInstances: Case[]): FimsCase[] {
+ return caseInstances.map(instance => mapToFimsCase(instance));
+}
diff --git a/src/app/sevices/portfolio/domain/mapper/fims-range.mapper.ts b/src/app/sevices/portfolio/domain/mapper/fims-range.mapper.ts
new file mode 100644
index 0000000..1e0a8ca
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/mapper/fims-range.mapper.ts
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {BalanceSegmentSet} from '../balance-segment-set.model';
+import {FimsRange} from '../range-model';
+
+export function mapToBalanceSegmentSet(range: FimsRange): BalanceSegmentSet {
+ const balanceSegmentSet: BalanceSegmentSet = {
+ identifier: range.identifier,
+ segments: range.segments.map(segment => segment.start),
+ segmentIdentifiers: range.segments.map(segment => segment.identifier)
+ };
+
+ return balanceSegmentSet;
+}
+
+export function mapToFimsRanges(balanceSegmentSets: BalanceSegmentSet[]): FimsRange[] {
+ return balanceSegmentSets.map(set => mapToFimsRange(set));
+}
+
+export function mapToFimsRange(balanceSegmentSet: BalanceSegmentSet): FimsRange {
+ return {
+ identifier: balanceSegmentSet.identifier,
+ segments: balanceSegmentSet.segments.map((segment, index, array) => ({
+ identifier: balanceSegmentSet.segmentIdentifiers[index],
+ start: segment,
+ end: hasNextIndex(array, index) ? array[index + 1] : undefined
+ }))
+ };
+}
+
+function hasNextIndex(array: number[], index: number): boolean {
+ return array.length - 1 > index;
+}
diff --git a/src/app/sevices/portfolio/domain/note.model.ts b/src/app/sevices/portfolio/domain/note.model.ts
new file mode 100644
index 0000000..7be73e2
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/note.model.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface Note {
+ content: string;
+}
diff --git a/src/app/sevices/portfolio/domain/pattern.model.ts b/src/app/sevices/portfolio/domain/pattern.model.ts
new file mode 100644
index 0000000..d049d20
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/pattern.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {RequiredAccountAssignment} from './required-account-assignment.model';
+
+export interface Pattern {
+ parameterPackage: string;
+ accountAssignmentGroups: string[];
+ accountAssignmentsRequired: RequiredAccountAssignment[];
+}
diff --git a/src/app/sevices/portfolio/domain/payment-cycle.model.ts b/src/app/sevices/portfolio/domain/payment-cycle.model.ts
new file mode 100644
index 0000000..0ff3285
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/payment-cycle.model.ts
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ChronoUnit} from './chrono-unit.model';
+
+export interface PaymentCycle {
+ temporalUnit: ChronoUnit;
+ period: number;
+ alignmentDay: number;
+ alignmentWeek: number;
+ alignmentMonth: number;
+}
diff --git a/src/app/sevices/portfolio/domain/payment.model.ts b/src/app/sevices/portfolio/domain/payment.model.ts
new file mode 100644
index 0000000..637703e
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/payment.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {CostComponent} from './cost-component.model';
+
+export interface Payment {
+ costComponents?: CostComponent[];
+ balanceAdjustments: { [key: string]: number };
+ date?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/permittable-group-ids.ts b/src/app/sevices/portfolio/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..0e335b6
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/permittable-group-ids.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class PortfolioPermittableGroupIds {
+ public static readonly PRODUCT_OPERATIONS_MANAGEMENT = 'portfolio__v1__products__enable';
+ public static readonly PRODUCT_LOSS_PROVISIONING_MANAGEMENT = 'portfolio__v1__products__lossprv';
+ public static readonly PRODUCT_MANAGEMENT = 'portfolio__v1__products';
+ public static readonly CASE_MANAGEMENT = 'portfolio__v1__case';
+ public static readonly CASE_DOCUMENT_MANAGEMENT = 'portfolio__v1__case_documents';
+}
diff --git a/src/app/sevices/portfolio/domain/product-page.model.ts b/src/app/sevices/portfolio/domain/product-page.model.ts
new file mode 100644
index 0000000..e6ca2bd
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/product-page.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Product} from './product.model';
+
+export interface ProductPage {
+ elements: Product[];
+ totalPages: number;
+ totalElements: number;
+}
diff --git a/src/app/sevices/portfolio/domain/product.model.ts b/src/app/sevices/portfolio/domain/product.model.ts
new file mode 100644
index 0000000..bf2f47e
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/product.model.ts
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {BalanceRange} from './balance-range.model';
+import {InterestRange} from './interest-range.model';
+import {TermRange} from './term-range.model';
+import {InterestBasis} from './interest-basis.model';
+import {AccountAssignment} from './account-assignment.model';
+
+export interface Product {
+ identifier: string;
+ name: string;
+ termRange: TermRange;
+ balanceRange: BalanceRange;
+ interestRange: InterestRange;
+ interestBasis: InterestBasis;
+ patternPackage: string;
+ description: string;
+ accountAssignments: AccountAssignment[];
+ parameters: string;
+ currencyCode: string;
+ minorCurrencyUnitDigits: number;
+ enabled?: boolean;
+ createdOn?: string;
+ createdBy?: string;
+ lastModifiedOn?: string;
+ lastModifiedBy?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/range-model.ts b/src/app/sevices/portfolio/domain/range-model.ts
new file mode 100644
index 0000000..e43cb23
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/range-model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {RangeSegment} from './range-segment.model';
+
+export interface FimsRange {
+ identifier: string;
+ segments: RangeSegment[];
+}
diff --git a/src/app/sevices/portfolio/domain/range-segment.model.ts b/src/app/sevices/portfolio/domain/range-segment.model.ts
new file mode 100644
index 0000000..1e8c474
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/range-segment.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface RangeSegment {
+ identifier: string;
+ start: number;
+ end?: number;
+}
diff --git a/src/app/sevices/portfolio/domain/required-account-assignment.model.ts b/src/app/sevices/portfolio/domain/required-account-assignment.model.ts
new file mode 100644
index 0000000..371ea33
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/required-account-assignment.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface RequiredAccountAssignment {
+ accountDesignator: string;
+ accountType: string;
+ group?: string;
+}
diff --git a/src/app/sevices/portfolio/domain/task-definition.model.ts b/src/app/sevices/portfolio/domain/task-definition.model.ts
new file mode 100644
index 0000000..26f8ae4
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/task-definition.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {WorkflowAction} from './individuallending/workflow-action.model';
+
+export interface TaskDefinition {
+ identifier: string;
+ name: string;
+ description: string;
+ actions: WorkflowAction[];
+ fourEyes: boolean;
+ mandatory: boolean;
+}
diff --git a/src/app/sevices/portfolio/domain/task-instance.model.ts b/src/app/sevices/portfolio/domain/task-instance.model.ts
new file mode 100644
index 0000000..44079e7
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/task-instance.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface TaskInstance {
+ // task definition identifier
+ taskIdentifier: string;
+ comment: string;
+ executedOn: string;
+ executedBy: string;
+}
diff --git a/src/app/sevices/portfolio/domain/term-range.model.ts b/src/app/sevices/portfolio/domain/term-range.model.ts
new file mode 100644
index 0000000..86f2172
--- /dev/null
+++ b/src/app/sevices/portfolio/domain/term-range.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ChronoUnit} from './chrono-unit.model';
+
+export interface TermRange {
+ temporalUnit: ChronoUnit;
+ maximum: number;
+}
diff --git a/src/app/sevices/portfolio/portfolio.service.ts b/src/app/sevices/portfolio/portfolio.service.ts
new file mode 100644
index 0000000..63bad61
--- /dev/null
+++ b/src/app/sevices/portfolio/portfolio.service.ts
@@ -0,0 +1,285 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Observable} from 'rxjs/Observable';
+import {Product} from './domain/product.model';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {TaskDefinition} from './domain/task-definition.model';
+import {ChargeDefinition} from './domain/charge-definition.model';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {buildSearchParams} from '../domain/paging/search-param.builder';
+import {CaseCommand} from './domain/case-command.model';
+import {TaskInstance} from './domain/task-instance.model';
+import {PlannedPaymentPage} from './domain/individuallending/planned-payment-page.model';
+import {CasePage} from './domain/case-page.model';
+import {AccountAssignment} from './domain/account-assignment.model';
+import {WorkflowAction} from './domain/individuallending/workflow-action.model';
+import {ProductPage} from './domain/product-page.model';
+import {FimsCase} from './domain/fims-case.model';
+import {FimsCasePage} from './domain/fims-case-page.model';
+import {Case} from './domain/case.model';
+import {mapToCase, mapToFimsCase, mapToFimsCases} from './domain/mapper/fims-case.mapper';
+import {mapToFimsCasePage} from './domain/mapper/fims-case-page.mapper';
+import {BalanceSegmentSet} from './domain/balance-segment-set.model';
+import {mapToBalanceSegmentSet, mapToFimsRange, mapToFimsRanges} from './domain/mapper/fims-range.mapper';
+import {FimsRange} from './domain/range-model';
+import {Payment} from './domain/payment.model';
+import {LossProvisionConfiguration} from './domain/loss-provision-configuration.model';
+import {CaseCustomerDocuments} from './domain/case-customer-documents.model';
+
+@Injectable()
+export class PortfolioService {
+
+ constructor(private http: HttpClient, @Inject('portfolioBaseUrl') private baseUrl: string) {
+ }
+
+ findAllPatterns(): Observable<void> {
+ return this.http.get(`${this.baseUrl}/patterns/`);
+ }
+
+ findAllProducts(includeDisabled?: boolean, fetchRequest?: FetchRequest): Observable<ProductPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+ params.append('includeDisabled', includeDisabled ? 'true' : 'false');
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+ return this.http.get(`${this.baseUrl}/products/`, requestOptions);
+ }
+
+ createProduct(product: Product): Observable<void> {
+ return this.http.post(`${this.baseUrl}/products`, product);
+ }
+
+ getProduct(identifier: string): Observable<Product> {
+ return this.http.get(`${this.baseUrl}/products/${identifier}`);
+ }
+
+ changeProduct(product: Product): Observable<void> {
+ return this.http.put(`${this.baseUrl}/products/${product.identifier}`, product);
+ }
+
+ deleteProduct(identifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/products/${identifier}`);
+ }
+
+ enableProduct(identifier: string, enabled: boolean): Observable<void> {
+ return this.http.put(`${this.baseUrl}/products/${identifier}/enabled`, enabled);
+ }
+
+ getProductEnabled(identifier: string): Observable<boolean> {
+ return this.http.get(`${this.baseUrl}/products/${identifier}/enabled`);
+ }
+
+ incompleteaccountassignments(identifier: string): Observable<AccountAssignment[]> {
+ return this.http.get(`${this.baseUrl}/products/${identifier}/incompleteaccountassignments`);
+ }
+
+ findAllTaskDefinitionsForProduct(identifier: string): Observable<TaskDefinition[]> {
+ return this.http.get(`${this.baseUrl}/products/${identifier}/tasks/`);
+ }
+
+ createTaskDefinition(productIdentifier: string, taskDefinition: TaskDefinition): Observable<void> {
+ return this.http.post(`${this.baseUrl}/products/${productIdentifier}/tasks/`, taskDefinition);
+ }
+
+ getTaskDefinition(productIdentifier: string, taskDefinitionIdentifier: string): Observable<TaskDefinition> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/tasks/${taskDefinitionIdentifier}`);
+ }
+
+ changeTaskDefinition(productIdentifier: string, taskDefinition: TaskDefinition): Observable<void> {
+ return this.http.put(`${this.baseUrl}/products/${productIdentifier}/tasks/${taskDefinition.identifier}`, taskDefinition);
+ }
+
+ deleteTaskDefinition(productIdentifier: string, taskDefinitionIdentifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/products/${productIdentifier}/tasks/${taskDefinitionIdentifier}`);
+ }
+
+ findAllChargeDefinitionsForProduct(identifier: string): Observable<ChargeDefinition[]> {
+ return this.http.get(`${this.baseUrl}/products/${identifier}/charges/`);
+ }
+
+ createChargeDefinition(productIdentifier: string, chargeDefinition: ChargeDefinition): Observable<void> {
+ return this.http.post(`${this.baseUrl}/products/${productIdentifier}/charges/`, chargeDefinition);
+ }
+
+ getChargeDefinition(productIdentifier: string, chargeDefinitionIdentifier: string): Observable<ChargeDefinition> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/charges/${chargeDefinitionIdentifier}`);
+ }
+
+ changeChargeDefinition(productIdentifier: string, chargeDefinition: ChargeDefinition): Observable<void> {
+ return this.http.put(`${this.baseUrl}/products/${productIdentifier}/charges/${chargeDefinition.identifier}`, chargeDefinition);
+ }
+
+ deleteChargeDefinition(productIdentifier: string, chargeDefinitionIdentifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/products/${productIdentifier}/charges/${chargeDefinitionIdentifier}`);
+ }
+
+ getAllCasesForProduct(productIdentifier: string, fetchRequest?: FetchRequest, includeClosed?: boolean): Observable<FimsCasePage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ params.append('includeClosed', includeClosed ? 'true' : 'false');
+ params.append('pageIndex', '1');
+ params.append('size', '10');
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/`, requestOptions)
+ .map((casePage: CasePage) => mapToFimsCasePage(casePage));
+ }
+
+ createCase(productIdentifier: string, fimsCase: FimsCase): Observable<void> {
+ const caseInstance: Case = mapToCase(fimsCase);
+
+ return this.http.post(`${this.baseUrl}/products/${productIdentifier}/cases/`, caseInstance);
+ }
+
+ getCase(productIdentifier: string, caseIdentifier: string): Observable<FimsCase> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}`)
+ .map((caseInstance: Case) => mapToFimsCase(caseInstance));
+ }
+
+ changeCase(productIdentifier: string, fimsCase: FimsCase): Observable<void> {
+ const caseInstance: Case = mapToCase(fimsCase);
+ return this.http.put(`${this.baseUrl}/products/${productIdentifier}/cases/${caseInstance.identifier}`, caseInstance);
+ }
+
+ getAllActionsForCase(productIdentifier: string, caseIdentifier: string): Observable<WorkflowAction[]> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/actions/`);
+ }
+
+ getCostComponentsForAction(productIdentifier: string, caseIdentifier: string, action: string,
+ touchingAccounts: string[] = [], forPaymentSize?: string, forDateTime?: string): Observable<Payment> {
+
+ const params: URLSearchParams = new URLSearchParams();
+
+ params.append('touchingaccounts', touchingAccounts.join(','));
+ params.append('forpaymentsize', forPaymentSize);
+ params.append('fordatetime', forDateTime);
+
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/actions/${action}/costcomponents`);
+ }
+
+ executeCaseCommand(productIdentifier: string, caseIdentifier: string, action: string, command: CaseCommand): Observable<void> {
+ return this.http.post(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/commands/${action}`, command);
+ }
+
+ findAllTasksForCase(productIdentifier: string, caseIdentifier: string, includeExcluded?: boolean): Observable<TaskInstance[]> {
+ const params: URLSearchParams = new URLSearchParams();
+
+ params.append('includeExecuted', String(includeExcluded));
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/tasks/`, requestOptions);
+ }
+
+ getTaskForCase(productIdentifier: string, caseIdentifier: string, taskIdentifier: string): Observable<TaskInstance> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/tasks/${taskIdentifier}`);
+ }
+
+ taskForCaseExecuted(productIdentifier: string, caseIdentifier: string, taskIdentifier: string, executed: boolean): Observable<void> {
+ return this.http.put(`${this.baseUrl}/products/${productIdentifier}/cases/${caseIdentifier}/tasks/${taskIdentifier}/executed`,
+ executed);
+ }
+
+ findAllCases(fetchRequest?: FetchRequest): Observable<FimsCase[]> {
+ const search: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search
+ };
+
+ return this.http.get(`${this.baseUrl}/cases/`, requestOptions)
+ .map((caseInstances: Case[]) => mapToFimsCases(caseInstances));
+ }
+
+ getPaymentScheduleForCase(productIdentifier: string, caseIdentifier: string,
+ initialDisbursalDate?: string): Observable<PlannedPaymentPage> {
+ const params: URLSearchParams = new URLSearchParams();
+ params.append('initialDisbursalDate', initialDisbursalDate ? new Date(initialDisbursalDate).toISOString() : undefined);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.get(`${this.baseUrl}/individuallending/products/${productIdentifier}/cases/${caseIdentifier}/plannedpayments`,
+ requestOptions);
+ }
+
+ getAllCasesForCustomer(customerIdentifier: string, fetchRequest?: FetchRequest): Observable<FimsCasePage> {
+ const search: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search
+ };
+
+ return this.http.get(`${this.baseUrl}/individuallending/customers/${customerIdentifier}/cases`, requestOptions)
+ .map((casePage: CasePage) => mapToFimsCasePage(casePage));
+ }
+
+ findAllRanges(productIdentifier: string): Observable<FimsRange[]> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/balancesegmentsets/`)
+ .map((segments: BalanceSegmentSet[]) => mapToFimsRanges(segments));
+ }
+
+ createRange(productIdentifier: string, range: FimsRange): Observable<void> {
+ const balanceSegmentSet = mapToBalanceSegmentSet(range);
+ return this.http.post(`${this.baseUrl}/products/${productIdentifier}/balancesegmentsets/`, balanceSegmentSet);
+ }
+
+ getRange(productIdentifier: string, rangeIdentifier: string): Observable<FimsRange> {
+ return this.http.get(`${this.baseUrl}/products/${productIdentifier}/balancesegmentsets/${rangeIdentifier}`)
+ .map(segments => mapToFimsRange(segments));
+ }
+
+ changeRange(productIdentifier: string, range: FimsRange): Observable<void> {
+ const balanceSegmentSet = mapToBalanceSegmentSet(range);
+ return this.http.put(`${this.baseUrl}/products/${productIdentifier}/balancesegmentsets/${balanceSegmentSet.identifier}`,
+ balanceSegmentSet);
+ }
+
+ deleteRange(productIdentifier: string, rangeIdentifier: string): Observable<void> {
+ return this.http.delete(`${this.baseUrl}/products/${productIdentifier}/balancesegmentsets/${rangeIdentifier}`);
+ }
+
+ changeLossProvisionConfiguration(productIdentifier: string, lossProvisionConfiguration: LossProvisionConfiguration): Observable<void> {
+ return this.http.put(
+ `${this.baseUrl}/individuallending/products/${productIdentifier}/lossprovisionconfiguration`, lossProvisionConfiguration
+ );
+ }
+
+ getLossProvisionConfiguration(productIdentifier: string): Observable<LossProvisionConfiguration> {
+ return this.http.get(`${this.baseUrl}/individuallending/products/${productIdentifier}/lossprovisionconfiguration`);
+ }
+
+ getCaseDocuments(productIdentifier: string, caseIdentifier: string): Observable<CaseCustomerDocuments> {
+ return this.http.get(`${this.baseUrl}/individuallending/products/${productIdentifier}/cases/${caseIdentifier}/documents`);
+ }
+
+ changeCaseDocuments(productIdentifier: string, caseIdentifier: string, documents: CaseCustomerDocuments): Observable<void> {
+ return this.http.put(`${this.baseUrl}/individuallending/products/${productIdentifier}/cases/${caseIdentifier}/documents`, documents);
+ }
+
+}
diff --git a/src/app/sevices/reporting/domain/auto-complete-resource.model.ts b/src/app/sevices/reporting/domain/auto-complete-resource.model.ts
new file mode 100644
index 0000000..220cd10
--- /dev/null
+++ b/src/app/sevices/reporting/domain/auto-complete-resource.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface AutoCompleteResource {
+ path: string;
+ terms: string[];
+}
diff --git a/src/app/sevices/reporting/domain/displayable-field.model.ts b/src/app/sevices/reporting/domain/displayable-field.model.ts
new file mode 100644
index 0000000..d00d334
--- /dev/null
+++ b/src/app/sevices/reporting/domain/displayable-field.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Type} from './type.model';
+
+export interface DisplayableField {
+ name: string;
+ type: Type;
+ mandatory: boolean;
+}
diff --git a/src/app/sevices/reporting/domain/footer.model.ts b/src/app/sevices/reporting/domain/footer.model.ts
new file mode 100644
index 0000000..0806327
--- /dev/null
+++ b/src/app/sevices/reporting/domain/footer.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Value} from './value.model';
+
+export interface Footer {
+ values: Value[];
+}
diff --git a/src/app/sevices/reporting/domain/header.model.ts b/src/app/sevices/reporting/domain/header.model.ts
new file mode 100644
index 0000000..b75fff4
--- /dev/null
+++ b/src/app/sevices/reporting/domain/header.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface Header {
+ columnNames: string[];
+}
diff --git a/src/app/sevices/reporting/domain/permittable-group-ids.ts b/src/app/sevices/reporting/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..69ad249
--- /dev/null
+++ b/src/app/sevices/reporting/domain/permittable-group-ids.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class ReportingPermittableGroupIds {
+ public static readonly REPORT_MANAGEMENT = 'reporting__v1__general';
+}
diff --git a/src/app/sevices/reporting/domain/query-parameter.model.ts b/src/app/sevices/reporting/domain/query-parameter.model.ts
new file mode 100644
index 0000000..9e48481
--- /dev/null
+++ b/src/app/sevices/reporting/domain/query-parameter.model.ts
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Type} from './type.model';
+import {AutoCompleteResource} from './auto-complete-resource.model';
+
+export type Operator = 'EQUALS' | 'IN' | 'LIKE' | 'BETWEEN' | 'GREATER' | 'LESSER';
+
+export interface QueryParameter {
+ name: string;
+ type: Type;
+ operator: Operator;
+ value?: string;
+ mandatory: boolean;
+ autoCompleteResource?: AutoCompleteResource;
+}
diff --git a/src/app/sevices/reporting/domain/report-definition.model.ts b/src/app/sevices/reporting/domain/report-definition.model.ts
new file mode 100644
index 0000000..1a48729
--- /dev/null
+++ b/src/app/sevices/reporting/domain/report-definition.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {QueryParameter} from './query-parameter.model';
+import {DisplayableField} from './displayable-field.model';
+
+export interface ReportDefinition {
+ identifier: string;
+ name: string;
+ description: string;
+ queryParameters: QueryParameter[];
+ displayableFields: DisplayableField[];
+}
diff --git a/src/app/sevices/reporting/domain/report-page.model.ts b/src/app/sevices/reporting/domain/report-page.model.ts
new file mode 100644
index 0000000..d05b698
--- /dev/null
+++ b/src/app/sevices/reporting/domain/report-page.model.ts
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Header} from './header.model';
+import {Row} from './row.model';
+import {Footer} from './footer.model';
+
+export interface ReportPage {
+ name: string;
+ description: string;
+ generatedOn: string;
+ generatedBy: string;
+ header: Header;
+ rows: Row[];
+ footer: Footer;
+ hasMore: boolean;
+}
diff --git a/src/app/sevices/reporting/domain/report-request.model.ts b/src/app/sevices/reporting/domain/report-request.model.ts
new file mode 100644
index 0000000..04d7aad
--- /dev/null
+++ b/src/app/sevices/reporting/domain/report-request.model.ts
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {QueryParameter} from './query-parameter.model';
+import {DisplayableField} from './displayable-field.model';
+
+export interface ReportRequest {
+ queryParameters: QueryParameter[];
+ displayableFields: DisplayableField[];
+}
diff --git a/src/app/sevices/reporting/domain/row.model.ts b/src/app/sevices/reporting/domain/row.model.ts
new file mode 100644
index 0000000..6ccbea0
--- /dev/null
+++ b/src/app/sevices/reporting/domain/row.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Value} from './value.model';
+
+export interface Row {
+ values: Value[];
+}
diff --git a/src/app/sevices/reporting/domain/type.model.ts b/src/app/sevices/reporting/domain/type.model.ts
new file mode 100644
index 0000000..7a86ea0
--- /dev/null
+++ b/src/app/sevices/reporting/domain/type.model.ts
@@ -0,0 +1,19 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export type Type = 'TEXT' | 'NUMBER' | 'DATE';
diff --git a/src/app/sevices/reporting/domain/value.model.ts b/src/app/sevices/reporting/domain/value.model.ts
new file mode 100644
index 0000000..ec2a625
--- /dev/null
+++ b/src/app/sevices/reporting/domain/value.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Type} from './type.model';
+
+export interface Value {
+ values: string[];
+ type: Type;
+}
diff --git a/src/app/sevices/reporting/reporting.service.ts b/src/app/sevices/reporting/reporting.service.ts
new file mode 100644
index 0000000..40a12d8
--- /dev/null
+++ b/src/app/sevices/reporting/reporting.service.ts
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Observable} from 'rxjs/Observable';
+import {ReportDefinition} from './domain/report-definition.model';
+import {FetchRequest} from '../domain/paging/fetch-request.model';
+import {ReportPage} from './domain/report-page.model';
+import {ReportRequest} from './domain/report-request.model';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {buildSearchParams} from '../domain/paging/search-param.builder';
+
+@Injectable()
+export class ReportingService {
+
+ constructor(private http: HttpClient, @Inject('reportingBaseUrl') private baseUrl: string) {
+ }
+
+ fetchCategories(): Observable<string[]> {
+ return this.http.get(`${this.baseUrl}/categories`);
+ }
+
+ fetchReportDefinitions(category: string): Observable<ReportDefinition[]> {
+ return this.http.get(`${this.baseUrl}/categories/${category}`);
+ }
+
+ findReportDefinition(category: string, identifier: string): Observable<ReportDefinition> {
+ return this.http.get(`${this.baseUrl}/categories/${category}/definitions/${identifier}`);
+ }
+
+ generateReport(category: string, identifier: string, reportRequest: ReportRequest, fetchRequest?: FetchRequest): Observable<ReportPage> {
+ const params: URLSearchParams = buildSearchParams(fetchRequest);
+
+ const requestOptions: RequestOptionsArgs = {
+ search: params
+ };
+
+ return this.http.post(`${this.baseUrl}/categories/${category}/reports/${identifier}`, reportRequest, requestOptions);
+ }
+
+}
diff --git a/src/app/sevices/security/authn/auth-guard.service.spec.ts b/src/app/sevices/security/authn/auth-guard.service.spec.ts
new file mode 100644
index 0000000..5d70011
--- /dev/null
+++ b/src/app/sevices/security/authn/auth-guard.service.spec.ts
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AuthGuard} from './auth-guard.service';
+import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from '@angular/router';
+import {inject, TestBed} from '@angular/core/testing';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../../store';
+
+describe('Test Auth Guard Service', () => {
+
+ const route: ActivatedRouteSnapshot = undefined;
+
+ const state: RouterStateSnapshot = undefined;
+
+ const mockRouter = {
+ navigate() {
+ }
+ };
+
+ describe('when logged in', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ AuthGuard,
+ {provide: Router, useValue: mockRouter},
+ {
+ provide: Store, useClass: class {
+ select = jasmine.createSpy('select').and.callFake(selector => {
+ if (selector === fromRoot.getAuthenticationLoading) {
+ return Observable.of(false);
+ }
+ if (selector === fromRoot.getAuthentication) {
+ return Observable.of({});
+ }
+ });
+ }
+ }
+ ]
+ });
+ });
+
+ it('should test if route is active', (done: DoneFn) => {
+ inject([AuthGuard], (authGuard: AuthGuard) => {
+ authGuard.canActivate(route, state).subscribe(canActivate => {
+ expect(canActivate).toBeTruthy();
+ done();
+ });
+ })();
+ });
+ });
+
+ describe('when not logged in', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ AuthGuard,
+ {provide: Router, useValue: mockRouter},
+ {
+ provide: Store, useClass: class {
+ select = jasmine.createSpy('select').and.callFake(selector => {
+ if (selector === fromRoot.getAuthenticationLoading) {
+ return Observable.of(false);
+ }
+ if (selector === fromRoot.getAuthentication) {
+ return Observable.of(null);
+ }
+ });
+ }
+ }
+ ]
+ });
+ });
+
+ it('should test if route gets deactivated when user is not logged in', (done: DoneFn) => {
+ inject([AuthGuard], (authGuard: AuthGuard) => {
+ authGuard.canActivate(route, state).subscribe(canActivate => {
+ expect(canActivate).toBeFalsy();
+ done();
+ });
+ })();
+ });
+
+ it('should test if guard redirects to login page when user is not logged in', (done: DoneFn) => {
+ inject([AuthGuard, Router], (authGuard: AuthGuard, router: Router) => {
+ spyOn(router, 'navigate');
+ authGuard.canActivate(route, state).subscribe(canActivate => {
+ expect(router.navigate).toHaveBeenCalledWith(['/login']);
+ done();
+ });
+ })();
+ });
+ });
+
+});
diff --git a/src/app/sevices/security/authn/auth-guard.service.ts b/src/app/sevices/security/authn/auth-guard.service.ts
new file mode 100644
index 0000000..458cef4
--- /dev/null
+++ b/src/app/sevices/security/authn/auth-guard.service.ts
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as fromRoot from '../../../store';
+import {Store} from '@ngrx/store';
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+
+ constructor(private store: Store<fromRoot.State>, private router: Router) {
+ }
+
+ waitForAuthentication(): Observable<boolean> {
+ return this.store.select(fromRoot.getAuthenticationLoading)
+ .filter(loading => !loading)
+ .take(1);
+ }
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+ return this.waitForAuthentication()
+ .switchMap(() => this.store.select(fromRoot.getAuthentication)
+ .map(authentication => {
+ if (!authentication) {
+ this.router.navigate(['/login']);
+ return false;
+ }
+ return true;
+ })
+ );
+
+ }
+}
diff --git a/src/app/sevices/security/authn/authentication.service.spec.ts b/src/app/sevices/security/authn/authentication.service.spec.ts
new file mode 100644
index 0000000..1d4e7fe
--- /dev/null
+++ b/src/app/sevices/security/authn/authentication.service.spec.ts
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {AuthenticationService} from './authentication.service';
+import {BaseRequestOptions, Http, Response, ResponseOptions} from '@angular/http';
+import {MockBackend, MockConnection} from '@angular/http/testing';
+import {Authentication} from '../../identity/domain/authentication.model';
+
+describe('Test Authentication Service', () => {
+
+ let authService: AuthenticationService;
+
+ const tenant = 'Reynholm Industries';
+
+ const mockAuthentication: Authentication = {
+ tokenType: 'iDontCare',
+ accessToken: 'accessToken',
+ accessTokenExpiration: new Date().toISOString(),
+ refreshTokenExpiration: new Date().toISOString(),
+ passwordExpiration: new Date().toISOString()
+ };
+
+ beforeEach(() => {
+ const mockBackend: MockBackend = new MockBackend();
+
+ mockBackend.connections.subscribe((connection: MockConnection) =>
+ connection.mockRespond(new Response(new ResponseOptions({body: mockAuthentication})))
+ );
+ const requestOptions: BaseRequestOptions = new BaseRequestOptions();
+ const http: Http = new Http(mockBackend, requestOptions);
+
+ authService = new AuthenticationService('/identity', http);
+ });
+
+ it('should login and return authentication', (done: DoneFn) => {
+ authService.login(tenant, 'moss', 'test').subscribe((authentication: Authentication) => {
+ expect(authentication.tokenType).toBe(mockAuthentication.tokenType);
+ expect(authentication.accessToken).toBe(mockAuthentication.accessToken);
+ expect(authentication.accessTokenExpiration).toBe(mockAuthentication.accessTokenExpiration);
+ expect(authentication.refreshTokenExpiration).toBe(mockAuthentication.refreshTokenExpiration);
+ expect(authentication.passwordExpiration).toBe(mockAuthentication.passwordExpiration);
+
+ done();
+ });
+ });
+
+});
diff --git a/src/app/sevices/security/authn/authentication.service.ts b/src/app/sevices/security/authn/authentication.service.ts
new file mode 100644
index 0000000..3295174
--- /dev/null
+++ b/src/app/sevices/security/authn/authentication.service.ts
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {Headers, Http, RequestOptionsArgs, Response} from '@angular/http';
+import {Error} from '../../domain/error.model';
+import {Authentication} from '../../identity/domain/authentication.model';
+import {Permission} from '../../identity/domain/permission.model';
+
+@Injectable()
+export class AuthenticationService {
+
+ private static encodePassword(password: string): string {
+ return btoa(password);
+ }
+
+ constructor(@Inject('identityBaseUrl') private identityBaseUrl: string, private http: Http) {
+ }
+
+ login(tenantId: string, userId: string, password: string): Observable<Authentication> {
+ const encodedPassword: string = AuthenticationService.encodePassword(password);
+ const loginUrl = '/token?grant_type=password&username=';
+ return this.http.post(this.identityBaseUrl + loginUrl + userId + '&password=' + encodedPassword, {}, this.tenantHeader(tenantId))
+ .map((response: Response) => this.mapResponse(response))
+ .catch(Error.handleError);
+ }
+
+ logout(tenantId: string, userId: string, accessToken: string): Observable<Response> {
+ return this.http.delete(this.identityBaseUrl + '/token/_current', this.authorizationHeader(tenantId, userId, accessToken))
+ .map((response: Response) => this.mapResponse(response))
+ .catch(Error.handleError);
+ }
+
+ getUserPermissions(tenantId: string, userId: string, accessToken: string): Observable<Permission[]> {
+ return this.http.get(this.identityBaseUrl + '/users/' + userId + '/permissions',
+ this.authorizationHeader(tenantId, userId, accessToken)
+ )
+ .map((response: Response) => this.mapResponse(response))
+ .catch(Error.handleError);
+ }
+
+ refreshAccessToken(tenantId: string): Observable<Authentication> {
+ const refreshTokenUrl = '/token?grant_type=refresh_token';
+ return this.http.post(this.identityBaseUrl + refreshTokenUrl, {}, this.tenantHeader(tenantId))
+ .map((response: Response): Authentication => this.mapResponse(response))
+ .catch(Error.handleError);
+ }
+
+ private mapResponse(response: Response): any {
+ if (response.text()) {
+ return response.json();
+ }
+ }
+
+ private authorizationHeader(tenantId: string, userId: string, accessToken: string): RequestOptionsArgs {
+ const requestOptions: RequestOptionsArgs = this.tenantHeader(tenantId);
+
+ requestOptions.headers.set('User', userId);
+ requestOptions.headers.set('Authorization', accessToken);
+
+ return requestOptions;
+ }
+
+ private tenantHeader(tenantId: string): RequestOptionsArgs {
+ const headers: Headers = new Headers();
+ headers.set('X-Tenant-Identifier', tenantId);
+
+ return {
+ headers: headers
+ };
+ }
+
+}
diff --git a/src/app/sevices/security/authz/fims-permission-descriptor.ts b/src/app/sevices/security/authz/fims-permission-descriptor.ts
new file mode 100644
index 0000000..d897416
--- /dev/null
+++ b/src/app/sevices/security/authz/fims-permission-descriptor.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PermissionId} from './permission-id.type';
+
+export interface FimsPermissionDescriptor {
+ id: PermissionId;
+ label: string;
+ description?: string;
+ readOnly?: boolean;
+}
diff --git a/src/app/sevices/security/authz/fims-permission.model.ts b/src/app/sevices/security/authz/fims-permission.model.ts
new file mode 100644
index 0000000..da650a9
--- /dev/null
+++ b/src/app/sevices/security/authz/fims-permission.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {PermissionId} from './permission-id.type';
+
+export interface FimsPermission {
+ id: PermissionId;
+ accessLevel: AccessLevel;
+}
+
+export type AccessLevel = 'READ' | 'CHANGE' | 'DELETE';
diff --git a/src/app/sevices/security/authz/permission-id.type.ts b/src/app/sevices/security/authz/permission-id.type.ts
new file mode 100644
index 0000000..efbe082
--- /dev/null
+++ b/src/app/sevices/security/authz/permission-id.type.ts
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * List of supported permission ids for fims
+ */
+export type PermissionId = 'identity_self' | 'identity_identities' | 'identity_roles' |
+ 'office_self' | 'office_offices' | 'office_employees' |
+ 'customer_customers' | 'customer_tasks' | 'catalog_catalogs' | 'customer_identifications' | 'customer_portrait' | 'customer_documents' |
+ 'accounting_accounts' | 'accounting_ledgers' | 'accounting_journals' | 'accounting_tx_types' | 'accounting_income_statement' |
+ 'accounting_fin_condition' |
+ 'portfolio_product_operations' | 'portfolio_loss_provision' | 'portfolio_products' | 'portfolio_cases' | 'portfolio_documents' |
+ 'deposit_definitions' | 'deposit_instances' |
+ 'teller_management' | 'teller_operations' |
+ 'reporting_management' |
+ 'cheque_management' | 'cheque_transaction' |
+ 'payroll_configuration' | 'payroll_distribution';
diff --git a/src/app/sevices/security/authz/permission.directive.spec.ts b/src/app/sevices/security/authz/permission.directive.spec.ts
new file mode 100644
index 0000000..ca528ad
--- /dev/null
+++ b/src/app/sevices/security/authz/permission.directive.spec.ts
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Component} from '@angular/core';
+import {TestBed} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {PermissionDirective} from './permission.directive';
+import {Store} from '@ngrx/store';
+import {Observable} from 'rxjs/Observable';
+import {FimsPermission} from './fims-permission.model';
+
+describe('Test permission directive', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ {
+ provide: Store, useClass: class {
+ select = function () {
+ };
+ }
+ }
+ ],
+ declarations: [PermissionDirective, TestComponent]
+ });
+ });
+
+ describe('Test permission directive with object parameter', () => {
+ it('should add item to dom', () => {
+ const store = TestBed.get(Store);
+
+ spyOn(store, 'select').and.returnValue(Observable.of<FimsPermission[]>([
+ {id: 'office_offices', accessLevel: 'READ'}
+ ]));
+
+ const fixture = TestBed.createComponent(TestComponent);
+ fixture.detectChanges();
+
+ const element = fixture.debugElement.query(By.css('button'));
+ expect(element).not.toBeNull('Button should be existent within the dom');
+ });
+
+ it('should remove item from dom', () => {
+ const store = TestBed.get(Store);
+ spyOn(store, 'select').and.returnValue(Observable.of([]));
+
+ const fixture = TestBed.createComponent(TestComponent);
+ fixture.detectChanges();
+ const element = fixture.debugElement.query(By.css('button'));
+ expect(element).toBeNull('Button should be not existent within the dom');
+ });
+ });
+
+});
+
+@Component({
+ template: `
+ <button *hasPermission="{ id: 'office_offices', accessLevel: 'READ' }">randomTestValue</button>
+ `
+})
+class TestComponent {
+}
diff --git a/src/app/sevices/security/authz/permission.directive.ts b/src/app/sevices/security/authz/permission.directive.ts
new file mode 100644
index 0000000..72ad743
--- /dev/null
+++ b/src/app/sevices/security/authz/permission.directive.ts
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Directive, Input, OnDestroy, OnInit, TemplateRef, ViewContainerRef} from '@angular/core';
+import {FimsPermission} from './fims-permission.model';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../../store';
+import {Subscription} from 'rxjs/Subscription';
+
+@Directive({
+ // tslint:disable-next-line:directive-selector
+ selector: '[hasPermission]'
+})
+export class PermissionDirective implements OnInit, OnDestroy {
+
+ private permissionSubscription: Subscription;
+
+ @Input('hasPermission') hasPermission: FimsPermission;
+
+ constructor(private store: Store<fromRoot.State>, private viewContainer: ViewContainerRef, private template: TemplateRef<Object>) {
+ }
+
+ ngOnInit(): void {
+ this.viewContainer.clear();
+
+ if (!this.hasPermission) {
+ this.viewContainer.createEmbeddedView(this.template);
+ return;
+ }
+
+ this.permissionSubscription = this.store.select(fromRoot.getPermissions)
+ .map(permissions => permissions.filter(permission => permission.id === this.hasPermission.id
+ && permission.accessLevel === this.hasPermission.accessLevel
+ ))
+ .map(matches => matches.length > 0)
+ .subscribe(hasPermission => {
+ this.viewContainer.clear();
+ if (hasPermission) {
+ this.viewContainer.createEmbeddedView(this.template);
+ }
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (this.permissionSubscription) {
+ this.permissionSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/src/app/sevices/security/authz/permission.guard.ts b/src/app/sevices/security/authz/permission.guard.ts
new file mode 100644
index 0000000..1b9565c
--- /dev/null
+++ b/src/app/sevices/security/authz/permission.guard.ts
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {FimsPermission} from './fims-permission.model';
+import {Store} from '@ngrx/store';
+import * as fromRoot from '../../../store';
+
+@Injectable()
+export class PermissionGuard implements CanActivateChild {
+
+ constructor(private store: Store<fromRoot.State>, private router: Router) {
+ }
+
+ waitForPermissions(): Observable<boolean> {
+ return this.store.select(fromRoot.getPermissionsLoading)
+ .filter(loading => !loading)
+ .take(1);
+ }
+
+ canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+ const routeData: any = route.data;
+
+ const routePermission: FimsPermission = routeData.hasPermission;
+
+ // No permission set on route at all
+ if (!routePermission) {
+ return Observable.of(true);
+ }
+
+ return this.waitForPermissions()
+ .switchMap(() => this.hasPermission(routePermission)
+ .map(hasPermission => {
+ if (hasPermission) {
+ return true;
+ }
+ this.router.navigate(['/denied']);
+ return false;
+ }));
+
+ }
+
+ private hasPermission(routePermission: FimsPermission): Observable<boolean> {
+ return this.store.select(fromRoot.getPermissions)
+ .map(permissions => permissions.filter(permission => permission.id === routePermission.id
+ && permission.accessLevel === routePermission.accessLevel))
+ .map(matches => matches.length > 0)
+ .take(1);
+ }
+}
diff --git a/src/app/sevices/security/authz/permittable-group-id-mapper.ts b/src/app/sevices/security/authz/permittable-group-id-mapper.ts
new file mode 100644
index 0000000..c81b30e
--- /dev/null
+++ b/src/app/sevices/security/authz/permittable-group-id-mapper.ts
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {FimsPermissionDescriptor} from './fims-permission-descriptor';
+import {IdentityPermittableGroupIds} from '../../identity/domain/permittable-group-ids.model';
+import {OfficePermittableGroupIds} from '../../office/domain/permittable-group-ids.model';
+import {CustomerPermittableGroupIds} from '../../customer/domain/permittable-group-ids';
+import {AccountingPermittableGroupIds} from '../../accounting/domain/permittable-group-ids';
+import {PortfolioPermittableGroupIds} from '../../portfolio/domain/permittable-group-ids';
+import {PermissionId} from './permission-id.type';
+import {Injectable} from '@angular/core';
+import {DepositAccountPermittableGroupIds} from '../../depositAccount/domain/permittable-group-ids';
+import {TellerPermittableGroupIds} from '../../teller/domain/permittable-group-ids';
+import {ReportingPermittableGroupIds} from '../../reporting/domain/permittable-group-ids';
+import {ChequePermittableGroupIds} from '../../cheque/domain/permittable-group-ids';
+import {PayrollPermittableGroupIds} from '../../payroll/domain/permittable-group-ids';
+
+interface PermittableGroupMap {
+ [s: string]: FimsPermissionDescriptor;
+}
+
+/**
+ * Maps permittable group ids to internal keys
+ */
+@Injectable()
+export class PermittableGroupIdMapper {
+
+ private _permittableGroupMap: PermittableGroupMap = {};
+
+ constructor() {
+ this._permittableGroupMap[OfficePermittableGroupIds.EMPLOYEE_MANAGEMENT] = {id: 'office_employees', label: 'Employees'};
+ this._permittableGroupMap[OfficePermittableGroupIds.OFFICE_MANAGEMENT] = {id: 'office_offices', label: 'Offices'};
+ this._permittableGroupMap[OfficePermittableGroupIds.SELF_MANAGEMENT] = {
+ id: 'office_self',
+ label: 'User created resources(Offices & Employees)'
+ };
+
+ this._permittableGroupMap[IdentityPermittableGroupIds.IDENTITY_MANAGEMENT] = {id: 'identity_identities', label: 'Identities'};
+ this._permittableGroupMap[IdentityPermittableGroupIds.ROLE_MANAGEMENT] = {id: 'identity_roles', label: 'Roles'};
+ this._permittableGroupMap[IdentityPermittableGroupIds.SELF_MANAGEMENT] = {
+ id: 'identity_self',
+ label: 'User created resources(Identity & Roles)'
+ };
+
+ this._permittableGroupMap[CustomerPermittableGroupIds.CUSTOMER_MANAGEMENT] = {id: 'customer_customers', label: 'Members'};
+ this._permittableGroupMap[CustomerPermittableGroupIds.TASK_MANAGEMENT] = {id: 'customer_tasks', label: 'Tasks'};
+ this._permittableGroupMap[CustomerPermittableGroupIds.CATALOG_MANAGEMENT] = {id: 'catalog_catalogs', label: 'Custom fields'};
+ this._permittableGroupMap[CustomerPermittableGroupIds.IDENTITY_CARD_MANAGEMENT] = {
+ id: 'customer_identifications',
+ label: 'Member identification cards'
+ };
+ this._permittableGroupMap[CustomerPermittableGroupIds.PORTRAIT_MANAGEMENT] = {id: 'customer_portrait', label: 'Member portrait'};
+ this._permittableGroupMap[CustomerPermittableGroupIds.CUSTOMER_DOCUMENT] = {id: 'customer_documents', label: 'Member documents'};
+
+ this._permittableGroupMap[AccountingPermittableGroupIds.ACCOUNT_MANAGEMENT] = {id: 'accounting_accounts', label: 'Accounts'};
+ this._permittableGroupMap[AccountingPermittableGroupIds.JOURNAL_MANAGEMENT] = {id: 'accounting_journals', label: 'Journal'};
+ this._permittableGroupMap[AccountingPermittableGroupIds.LEDGER_MANAGEMENT] = {id: 'accounting_ledgers', label: 'Ledger'};
+ this._permittableGroupMap[AccountingPermittableGroupIds.TRANSACTION_TYPES] = {id: 'accounting_tx_types', label: 'Transaction types'};
+ this._permittableGroupMap[AccountingPermittableGroupIds.THOTH_INCOME_STMT] = {
+ id: 'accounting_income_statement',
+ label: 'Income statement'
+ };
+ this._permittableGroupMap[AccountingPermittableGroupIds.THOTH_FIN_CONDITION] = {
+ id: 'accounting_fin_condition',
+ label: 'Financial condition'
+ };
+
+ this._permittableGroupMap[PortfolioPermittableGroupIds.PRODUCT_OPERATIONS_MANAGEMENT] = {
+ id: 'portfolio_product_operations',
+ label: 'Loan product operations'
+ };
+ this._permittableGroupMap[PortfolioPermittableGroupIds.PRODUCT_LOSS_PROVISIONING_MANAGEMENT] = {
+ id: 'portfolio_loss_provision', label: 'Loan loss provision'
+ };
+ this._permittableGroupMap[PortfolioPermittableGroupIds.PRODUCT_MANAGEMENT] = {id: 'portfolio_products', label: 'Loan products'};
+ this._permittableGroupMap[PortfolioPermittableGroupIds.CASE_MANAGEMENT] = {id: 'portfolio_cases', label: 'Member loans'};
+ this._permittableGroupMap[PortfolioPermittableGroupIds.CASE_DOCUMENT_MANAGEMENT] = {
+ id: 'portfolio_documents',
+ label: 'Member loan documents'
+ };
+
+ this._permittableGroupMap[DepositAccountPermittableGroupIds.DEFINITION_MANAGEMENT] = {
+ id: 'deposit_definitions',
+ label: 'Deposit account management'
+ };
+ this._permittableGroupMap[DepositAccountPermittableGroupIds.INSTANCE_MANAGEMENT] = {
+ id: 'deposit_instances',
+ label: 'Deposit account for members'
+ };
+
+ this._permittableGroupMap[TellerPermittableGroupIds.TELLER_MANAGEMENT] = {id: 'teller_management', label: 'Teller management'};
+ this._permittableGroupMap[TellerPermittableGroupIds.TELLER_OPERATION] = {id: 'teller_operations', label: 'Teller operations'};
+
+ this._permittableGroupMap[ReportingPermittableGroupIds.REPORT_MANAGEMENT] = {id: 'reporting_management', label: 'Report management'};
+
+ this._permittableGroupMap[ChequePermittableGroupIds.CHEQUE_TRANSACTION] = {id: 'cheque_transaction', label: 'Cheque transaction'};
+ this._permittableGroupMap[ChequePermittableGroupIds.CHEQUE_MANAGEMENT] = {id: 'cheque_management', label: 'Cheque management'};
+
+ this._permittableGroupMap[PayrollPermittableGroupIds.CONFIGURATION] = {id: 'payroll_configuration', label: 'Payroll configuration'};
+ this._permittableGroupMap[PayrollPermittableGroupIds.DISTRIBUTION] = {id: 'payroll_distribution', label: 'Payroll distribution'};
+ }
+
+ public map(permittableGroupId: string): FimsPermissionDescriptor {
+ const descriptor: FimsPermissionDescriptor = this._permittableGroupMap[permittableGroupId];
+ if (!descriptor) {
+ console.warn(`Could not find permission descriptor for permittable group id '${permittableGroupId}'`);
+ }
+ return descriptor;
+ }
+
+ public isValid(id: PermissionId): boolean {
+ for (const key in this._permittableGroupMap) {
+ if (this._permittableGroupMap.hasOwnProperty(key)) {
+ const descriptor: FimsPermissionDescriptor = this._permittableGroupMap[key];
+ if (descriptor.id === id) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/app/sevices/security/change.password.service.spec.ts b/src/app/sevices/security/change.password.service.spec.ts
new file mode 100644
index 0000000..897bec3
--- /dev/null
+++ b/src/app/sevices/security/change.password.service.spec.ts
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from '@angular/router';
+import {TestBed} from '@angular/core/testing';
+import {Observable} from 'rxjs/Observable';
+import {Store} from '@ngrx/store';
+import {ChangePasswordGuard} from './change.password.service';
+import {Authentication} from '../identity/domain/authentication.model';
+
+describe('Test Password Change Service', () => {
+
+ const route: ActivatedRouteSnapshot = undefined;
+
+ const state: RouterStateSnapshot = undefined;
+
+ const router = {
+ navigate() {
+ }
+ };
+
+ describe('when logged in', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ ChangePasswordGuard,
+ {provide: Router, useValue: router},
+ {
+ provide: Store, useClass: class {
+ select = jasmine.createSpy('select').and.callFake(selector => Observable.of({}));
+ }
+ }
+ ]
+ });
+ });
+
+ function setup(authentication: Authentication): ChangePasswordGuard {
+ const store = TestBed.get(Store);
+
+ store.select.and.returnValue(Observable.of({
+ authentication
+ }));
+
+ return TestBed.get(ChangePasswordGuard);
+ }
+
+ it('should test if route is not active when password expiration is in the past', (done: DoneFn) => {
+ const changePasswordGuard = setup({
+ passwordExpiration: '2016-01-01',
+ accessToken: '',
+ accessTokenExpiration: '',
+ tokenType: '',
+ refreshTokenExpiration: ''
+ });
+
+ changePasswordGuard.canActivateChild(route, state).subscribe(canActivateChild => {
+ expect(canActivateChild).toBeFalsy();
+ done();
+ });
+ });
+
+ it('should test if route is active when no password expiration is set', (done: DoneFn) => {
+ const changePasswordGuard = setup({
+ passwordExpiration: null,
+ accessToken: '',
+ accessTokenExpiration: '',
+ tokenType: '',
+ refreshTokenExpiration: ''
+ });
+
+ changePasswordGuard.canActivateChild(route, state).subscribe(canActivateChild => {
+ expect(canActivateChild).toBeTruthy();
+ done();
+ });
+ });
+ });
+
+});
diff --git a/src/app/sevices/security/change.password.service.ts b/src/app/sevices/security/change.password.service.ts
new file mode 100644
index 0000000..1a844ff
--- /dev/null
+++ b/src/app/sevices/security/change.password.service.ts
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {ActivatedRouteSnapshot, CanActivateChild, Router, RouterStateSnapshot} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import * as fromRoot from '../../store';
+import {Store} from '@ngrx/store';
+
+@Injectable()
+export class ChangePasswordGuard implements CanActivateChild {
+
+ constructor(private store: Store<fromRoot.State>, private router: Router) {
+ }
+
+ canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
+ return this.isPasswordChangeNeeded()
+ .switchMap(passwordChangeNeeded => {
+ if (passwordChangeNeeded) {
+ this.router.navigate(['/changePassword'], {queryParams: {forced: true}});
+ return Observable.of(false);
+ }
+
+ return Observable.of(true);
+ });
+
+ }
+
+ private isPasswordChangeNeeded(): Observable<boolean> {
+ return this.store.select(fromRoot.getAuthenticationState)
+ .map(state => state.authentication.passwordExpiration ? new Date(state.authentication.passwordExpiration) : undefined)
+ .map(expiryDate => expiryDate ? expiryDate.getTime() < new Date().getTime() : false);
+ }
+}
diff --git a/src/app/sevices/teller/domain/charge.model.ts b/src/app/sevices/teller/domain/charge.model.ts
new file mode 100644
index 0000000..f578570
--- /dev/null
+++ b/src/app/sevices/teller/domain/charge.model.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface Charge {
+ code: string;
+ name: string;
+ amount: number;
+}
diff --git a/src/app/sevices/teller/domain/cheque.model.ts b/src/app/sevices/teller/domain/cheque.model.ts
new file mode 100644
index 0000000..e65e19c
--- /dev/null
+++ b/src/app/sevices/teller/domain/cheque.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {MICR} from './micr.model';
+
+export interface Cheque {
+ micr: MICR;
+ drawee: string;
+ drawer: string;
+ payee: string;
+ amount: number;
+ dateIssued: string;
+ openCheque?: boolean;
+}
diff --git a/src/app/sevices/teller/domain/micr.model.ts b/src/app/sevices/teller/domain/micr.model.ts
new file mode 100644
index 0000000..288d282
--- /dev/null
+++ b/src/app/sevices/teller/domain/micr.model.ts
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface MICR {
+ chequeNumber: string;
+ branchSortCode: string;
+ accountNumber: string;
+}
diff --git a/src/app/sevices/teller/domain/permittable-group-ids.ts b/src/app/sevices/teller/domain/permittable-group-ids.ts
new file mode 100644
index 0000000..c91e63e
--- /dev/null
+++ b/src/app/sevices/teller/domain/permittable-group-ids.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export class TellerPermittableGroupIds {
+ public static readonly TELLER_MANAGEMENT = 'teller__v1__management';
+ public static readonly TELLER_OPERATION = 'teller__v1__operation';
+}
diff --git a/src/app/sevices/teller/domain/teller-authentication.model.ts b/src/app/sevices/teller/domain/teller-authentication.model.ts
new file mode 100644
index 0000000..589774b
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-authentication.model.ts
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+export interface TellerAuthentication {
+ employeeIdentifier: string;
+ password: string;
+}
diff --git a/src/app/sevices/teller/domain/teller-balance-sheet.model.ts b/src/app/sevices/teller/domain/teller-balance-sheet.model.ts
new file mode 100644
index 0000000..989b18d
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-balance-sheet.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {TellerEntry} from './teller-entry.model';
+
+export interface TellerBalanceSheet {
+ day?: string;
+ cashOnHand: string;
+ cashReceivedTotal: string;
+ cashDisbursedTotal: string;
+ chequesReceivedTotal: string;
+ cashEntries: TellerEntry[];
+ chequeEntries: TellerEntry[];
+}
diff --git a/src/app/sevices/teller/domain/teller-denomination.model.ts b/src/app/sevices/teller/domain/teller-denomination.model.ts
new file mode 100644
index 0000000..b28dc8e
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-denomination.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export interface TellerDenomination {
+ countedTotal: string;
+ note: string;
+ adjustingJournalEntry?: string;
+ createdOn?: string;
+ createdBy?: string;
+}
diff --git a/src/app/sevices/teller/domain/teller-entry.model.ts b/src/app/sevices/teller/domain/teller-entry.model.ts
new file mode 100644
index 0000000..6839269
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-entry.model.ts
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type Type = 'DEBIT' | 'CREDIT' | 'CHEQUE';
+
+export interface TellerEntry {
+ type?: Type;
+ transactionDate: string;
+ message: string;
+ amount: number;
+ balance: number;
+}
diff --git a/src/app/sevices/teller/domain/teller-management-command.model.ts b/src/app/sevices/teller/domain/teller-management-command.model.ts
new file mode 100644
index 0000000..97f395f
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-management-command.model.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type Action = 'OPEN' | 'CLOSE';
+
+export type Adjustment = 'NONE' | 'DEBIT' | 'CREDIT';
+
+export interface TellerManagementCommand {
+ action: Action;
+ adjustment?: Adjustment;
+ amount?: number;
+ assignedEmployeeIdentifier?: string;
+}
diff --git a/src/app/sevices/teller/domain/teller-transaction-costs.model.ts b/src/app/sevices/teller/domain/teller-transaction-costs.model.ts
new file mode 100644
index 0000000..307071d
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-transaction-costs.model.ts
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Charge} from './charge.model';
+
+export interface TellerTransactionCosts {
+ tellerTransactionIdentifier?: string;
+ totalAmount?: string;
+ charges?: Charge[];
+}
diff --git a/src/app/sevices/teller/domain/teller-transaction.model.ts b/src/app/sevices/teller/domain/teller-transaction.model.ts
new file mode 100644
index 0000000..11de9cf
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller-transaction.model.ts
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Cheque} from './cheque.model';
+
+export type State = 'PENDING' | 'CANCELED' | 'CONFIRMED';
+
+export type TransactionType = 'ACCO' | 'ACCC' | 'ACCT' | 'CDPT' | 'CWDL' | 'PPAY' | 'CCHQ';
+
+export interface TellerTransaction {
+ identifier?: string;
+ transactionType: TransactionType;
+ transactionDate: string;
+ customerIdentifier: string;
+ productIdentifier: string;
+ productCaseIdentifier?: string;
+ customerAccountIdentifier: string;
+ targetAccountIdentifier?: string;
+ clerk: string;
+ amount: number;
+ state?: State;
+ cheque?: Cheque;
+}
diff --git a/src/app/sevices/teller/domain/teller.model.ts b/src/app/sevices/teller/domain/teller.model.ts
new file mode 100644
index 0000000..6e5ef2c
--- /dev/null
+++ b/src/app/sevices/teller/domain/teller.model.ts
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export type Status = 'ACTIVE' | 'CLOSED' | 'OPEN' | 'PAUSED';
+
+export interface Teller {
+ code: string;
+ password: string;
+ cashdrawLimit: number;
+ tellerAccountIdentifier: string;
+ vaultAccountIdentifier: string;
+ chequesReceivableAccount: string;
+ cashOverShortAccount: string;
+ denominationRequired: boolean;
+ assignedEmployee?: string;
+ state?: Status;
+ createdBy?: string;
+ createdOn?: string;
+ lastModifiedBy?: string;
+ lastModifiedOn?: string;
+ lastOpenedBy?: string;
+ lastOpenedOn?: string;
+}
diff --git a/src/app/sevices/teller/teller-service.ts b/src/app/sevices/teller/teller-service.ts
new file mode 100644
index 0000000..1655496
--- /dev/null
+++ b/src/app/sevices/teller/teller-service.ts
@@ -0,0 +1,106 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Inject, Injectable} from '@angular/core';
+import {HttpClient} from '../http/http.service';
+import {Teller} from './domain/teller.model';
+import {Observable} from 'rxjs/Observable';
+import {TellerManagementCommand} from './domain/teller-management-command.model';
+import {TellerBalanceSheet} from './domain/teller-balance-sheet.model';
+import {TellerAuthentication} from './domain/teller-authentication.model';
+import {RequestOptionsArgs, URLSearchParams} from '@angular/http';
+import {TellerTransactionCosts} from './domain/teller-transaction-costs.model';
+import {TellerTransaction} from './domain/teller-transaction.model';
+import {TellerDenomination} from './domain/teller-denomination.model';
+
+@Injectable()
+export class TellerService {
+
+ constructor(private http: HttpClient, @Inject('tellerBaseUrl') private baseUrl: string) {
+ }
+
+ create(officeIdentifier: string, teller: Teller): Observable<void> {
+ return this.http.post(`${this.baseUrl}/offices/${officeIdentifier}/teller`, teller);
+ }
+
+ find(officeIdentifier: string, tellerCode: string): Observable<Teller> {
+ return this.http.get(`${this.baseUrl}/offices/${officeIdentifier}/teller/${tellerCode}`);
+ }
+
+ fetch(officeIdentifier: string): Observable<Teller[]> {
+ return this.http.get(`${this.baseUrl}/offices/${officeIdentifier}/teller`);
+ }
+
+ change(officeIdentifier: string, teller: Teller): Observable<void> {
+ return this.http.put(`${this.baseUrl}/offices/${officeIdentifier}/teller/${teller.code}`, teller);
+ }
+
+ createCommand(officeIdentifier: string, tellerCode: string, tellerManagementCommand: TellerManagementCommand): Observable<void> {
+ return this.http.post(`${this.baseUrl}/offices/${officeIdentifier}/teller/${tellerCode}/commands`, tellerManagementCommand);
+ }
+
+ getBalance(officeIdentifier: string, tellerCode: string): Observable<TellerBalanceSheet> {
+ return this.http.get(`${this.baseUrl}/offices/${officeIdentifier}/teller/${tellerCode}/balance`);
+ }
+
+ unlockDrawer(tellerCode: string, tellerAuthentication: TellerAuthentication): Observable<Teller> {
+ return this.http.post(`${this.baseUrl}/teller/${tellerCode}/drawer`, tellerAuthentication, undefined, true);
+ }
+
+ executeCommand(tellerCode: string, command: string): Observable<void> {
+ const params = new URLSearchParams();
+ params.append('command', command);
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+
+ return this.http.post(`${this.baseUrl}/teller/${tellerCode}`, {}, requestOptions);
+ }
+
+ createTransaction(tellerCode: string, tellerTransaction: TellerTransaction): Observable<TellerTransactionCosts> {
+ return this.http.post(`${this.baseUrl}/teller/${tellerCode}/transactions`, tellerTransaction);
+ }
+
+ confirmTransaction(tellerCode: string, tellerTransactionIdentifier: string, command: string,
+ chargesIncluded?: boolean): Observable<void> {
+ const params = new URLSearchParams();
+ params.append('command', command);
+ params.append('charges', chargesIncluded ? 'included' : 'excluded');
+
+ const requestOptions: RequestOptionsArgs = {
+ params
+ };
+
+ return this.http.post(`${this.baseUrl}/teller/${tellerCode}/transactions/${tellerTransactionIdentifier}`, {}, requestOptions);
+ }
+
+ getTransactions(tellerCode: string): Observable<TellerTransaction[]> {
+ return this.http.get(`${this.baseUrl}/teller/${tellerCode}/transactions`);
+ }
+
+ saveTellerDenomination(officeIdentifier: string, tellerCode: string, tellerDenomination: TellerDenomination): Observable<void> {
+ return this.http.post(`${this.baseUrl}/offices/${officeIdentifier}/teller/${tellerCode}/denominations`, tellerDenomination);
+ }
+
+ fetchTellerDenominations(officeIdentifier: string, tellerCode: string): Observable<TellerDenomination[]> {
+ return this.http.get(`${this.baseUrl}/offices/${officeIdentifier}/teller/${tellerCode}/denominations`);
+ }
+
+}
diff --git a/src/app/store/account/account.actions.ts b/src/app/store/account/account.actions.ts
new file mode 100644
index 0000000..17d91f0
--- /dev/null
+++ b/src/app/store/account/account.actions.ts
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Account] Search');
+export const SEARCH_BY_LEDGER = type('[Account] Search by Ledger');
+export const SEARCH_COMPLETE = type('[Account] Search Complete');
+
+export interface SearchByLedgerPayload {
+ ledgerId: string;
+ fetchRequest: FetchRequest;
+}
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor(public payload: FetchRequest) { }
+}
+
+export class SearchByLedgerAction implements Action {
+ readonly type = SEARCH_BY_LEDGER;
+
+ constructor(public payload: SearchByLedgerPayload) { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction
+ | SearchByLedgerAction;
diff --git a/src/app/store/account/accounts.reducer.ts b/src/app/store/account/accounts.reducer.ts
new file mode 100644
index 0000000..a4e463d
--- /dev/null
+++ b/src/app/store/account/accounts.reducer.ts
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import * as accounts from './account.actions';
+import {SearchState} from '../../common/store/search.reducer';
+
+export function reducer(state, action: accounts.Actions): SearchState {
+
+ switch (action.type) {
+
+ case accounts.SEARCH_BY_LEDGER: {
+ return Object.assign({}, state, {
+ loading: true,
+ entities: []
+ });
+ }
+
+ default:
+ return state;
+ }
+}
diff --git a/src/app/store/account/effects/service.effects.spec.ts b/src/app/store/account/effects/service.effects.spec.ts
new file mode 100644
index 0000000..1da4206
--- /dev/null
+++ b/src/app/store/account/effects/service.effects.spec.ts
@@ -0,0 +1,173 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {AccountSearchApiEffects} from './service.effects';
+import {AccountingService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/accounting.service';
+import {AccountPage} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/domain/account-page.model';
+import {SearchAction, SearchByLedgerAction, SearchCompleteAction} from '../account.actions';
+import {Observable} from 'rxjs/Observable';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+describe('Account Search Api Effects', () => {
+ beforeEach(() => {
+
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ AccountSearchApiEffects,
+ {
+ provide: AccountingService,
+ useValue: jasmine.createSpyObj('accountingService', ['fetchAccounts', 'fetchAccountsOfLedger'])
+ }
+ ]
+ });
+
+ });
+
+ describe('searchAccounts$', () => {
+
+ function setup(params?: {searchAccountsReturnValue: any}) {
+ const accountingService = TestBed.get(AccountingService);
+ if (params) {
+ accountingService.fetchAccounts.and.returnValue(params.searchAccountsReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ accountEffects: TestBed.get(AccountSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with AccountPage', fakeAsync(() => {
+ const accountPage: AccountPage = {
+ accounts: [
+ { identifier: 'test', name: 'test', ledger: '' }
+ ],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ const { runner, accountEffects } = setup({ searchAccountsReturnValue: Observable.of(accountPage) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: accountPage.accounts,
+ totalPages: accountPage.totalPages,
+ totalElements: accountPage.totalElements
+ });
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ accountEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with an empty array, if accounting service throws', fakeAsync(() => {
+ const {runner, accountEffects} = setup({searchAccountsReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction(emptySearchResult());
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ accountEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('searchByLedger$', () => {
+
+ function setup(params?: {searchAccountsOfLedgerReturnValue: any}) {
+ const accountingService = TestBed.get(AccountingService);
+ if (params) {
+ accountingService.fetchAccountsOfLedger.and.returnValue(params.searchAccountsOfLedgerReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ accountEffects: TestBed.get(AccountSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with AccountPage', fakeAsync(() => {
+ const accountPage: AccountPage = {
+ accounts: [
+ { identifier: 'test', name: 'test', ledger: '' }
+ ],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ const { runner, accountEffects } = setup({ searchAccountsOfLedgerReturnValue: Observable.of(accountPage) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: accountPage.accounts,
+ totalPages: accountPage.totalPages,
+ totalElements: accountPage.totalElements
+ });
+
+ runner.queue(new SearchByLedgerAction({
+ ledgerId: 'abc',
+ fetchRequest: {}
+ }));
+
+ let result = null;
+ accountEffects.searchByLedger$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with an empty array, if accounting service throws', fakeAsync(() => {
+ const {runner, accountEffects} = setup({searchAccountsOfLedgerReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction({
+ elements: [],
+ totalElements: 0,
+ totalPages: 0
+ });
+
+ runner.queue(new SearchByLedgerAction({
+ ledgerId: 'abc',
+ fetchRequest: {}
+ }));
+
+ let result = null;
+ accountEffects.searchByLedger$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/account/effects/service.effects.ts b/src/app/store/account/effects/service.effects.ts
new file mode 100644
index 0000000..9794088
--- /dev/null
+++ b/src/app/store/account/effects/service.effects.ts
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as accountActions from '../account.actions';
+import {AccountingService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/accounting.service';
+import {emptySearchResult, SearchResult} from '../../../common/store/search.reducer';
+import {AccountPage} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/domain/account-page.model';
+
+@Injectable()
+export class AccountSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(accountActions.SEARCH)
+ .debounceTime(300)
+ .map((action: accountActions.SearchAction) => action.payload)
+ .switchMap(fetchRequest => {
+ const nextSearch$ = this.actions$.ofType(accountActions.SEARCH).skip(1);
+
+ return this.accountingService.fetchAccounts(fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(this.mapToSearchResult)
+ .map(searchResult => new accountActions.SearchCompleteAction(searchResult))
+ .catch(() => of(new accountActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ @Effect()
+ searchByLedger$: Observable<Action> = this.actions$
+ .ofType(accountActions.SEARCH_BY_LEDGER)
+ .debounceTime(300)
+ .map((action: accountActions.SearchByLedgerAction) => action.payload)
+ .switchMap(payload => {
+ const nextSearch$ = this.actions$.ofType(accountActions.SEARCH_BY_LEDGER).skip(1);
+
+ return this.accountingService.fetchAccountsOfLedger(payload.ledgerId, payload.fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(this.mapToSearchResult)
+ .map(searchResult => new accountActions.SearchCompleteAction(searchResult))
+ .catch(() => of(new accountActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ private mapToSearchResult(accountPage: AccountPage): SearchResult {
+ return {
+ elements: accountPage.accounts,
+ totalElements: accountPage.totalElements,
+ totalPages: accountPage.totalPages
+ };
+ }
+
+ constructor(private actions$: Actions, private accountingService: AccountingService) { }
+
+}
diff --git a/src/app/store/customer/customer.actions.ts b/src/app/store/customer/customer.actions.ts
new file mode 100644
index 0000000..4f6a8ce
--- /dev/null
+++ b/src/app/store/customer/customer.actions.ts
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Customer] Search');
+export const SEARCH_COMPLETE = type('[Customer] Search Complete');
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor(public payload: FetchRequest) { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction;
diff --git a/src/app/store/customer/effects/service.effects.spec.ts b/src/app/store/customer/effects/service.effects.spec.ts
new file mode 100644
index 0000000..fee8a94
--- /dev/null
+++ b/src/app/store/customer/effects/service.effects.spec.ts
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {CustomerSearchApiEffects} from './service.effects';
+import {Observable} from 'rxjs/Observable';
+import {CustomerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/customer/customer.service';
+import {SearchAction, SearchCompleteAction} from '../customer.actions';
+import {CustomerPage} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/customer/domain/customer-page.model';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+describe('Customer Search Api Effects', () => {
+ beforeEach(() => {
+
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ CustomerSearchApiEffects,
+ {
+ provide: CustomerService,
+ useValue: jasmine.createSpyObj('customerService', ['fetchCustomers'])
+ }
+ ]
+ });
+
+ });
+
+ describe('search$', () => {
+
+ function setup(params?: {searchCustomersReturnValue: any}) {
+ const customerService = TestBed.get(CustomerService);
+ if (params) {
+ customerService.fetchCustomers.and.returnValue(params.searchCustomersReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ customerEffects: TestBed.get(CustomerSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with CustomerPage', fakeAsync(() => {
+ const customerPage: CustomerPage = {
+ customers: [
+ { identifier: 'test', type: 'PERSON', givenName: '', surname: '', dateOfBirth: {}, member: true, address: {
+ street: '', city: '', countryCode: '', country: ''
+ }, customValues: [] }
+ ],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ const { runner, customerEffects } = setup({ searchCustomersReturnValue: Observable.of(customerPage) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: customerPage.customers,
+ totalPages: customerPage.totalPages,
+ totalElements: customerPage.totalElements
+ });
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ customerEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with an empty array, if customer service throws', fakeAsync(() => {
+ const {runner, customerEffects} = setup({searchCustomersReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction(emptySearchResult());
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ customerEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/customer/effects/service.effects.ts b/src/app/store/customer/effects/service.effects.ts
new file mode 100644
index 0000000..cd850db
--- /dev/null
+++ b/src/app/store/customer/effects/service.effects.ts
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as customerActions from '../customer.actions';
+import {CustomerService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/customer/customer.service';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+@Injectable()
+export class CustomerSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(customerActions.SEARCH)
+ .debounceTime(300)
+ .map((action: customerActions.SearchAction) => action.payload)
+ .switchMap(fetchRequest => {
+ const nextSearch$ = this.actions$.ofType(customerActions.SEARCH).skip(1);
+
+ return this.customerService.fetchCustomers(fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(customerPage => new customerActions.SearchCompleteAction({
+ elements: customerPage.customers,
+ totalElements: customerPage.totalElements,
+ totalPages: customerPage.totalPages
+ }))
+ .catch(() => of(new customerActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ constructor(private actions$: Actions, private customerService: CustomerService) { }
+
+}
diff --git a/src/app/store/employee/effects/service.effects.spec.ts b/src/app/store/employee/effects/service.effects.spec.ts
new file mode 100644
index 0000000..a00b797
--- /dev/null
+++ b/src/app/store/employee/effects/service.effects.spec.ts
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {EmployeeSearchApiEffects} from './service.effects';
+import {Observable} from 'rxjs/Observable';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {SearchAction, SearchCompleteAction} from '../employee.actions';
+import {EmployeePage} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/domain/employee-page.model';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+describe('Employee Search Api Effects', () => {
+ beforeEach(() => {
+
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ EmployeeSearchApiEffects,
+ {
+ provide: OfficeService,
+ useValue: jasmine.createSpyObj('officeService', ['listEmployees'])
+ }
+ ]
+ });
+
+ });
+
+ describe('search$', () => {
+
+ function setup(params?: {searchEmployeesReturnValue: any}) {
+ const officeService = TestBed.get(OfficeService);
+ if (params) {
+ officeService.listEmployees.and.returnValue(params.searchEmployeesReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ employeeEffects: TestBed.get(EmployeeSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with EmployeePage', fakeAsync(() => {
+ const employeePage: EmployeePage = {
+ employees: [
+ { identifier: '', givenName: '', surname: '', contactDetails: [] }
+ ],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ const { runner, employeeEffects } = setup({ searchEmployeesReturnValue: Observable.of(employeePage) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: employeePage.employees,
+ totalElements: employeePage.totalElements,
+ totalPages: employeePage.totalPages
+ });
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ employeeEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with an empty array, if office service throws', fakeAsync(() => {
+ const {runner, employeeEffects} = setup({searchEmployeesReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction(emptySearchResult());
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ employeeEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/employee/effects/service.effects.ts b/src/app/store/employee/effects/service.effects.ts
new file mode 100644
index 0000000..96c9d01
--- /dev/null
+++ b/src/app/store/employee/effects/service.effects.ts
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as employeeActions from '../employee.actions';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+@Injectable()
+export class EmployeeSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(employeeActions.SEARCH)
+ .debounceTime(300)
+ .map((action: employeeActions.SearchAction) => action.payload)
+ .switchMap(fetchRequest => {
+ const nextSearch$ = this.actions$.ofType(employeeActions.SEARCH).skip(1);
+
+ return this.officeService.listEmployees(fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(employeePage => new employeeActions.SearchCompleteAction({
+ elements: employeePage.employees,
+ totalElements: employeePage.totalElements,
+ totalPages: employeePage.totalPages
+ }))
+ .catch(() => of(new employeeActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ constructor(private actions$: Actions, private officeService: OfficeService) { }
+}
diff --git a/src/app/store/employee/employee.actions.ts b/src/app/store/employee/employee.actions.ts
new file mode 100644
index 0000000..efb3c17
--- /dev/null
+++ b/src/app/store/employee/employee.actions.ts
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Employee] Search');
+export const SEARCH_COMPLETE = type('[Employee] Search Complete');
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor(public payload: FetchRequest) { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction;
diff --git a/src/app/store/index.ts b/src/app/store/index.ts
new file mode 100644
index 0000000..f31b4ab
--- /dev/null
+++ b/src/app/store/index.ts
@@ -0,0 +1,218 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {createSelector} from 'reselect';
+import {ActionReducer, combineReducers} from '@ngrx/store';
+
+import * as fromAuthentication from './security/authentication.reducer';
+import * as fromAuthorization from './security/authorization.reducer';
+import * as fromAccounts from './account/accounts.reducer';
+import * as authenticationActions from './security/security.actions';
+import {compose} from '@ngrx/core/compose';
+import {localStorageSync} from 'ngrx-store-localstorage';
+import {
+ createSearchReducer,
+ getSearchEntities,
+ getSearchLoading,
+ getSearchTotalElements,
+ getSearchTotalPages,
+ SearchState
+} from '../common/store/search.reducer';
+
+export interface State {
+ authentication: fromAuthentication.State;
+ authorization: fromAuthorization.State;
+ officeSearch: SearchState;
+ countrySearch: SearchState;
+ employeeSearch: SearchState;
+ roleSearch: SearchState;
+ customerSearch: SearchState;
+ accountSearch: SearchState;
+ ledgerSearch: SearchState;
+}
+
+export const reducers = {
+ authentication: fromAuthentication.reducer,
+ authorization: fromAuthorization.reducer,
+
+ officeSearch: createSearchReducer('Office'),
+ countrySearch: createSearchReducer('Country'),
+ employeeSearch: createSearchReducer('Employee'),
+ roleSearch: createSearchReducer('Role'),
+ customerSearch: createSearchReducer('Customer'),
+ accountSearch: createSearchReducer('Account', fromAccounts.reducer),
+ ledgerSearch: createSearchReducer('Ledger'),
+};
+
+export function createReducer(asyncReducers = {}): ActionReducer<any> {
+ const actionReducer = compose(localStorageSync({
+ keys: [],
+ rehydrate: true
+ }), combineReducers)(Object.assign(reducers, asyncReducers));
+
+ return function(state: any, action: any) {
+ // Reset state
+ if (action.type === authenticationActions.LOGOUT_SUCCESS) {
+ return actionReducer(undefined, action);
+ }
+ return actionReducer(state, action);
+ };
+}
+
+export const productionReducer: ActionReducer<State> = createReducer();
+
+export function reducer(state: any, action: any) {
+ return productionReducer(state, action);
+}
+
+/**
+ * Office Search Selectors
+ */
+
+export const getOfficeSearchState = (state: State) => state.officeSearch;
+
+export const getSearchOffices = createSelector(getOfficeSearchState, getSearchEntities);
+export const getOfficeSearchTotalElements = createSelector(getOfficeSearchState, getSearchTotalElements);
+export const getOfficeSearchTotalPages = createSelector(getOfficeSearchState, getSearchTotalPages);
+export const getOfficeSearchLoading = createSelector(getOfficeSearchState, getSearchLoading);
+
+export const getOfficeSearchResults = createSelector(getSearchOffices, getOfficeSearchTotalPages, getOfficeSearchTotalElements,
+ (offices, totalPages, totalElements) => {
+ return {
+ offices: offices,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+/**
+ * Country Search Selectors
+ */
+
+export const getCountrySearchState = (state: State) => state.countrySearch;
+
+export const getSearchCountry = createSelector(getCountrySearchState, getSearchEntities);
+
+/**
+ * Employee Search Selectors
+ */
+export const getEmployeeSearchState = (state: State) => state.employeeSearch;
+
+export const getSearchEmployees = createSelector(getEmployeeSearchState, getSearchEntities);
+export const getEmployeeSearchTotalElements = createSelector(getEmployeeSearchState, getSearchTotalElements);
+export const getEmployeeSearchTotalPages = createSelector(getEmployeeSearchState, getSearchTotalPages);
+export const getEmployeeSearchLoading = createSelector(getEmployeeSearchState, getSearchLoading);
+
+export const getEmployeeSearchResults = createSelector(getSearchEmployees, getEmployeeSearchTotalPages, getEmployeeSearchTotalElements,
+ (employees, totalPages, totalElements) => {
+ return {
+ employees: employees,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+/**
+ * Role Search Selectors
+ */
+export const getRoleSearchState = (state: State) => state.roleSearch;
+
+export const getSearchRoles = createSelector(getRoleSearchState, getSearchEntities);
+export const getRoleSearchTotalElements = createSelector(getRoleSearchState, getSearchTotalElements);
+export const getRoleSearchTotalPages = createSelector(getRoleSearchState, getSearchTotalPages);
+export const getRoleSearchLoading = createSelector(getRoleSearchState, getSearchLoading);
+
+export const getRoleSearchResults = createSelector(getSearchRoles, getRoleSearchTotalPages, getRoleSearchTotalElements,
+ (roles, totalPages, totalElements) => {
+ return {
+ roles: roles,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+/**
+ * Customer Search Selectors
+ */
+export const getCustomerSearchState = (state: State) => state.customerSearch;
+
+export const getSearchCustomers = createSelector(getCustomerSearchState, getSearchEntities);
+export const getCustomerSearchTotalElements = createSelector(getCustomerSearchState, getSearchTotalElements);
+export const getCustomerSearchTotalPages = createSelector(getCustomerSearchState, getSearchTotalPages);
+export const getCustomerSearchLoading = createSelector(getCustomerSearchState, getSearchLoading);
+
+export const getCustomerSearchResults = createSelector(getSearchCustomers, getCustomerSearchTotalPages, getCustomerSearchTotalElements,
+ (customers, totalPages, totalElements) => {
+ return {
+ customers: customers,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+/**
+ * Account Search Selectors
+ */
+export const getAccountSearchState = (state: State) => state.accountSearch;
+
+export const getSearchAccounts = createSelector(getAccountSearchState, getSearchEntities);
+export const getAccountSearchTotalElements = createSelector(getAccountSearchState, getSearchTotalElements);
+export const getAccountSearchTotalPages = createSelector(getAccountSearchState, getSearchTotalPages);
+export const getAccountSearchLoading = createSelector(getAccountSearchState, getSearchLoading);
+
+export const getAccountSearchResults = createSelector(getSearchAccounts, getAccountSearchTotalPages, getAccountSearchTotalElements,
+ (accounts, totalPages, totalElements) => {
+ return {
+ accounts: accounts,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+/**
+ * Ledger Search Selectors
+ */
+export const getLedgerSearchState = (state: State) => state.ledgerSearch;
+
+export const getSearchLedgers = createSelector(getLedgerSearchState, getSearchEntities);
+export const getLedgerSearchTotalElements = createSelector(getLedgerSearchState, getSearchTotalElements);
+export const getLedgerSearchTotalPages = createSelector(getLedgerSearchState, getSearchTotalPages);
+
+export const getLedgerSearchResults = createSelector(getSearchLedgers, getLedgerSearchTotalElements, getLedgerSearchTotalPages,
+ (ledgers, totalPages, totalElements) => {
+ return {
+ ledgers: ledgers,
+ totalPages: totalPages,
+ totalElements: totalElements
+ };
+});
+
+export const getAuthenticationState = (state: State) => state.authentication;
+
+export const getAuthentication = createSelector(getAuthenticationState, fromAuthentication.getAuthentication);
+export const getAuthenticationError = createSelector(getAuthenticationState, fromAuthentication.getError);
+export const getAuthenticationLoading = createSelector(getAuthenticationState, fromAuthentication.getLoading);
+export const getUsername = createSelector(getAuthenticationState, fromAuthentication.getUsername);
+export const getTenant = createSelector(getAuthenticationState, fromAuthentication.getTenant);
+export const getPasswordError = createSelector(getAuthenticationState, fromAuthentication.getPasswordError);
+
+export const getAuthorizationState = (state: State) => state.authorization;
+
+export const getPermissions = createSelector(getAuthorizationState, fromAuthorization.getPermissions);
+
+export const getPermissionsLoading = createSelector(getAuthorizationState, fromAuthorization.getLoading);
diff --git a/src/app/store/ledger/effects/service.effects.ts b/src/app/store/ledger/effects/service.effects.ts
new file mode 100644
index 0000000..133e544
--- /dev/null
+++ b/src/app/store/ledger/effects/service.effects.ts
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as ledgerActions from '../ledger.actions';
+import {AccountingService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/accounting/accounting.service';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+@Injectable()
+export class LedgerSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(ledgerActions.SEARCH)
+ .debounceTime(300)
+ .map((action: ledgerActions.SearchAction) => action.payload)
+ .switchMap(fetchRequest => {
+ const nextSearch$ = this.actions$.ofType(ledgerActions.SEARCH).skip(1);
+
+ return this.accountingService.fetchLedgers(true, fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(ledgerPage => new ledgerActions.SearchCompleteAction({
+ elements: ledgerPage.ledgers,
+ totalPages: ledgerPage.totalPages,
+ totalElements: ledgerPage.totalElements
+ }))
+ .catch(() => of(new ledgerActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ constructor(private actions$: Actions, private accountingService: AccountingService) { }
+}
diff --git a/src/app/store/ledger/ledger.actions.ts b/src/app/store/ledger/ledger.actions.ts
new file mode 100644
index 0000000..5fdf02e
--- /dev/null
+++ b/src/app/store/ledger/ledger.actions.ts
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {type} from '../util';
+import {Action} from '@ngrx/store';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Ledger] Search');
+export const SEARCH_COMPLETE = type('[Ledger] Search Complete');
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor(public payload: FetchRequest) { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction;
diff --git a/src/app/store/office/effects/service.effects.spec.ts b/src/app/store/office/effects/service.effects.spec.ts
new file mode 100644
index 0000000..1316ddc
--- /dev/null
+++ b/src/app/store/office/effects/service.effects.spec.ts
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {OfficeSearchApiEffects} from './service.effects';
+import {Observable} from 'rxjs/Observable';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {SearchAction, SearchCompleteAction} from '../office.actions';
+import {OfficePage} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/domain/office-page.model';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+describe('Office Search Api Effects', () => {
+ beforeEach(() => {
+
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ OfficeSearchApiEffects,
+ {
+ provide: OfficeService,
+ useValue: jasmine.createSpyObj('officeService', ['listOffices'])
+ }
+ ]
+ });
+
+ });
+
+ describe('search$', () => {
+
+ function setup(params?: {searchOfficesReturnValue: any}) {
+ const officeService = TestBed.get(OfficeService);
+ if (params) {
+ officeService.listOffices.and.returnValue(params.searchOfficesReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ officeEffects: TestBed.get(OfficeSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with EmployeePage', fakeAsync(() => {
+ const officePage: OfficePage = {
+ offices: [
+ { identifier: '', name: '' }
+ ],
+ totalElements: 1,
+ totalPages: 1
+ };
+
+ const { runner, officeEffects } = setup({ searchOfficesReturnValue: Observable.of(officePage) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: officePage.offices,
+ totalPages: officePage.totalPages,
+ totalElements: officePage.totalElements
+ });
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ officeEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with an empty array, if office service throws', fakeAsync(() => {
+ const {runner, officeEffects} = setup({searchOfficesReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction(emptySearchResult());
+
+ runner.queue(new SearchAction({}));
+
+ let result = null;
+ officeEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/office/effects/service.effects.ts b/src/app/store/office/effects/service.effects.ts
new file mode 100644
index 0000000..40dff1b
--- /dev/null
+++ b/src/app/store/office/effects/service.effects.ts
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {OfficeService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/office/office.service';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as officeActions from '../office.actions';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+@Injectable()
+export class OfficeSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(officeActions.SEARCH)
+ .debounceTime(300)
+ .map((action: officeActions.SearchAction) => action.payload)
+ .switchMap(fetchRequest => {
+ const nextSearch$ = this.actions$.ofType(officeActions.SEARCH).skip(1);
+
+ return this.officeService.listOffices(fetchRequest)
+ .takeUntil(nextSearch$)
+ .map(officePage => new officeActions.SearchCompleteAction({
+ elements: officePage.offices,
+ totalElements: officePage.totalElements,
+ totalPages: officePage.totalPages
+ }))
+ .catch(() => of(new officeActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ constructor(private actions$: Actions, private officeService: OfficeService) { }
+}
diff --git a/src/app/store/office/office.actions.ts b/src/app/store/office/office.actions.ts
new file mode 100644
index 0000000..dd6d934
--- /dev/null
+++ b/src/app/store/office/office.actions.ts
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {FetchRequest} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/domain/paging/fetch-request.model';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Office] Search');
+export const SEARCH_COMPLETE = type('[Office] Search Complete');
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor(public payload: FetchRequest) { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction;
diff --git a/src/app/store/role/effects/service.effects.spec.ts b/src/app/store/role/effects/service.effects.spec.ts
new file mode 100644
index 0000000..2ef85a6
--- /dev/null
+++ b/src/app/store/role/effects/service.effects.spec.ts
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {RoleSearchApiEffects} from './service.effects';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/identity.service';
+import {Role} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/role.model';
+import {SearchAction, SearchCompleteAction} from '../role.actions';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+
+describe('Role Search Api Effects', () => {
+ beforeEach(() => {
+
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ RoleSearchApiEffects,
+ {
+ provide: IdentityService,
+ useValue: jasmine.createSpyObj('identityService', ['listRoles'])
+ }
+ ]
+ });
+
+ });
+
+ describe('search$', () => {
+
+ function setup(params?: {searchRolesReturnValue: any}) {
+ const identityService = TestBed.get(IdentityService);
+ if (params) {
+ identityService.listRoles.and.returnValue(params.searchRolesReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ roleEffects: TestBed.get(RoleSearchApiEffects)
+ };
+ }
+
+ it('should return a new SearchCompleteAction with Roles', fakeAsync(() => {
+ const roles: Role[] = [
+ { identifier: '', permissions: []}
+ ];
+
+ const { runner, roleEffects } = setup({ searchRolesReturnValue: Observable.of(roles) });
+
+ const expectedResult = new SearchCompleteAction({
+ elements: roles,
+ totalPages: 1,
+ totalElements: roles.length
+ });
+
+ runner.queue(new SearchAction());
+
+ let result = null;
+ roleEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new SearchCompleteAction, with empty search result, if identity service throws', fakeAsync(() => {
+ const {runner, roleEffects} = setup({searchRolesReturnValue: Observable.throw(new Error())});
+
+ const expectedResult = new SearchCompleteAction(emptySearchResult());
+
+ runner.queue(new SearchAction());
+
+ let result = null;
+ roleEffects.search$.subscribe(_result => result = _result);
+
+ tick(299);
+ expect(result).toBe(null);
+ tick(300);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/role/effects/service.effects.ts b/src/app/store/role/effects/service.effects.ts
new file mode 100644
index 0000000..f462bc3
--- /dev/null
+++ b/src/app/store/role/effects/service.effects.ts
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as roleActions from '../role.actions';
+import {IdentityService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/identity.service';
+import {emptySearchResult} from '../../../common/store/search.reducer';
+import {Role} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/role.model';
+
+const SYSTEM_ROLES: string[] = ['pharaoh', 'scheduler'];
+
+@Injectable()
+export class RoleSearchApiEffects {
+
+ @Effect()
+ search$: Observable<Action> = this.actions$
+ .ofType(roleActions.SEARCH)
+ .debounceTime(300)
+ .switchMap(() => {
+ const nextSearch$ = this.actions$.ofType(roleActions.SEARCH).skip(1);
+
+ return this.identityService.listRoles()
+ .takeUntil(nextSearch$)
+ .map(this.excludeSystemRoles)
+ .map(roles => new roleActions.SearchCompleteAction({
+ elements: roles,
+ totalPages: 1,
+ totalElements: roles.length
+ }))
+ .catch(() => of(new roleActions.SearchCompleteAction(emptySearchResult())));
+ });
+
+ private excludeSystemRoles(roles: Role[]): Role[] {
+ return roles.filter(role => SYSTEM_ROLES.indexOf(role.identifier) === -1);
+ }
+
+ constructor(private actions$: Actions, private identityService: IdentityService) { }
+}
diff --git a/src/app/store/role/role.actions.ts b/src/app/store/role/role.actions.ts
new file mode 100644
index 0000000..5fc0b86
--- /dev/null
+++ b/src/app/store/role/role.actions.ts
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {SearchResult} from '../../common/store/search.reducer';
+
+export const SEARCH = type('[Role] Search');
+export const SEARCH_COMPLETE = type('[Role] Search Complete');
+
+export class SearchAction implements Action {
+ readonly type = SEARCH;
+
+ constructor() { }
+}
+
+export class SearchCompleteAction implements Action {
+ readonly type = SEARCH_COMPLETE;
+
+ constructor(public payload: SearchResult) { }
+}
+
+export type Actions
+ = SearchAction
+ | SearchCompleteAction;
diff --git a/src/app/store/security/authentication.reducer.spec.ts b/src/app/store/security/authentication.reducer.spec.ts
new file mode 100644
index 0000000..0cf2e73
--- /dev/null
+++ b/src/app/store/security/authentication.reducer.spec.ts
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {mockAuthentication} from './testing/authentication.mock';
+import {reducer} from './authentication.reducer';
+import {LoginSuccessAction, LoginSuccessPayload, RefreshAccessTokenSuccessAction} from './security.actions';
+import {Authentication} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/authentication.model';
+
+describe('Authentication Reducer', () => {
+
+ function mockInitialState(authentication: Authentication) {
+ return {
+ username: 'test',
+ tenant: 'test',
+ authentication: authentication,
+ loading: false,
+ error: null,
+ passwordError: null
+ };
+ }
+
+ describe('LOGIN_SUCCESS', () => {
+ it('should set authentication, username and tenant', () => {
+ const authentication = mockAuthentication();
+ const loginPayload: LoginSuccessPayload = {
+ username: 'test',
+ tenant: 'test',
+ authentication: authentication
+ };
+
+ const expectedResult = {
+ username: 'test',
+ tenant: 'test',
+ authentication: authentication,
+ loading: false,
+ error: null,
+ passwordError: null
+ };
+
+ const result = reducer(undefined, new LoginSuccessAction(loginPayload));
+ expect(result).toEqual(expectedResult);
+ });
+ });
+
+ describe('REFRESH_ACCESS_TOKEN_SUCCESS', () => {
+ it('should update authentication', () => {
+ const authentication = mockAuthentication();
+
+ const updatedAuthentication = Object.assign({}, authentication, {
+ accessToken: 'iamupdated'
+ });
+
+ const initialState = mockInitialState(authentication);
+
+ const result = reducer(initialState, new RefreshAccessTokenSuccessAction(updatedAuthentication));
+
+ const expectedResult = Object.assign({}, initialState, {
+ authentication: updatedAuthentication
+ });
+
+ expect(result).toEqual(expectedResult);
+ });
+ });
+
+});
diff --git a/src/app/store/security/authentication.reducer.ts b/src/app/store/security/authentication.reducer.ts
new file mode 100644
index 0000000..e7ae797
--- /dev/null
+++ b/src/app/store/security/authentication.reducer.ts
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import * as security from './security.actions';
+import {LoginSuccessPayload} from './security.actions';
+import {Authentication} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/authentication.model';
+
+export interface State {
+ username: string;
+ tenant: string;
+ authentication: Authentication;
+ loading: boolean;
+ error: Error;
+ passwordError: Error;
+}
+
+const initialState: State = {
+ username: null,
+ tenant: null,
+ authentication: null,
+ loading: false,
+ error: null,
+ passwordError: null
+};
+
+export function reducer(state = initialState, action: security.Actions): State {
+ switch (action.type) {
+
+ case security.LOGIN: {
+ return Object.assign({}, state, {
+ loading: true
+ });
+ }
+
+ case security.LOGIN_SUCCESS: {
+ const payload: LoginSuccessPayload = action.payload;
+ return Object.assign({}, state, {
+ loading: false,
+ authentication: payload.authentication,
+ username: payload.username,
+ tenant: payload.tenant
+ });
+ }
+
+ case security.REFRESH_ACCESS_TOKEN_SUCCESS: {
+ const authentication = action.payload;
+ return Object.assign({}, state, {
+ authentication
+ });
+ }
+
+ case security.CHANGE_PASSWORD_FAIL: {
+ const error = action.payload;
+ return Object.assign({}, state, {
+ passwordError: error
+ });
+ }
+
+ case security.LOGIN_FAIL: {
+ const error = action.payload;
+ return Object.assign({}, state, {
+ loading: false,
+ error
+ });
+ }
+
+ case security.LOGOUT_SUCCESS: {
+ return initialState;
+ }
+
+ default:
+ return state;
+ }
+}
+
+export const getAuthentication = (state: State) => state.authentication;
+
+export const getTenant = (state: State) => state.tenant;
+
+export const getUsername = (state: State) => state.username;
+
+export const getError = (state: State) => state.error;
+
+export const getPasswordError = (state: State) => state.passwordError;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/store/security/authorization.reducer.ts b/src/app/store/security/authorization.reducer.ts
new file mode 100644
index 0000000..271dbf3
--- /dev/null
+++ b/src/app/store/security/authorization.reducer.ts
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import * as security from './security.actions';
+import {FimsPermission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/fims-permission.model';
+
+export interface State {
+ permissions: FimsPermission[];
+ loading: boolean;
+}
+
+const initialState: State = {
+ permissions: [],
+ loading: false
+};
+
+export function reducer(state = initialState, action: security.Actions): State {
+ switch (action.type) {
+
+ case security.LOGIN_SUCCESS: {
+ return Object.assign({}, state, {
+ loading: true
+ });
+ }
+
+ case security.PERMISSIONS_UPDATE_SUCCESS: {
+ const permissions = action.payload;
+ return Object.assign({}, state, {
+ loading: false,
+ permissions
+ });
+ }
+
+ case security.PERMISSIONS_UPDATE_FAIL:
+ case security.LOGOUT_SUCCESS: {
+ return initialState;
+ }
+
+ default:
+ return state;
+ }
+}
+
+export const getPermissions = (state: State) => state.permissions;
+
+export const getLoading = (state: State) => state.loading;
diff --git a/src/app/store/security/effects/notification.effects.ts b/src/app/store/security/effects/notification.effects.ts
new file mode 100644
index 0000000..9a5c341
--- /dev/null
+++ b/src/app/store/security/effects/notification.effects.ts
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as securityActions from '../security.actions';
+import {LoginSuccessAction} from '../security.actions';
+import {NotificationService, NotificationType} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/notification/notification.service';
+
+@Injectable()
+export class SecurityNotificationEffects {
+
+ @Effect({ dispatch: false })
+ loginSuccess$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN_SUCCESS)
+ .do((action: LoginSuccessAction) => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: `You are logged in with user '${action.payload.username}'`
+ }));
+
+ @Effect({ dispatch: false })
+ changePasswordSuccess$: Observable<Action> = this.actions$
+ .ofType(securityActions.CHANGE_PASSWORD_SUCCESS)
+ .do(() => this.notificationService.send({
+ type: NotificationType.MESSAGE,
+ message: 'Your password has been changed'
+ }));
+
+ constructor(private actions$: Actions, private notificationService: NotificationService) {}
+}
+
diff --git a/src/app/store/security/effects/route.effects.ts b/src/app/store/security/effects/route.effects.ts
new file mode 100644
index 0000000..1adae99
--- /dev/null
+++ b/src/app/store/security/effects/route.effects.ts
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Router} from '@angular/router';
+import {Observable} from 'rxjs/Observable';
+import {Action} from '@ngrx/store';
+import * as securityActions from '../security.actions';
+
+@Injectable()
+export class SecurityRouteEffects {
+
+ @Effect({ dispatch: false })
+ loginSuccess$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN_SUCCESS)
+ .do((payload) => this.router.navigate(['/']));
+
+ @Effect({ dispatch: false })
+ logoutSuccess$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGOUT_SUCCESS)
+ .do((payload) => this.router.navigate(['/login']));
+
+ @Effect({ dispatch: false })
+ passwordChangeSuccess$: Observable<Action> = this.actions$
+ .ofType(securityActions.CHANGE_PASSWORD_SUCCESS)
+ .do((payload) => this.router.navigate(['/']));
+
+ constructor(private actions$: Actions, private router: Router) { }
+}
diff --git a/src/app/store/security/effects/service.effects.spec.ts b/src/app/store/security/effects/service.effects.spec.ts
new file mode 100644
index 0000000..b698133
--- /dev/null
+++ b/src/app/store/security/effects/service.effects.spec.ts
@@ -0,0 +1,432 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {fakeAsync, TestBed, tick} from '@angular/core/testing';
+import {EffectsRunner, EffectsTestingModule} from '@ngrx/effects/testing';
+import {SecurityApiEffects} from './service.effects';
+import {Observable} from 'rxjs/Observable';
+import {IdentityService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/identity.service';
+import {AuthenticationService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authn/authentication.service';
+import {
+ ChangePasswordAction,
+ ChangePasswordSuccessAction,
+ LoginAction,
+ LoginSuccessAction,
+ LogoutAction,
+ LogoutSuccessAction,
+ PermissionUpdateSuccessAction,
+ RefreshAccessTokenAction,
+ RefreshAccessTokenStartTimerAction,
+ RefreshAccessTokenSuccessAction,
+ RefreshTokenStartTimerAction
+} from '../security.actions';
+import {PermittableGroupIdMapper} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/permittable-group-id-mapper';
+import {Store} from '@ngrx/store';
+import {FimsPermission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/fims-permission.model';
+import {Permission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/permission.model';
+import {IdentityPermittableGroupIds} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/permittable-group-ids.model';
+import {mockAuthentication} from '../testing/authentication.mock';
+
+describe('Security Api Effects', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ EffectsTestingModule
+ ],
+ providers: [
+ SecurityApiEffects,
+ PermittableGroupIdMapper,
+ { provide: 'tenantId', useValue: 'test' },
+ { provide: 'tokenExpiryBuffer', useValue: 5000 },
+ {
+ provide: Store,
+ useValue: jasmine.createSpyObj('store', ['select'])
+ },
+ {
+ provide: AuthenticationService,
+ useValue: jasmine.createSpyObj('authenticationService', ['login', 'getUserPermissions', 'logout', 'refreshAccessToken'])
+ },
+ {
+ provide: IdentityService,
+ useValue: jasmine.createSpyObj('identityService', ['changePassword'])
+ }
+ ]
+ });
+
+ });
+
+ describe('login$', () => {
+
+ function setup(params?: {loginReturnValue: any}) {
+ const authenticationService = TestBed.get(AuthenticationService);
+ if (params) {
+ authenticationService.login.and.returnValue(params.loginReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return a new LoginSuccessAction with Authentication', fakeAsync(() => {
+ const authentication = mockAuthentication();
+
+ const { runner, securityEffects } = setup({ loginReturnValue: Observable.of(authentication) });
+
+ const expectedResult = new LoginSuccessAction({
+ username: 'test',
+ tenant: 'test',
+ authentication
+ });
+
+ runner.queue(new LoginAction({
+ tenant: 'test',
+ username: 'test',
+ password: ''
+ }));
+
+ let result = null;
+ securityEffects.login$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+
+ });
+
+ describe('loadPermissions$', () => {
+
+ function setup(params?: {loadPermissionReturnValue: any}) {
+ const authenticationService = TestBed.get(AuthenticationService);
+ if (params) {
+ authenticationService.getUserPermissions.and.returnValue(params.loadPermissionReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return a new PermissionUpdateSuccessAction with FimsPermissions', fakeAsync(() => {
+ const authentication = mockAuthentication();
+
+ const identityPermissions: Permission[] = [
+ { permittableEndpointGroupIdentifier: IdentityPermittableGroupIds.SELF_MANAGEMENT, allowedOperations: ['CHANGE']}
+ ];
+
+ const fimsPermissions: FimsPermission[] = [
+ { id: 'identity_self', accessLevel: 'CHANGE' }
+ ];
+
+ const { runner, securityEffects } = setup({ loadPermissionReturnValue: Observable.of(identityPermissions) });
+
+ const expectedResult = new PermissionUpdateSuccessAction(fimsPermissions);
+
+ runner.queue(new LoginSuccessAction({
+ tenant: 'test',
+ username: 'test',
+ authentication
+ }));
+
+ let result = null;
+ securityEffects.loadPermissions$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+
+ });
+
+ describe('startRefreshTokenTimer$', () => {
+
+ function setup() {
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects),
+ tokenExpiryBuffer: TestBed.get('tokenExpiryBuffer')
+ };
+ }
+
+ it('should start refresh token timer up on LoginSuccessAction', fakeAsync(() => {
+ const { runner, securityEffects, tokenExpiryBuffer } = setup();
+
+ const authentication = mockAuthentication();
+
+ const refreshTokenMillies = new Date(authentication.refreshTokenExpiration).getTime();
+
+ const expectedResult = new RefreshTokenStartTimerAction(new Date(refreshTokenMillies - tokenExpiryBuffer));
+
+ runner.queue(new LoginSuccessAction({
+ tenant: 'test',
+ username: 'test',
+ authentication
+ }));
+
+ let result = null;
+ securityEffects.startRefreshTokenTimer$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('logout$', () => {
+
+ function setup(params?: { logoutReturnValue: any }) {
+ const authenticationService = TestBed.get(AuthenticationService);
+
+ if (params) {
+ authenticationService.logout.and.returnValue(params.logoutReturnValue);
+ }
+
+ const store = TestBed.get(Store);
+ store.select.and.returnValue(Observable.of({
+ username: 'test',
+ tenant: 'test',
+ authentication: mockAuthentication()
+ }));
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return a new LogoutSuccessAction when logout successful', fakeAsync(() => {
+ const { runner, securityEffects } = setup({ logoutReturnValue: Observable.of({}) });
+
+ const expectedResult = new LogoutSuccessAction();
+
+ runner.queue(new LogoutAction());
+
+ let result = null;
+ securityEffects.logout$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+
+ it('should return a new LogoutSuccessAction when logout not successful', fakeAsync(() => {
+ const { runner, securityEffects } = setup({ logoutReturnValue: Observable.throw(new Error()) });
+
+ const expectedResult = new LogoutSuccessAction();
+
+ runner.queue(new LogoutAction());
+
+ let result = null;
+ securityEffects.logout$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('refreshToken$', () => {
+ function setup(params?: { refreshAccessTokenReturnValue: any }) {
+ const authenticationService = TestBed.get(AuthenticationService);
+
+ if (params) {
+ authenticationService.refreshAccessToken.and.returnValue(params.refreshAccessTokenReturnValue);
+ }
+
+ const store = TestBed.get(Store);
+ store.select.and.returnValue(Observable.of({
+ username: 'test',
+ tenant: 'test',
+ authentication: mockAuthentication()
+ }));
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return new RefreshTokenSuccessAction when successful', fakeAsync(() => {
+ const authentication = mockAuthentication();
+
+ const { runner, securityEffects } = setup({ refreshAccessTokenReturnValue: Observable.of(authentication) });
+
+
+ const expectedResult = new RefreshAccessTokenSuccessAction(authentication);
+
+ runner.queue(new RefreshAccessTokenAction());
+
+ let result = null;
+ securityEffects.refreshToken$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('startAccessTokenRefreshTimerAfterRefresh$', () => {
+ function setup() {
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects),
+ tokenExpiryBuffer: TestBed.get('tokenExpiryBuffer')
+ };
+ }
+
+ it('should return new RefreshAccessTokenStartTimerAction', fakeAsync(() => {
+ const authentication = mockAuthentication();
+
+ const { runner, securityEffects, tokenExpiryBuffer } = setup();
+
+ const accessTokenMillies = new Date(authentication.accessTokenExpiration).getTime();
+
+ const expectedResult = new RefreshAccessTokenStartTimerAction(new Date(accessTokenMillies - tokenExpiryBuffer));
+
+ runner.queue(new RefreshAccessTokenSuccessAction(authentication));
+
+ let result = null;
+ securityEffects.startAccessTokenRefreshTimerAfterRefresh$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('startAccessTokenRefreshTimerAfterLogin$', () => {
+ function setup() {
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects),
+ tokenExpiryBuffer: TestBed.get('tokenExpiryBuffer')
+ };
+ }
+
+ it('should return new RefreshAccessTokenStartTimerAction', fakeAsync(() => {
+ const authentication = mockAuthentication();
+
+ const { runner, securityEffects, tokenExpiryBuffer } = setup();
+
+ const accessTokenMillies = new Date(authentication.accessTokenExpiration).getTime();
+
+ const expectedResult = new RefreshAccessTokenStartTimerAction(new Date(accessTokenMillies - tokenExpiryBuffer));
+
+ runner.queue(new LoginSuccessAction({
+ tenant: 'test',
+ username: 'test',
+ authentication
+ }));
+
+ let result = null;
+ securityEffects.startAccessTokenRefreshTimerAfterLogin$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('refreshAccessTokenStartTimer$', () => {
+ function setup() {
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return new RefreshAccessTokenAction when time is due', fakeAsync(() => {
+ const { runner, securityEffects } = setup();
+
+ const dueDate = new Date(Date.now() + 500);
+
+ const expectedResult = new RefreshAccessTokenAction();
+
+ runner.queue(new RefreshAccessTokenStartTimerAction(dueDate));
+
+ let result = null;
+ securityEffects.refreshAccessTokenStartTimer$.subscribe(_result => result = _result);
+
+ tick();
+
+ expect(result).toBe(null);
+
+ tick(500);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('refreshTokenStartTimer$', () => {
+
+ function setup() {
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return new LogoutAction when time is due', fakeAsync(() => {
+ const { runner, securityEffects } = setup();
+
+ const dueDate = new Date(Date.now() + 500);
+
+ const expectedResult = new LogoutAction();
+
+ runner.queue(new RefreshTokenStartTimerAction(dueDate));
+
+ let result = null;
+ securityEffects.refreshTokenStartTimer$.subscribe(_result => result = _result);
+
+ tick();
+
+ expect(result).toBe(null);
+
+ tick(500);
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+
+ describe('changePassword$', () => {
+
+ function setup(params?: { changePasswordReturnValue: any }) {
+ const identityService = TestBed.get(IdentityService);
+ if (params) {
+ identityService.changePassword.and.returnValue(params.changePasswordReturnValue);
+ }
+
+ return {
+ runner: TestBed.get(EffectsRunner),
+ securityEffects: TestBed.get(SecurityApiEffects)
+ };
+ }
+
+ it('should return new ChangePasswordSuccessAction when password is changed successfully', fakeAsync(() => {
+ const { runner, securityEffects } = setup({ changePasswordReturnValue: Observable.of({})});
+
+ const expectedResult = new ChangePasswordSuccessAction();
+
+ runner.queue(new ChangePasswordAction({
+ username: '',
+ password: ''
+ }));
+
+ let result = null;
+ securityEffects.changePassword$.subscribe(_result => result = _result);
+
+ tick();
+ expect(result).toEqual(expectedResult);
+ }));
+ });
+});
diff --git a/src/app/store/security/effects/service.effects.ts b/src/app/store/security/effects/service.effects.ts
new file mode 100644
index 0000000..cb896ce
--- /dev/null
+++ b/src/app/store/security/effects/service.effects.ts
@@ -0,0 +1,167 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Inject, Injectable} from '@angular/core';
+import {Actions, Effect} from '@ngrx/effects';
+import {Observable} from 'rxjs/Observable';
+import {Action, Store} from '@ngrx/store';
+import {of} from 'rxjs/observable/of';
+import * as securityActions from '../security.actions';
+import {AuthenticationService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authn/authentication.service';
+import {PermissionId} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/permission-id.type';
+import {FimsPermission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/fims-permission.model';
+import {Permission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/permission.model';
+import {PermittableGroupIdMapper} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/permittable-group-id-mapper';
+import * as fromRoot from '../../index';
+import {IdentityService} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/identity.service';
+import {Password} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/password.model';
+
+@Injectable()
+export class SecurityApiEffects {
+
+ @Effect()
+ login$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN)
+ .map((action: securityActions.LoginAction) => action.payload)
+ .mergeMap(payload =>
+ this.authenticationService.login(payload.tenant, payload.username, payload.password)
+ .map(authentication => Object.assign({}, {
+ username: payload.username,
+ tenant: payload.tenant,
+ authentication: authentication
+ }))
+ .map(successPayload => new securityActions.LoginSuccessAction(successPayload))
+ .catch((error) => of(new securityActions.LoginFailAction(error)))
+ );
+
+ @Effect()
+ loadPermissions$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN_SUCCESS)
+ .map((action: securityActions.LoginSuccessAction) => action.payload)
+ .mergeMap(payload =>
+ this.fetchPermissions(payload.tenant, payload.username, payload.authentication.accessToken)
+ .map(permissions => new securityActions.PermissionUpdateSuccessAction(permissions))
+ .catch(error => of(new securityActions.PermissionUpdateFailAction(error)))
+ );
+
+ @Effect()
+ startRefreshTokenTimer$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN_SUCCESS)
+ .map((action: securityActions.LoginSuccessAction) => action.payload)
+ .map(payload => new Date(payload.authentication.refreshTokenExpiration).getTime())
+ .map(refreshTokenExpirationMillies => new Date(refreshTokenExpirationMillies - this.tokenExpiryBuffer))
+ .map(delay => new securityActions.RefreshTokenStartTimerAction(delay));
+
+ @Effect()
+ logout$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGOUT)
+ .mergeMap(() => this.store.select(fromRoot.getAuthenticationState).take(1))
+ .mergeMap(state =>
+ this.authenticationService.logout(state.tenant, state.username, state.authentication.accessToken)
+ .map(() => new securityActions.LogoutSuccessAction())
+ .catch((error) => of(new securityActions.LogoutSuccessAction()))
+ );
+
+ @Effect()
+ refreshToken$: Observable<Action> = this.actions$
+ .ofType(securityActions.REFRESH_ACCESS_TOKEN)
+ .mergeMap(() => this.store.select(fromRoot.getAuthenticationState).take(1))
+ .mergeMap(state =>
+ this.authenticationService.refreshAccessToken(state.tenant)
+ .map(authentication => new securityActions.RefreshAccessTokenSuccessAction(authentication))
+ .catch((error) => of(new securityActions.RefreshAccessTokenFailAction(error)))
+ );
+
+ @Effect()
+ startAccessTokenRefreshTimerAfterLogin$: Observable<Action> = this.actions$
+ .ofType(securityActions.LOGIN_SUCCESS)
+ .map((action: securityActions.LoginSuccessAction) => action.payload)
+ .map(payload => new Date(payload.authentication.accessTokenExpiration).getTime())
+ .map(accessTokenExpirationMillies => new Date(accessTokenExpirationMillies - this.tokenExpiryBuffer))
+ .map(dueTime => new securityActions.RefreshAccessTokenStartTimerAction(dueTime));
+
+ @Effect()
+ startAccessTokenRefreshTimerAfterRefresh$: Observable<Action> = this.actions$
+ .ofType(securityActions.REFRESH_ACCESS_TOKEN_SUCCESS)
+ .map((action: securityActions.RefreshAccessTokenSuccessAction) => action.payload)
+ .map(payload => new Date(payload.accessTokenExpiration).getTime())
+ .map(accessTokenExpirationMillies => new Date(accessTokenExpirationMillies - this.tokenExpiryBuffer))
+ .map(dueTime => new securityActions.RefreshAccessTokenStartTimerAction(dueTime));
+
+ @Effect()
+ refreshAccessTokenStartTimer$: Observable<Action> = this.actions$
+ .ofType(securityActions.REFRESH_ACCESS_TOKEN_START_TIMER)
+ .map((action: securityActions.RefreshAccessTokenStartTimerAction) => action.payload)
+ .mergeMap(dueTime =>
+ Observable.timer(dueTime)
+ .switchMap(() => of(new securityActions.RefreshAccessTokenAction()))
+ );
+
+ @Effect()
+ refreshTokenStartTimer$: Observable<Action> = this.actions$
+ .ofType(securityActions.REFRESH_TOKEN_START_TIMER)
+ .map((action: securityActions.RefreshTokenStartTimerAction) => action.payload)
+ .mergeMap(dueTime =>
+ Observable.timer(dueTime)
+ .switchMap(() => of(new securityActions.LogoutAction()))
+ );
+
+ @Effect()
+ changePassword$: Observable<Action> = this.actions$
+ .ofType(securityActions.CHANGE_PASSWORD)
+ .map((action: securityActions.ChangePasswordAction) => action.payload)
+ .mergeMap(payload =>
+ this.identityService.changePassword(payload.username, new Password(payload.password))
+ .map(() => new securityActions.ChangePasswordSuccessAction())
+ .catch(error => of(new securityActions.ChangePasswordFailAction(error)))
+ );
+
+ @Effect()
+ logoutOnPasswordChange$: Observable<Action> = this.actions$
+ .ofType(securityActions.CHANGE_PASSWORD_SUCCESS)
+ .mergeMap(() => Observable.of(new securityActions.LogoutAction()));
+
+ private fetchPermissions(tenantId: string, username: string, accessToken: string): Observable<FimsPermission[]> {
+ return this.authenticationService.getUserPermissions(tenantId, username, accessToken)
+ .flatMap((permissions: Permission[]) => Observable.from(permissions))
+ .map((permission: Permission) => this.mapPermissions(permission))
+ .reduce((acc: FimsPermission[], permissions: FimsPermission[]) => acc.concat(permissions), []);
+ }
+
+ private mapPermissions(permission: Permission): FimsPermission[] {
+ const result: FimsPermission[] = [];
+ const descriptor = this.idMapper.map(permission.permittableEndpointGroupIdentifier);
+
+ if (descriptor) {
+ const internalKey: PermissionId = descriptor.id;
+
+ for (const operation of permission.allowedOperations){
+ result.push({
+ id: internalKey,
+ accessLevel: operation
+ });
+ }
+ }
+
+ return result;
+ }
+
+ constructor(private actions$: Actions, private identityService: IdentityService, private authenticationService: AuthenticationService,
+ private idMapper: PermittableGroupIdMapper, @Inject('tokenExpiryBuffer') private tokenExpiryBuffer: number,
+ private store: Store<fromRoot.State>) {}
+}
diff --git a/src/app/store/security/security.actions.ts b/src/app/store/security/security.actions.ts
new file mode 100644
index 0000000..71f5660
--- /dev/null
+++ b/src/app/store/security/security.actions.ts
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Action} from '@ngrx/store';
+import {type} from '../util';
+import {Authentication} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/authentication.model';
+import {FimsPermission} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/security/authz/fims-permission.model';
+
+export const LOGIN = type('[Security] Login');
+export const LOGIN_SUCCESS = type('[Security] Login Success');
+export const LOGIN_FAIL = type('[Security] Login Fail');
+export const LOGOUT = type('[Security] Logout');
+export const LOGOUT_SUCCESS = type('[Security] Logout Success');
+
+export const REFRESH_TOKEN_START_TIMER = type('[Security] Refresh Token Start Timer');
+
+export const REFRESH_ACCESS_TOKEN_START_TIMER = type('[Security] Refresh Access Token Start Timer');
+export const REFRESH_ACCESS_TOKEN = type('[Security] Refresh Access Token');
+export const REFRESH_ACCESS_TOKEN_SUCCESS = type('[Security] Refresh Access Token Success');
+export const REFRESH_ACCESS_TOKEN_FAIL = type('[Security] Refresh Access Token Fail');
+
+export const PERMISSIONS_UPDATE_SUCCESS = type('[Security] Permission Update Success');
+export const PERMISSIONS_UPDATE_FAIL = type('[Security] Permission Update Fail');
+
+export const CHANGE_PASSWORD = type('[Security] Change Password');
+export const CHANGE_PASSWORD_SUCCESS = type('[Security] Change Password Success');
+export const CHANGE_PASSWORD_FAIL = type('[Security] Change Password Fail');
+
+export interface LoginPayload {
+ username: string;
+ password: string;
+ tenant: string;
+}
+
+export interface LoginSuccessPayload {
+ username: string;
+ tenant: string;
+ authentication: Authentication;
+}
+
+export interface ChangePasswordPayload {
+ username: string;
+ password: string;
+}
+
+export class LoginAction implements Action {
+ readonly type = LOGIN;
+
+ constructor(public payload: LoginPayload) { }
+}
+
+export class LoginSuccessAction implements Action {
+ readonly type = LOGIN_SUCCESS;
+
+ constructor(public payload: LoginSuccessPayload) { }
+}
+
+export class LoginFailAction implements Action {
+ readonly type = LOGIN_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class LogoutAction implements Action {
+ readonly type = LOGOUT;
+
+ constructor() { }
+}
+
+export class LogoutSuccessAction implements Action {
+ readonly type = LOGOUT_SUCCESS;
+
+ constructor() { }
+}
+
+export class RefreshTokenStartTimerAction implements Action {
+ readonly type = REFRESH_TOKEN_START_TIMER;
+
+ constructor(public payload: Date) { }
+}
+
+export class RefreshAccessTokenStartTimerAction implements Action {
+ readonly type = REFRESH_ACCESS_TOKEN_START_TIMER;
+
+ constructor(public payload: Date) { }
+}
+
+export class RefreshAccessTokenAction implements Action {
+ readonly type = REFRESH_ACCESS_TOKEN;
+
+ constructor() { }
+}
+
+export class RefreshAccessTokenSuccessAction implements Action {
+ readonly type = REFRESH_ACCESS_TOKEN_SUCCESS;
+
+ constructor(public payload: Authentication) { }
+}
+
+export class RefreshAccessTokenFailAction implements Action {
+ readonly type = REFRESH_ACCESS_TOKEN_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class PermissionUpdateSuccessAction implements Action {
+ readonly type = PERMISSIONS_UPDATE_SUCCESS;
+
+ constructor(public payload: FimsPermission[]) { }
+}
+
+export class PermissionUpdateFailAction implements Action {
+ readonly type = PERMISSIONS_UPDATE_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export class ChangePasswordAction implements Action {
+ readonly type = CHANGE_PASSWORD;
+
+ constructor(public payload: ChangePasswordPayload) { }
+}
+
+export class ChangePasswordSuccessAction implements Action {
+ readonly type = CHANGE_PASSWORD_SUCCESS;
+
+ constructor() { }
+}
+
+export class ChangePasswordFailAction implements Action {
+ readonly type = CHANGE_PASSWORD_FAIL;
+
+ constructor(public payload: Error) { }
+}
+
+export type Actions
+ = LoginAction
+ | LoginSuccessAction
+ | LoginFailAction
+ | LogoutAction
+ | LogoutSuccessAction
+ | RefreshTokenStartTimerAction
+ | RefreshAccessTokenStartTimerAction
+ | RefreshAccessTokenAction
+ | RefreshAccessTokenSuccessAction
+ | RefreshAccessTokenFailAction
+ | PermissionUpdateSuccessAction
+ | PermissionUpdateFailAction
+ | ChangePasswordAction
+ | ChangePasswordSuccessAction
+ | ChangePasswordFailAction;
diff --git a/src/app/store/security/testing/authentication.mock.ts b/src/app/store/security/testing/authentication.mock.ts
new file mode 100644
index 0000000..b816792
--- /dev/null
+++ b/src/app/store/security/testing/authentication.mock.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import {Authentication} from '/home/pembe/Desktop/fineract-cn-web-app/src/app/sevices/identity/domain/authentication.model';
+
+export function mockAuthentication(): Authentication {
+ return {
+ accessToken: '',
+ accessTokenExpiration: new Date().toISOString(),
+ refreshTokenExpiration: new Date().toISOString(),
+ passwordExpiration: '',
+ tokenType: ''
+ };
+}
diff --git a/src/app/store/util.ts b/src/app/store/util.ts
new file mode 100644
index 0000000..3d7e9b4
--- /dev/null
+++ b/src/app/store/util.ts
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+const typeCache: { [label: string]: boolean } = {};
+
+export function type<T>(label: T | ''): T {
+ if (typeCache[<string>label]) {
+ throw new Error(`Action type "${label}" is not unique"`);
+ }
+
+ typeCache[<string>label] = true;
+
+ return <T>label;
+}
diff --git a/src/main.ts b/src/main.ts
index 71fac2b..7042750 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -4,10 +4,23 @@
import 'material-design-icons/iconfont/material-icons.css'
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
+import './rxjs.imports';
if (environment.production) {
enableProdMode();
}
-platformBrowserDynamic().bootstrapModule(AppModule)
- .catch(err => console.log(err));
+platformBrowserDynamic([
+ { provide: 'tokenExpiryBuffer', useValue: 1000 * 60},
+ { provide: 'cacheExpiry', useValue: 10000},
+ { provide: 'identityBaseUrl', useValue: '/identity/v1' },
+ { provide: 'officeBaseUrl', useValue: '/api/office/v1' },
+ { provide: 'customerBaseUrl', useValue: '/api/customer/v1' },
+ { provide: 'accountingBaseUrl', useValue: '/api/accounting/v1' },
+ { provide: 'portfolioBaseUrl', useValue: '/api/portfolio/v1' },
+ { provide: 'depositAccountBaseUrl', useValue: '/api/deposit/v1' },
+ { provide: 'tellerBaseUrl', useValue: '/api/teller/v1' },
+ { provide: 'reportingBaseUrl', useValue: '/api/reporting/v1' },
+ { provide: 'chequeBaseUrl', useValue: '/api/cheques/v1' },
+ { provide: 'payrollBaseUrl', useValue: '/api/payroll/v1' }
+]).bootstrapModule(AppModule);
\ No newline at end of file
diff --git a/src/polyfills.ts b/src/polyfills.ts
index af84770..eb16e86 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -19,20 +19,25 @@
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
-// import 'core-js/es6/symbol';
-// import 'core-js/es6/object';
-// import 'core-js/es6/function';
-// import 'core-js/es6/parse-int';
-// import 'core-js/es6/parse-float';
-// import 'core-js/es6/number';
-// import 'core-js/es6/math';
-// import 'core-js/es6/string';
-// import 'core-js/es6/date';
-// import 'core-js/es6/array';
-// import 'core-js/es6/regexp';
-// import 'core-js/es6/map';
-// import 'core-js/es6/weak-map';
-// import 'core-js/es6/set';
+ import 'core-js/es6/symbol';
+ import 'core-js/es6/object';
+ import 'core-js/es6/function';
+ import 'core-js/es6/parse-int';
+ import 'core-js/es6/parse-float';
+ import 'core-js/es6/number';
+ import 'core-js/es6/math';
+ import 'core-js/es6/string';
+ import 'core-js/es6/date';
+ import 'core-js/es6/array';
+ import 'core-js/es6/regexp';
+ import 'core-js/es6/map';
+ import 'core-js/es6/weak-map';
+ import 'core-js/es6/set';
+ import 'core-js/es6/reflect';
+
+import 'core-js/es7/reflect';
+
+
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
diff --git a/src/rxjs.imports.ts b/src/rxjs.imports.ts
new file mode 100644
index 0000000..5992076
--- /dev/null
+++ b/src/rxjs.imports.ts
@@ -0,0 +1,23 @@
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/switchMap';
+import 'rxjs/add/operator/mergeMap';
+import 'rxjs/add/operator/take';
+import 'rxjs/add/operator/takeUntil';
+import 'rxjs/add/operator/finally';
+import 'rxjs/add/operator/filter';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/operator/do';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/startWith';
+import 'rxjs/add/operator/reduce';
+import 'rxjs/add/operator/pluck';
+import 'rxjs/add/operator/skip';
+
+import 'rxjs/add/observable/combineLatest';
+import 'rxjs/add/observable/empty';
+import 'rxjs/add/observable/fromPromise';
+import 'rxjs/add/observable/from';
+import 'rxjs/add/observable/throw';
+import 'rxjs/add/observable/forkJoin';
+import 'rxjs/add/observable/timer';
diff --git a/tsconfig.json b/tsconfig.json
index a6c016b..2e2f5a9 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
+ "baseUrl": "src",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",